CI has no release-profile Windows smoke — the profile went out with the gnu triple, and the two profiles catch different arms #232

Closed
opened 2026-09-08 19:38:03 +02:00 by buildagent · 4 comments
Member

Found by external review of the #231 change, before it merged.

The gap

ci-windows.yml had two plugin smoke steps. After #231 it has one:

step profile blocking
master, before Plugin smoke … (native MSVC build) debug yes
master, before Plugin smoke … (windows-GNU, the shipped linkage) release continue-on-error
after #231 Plugin smoke … (MSVC, the SHIPPED CRT) debug yes

The MSVC leg was always debug — that did not change. What changed is that the only release-profile step in the workflow was deleted along with the gnu target it happened to be attached to. The profile went out with the triple, not because anyone decided the profile was expendable.

The deleted step's own comment argued the point that still stands:

--release, NOT debug. The archive that failed is release-profile, and a trap/stack fault is precisely the class that differs between profiles — a debug build could pass and would then be a false all-clear about the artifact we actually ship.

Why this is a real gap and not a duplicate

The two profiles catch different arms of the same step:

  • debug trips the stack-exhaustion arm sooner, because frames are larger — it is the stricter test of WORKER_SERVE_STACK_BYTES, which is the defect that step exists to catch;
  • release is where the setjmp/unwind arm differs, and that is the arm the whole #231 episode lived on.

Swapping one for the other loses an arm in either direction.

Severity: bounded, and the bound is worth stating

The shipped artifact is covered. release.yml's windows-archive-smoke executes the real release archive and its verdict gates publication; it has correctly refused to publish three times. This is not a repeat of #231's shape, which was "nothing ever executed the shipped bytes."

What is lost is time-to-signal: a release-only trap regression is not caught on the push that introduces it, and surfaces at tag time instead — the expensive place.

The gap is disclosed in ci-windows.yml rather than left to be noticed, with the named owner and the named cost.

Why the obvious cheap fix does not work

The natural proposal is to scope a release leg to one crate:

cargo build -p code-index-plugin-host --bin code-index-plugin-host --example release_smoke --release

on the ground that trap/timeout/stack are plugin-host properties. That fails immediately, and on an anti-vacuity guard rather than on anything real:

const SHIPPED_BINARIES: [&str; 4] = [
    "code-index", "code-index-daemon", "code-index-mcp", "code-index-plugin-host",
];

for stem in SHIPPED_BINARIES {
    if !p.is_file() {
        fail(name, "... is missing beside the worker, so the shipped set was not graded ...")

release_smoke does not only exercise the worker — it grades the stock-DLL import property across all four shipped binaries, the check that refuses vcruntime140.dll. A missing binary is a deliberate hard failure, because "I graded three of four and said nothing" is exactly what that check exists to refuse.

So a scoped build cannot be used as-is, and a release leg today means a full workspace release build: a second multi-gigabyte target directory on a runner that has already hit 92.8 GB and LNK1201, and which is the queue bottleneck for every Windows job.

What the fix actually needs

A mode on release_smoke that grades the worker arms (trap, timeout, stack exhaustion) without claiming to have graded the shipped set — so a release leg can be scoped to one crate.

And that mode carries its own hazard, which is the real design work: the moment it exists, someone can reach for it in the ARCHIVE smoke and silently stop grading three binaries. So the mode must be refused wherever the shipped set is claimed, not merely documented as "don't use it there". That is a design, not a flag.

Acceptance:

  • a release-profile Windows smoke exists and grades trap/timeout/stack on the shipped CRT;
  • the debug leg stays — it is the stricter stack test, not a duplicate;
  • the worker-only mode is structurally unusable from the archive smoke, graded by a mutation that tries it and must go RED;
  • the disk cost is measured, not estimated, before it lands on the bottleneck runner.

Related: #231.

Found by external review of the #231 change, before it merged. ## The gap `ci-windows.yml` had **two** plugin smoke steps. After #231 it has one: | | step | profile | blocking | |---|---|---|---| | master, before | `Plugin smoke … (native MSVC build)` | **debug** | yes | | master, before | `Plugin smoke … (windows-GNU, the shipped linkage)` | **release** | `continue-on-error` | | after #231 | `Plugin smoke … (MSVC, the SHIPPED CRT)` | **debug** | yes | The MSVC leg was *always* debug — that did not change. What changed is that the only **release-profile** step in the workflow was deleted along with the gnu target it happened to be attached to. **The profile went out with the triple**, not because anyone decided the profile was expendable. The deleted step's own comment argued the point that still stands: > `--release`, NOT debug. The archive that failed is release-profile, and a trap/stack fault is precisely the class that differs between profiles — a debug build could pass and would then be a false all-clear about the artifact we actually ship. ## Why this is a real gap and not a duplicate The two profiles catch **different arms of the same step**: * **debug** trips the stack-exhaustion arm *sooner*, because frames are larger — it is the stricter test of `WORKER_SERVE_STACK_BYTES`, which is the defect that step exists to catch; * **release** is where the `setjmp`/unwind arm differs, and that is the arm the whole #231 episode lived on. Swapping one for the other loses an arm in either direction. ## Severity: bounded, and the bound is worth stating The **shipped artifact is covered.** `release.yml`'s `windows-archive-smoke` executes the real release archive and its verdict gates publication; it has correctly refused to publish three times. This is not a repeat of #231's shape, which was *"nothing ever executed the shipped bytes."* What is lost is **time-to-signal**: a release-only trap regression is not caught on the push that introduces it, and surfaces at tag time instead — the expensive place. The gap is disclosed in `ci-windows.yml` rather than left to be noticed, with the named owner and the named cost. ## Why the obvious cheap fix does not work The natural proposal is to scope a release leg to one crate: ``` cargo build -p code-index-plugin-host --bin code-index-plugin-host --example release_smoke --release ``` on the ground that trap/timeout/stack are plugin-host properties. **That fails immediately**, and on an anti-vacuity guard rather than on anything real: ```rust const SHIPPED_BINARIES: [&str; 4] = [ "code-index", "code-index-daemon", "code-index-mcp", "code-index-plugin-host", ]; for stem in SHIPPED_BINARIES { if !p.is_file() { fail(name, "... is missing beside the worker, so the shipped set was not graded ...") ``` `release_smoke` does not only exercise the worker — it grades the **stock-DLL import property across all four shipped binaries**, the check that refuses `vcruntime140.dll`. A missing binary is a deliberate hard failure, because *"I graded three of four and said nothing"* is exactly what that check exists to refuse. So a scoped build cannot be used as-is, and a release leg today means a full workspace release build: a second multi-gigabyte target directory on a runner that has already hit 92.8 GB and LNK1201, and which is the queue bottleneck for every Windows job. ## What the fix actually needs A mode on `release_smoke` that grades the **worker arms** (trap, timeout, stack exhaustion) *without* claiming to have graded the shipped set — so a release leg can be scoped to one crate. **And that mode carries its own hazard, which is the real design work:** the moment it exists, someone can reach for it in the ARCHIVE smoke and silently stop grading three binaries. So the mode must be **refused wherever the shipped set is claimed**, not merely documented as "don't use it there". That is a design, not a flag. Acceptance: * a release-profile Windows smoke exists and grades trap/timeout/stack on the shipped CRT; * the debug leg stays — it is the stricter stack test, not a duplicate; * the worker-only mode is structurally unusable from the archive smoke, graded by a mutation that tries it and must go RED; * the disk cost is measured, not estimated, before it lands on the bottleneck runner. Related: #231.
Author
Member

Two corrections/additions from the reviewer, both worth keeping

1. The scoped-build proposal was not naive — the constraint is NEW in the #231 branch.

ea74dbc  crates/plugin-host/examples/release_smoke.rs   SHIPPED_BINARIES: ABSENT
1c9e002  same file, :278                                SHIPPED_BINARIES: [&str; 4]
         same file, :414                                the hard-fail loop over all four

At the revision the reviewer had, release_smoke took $worker and graded the worker. The four-binary stock-DLL check arrived with #231. So "scope the release leg to one crate" was correct against the code in front of them and wrong against the code being written — the ordinary hazard of reviewing a moving branch, recorded here so the next reader does not mistake it for a careless suggestion.

There is a neat irony worth preserving: the check that blocks the optimisation is the one that reviewer's own finding caused. The four-binary loop exists to refuse vcruntime140.dll across the shipped set — the +crt-static work — and that is precisely what makes a one-crate release build impossible. As they put it: a gate that only constrains other people is not a gate.

2. The design for the worker-only mode already exists in this tree — copy it, don't invent it.

The hazard named above (a worker-arms mode leaking into the archive smoke and silently grading three of four binaries) is structurally the same problem pe_linkage solved in #231, and the solution is the same shape:

Two axes, asserted independently, with the weaker unable to satisfy the stronger's claim.

Concretely: a worker-arms mode and a shipped-set mode are two axes. The archive smoke must ASSERT the shipped set rather than merely not-disable it, so a mode that grades fewer binaries cannot silently satisfy a leg that claims all four. That is the same rule as a CRT verdict cannot be inferred from a toolchain marker — support::artifact already refuses when markers are absent on both sides, or present on both, rather than guessing.

That pattern already carries mutation coverage in artifact_linkage.rs (arms A6–A9). Whoever picks this up should model the two smoke modes on it rather than designing a fresh flag.

### Two corrections/additions from the reviewer, both worth keeping **1. The scoped-build proposal was not naive — the constraint is NEW in the #231 branch.** ``` ea74dbc crates/plugin-host/examples/release_smoke.rs SHIPPED_BINARIES: ABSENT 1c9e002 same file, :278 SHIPPED_BINARIES: [&str; 4] same file, :414 the hard-fail loop over all four ``` At the revision the reviewer had, `release_smoke` took `$worker` and graded the worker. The four-binary stock-DLL check arrived with #231. So "scope the release leg to one crate" was correct against the code in front of them and wrong against the code being written — the ordinary hazard of reviewing a moving branch, recorded here so the next reader does not mistake it for a careless suggestion. There is a neat irony worth preserving: **the check that blocks the optimisation is the one that reviewer's own finding caused.** The four-binary loop exists to refuse `vcruntime140.dll` across the shipped set — the `+crt-static` work — and that is precisely what makes a one-crate release build impossible. As they put it: *a gate that only constrains other people is not a gate.* **2. The design for the worker-only mode already exists in this tree — copy it, don't invent it.** The hazard named above (a worker-arms mode leaking into the archive smoke and silently grading three of four binaries) is structurally the same problem `pe_linkage` solved in #231, and the solution is the same shape: > **Two axes, asserted independently, with the weaker unable to satisfy the stronger's claim.** Concretely: a worker-arms mode and a shipped-set mode are two axes. The archive smoke must **ASSERT the shipped set** rather than merely not-disable it, so a mode that grades fewer binaries cannot silently satisfy a leg that claims all four. That is the same rule as *a CRT verdict cannot be inferred from a toolchain marker* — `support::artifact` already refuses when markers are absent on both sides, or present on both, rather than guessing. That pattern already carries mutation coverage in `artifact_linkage.rs` (arms A6–A9). Whoever picks this up should model the two smoke modes on it rather than designing a fresh flag.
Author
Member

Correction: the gap is narrower than filed — the release path's smoke IS release-profile

The issue says CI has no release-profile Windows smoke and names release.yml's windows-archive-smoke as the owner. Both true. But "covered at tag time" understated what that job does, and v0.27.0's run makes it exact. Its own banner:

=== plugin smoke: x86_64-pc-windows-msvc (release, SHIPPED archive, static CRT) ===
  worker : unpacked\code-index-v0.27.0-windows-x86_64\code-index-plugin-host.exe

archive sha256 dbd81196a080ea87d5e7c4786fa548a73c0b84d6f478d86d51898218a48ad4ff
  matches its published sidecar

SMOKE artifact:      PASS - 10462208 bytes, msvc-linked, static CRT, executes
SMOKE stock-imports: PASS - 4 of 4 shipped binaries carry a PE import table,
                     and every DLL in it ships with Windows
SMOKE trap:          PASS - an honest guest produced facts, `unreachable` was
                     host.worker_trapped, and unbounded recursion trapped
                     instead of killing the worker
SMOKE RESULT:        PASS - 7 checks

So the late leg is release profile, on the published bytes, re-downloaded and verified against its own sidecar before unpacking — strictly stronger than the continue-on-error release leg that was deleted, which built its own binary rather than testing the shipped one.

What that means for this issue's severity. The gap is early-signal ONLY, and the thing that runs late is stronger than the thing that ran early. A release-only trap regression is still not caught on the push that introduces it — which is the real cost and the reason this stays open — but it cannot reach users, and the artifact that would carry it is executed before publication rather than after.

Restating the acceptance in that light: what a release-profile CI leg buys is time-to-signal, not coverage of the shipped artifact. That should be weighed against the disk cost on the bottleneck runner accordingly — it is a developer-experience improvement, not a safety fix, and it should not be sold as the latter when someone picks it up.

### Correction: the gap is narrower than filed — the release path's smoke IS release-profile The issue says CI has no release-profile Windows smoke and names `release.yml`'s `windows-archive-smoke` as the owner. Both true. But "covered at tag time" understated what that job does, and v0.27.0's run makes it exact. Its own banner: ``` === plugin smoke: x86_64-pc-windows-msvc (release, SHIPPED archive, static CRT) === worker : unpacked\code-index-v0.27.0-windows-x86_64\code-index-plugin-host.exe archive sha256 dbd81196a080ea87d5e7c4786fa548a73c0b84d6f478d86d51898218a48ad4ff matches its published sidecar SMOKE artifact: PASS - 10462208 bytes, msvc-linked, static CRT, executes SMOKE stock-imports: PASS - 4 of 4 shipped binaries carry a PE import table, and every DLL in it ships with Windows SMOKE trap: PASS - an honest guest produced facts, `unreachable` was host.worker_trapped, and unbounded recursion trapped instead of killing the worker SMOKE RESULT: PASS - 7 checks ``` So the late leg is **release profile, on the published bytes, re-downloaded and verified against its own sidecar before unpacking** — strictly stronger than the `continue-on-error` release leg that was deleted, which built its own binary rather than testing the shipped one. **What that means for this issue's severity.** The gap is early-signal ONLY, and the thing that runs late is stronger than the thing that ran early. A release-only trap regression is still not caught on the push that introduces it — which is the real cost and the reason this stays open — but it cannot reach users, and the artifact that would carry it is executed before publication rather than after. Restating the acceptance in that light: what a release-profile CI leg buys is **time-to-signal**, not coverage of the shipped artifact. That should be weighed against the disk cost on the bottleneck runner accordingly — it is a developer-experience improvement, not a safety fix, and it should not be sold as the latter when someone picks it up.
Author
Member

The blocker is gone: the runner volume is back and a full Windows job now completes. Recording the baseline this issue's acceptance asks for ("the disk cost is measured, not estimated, before it lands on the bottleneck runner"), from run 726 on bd1c4e2 — the first Windows run to reach the suite since 1d3228e.

DISK PRE-FLIGHT: drive C: 110 GB free of 511 GB (78% used, advisory); floor 40 GB, margin 60 GB.

=== DISK REPORT (drive C: 88 GB free) ===
    <run>\hostexecutor\target                      :     24,2 GB
    target (total)                                 :     25,5 GB
    .rustup                                        :      2,0 GB
    .cargo\registry                                :      0,5 GB
  accounted for 52,3 GB of 422 GB used on drive C.
=== reclaimed 0,0 GB; drive C: 88 GB free (was 88 GB) ===

So, as the DEBUG-only baseline:

free at pre-flight 110 GB
free at post-job 88 GB
consumed by this run 22 GB
target (total) after 25,5 GB
floor 40 GB
slack above the floor 48 GB

That is the number the release-profile leg has to fit inside. The three lines are still exactly as parked in ci-windows.yml:

cargo build --release -p code-index-plugin-host --bin code-index-plugin-host
cargo build --release -p code-index-plugin-host --example release_smoke
& $smoke $worker msvc static worker-only

What still has to be done properly, and it is not "add it and hope". The leg compiles the plugin-host graph under WINDOWS_RUSTFLAGS (the string release.yml's build-windows uses), so it caches a SECOND dependency graph. The honest sequence is: put the leg on a branch, dispatch ci-windows.yml at that ref, read the post-job DISK REPORT from that run, and compare target (total) against the 25,5 GB above. The delta is the measured cost. Merge only if it leaves comfortable room above the 40 GB floor — the runner is the queue bottleneck for every Windows job, so an eviction here costs every other job too.

A branch run and a master run sit in different concurrency groups (ci-windows-${{ github.ref }}), so the measurement does not cancel a master verdict — but they serialise on the one machine.

Also worth noting for whoever picks this up: run 726 was RED, on one test, and it was NOT a disk or profile problem — link_payload_scaling_e2e's per-link ceiling was measuring the temp directory's path length (#252). The disk pre-flight passed with 70 GB of margin.

**The blocker is gone: the runner volume is back and a full Windows job now completes.** Recording the baseline this issue's acceptance asks for ("the disk cost is measured, not estimated, before it lands on the bottleneck runner"), from run **726** on `bd1c4e2` — the first Windows run to reach the suite since `1d3228e`. ``` DISK PRE-FLIGHT: drive C: 110 GB free of 511 GB (78% used, advisory); floor 40 GB, margin 60 GB. === DISK REPORT (drive C: 88 GB free) === <run>\hostexecutor\target : 24,2 GB target (total) : 25,5 GB .rustup : 2,0 GB .cargo\registry : 0,5 GB accounted for 52,3 GB of 422 GB used on drive C. === reclaimed 0,0 GB; drive C: 88 GB free (was 88 GB) === ``` So, as the DEBUG-only baseline: | | | |---|---:| | free at pre-flight | 110 GB | | free at post-job | 88 GB | | consumed by this run | **22 GB** | | `target (total)` after | **25,5 GB** | | floor | 40 GB | | slack above the floor | **48 GB** | That is the number the release-profile leg has to fit inside. The three lines are still exactly as parked in `ci-windows.yml`: ``` cargo build --release -p code-index-plugin-host --bin code-index-plugin-host cargo build --release -p code-index-plugin-host --example release_smoke & $smoke $worker msvc static worker-only ``` **What still has to be done properly, and it is not "add it and hope".** The leg compiles the plugin-host graph under `WINDOWS_RUSTFLAGS` (the string `release.yml`'s `build-windows` uses), so it caches a SECOND dependency graph. The honest sequence is: put the leg on a branch, dispatch `ci-windows.yml` at that ref, read the post-job `DISK REPORT` from that run, and compare `target (total)` against the 25,5 GB above. The delta is the measured cost. Merge only if it leaves comfortable room above the 40 GB floor — the runner is the queue bottleneck for every Windows job, so an eviction here costs every other job too. A branch run and a master run sit in different `concurrency` groups (`ci-windows-${{ github.ref }}`), so the measurement does not cancel a master verdict — but they serialise on the one machine. Also worth noting for whoever picks this up: run 726 was RED, on one test, and it was NOT a disk or profile problem — `link_payload_scaling_e2e`'s per-link ceiling was measuring the temp directory's path length (#252). The disk pre-flight passed with 70 GB of margin.
Author
Member

Done in ee39fd1. All four acceptance criteria met, each verified rather than assumed.

The disk cost, measured on a branch before it landed

Run 731 on measure/232-release-smoke-disk, against runs 726/727/729 on master:

master (debug only) with this leg (731)
pre-flight free 110 GB 113 GB
post-job free 88 GB 90 GB
target\debug — 28,2 GB
target\release absent 1,0 GB
target (total) 25,5 GB 29,2 GB
wall clock 43:10 / 43:40 48:46

The leg costs 1,0 GB, and that is the from-scratch figure — the restored cache held no target\release at all, so this is the cost of building it rather than an incremental top-up. Scoping to one crate is what bought that; --release --workspace would have cached a second full dependency graph.

The 25,5 → 29,2 GB move in target (total) is mostly target\debug growing 25,5 → 28,2 across the commits between bd1c4e2 and here, which this leg did not cause.

The comparison is apples to apples: the cache key is hashFiles('**/Cargo.lock') and the branch does not touch Cargo.lock, so run 731 restored the same warm target/ the baseline runs wrote.

Against a threshold fixed before the number arrived — merge only if green and post-job free ≥ 60 GB (the 40 GB floor plus 20 GB of margin, because this runner bottlenecks every Windows job). 90 GB, so 50 GB of margin.

It also costs ~5–6 minutes of wall clock (~12%), which was not in the disk criterion and is stated rather than omitted. That is the price of moving release-profile discovery off the tag.

Acceptance, point by point

  • a release-profile Windows smoke exists and grades trap/timeout/stack on the shipped CRT — SMOKE RESULT: PASS (worker-only) - 6 checks against target\release\code-index-plugin-host.exe, built under the same -C target-feature=+crt-static string release.yml's build-windows uses. Stack is covered: check_traps's own doc says "THE RECURSION CASE IS THE ONE THIS STEP EXISTS FOR. Stack exhaustion inside a guest must be an ordinary trap."
  • the debug leg stays — unchanged and still stronger: SMOKE RESULT: PASS (shipped-set) - 7 checks. Debug frames are larger, so it remains the stricter WORKER_SERVE_STACK_BYTES test.
  • worker-only structurally unusable from the archive smoke, graded by a mutation that goes RED — scope_refusal runs before anything is graded, and the_weaker_scope_is_refused_where_the_shipped_set_is_present passes. Unchanged by this commit.
  • disk cost measured, not estimated, before it lands — above.

The gate that forbade this had to be rewritten, not relaxed

artifact_linkage::every_ci_plugin_smoke_asserts_the_shipped_set asserted no leg may declare worker-only at all, and its failure message named the release condition: "worker-only exists for a leg scoped to ONE crate, which no leg here is yet — see #232."

Simply permitting worker-only would have let any leg drop to the weaker claim, which is the drift the ban existed to prevent. It now grades the justification: a leg may claim worker-only only where its own step builds -p code-index-plugin-host and does not build the workspace. Structural, so a sixth leg tomorrow is graded by the same clause.

MUTATIONS RUN:

  • give this leg --workspace --bins and keep worker-only → RED, "claims worker-only but its step builds the workspace, so all four shipped binaries are present and the stronger shipped-set claim is available to it". The new rule constrains the leg it was written for.
  • flip the debug leg to worker-only → RED, same clause — the case the old blanket ban caught is still caught.

md5 verified changed then restored on each; control green. Anti-vacuity floor moved 4 → 5 with the population.

The honest residual: worker-only prints SMOKE stock-imports: NOT GRADED — … That claim belongs to a shipped-set run, so the release leg says plainly what it did not check rather than letting a pass read as a statement about all four binaries.

Done in `ee39fd1`. All four acceptance criteria met, each verified rather than assumed. ## The disk cost, measured on a branch before it landed Run **731** on `measure/232-release-smoke-disk`, against runs 726/727/729 on master: | | master (debug only) | with this leg (731) | |---|---:|---:| | pre-flight free | 110 GB | 113 GB | | post-job free | 88 GB | **90 GB** | | `target\debug` | — | 28,2 GB | | **`target\release`** | absent | **1,0 GB** | | `target (total)` | 25,5 GB | 29,2 GB | | wall clock | 43:10 / 43:40 | **48:46** | **The leg costs 1,0 GB**, and that is the from-scratch figure — the restored cache held no `target\release` at all, so this is the cost of building it rather than an incremental top-up. Scoping to one crate is what bought that; `--release --workspace` would have cached a second full dependency graph. The `25,5 → 29,2 GB` move in `target (total)` is mostly `target\debug` growing `25,5 → 28,2` across the commits between `bd1c4e2` and here, which this leg did not cause. **The comparison is apples to apples**: the cache key is `hashFiles('**/Cargo.lock')` and the branch does not touch `Cargo.lock`, so run 731 restored the same warm `target/` the baseline runs wrote. **Against a threshold fixed before the number arrived** — merge only if green and post-job free ≥ 60 GB (the 40 GB floor plus 20 GB of margin, because this runner bottlenecks every Windows job). 90 GB, so 50 GB of margin. **It also costs ~5–6 minutes of wall clock (~12%)**, which was not in the disk criterion and is stated rather than omitted. That is the price of moving release-profile discovery off the tag. ## Acceptance, point by point * **a release-profile Windows smoke exists and grades trap/timeout/stack on the shipped CRT** — `SMOKE RESULT: PASS (worker-only) - 6 checks against target\release\code-index-plugin-host.exe`, built under the same `-C target-feature=+crt-static` string `release.yml`'s `build-windows` uses. Stack is covered: `check_traps`'s own doc says *"THE RECURSION CASE IS THE ONE THIS STEP EXISTS FOR. Stack exhaustion inside a guest must be an ordinary trap."* * **the debug leg stays** — unchanged and still stronger: `SMOKE RESULT: PASS (shipped-set) - 7 checks`. Debug frames are larger, so it remains the stricter `WORKER_SERVE_STACK_BYTES` test. * **worker-only structurally unusable from the archive smoke, graded by a mutation that goes RED** — `scope_refusal` runs before anything is graded, and `the_weaker_scope_is_refused_where_the_shipped_set_is_present` passes. Unchanged by this commit. * **disk cost measured, not estimated, before it lands** — above. ## The gate that forbade this had to be rewritten, not relaxed `artifact_linkage::every_ci_plugin_smoke_asserts_the_shipped_set` asserted no leg may declare `worker-only` at all, and its failure message named the release condition: *"`worker-only` exists for a leg scoped to ONE crate, which no leg here is yet — see #232."* Simply permitting `worker-only` would have let **any** leg drop to the weaker claim, which is the drift the ban existed to prevent. It now grades the **justification**: a leg may claim `worker-only` only where its own step builds `-p code-index-plugin-host` and does **not** build the workspace. Structural, so a sixth leg tomorrow is graded by the same clause. MUTATIONS RUN: * give **this leg** `--workspace --bins` and keep `worker-only` → RED, *"claims `worker-only` but its step builds the workspace, so all four shipped binaries are present and the stronger `shipped-set` claim is available to it"*. The new rule constrains the leg it was written for. * flip the **debug** leg to `worker-only` → RED, same clause — the case the old blanket ban caught is still caught. md5 verified changed then restored on each; control green. Anti-vacuity floor moved 4 → 5 with the population. The honest residual: `worker-only` prints `SMOKE stock-imports: NOT GRADED — … That claim belongs to a `shipped-set` run`, so the release leg says plainly what it did not check rather than letting a pass read as a statement about all four binaries.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
h-dv/code-index#232
No description provided.