Two $minFreeGb assignments with different values, and the gate pinning them matches whole-file so it only ever sees the first — the Windows reclaim can never fire #193

Closed
opened 2026-09-06 12:53:28 +02:00 by buildagent · 1 comment
Member

Found during close-out verification and confirmed at source. This is why the Windows leg cannot recover on its own, and it is #178's class occurring inside the fix for #179.

Measured

.forgejo/workflows/ci-windows.yml:150   $minFreeGb = 40      <- pre-flight floor
.forgejo/workflows/ci-windows.yml:165   if ($freeGb -lt $minFreeGb) { ...FAILED... }

.forgejo/workflows/ci-windows.yml:456   $minFreeGb = 20      <- SECOND assignment, post-job reclaim
.forgejo/workflows/ci-windows.yml:563   if ($freeGb -ge 0 -and $freeGb -lt $minFreeGb) { ...reclaim... }

The gate meant to pin these:

// crates/indexer/tests/ci_disk_preflight.rs:482
for (name, want) in [("$minFreeGb", MIN_FREE_GB), ("$marginPct", MARGIN_PCT)] {

It searches the whole file for $minFreeGb, finds = 40 at line 150, and is satisfied. The = 20 at line 456 is invisible to it.

The consequence, observed twice

The pre-flight refuses below 40 GB. The reclaim fires only below 20 GB. Both dispatched Windows runs ended at exactly 20 GB free — and 20 < 20 is false, so the reclaim did not fire, and by construction never can from that state. The volume sits in a band where the job refuses to start and the automated recovery declines to act, which is the worst of both.

That also explains a result previously attributed to something else: the earlier report that "the prune found 0.0 GB stale" is true but not the whole story — even had there been stale bytes, the threshold that gates the reclaim was never crossed.

Two defects, and they need separating

  1. The gate is blind to a second assignment. Same shape as #180: a whole-file contains satisfied by the first occurrence, where the hazard lives in the second. Eleven population gates were audited this week and eleven were confirmed vulnerable; this is the twelfth, in a file written after that audit. Fix it the way the others were fixed — grade every assignment of the name, not the first — and reuse the shared lexer at crates/test-support/src/source.rs rather than writing another contains.
  2. The two numbers disagree on purpose or by accident, and nobody knows which. 40 is a measured floor (peak build footprint 35.6 GiB target + 2.1 GiB registry = 37.7 GiB). 20 has no recorded basis. Decide what the reclaim threshold should be and record why: the defensible answer is probably that reclaim should trigger at or above the margin (60 GB), so the volume is restored before the next run's pre-flight refuses — a reclaim that only acts below the failure floor is a recovery that arrives after the outage.

What must NOT be done

  • Do not simply set the second constant to 40. That makes the numbers agree while leaving the gate blind to a third assignment tomorrow, and this issue is primarily about the gate.
  • Do not lower the pre-flight floor to 20 to match. 40 came from a measurement; 20 did not.
  • Do not treat "both runs ended at 20 GB" as coincidence worth ignoring — it is the reclaim threshold, and the volume settling exactly there is the signature of a boundary that never triggers.

Verification owed

The mutation that matters: change the second assignment only and confirm the gate goes RED. If it stays green, the fix is not done. Note that this change is unverifiable except by dispatch — the Windows runner is the only place this executes.

#179 (the pre-flight, whose measured floor is correct), #178 and #180 (the gate-blindness class), #169 (masked CI steps, same file).

🤖 Generated with Claude Code

https://claude.ai/code/session_01K1zj5VcFJvJt3pQxe9259K

Found during close-out verification and confirmed at source. This is why the Windows leg cannot recover on its own, and it is **#178's class occurring inside the fix for #179**. ## Measured ``` .forgejo/workflows/ci-windows.yml:150 $minFreeGb = 40 <- pre-flight floor .forgejo/workflows/ci-windows.yml:165 if ($freeGb -lt $minFreeGb) { ...FAILED... } .forgejo/workflows/ci-windows.yml:456 $minFreeGb = 20 <- SECOND assignment, post-job reclaim .forgejo/workflows/ci-windows.yml:563 if ($freeGb -ge 0 -and $freeGb -lt $minFreeGb) { ...reclaim... } ``` The gate meant to pin these: ```rust // crates/indexer/tests/ci_disk_preflight.rs:482 for (name, want) in [("$minFreeGb", MIN_FREE_GB), ("$marginPct", MARGIN_PCT)] { ``` It searches the **whole file** for `$minFreeGb`, finds `= 40` at line 150, and is satisfied. The `= 20` at line 456 is invisible to it. ## The consequence, observed twice The pre-flight refuses below **40 GB**. The reclaim fires only below **20 GB**. Both dispatched Windows runs ended at **exactly 20 GB free** — and `20 < 20` is false, so **the reclaim did not fire, and by construction never can from that state**. The volume sits in a band where the job refuses to start and the automated recovery declines to act, which is the worst of both. That also explains a result previously attributed to something else: the earlier report that "the prune found 0.0 GB stale" is true but not the whole story — even had there been stale bytes, the threshold that gates the reclaim was never crossed. ## Two defects, and they need separating 1. **The gate is blind to a second assignment.** Same shape as #180: a whole-file `contains` satisfied by the first occurrence, where the hazard lives in the second. Eleven population gates were audited this week and eleven were confirmed vulnerable; this is the twelfth, in a file written *after* that audit. Fix it the way the others were fixed — grade **every** assignment of the name, not the first — and reuse the shared lexer at `crates/test-support/src/source.rs` rather than writing another `contains`. 2. **The two numbers disagree on purpose or by accident, and nobody knows which.** 40 is a *measured* floor (peak build footprint 35.6 GiB target + 2.1 GiB registry = 37.7 GiB). 20 has no recorded basis. Decide what the reclaim threshold should be and record why: the defensible answer is probably that reclaim should trigger at or above the **margin** (60 GB), so the volume is restored *before* the next run's pre-flight refuses — a reclaim that only acts below the failure floor is a recovery that arrives after the outage. ## What must NOT be done - Do not simply set the second constant to 40. That makes the numbers agree while leaving the gate blind to a third assignment tomorrow, and this issue is primarily about the gate. - Do not lower the pre-flight floor to 20 to match. 40 came from a measurement; 20 did not. - Do not treat "both runs ended at 20 GB" as coincidence worth ignoring — it is the reclaim threshold, and the volume settling exactly there is the signature of a boundary that never triggers. ## Verification owed The mutation that matters: change the **second** assignment only and confirm the gate goes RED. If it stays green, the fix is not done. Note that this change is unverifiable except by dispatch — the Windows runner is the only place this executes. ## Related #179 (the pre-flight, whose measured floor is correct), #178 and #180 (the gate-blindness class), #169 (masked CI steps, same file). 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01K1zj5VcFJvJt3pQxe9259K
Author
Member

FIXED in 38ccbd2, merged as a3f2218.

There is now one $minFreeGb assignment (ci-windows.yml:155), and the gate that pins it sees every assignment rather than matching whole-file and stopping at the first. That was the actual defect this issue named: a gate matching on a shape that a second occurrence could hide behind.

Closing.

FIXED in `38ccbd2`, merged as `a3f2218`. There is now one `$minFreeGb` assignment (`ci-windows.yml:155`), and the gate that pins it **sees every assignment** rather than matching whole-file and stopping at the first. That was the actual defect this issue named: a gate matching on a shape that a second occurrence could hide behind. Closing.
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#193
No description provided.