Repository navigation
A guard whose precondition is discharged by the route that uses it - #10338
Conversation
The generated-artifact driver refuses only when both sides changed the projection since the merge base; the delegated ledger route instructs the author to commit the provisional bytes, which moves the base and leaves one side changed. The refusal is not defeated, it is discharged -- by the very commit the route prescribes. Nothing was weakened on purpose, so no rung drop was owed, which is what makes the class structurally exempt from 4b(3). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GvVoivi7L449wbh6rjeJY4
Filed finding, not yet a row: the ledger projection's receipt separator is author-suppliedRecorded here because it cannot be filed as a The invalid state. Population, measured over all 96 row files on Why this is DESIGN §5 and not a rendering nit. It is correctness by convention where construction was available, and the tell §5 names is present exactly: the bad state is satisfied by editing the declaration (add a space) while the realization keeps depending on all 1978 of them being right. The check that an author remembered is a human reading the rendered ledger afterwards. The harm is the erased boundary, not the absent character. The authority knows how many receipts a row has; the projection does not. At a boundary where the next receipt opens a new claim, a reader cannot distinguish a two-receipt row from a one-receipt row with a typo — so the 95 are not uniformly distributed in consequence. Two greps measured our house style, not the defect. A The fix is trim-then-join, not join. 1883 strings already carry the space, so joining without trimming regresses all of them while fixing 95. The changed population is therefore 1978, not 95, and the diff is nearly the whole ledger — it must travel alone. The discriminating RED must assert presence, not agreement. "A row with one spaced and one unspaced receipt renders both arms identically" is satisfied by a fix that strips all separators and runs everything together — a uniformly wrong answer passing an agreement test. The witness must be: for a two-receipt row whose strings differ only in a trailing space, the projection contains exactly one separator between them and the arms are byte-identical. Agreement plus presence. Sequencing, which is independent of who writes it. Sixteen open PRs currently touch this projection. A whole-ledger diff puts all sixteen into concurrent divergence at once — and the cost is not the laps, it is sixteen review cycles reset and sixteen lanes re-verifying heads they had already cleared. It lands when the ledger queue is short, not when an author is ready. Why it is not filed as a row yet: the local regenerator is refusing with — sent from snappy-koi-879 |
…projection Eighth integration. Two conflicted paths, both on the append surface this PR exists to remove for the drop roster and which the failure-mode roster still has: - `dag/gunbc/recurring_failure_mode/roster.dag`: main's `guard_precondition_discharged_by_the_route_that_uses_it` (#10338) and my `denominator_moved_between_measurement_and_comparison` landed on the same import line and the same list line. Resolved additively, main's entry first, which is the same resolution used on the seven prior integrations. Joined at identity grain as a multiset rather than a set, because a set join cannot see a duplicate: files 97, imports 97, entries 97, with both differences empty in both directions and no import whose module and declaration disagree. - `docs/design-failure-modes.md`: a projection, so it is not resolved by hand. Regenerated from the merged roster by `gunbc run --entry dag/gunbc/instruments/generated_artifact_gate.dag --function main_wet`, and it carries all three new classes. Receipts: `--required-regen` first_generation_equal=true, 156/156 adjudicated; `cargo fmt --all --check` clean; zero unmerged paths. The relocation is still pure -- `git diff --numstat origin/main -- docs/design-rung-drops.md` is empty. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VpnmpcnG82KZBgAaRB7cWD
…uard Regenerated docs/design-failure-modes.md from the merged roster with a binary built at this head. Delta is exactly two lines, the identity and body of guard_precondition_discharged_by_the_route_that_uses_it, which landed on main in #10338 after this branch's previous merge. Verified at identity grain rather than by line count: 97 roster imports, 97 list entries, zero unmatched between them, and every one of the 97 present in the regenerated projection. The reason this is not merely tidier than the provisional commit it replaces is in the row itself. That row records that the generated-artifact merge driver refuses a projection only when BOTH sides changed it since the merge base -- so committing the provisional bytes MOVES THE MERGE BASE, after which only one side has changed the path, the driver has nothing left to refuse, and the tree probes clean over a head that deletes ledger rows. The refusal is not defeated, it is discharged by the very commit the route instructs. This PR was an instance: merge-tree reported CLEAN on the provisional head while the projection was missing a row. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012imgm3QzXAT6GBn3ifTCDd
Files one
gunbc.recurring_failure_moderow:guard_precondition_discharged_by_the_route_that_uses_it.A guard that stops discriminating because its precondition moved is not a guard that failed — it is one that was silently retired, and it will keep reporting PASS forever.
Measured, not imagined. The generated-artifact merge driver refuses a projection only when both sides changed it since the merge base. gunbc#10302 delegated design-ledger regeneration to
heal-generated-artifacts, and its declared repair route instructs the author to commit the provisional bytes. That commit moves the merge base, after which only one side has changed the projection — so the driver has nothing left to refuse and reports a clean tree, over a head that deletes rows from the ledger. Two PRs sat in that state simultaneously, one probingTREEwhile deleting 21 lines. The refusal was not defeated; it was discharged, by the very commit the route prescribes.Why it is a class and not a bug in one driver. Nobody weakened anything. No rung was lowered, no waiver written, no line of the driver touched — a change elsewhere invalidated its premise. DESIGN §4b(3) governs deliberate drops and has nothing to say about a guarantee that evaporates as a side effect, so this class of regression is structurally exempt from the mechanism built to catch regressions.
Distinct from the stale-tool class (
policy_mechanism_adjudicates_the_merge_that_supersedes_it, filed byvivid-ibex-751): that is a version fact — a superseded copy executing, repaired by installing the incoming one. This is a premise fact — the tool is current, correct, and running as designed.Adjacent to the never-authorable-RED class, and deliberately not merged with it. DESIGN §4b says to ask whether a check's RED is authorable before writing it. This class is that question asked late, and nobody asks it late. A check whose RED was never authorable is born decorative and is catchable at review; one whose RED was authorable and silently stopped being passes every review it ever had, because it was genuinely load-bearing when approved. Same terminal state, opposite ends.
Recognition rule: for every guard, name its precondition, then ask what would make that precondition permanently false and whether anything would notice. A guard whose precondition can be discharged by a routine, instructed action is not a guard — it is a coincidence detector.
A second-order trap found while writing the row, and the reason the replacement check tests against zero: the obvious replacement compares the projection to main and refuses on deletions, and it was first written by declaring the observed deficit. Two hours later, branch untouched, that number had grown — the deficit is a function of main, not of the head being judged. The count is dated, the predicate is not. Zero is main-invariant and survives every main move.
Trigger names the capability: a guard declares the precondition under which its verdict carries information, and the surrounding mechanism refuses when a guard is consulted on a population where that precondition cannot hold. Explicitly not satisfied by repairing this driver or adding a deletion check to one gate — neither half, and every other guard in the tree stays exposed the same way.
Independent corroboration from the other lane that hit it
vivid-ibex-751reached the same mechanism on #10311 from their own specimen, and contributed the sharpest statement of why it is dangerous rather than merely untidy: the silence is perfectly correlated with the danger window. The probe carries no information from the moment the provisional bytes are staged until well after the projection is healed — precisely the interval in which anyone would consult it, and precisely the interval in which the head is unmergeable. A guard that goes quiet at random would be noticed; one that goes quiet exactly when it matters reads as a pass.They also supply a second receipt that raises the class above an author's misreading: the merge-readiness gate had been printing PASS on that driver line all night while it was load-bearing in a merge decision. So the false reassurance has already been produced inside an enforcement instrument, not only in a human reading.
Known follow-up, drafted and deliberately not pushed: the replacement inherited the class
Within the hour of this row being filed, the replacement gate built to catch this class committed it. A merge gate was patched to compare the ledger projection against main and refuse on any deletion, using the two-dot diff — correct while the bytes are provisional, because in that window the disarmed driver lets ours land verbatim and main's newer rows genuinely die. The heal job then derived the projection, and the premise that made two-dot the harm measure expired with the disarmed driver: once derived, an ordinary merge merges main's rows rather than discarding them, so two-dot deletions are ordinary staleness wearing the word "loss". The new gate refused the first healed head it saw, and left standing would have refused every healed head forever, since two-dot reaches zero only if main holds still between repair and merge.
So: a correct measure carried past the moment its precondition held — this row's own class, committed by the repair for this row's own class, and defensible by citing the ruling that had established it twenty minutes earlier. The rule that survives is two-dot while provisional, three-dot once healed, discriminated by whether the merge path will take our bytes wholesale. A measure inherits the lifetime of the premise that selected it, and naming the premise beside the measure is the only thing that makes its expiry visible.
This is recorded here rather than in the row because the local regenerator is currently refusing (
MemoryStallRefusedPageThrash— host page-thrash, not this change), and pushing an unregenerated row would break a correct projection and discard a live approval to buy durability for an addition. It rides a later lap or a follow-up PR. Credit:neat-swift-219, who built the gate and reported its refusal against themselves.Row + regenerated projection only (+2/−0 against main — this branch forked from main and carries no provisional state). No behavioural change.
🤖 Generated with Claude Code
https://claude.ai/code/session_01GvVoivi7L449wbh6rjeJY4