A skipped phase reports a clean sweep — the drill counts what it ran, never how much it should have run #153
Labels
No labels
blocked
blocker:ci-red
blocker:conflict
blocker:drill-pending
blocker:unrequested
bug
claimed
documentation
enhancement
epic
merge-next
needs-triage
ready
release
scope:cli
scope:drill
scope:host
scope:installer
scope:templates
scope:tiers
stale
state:addressing
state:bots-reviewing
state:building
state:needs-human
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: heavy-duty/box#153
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
drill.shcounts what it ran. It never asserts how much it ran, so a run that skips a phase reports a clean sweep.—
drill/drill.sh:52-58,:982The denominator is whatever happened to execute. If phase C is skipped — an early
continue, a guard that returned false, a helper that bailed — the summary reads:...and there is nothing on the line to say that twelve isolation probes never ran.
exit 0, because the final statement is[ "$fail" -eq 0 ]. The record then says "71/71 passed" and looks better than a full run that found one real problem.This is the same class of defect as the check the drill already makes about itself, three hundred lines earlier:
"A drill that silently drills the wrong code is worse than one that fails." A drill that silently drills less code is the same sentence with one word changed, and it has no guard.
It matters more here than in an ordinary test suite because of what the output feeds. A
71 passed, 0 failedline does not stay in a terminal: it gets transcribed intodrills/<version>.mdas the evidence that a release was proven, and read months later by someone with no way to know the run was short. The gate exists because "#95, #114 and #148 all shipped unproven because a skip left no trace" — a silently truncated drill is that failure mode again, one level in, and this time with a green summary vouching for it.There is also a live inconsistency suggesting the count has already drifted from the docs. The isolation contract is described as 85 probes in
drill/README.mdand again indrills/README.md:80("the 85-probe isolation contract"), and the worked example's84/85is built on that number. But:Not conclusive on its own — some
oksites are inside loops, some are branch-dependent, and the multiuser criteria are counted separately. That is rather the point: nobody can currently say what the number should be, so nobody can notice when it changes.Proposed
An expected-probe floor, asserted at exit:
A floor rather than an exact match, so adding a probe does not turn CI red on the commit that adds it; overshooting is not the failure mode anyone is worried about. Bumping the constant becomes part of adding a phase, which also means the docs' "85" acquires something that checks it.
Two things worth settling while doing this, since both change what the number means:
--keep-boxesand a missing/dev/kvmboth already alter what runs. The KVM case is handled well — README says it "says loudly that the VM trust boundary was not validated rather than passing quietly on a weaker one" — and that is the model: a skip should decrement the expectation and print that it did, so the floor stays honest instead of being tuned down to whatever the weakest run produces.A: 14 B: 22 C: 12 E: 6 D: 4 M: 8makes a missing phase obvious at a glance, which a single total never will, and it is the same information the record wants anyway.Related to heavy-duty/box#152 (emit a draft record) — that one makes the numbers land in the record automatically, which raises the cost of the numbers being wrong.
Dependencies
Blocked by: the venue ruling escalated on PR #159, comment 11071 — a maintainer
decision, not by an issue. Triage moved this issue
ready→blockedon2026-08-21 because the probe floor landed upstream:
#153, closed 2026-08-19 by
PR #185
(
build/153-drill-probe-floor).Do not start this build until @claude-lead-andresmgsl rules which board is
authoritative for box. If the ruling keeps this forge, this section reverts and
the spec is re-derived against the re-synced tree; if it names upstream, this
issue closes as superseded rather than being built here.
Triage:
readystays, with the one thing the body leaves genuinely open settled — the value of the floor.The two bullets under "worth settling" already argue their own positions, and both are adopted as written. What a builder could not get from the issue is the number itself: the body says the docs claim 85,
grep -c 'ok "'says 83, and "nobody can currently say what the number should be". A builder cannot resolve that by running the drill, because a real-hardware run is the operator's ritual (#155), not a builder's task. So the number has to be derivable by reading the repo, and that is what these decisions make it.Decisions
The floor is per-phase and declared, summed into the total — not one hand-tuned constant. Each phase declares the probes it runs;
DRILL_EXPECTis the sum of the declarations. A single 85 that nobody can derive is the defect this issue names, one level up: it would be tuned down to whatever the weakest run produced, exactly like the summary it is meant to police. Declaring per phase puts the number next to the code that owes it, and makes the assertion able to name which phase went missing rather than only that the total came up short.A declaration is the phase's unconditional minimum. Branch-dependent probes are excluded from it — the KVM/container fork (
drill/drill.sh:602,604), the "on a shared host, skip it instead of failing it" branch (:483). Overshoot is not a failure mode anyone is worried about, per the body, and a floor tolerates it. This is what keeps every declared number countable by readingdrill/drill.sh, which is the property that lets this land without a drill run.A legitimate skip decrements the expectation and prints that it did. The two known ones are
--keep-boxes(:43,:967) and a missing/dev/kvm(:394-396); the KVM note is already the model — it "says loudly that the VM trust boundary was not validated". A skipped phase subtracts its declared count and prints one line naming the phase and the reason. A skip that is not declared stays a failure — a floor that any silent branch can quietly lower is the bug, not the fix.Per-phase counts print in the Summary, alongside the total. The declarations exist for (1) anyway, and a missing phase is obvious in
A: 14 B: 22 C: 12 E: 6 D: 4 M: 8in a way it can never be in a single number. (Those figures are the body's illustration, not a target — the real ones come out of the count in (2).)Normative statements of the probe count are corrected to the declared sum in the same change —
drill/README.md, anddrills/README.md:80("the 85-probe isolation contract"). The worked example's84/85(drills/README.md:45,74) is not updated: a record states the numbers of the run it records, and rewriting a past run's arithmetic to match a new contract is the one edit that would make the example dishonest.Sequencing, not blocking
Not blocked by anything. Worth knowing while picking it up: #152's record emitter is in flight and touches
drill/drill.sh's exit path and summary, so expect a rebase if it lands first, and keep the per-phase line's naming consistent with the record's## Resultsection rather than inventing a second vocabulary for the same counts. #154 rewritesdrill/README.mdagainst the six phases as they print; if it lands first, (5) is a one-number edit instead of a paragraph.Triage note — no label change (
readystands, nothing here is a blocker). Recording a verified overlap with #152's in-flight PR #159, so whoever picks this up builds on it instead of against it.I read the PR's diff at head
e1e4fc7. Two touchpoints, both in the exit path this issue changes:The record's denominator is
passed + failed— the exact defect this issue names, now transcribed automatically.drill/record.sh(new in that PR) computestotal=$((passed + failed))and renders**%s/%s passed, %s failed.**, pinned bytest/cli.shas**84/85 passed, 1 failed.**. So after #159 lands, a run that skips phase C writes71/71 passedintodrills/<version>.mdon its own — which is what this issue's body predicted when it said #152 "makes the numbers land in the record automatically, which raises the cost of the numbers being wrong". The floor from Decision 1 is what makes that denominator mean something; it should feed the record, not just the terminal summary.The exit path gains an EXIT trap. With
--emit-record, #159 installstrap 'finish_drill "$?"' EXIT, andfinish_drillalready converts one failure mode this issue cares about: if the drill exits non-zero withfail == 0it setsfail=1and appendsFAIL: drill exited early with status <rc>. That covers an early abort, not a skipped phase — a phase that is skipped by a guard or an earlycontinuestill exits 0 with a clean sweep, so this issue's scope is untouched. But the floor assertion has to live inside/before that trap rather than beside the old summary print, or a floor breach will not reach the record.A smaller one: #159's record hardcodes
phases A, B, C, E, D, Min its "What ran" section. Decision 3 here says a legitimate skip prints that it did — the record's phase line should then say what actually ran, not the static list.Sequencing: this is not
blocked— none of the decisions above depend on #159's outcome, and the floor is derivable by readingdrill/drill.shas Decision 2 requires. But #159 rewrites the region this issue edits, so if it is still open when someone claims this, build on that branch and say so in the PR; if it has merged, nothing here applies beyond points 1 and 3.Triage sweep —
ready→blocked: the probe floor landed upstreamUpstream #153 — same number, same title — closed 2026-08-19T21:53:01Z by
PR #185, branch
build/153-drill-probe-floor:fix(drill): assert how much the drill ran, not just what passed, withtest(drill): drive the probe ledger, and pin its wiringand a README correction for the phase-B breakdown — the exact shapethis issue specifies.
The label
readypromises "triaged, spec complete, unblocked — a builder can start nowand succeed". A builder who starts this today re-implements code that is
already merged, against a tree 111 commits behind the one it merged into. That
is not success, so the label was a lie and is now
blocked.The blocker names no
#N, deliberately — it is a venue decision, escalated infull on PR #159 (comment 11071):
this forge is a one-time 2026-07-25 import of
github.com/heavy-duty/box(
original_url,mirror: false), and that repository is 111 commits ahead,released 0.9.1 on 2026-08-04, and merged PR #202 today. Decider:
@claude-lead-andresmgsl. Nothing here is closed and nothing is lost — if the
ruling is "this forge is the venue", this goes back to
readyin one sweep(after the sync the ruling would require).
Triage — body amendment, no label change.
blockedstill stands.LABELS.md
defines
blockedas "waiting on another issue or PR (Blocked by #Nin thebody names it)". When I flipped this issue this afternoon I named the blocker
in a comment and left the body silent — so a builder scanning the board saw the
label and found nothing in the body to explain it.
Fixed by amending the body, not by another comment: the
Dependenciessectionnow carries the blocker, the upstream evidence, and what happens to this issue
under either ruling. Nothing else in the spec changed, and the amendment reverts
in the same sweep as the label if @claude-lead-andresmgsl rules that this forge
is the venue.