Skip to content

File the class: a correct detector promoted to an actuator predicate - #10417

Merged
briansrls merged 27 commits into
mainfrom
work/detector-promoted-to-actuator
Sep 5, 2026
Merged

briansrls merged 27 commits into
mainfrom
work/detector-promoted-to-actuator

Conversation

@gunbai-bot

@gunbai-bot gunbai-bot Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

A predicate that truly and completely describes a condition gets promoted to select the population an actuator runs over, without the clause separating members that need the action from members for which the same condition is the correct, healthy state. The detection is never wrong. The promotion is.

The asymmetry is why the two must be kept apart: observing a member the predicate correctly returns costs nothing; acting on it can be destructive. A predicate can be fully validated as a description and remain unsafe as a trigger — and no amount of confirming the description finds it.

Recognition rule, checkable before any code changes: was the predicate validated against the specimen that prompted it, or against the population it will act on? The prompting specimen is by construction a member needing the action — that is why it prompted — so it can never expose the healthy majority.

Specimen (both halves mine). A fleet sweep for parked CI runs filtered on completed AND action_required. A peer found a third parked shape that filter could not reach and correctly diagnosed a container-field selector, then proposed filtering on terminal AND zero jobs — a true and complete description of the parked state. At member grain that returns 21 hits across 11 branches, of which ~20 are superseded heads, where zero jobs is exactly correct. Rerunning them resurrects dead heads and spends capacity in a queue measured at 80+ minutes deep. n=1 confirming, n=20 refuting, and the 20 were never queried — nobody asks a detector what else it returns.

The missing clause is liveness, and it discriminates structurally rather than by threshold: head_sha == the CURRENT head of an OPEN pull request. A superseded run fails it by construction.

The row also records two things it would be easy to leave out: the corrected sweep's zero was controlled against a known-live head (otherwise indistinguishable from a join that matches nothing), and the same defect recurred one layer up within the hour — "no runs awaiting approval" reported as "no parked runs", a summary inheriting the narrowness of the query beneath it while its words name the wider set.

Boundaries cited by identity against the three adjacent rows, per roster practice: population_selector_that_cannot_admit_a_counterexample (there the query is wrong and cannot return the negative; here it is right and returns faithfully), repair_enumerates_its_own_blast_radius_by_inspection (there a count has no decidable search behind it; here the search is decidable and executed), guard_precondition_discharged_by_the_route_that_uses_it (there a precondition moved; here nothing moved).

RUNG 1, CEILING 3 — whether a predicate reaching an effectful operation carries a clause its pure-observation path does not is a static question over the same Node tree a lens already reads. The trigger names a capability: an actuator carrier constructed from a detected member plus an admissibility receipt, with no constructor from a detection result alone.

Authority only. docs/design-failure-modes.md is left to the heal lane rather than hand-written, so this does not touch the generated projection three other open PRs are appending to.

🤖 Generated with Claude Code

https://claude.ai/code/session_01PGkDt1W1m3U28vBpV6BY9Z

gunbc-ci-auto-heal and others added 2 commits September 4, 2026 13:39
A predicate that TRULY and COMPLETELY describes a condition is used to select the
population an actuator runs over, without the clause separating members that need
the action from members for which the same condition is the correct, healthy state.
The detection is never wrong. The promotion is.

The asymmetry is the reason the two must be kept apart: observing a member the
predicate correctly returns costs nothing; acting on it can be destructive. So a
predicate can be fully validated as a DESCRIPTION and remain unsafe as a TRIGGER,
and no amount of confirming the description finds it.

RECOGNITION, checkable before any code changes: was the predicate validated against
the SPECIMEN THAT PROMPTED IT, or against the POPULATION IT WILL ACT ON? The
prompting specimen is by construction a member needing the action -- that is why it
prompted -- so it can never expose the healthy majority.

SPECIMEN, both halves mine. A fleet sweep for parked CI runs filtered on
`completed AND action_required`. A peer found a third parked shape it could not
reach and correctly diagnosed a container-field selector, and proposed filtering on
TERMINAL AND ZERO JOBS -- a true and complete description of the parked state. Run
at member grain it returns 21 hits over 11 branches, of which ~20 are SUPERSEDED
HEADS where zero jobs is exactly correct. Rerunning them resurrects dead heads and
spends capacity in a queue measured at 80+ minutes. n=1 confirming, n=20 refuting,
and the 20 were never queried.

The missing clause is liveness, and it discriminates structurally rather than by
threshold: `head_sha == the CURRENT head of an OPEN pull request`. A superseded run
fails it BY CONSTRUCTION.

The row also records the corrected sweep's zero being controlled against a
known-live head, and the same defect recurring one layer up in the reporting --
"no runs awaiting approval" published as "no parked runs".

Boundaries are cited against the three adjacent rows:
`population_selector_that_cannot_admit_a_counterexample` (there the query is wrong;
here it is right), `repair_enumerates_its_own_blast_radius_by_inspection` (there the
count has no decidable search; here it does), and
`guard_precondition_discharged_by_the_route_that_uses_it` (there the precondition
moved; here nothing moved).

RUNG 1, CEILING 3 -- whether a predicate reaching an effectful operation carries a
clause its pure-observation path does not is a static question over the same `Node`
tree a lens reads. Trigger names the capability: an actuator carrier constructed
from a detected member PLUS an admissibility receipt, with no constructor from a
detection result alone.

Authority only. `docs/design-failure-modes.md` is left to the heal lane rather than
hand-written, so this does not touch the generated projection other lanes are
appending to.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PGkDt1W1m3U28vBpV6BY9Z
@gunbai-bot

gunbai-bot Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

Verified review 60136 against the carrier and the authority rather than against the diff alone. Not making the change, and here is the measurement behind that.

The shape is the carrier, not a choice this row made. gunbc.recurring_failure_mode declares exactly one constructor:

type RecurringFailureMode {
  identity: NonEmptyStr
  receipts: List<String>
  evidence: List<DeclarationRef>
}

All 99 rows on main use it. There is no typed receipt variant to reach for — which the review itself concedes with "until that carrier exists". So the remedy as written is not available at authoring time.

Rulings, ceilings and counts in receipts are established practice, not new. Measured on origin/main:

pattern rows carrying it
CEILING: 40 / 99
NEXT-RUNG TRIGGER 37 / 99
RUNG: 17 / 99

and quantities sit in receipts too — censored_estimator_drops_its_own_tail carries median 1.053, p90 1.107, 500/418. If placing a rung and a trigger in a List<String> receipt is a defect, it is a defect in roughly 40% of the ledger and this row is indistinguishable from them. That makes it a finding about the carrier, which is a different subject from this PR.

The gap is already declared, with a reason and a capability trigger. From the annotation in gunbc.recurring_failure_mode — the paragraph headed "WHY CLASSIFICATION IS NOT DONE HERE, since a gap named without a reason is just an excuse":

The carrier holds 1,837 receipts across 83 rows; classifying them is an authored judgement per receipt, not a derivation … NEXT-RUNG TRIGGER, a capability rather than an artifact: a receipt variant model whose constructors cover the kinds section 4c enumerates, sufficient for an EXHAUSTIVE match over a row's parts — at which point an unclassified receipt has no constructor and the prose cannot be written, which is rung 4 rather than a lens.

So the debt is declared §4b(3)-style, its trigger names the capability, and this row lands inside it rather than beside it.

The proposed alternative would empty the row. Quarantining the substance into // annotations does not put it in the ledger — the projection renders receipts, and §4c is explicit that "an annotation is never evidence that a machine claim holds". A row whose recognition rule, boundaries and specimen live in comments is a row that establishes nothing, and docs/design-failure-modes.md would render an identity with no content.

And DESIGN requires the row. §4b: "Every newly discovered error class — incident, review finding, runtime exception, falsifier divergence — files or updates one row in docs/design-failure-modes.md, authority gunbc.recurring_failure_mode." Under the review's remedy no class could be filed until the variant model lands, which freezes the ledger the same section mandates keeping.

What I agree with: ten more unclassified receipts is real debt, and I am not calling it typed. That is why the row states its own rung honestly rather than claiming construction. The correct place to discharge it is the declared trigger — the receipt variant model — which retires this row's prose and the other 99 rows' prose in one motion. Doing it for row 100 alone would leave 99 unclassified and buy nothing.

Happy to be overruled if the intent is that the ledger stops accepting rows until the variant model exists — but that is an operator call about the carrier, not something to settle inside this PR.

— sent from warm-seal-35

@gunbai-bot

gunbai-bot Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

Both failing checks are inherited from main, not this diff — and one claim in the PR body is now stale. Neither needs a fix pushed here.

The failures. Floor reports parse (16 error(s)); every one of the 16 names a file this PR does not touch:

dag/test/claim/emit_copy_qualification_witness_test.dag:483..500

This PR's files are dag/gunbc/recurring_failure_mode/detector_promoted_to_an_actuator_predicate.dag, dag/gunbc/recurring_failure_mode/roster.dag, and docs/design-failure-modes.md. Disjoint sets.

main has been parse-red since 11:48Z — #10390 deleted three test fn rows and left the 17-line annotation above them dangling at EOF, which §4c refuses (leading blocks only). This branch was cut from an already-broken main, so it carries the broken file in its head. required-witnesses-build passes; the aggregate witnesses fails only because the floor lane does. The repair is #10414 (I closed my own #10418 in its favour on an earliest-with-proof tie-break), and it cannot go green either, because namespace-wave-admission parses at the base revision and every repair inherits the red from what it repairs. There is an operator ask open for it.

Nothing to push here: once #10414 lands, this branch needs one fresh push — not a re-run, since refs/pull/N/merge pins at push time and a re-run would replay a tree that still contains the broken file.

The stale claim, which I would rather correct than leave standing. The body says this is "Authority only … does not touch the generated projection three other open PRs are appending to." That was true when I opened it and is no longer true of the branch: the heal lane auto-pushed d695b3ab63e — chore: regenerate drifted generated artifacts (ci auto-heal), adding two lines to docs/design-failure-modes.md (the identity index entry and the row body).

The content is exactly right — it is the projection of this row, derived rather than hand-written, which is what I wanted. But the collision property I was claiming is gone: the branch now touches the same generated file as #10399 and #10387, so whichever of us lands second gets the generated-artifact driver's refusal (no conflict markers, path left unmerged) and must regenerate rather than hand-merge.

Worth stating plainly because it generalises: choosing not to touch a generated projection does not keep it out of your PR. The heal lane will add it on your behalf, and the deconfliction you designed into the diff quietly stops holding. The property is a fact about the branch after CI, not about the commit you authored.

— sent from warm-seal-35

…d-to-actuator

# Conflicts:
#	docs/design-failure-modes.md
gunbc-ci-auto-heal and others added 4 commits September 4, 2026 16:06
…d-to-actuator

# Conflicts:
#	dag/gunbc/recurring_failure_mode/roster.dag
#	docs/design-failure-modes.md
…view 60227)

The row carried a receipt calling a REPORTING defect "the same defect": the sweep's author
reported "the fleet is clear" when the filter beneath it owned only runs awaiting approval, not
parked runs. Review 60227 is correct that this widens the class past its own boundary. This
row's stated distinguishing fact is that DETECTION AND ACTUATION HAVE DIFFERENT ADMISSIBLE
POPULATIONS and one predicate was used for both. A status summary has no actuator and no side
effect, so that fact does not obtain, and filing it here forks an authority that already
exists.

Verified before changing anything: green_reported_over_a_population_the_instrument_does_not_own
exists and is rostered, and its subject is exactly an instrument reporting over a population it
does not own - which is this specimen.

The specimen is kept, because it is real and was found in the same hour, but it is now marked
explicitly as a NEIGHBOUR that this row is not the authority for, and it names the row that is,
so a later reader tracing it is sent onward rather than back here. Added as a fourth boundary
beside the three already cited: there a report names a wider population than its query owns
with no effect anywhere in it; here the defect is an EFFECT admitted on the wrong population.

Worth recording that this is the same error, in the opposite direction, that I argued against
on a sibling PR earlier today - uniting two specimens on a shared symptom rather than a shared
mechanism. I applied the rule to the class I was arguing about and not to the one already
written. The reviewer found it by reading this row's own stated boundary against its own
contents.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PGkDt1W1m3U28vBpV6BY9Z
@gunbai-bot

gunbai-bot Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

The three failing checks here were inherited from main, not introduced by this branch — and
main has since been repaired, so the next run against a merged base should clear them.

Diagnosed rather than assumed, by comparing this PR's failure to main's own run at the time:

  • required-witnesses-floor — adjudication BLOCKING phase=floor identity=test.claim.declared_type_expected_type_path_witness.w_a_different_plain_record_at_a_declared_return_is_refused is enrolled as expected-red and PASSED … cause=stale_quarantine
  • required-witnesses-build — regen FAIL generated surface drift: v1_compiler_infer.rs

Both appeared identically, with the same witness identity and the same drifted file, on
main's own push runs. Main was red for five consecutive pushes (last green 0ed8345026, then
five failures through 3d4f0cec84). This branch touches neither
src/v2/workflow/floor_expected_red.dag nor src/v1/stage0/src/v1_compiler_infer.rs.

The phase this PR does own was green in the same run:
generated-artifact rostered=38 adjudicated=38 matches=38 drifted=0 absent=0 unadjudicated=0.

I did not open a repair PR for either cause: six were already open against those two files
(#10450, #10448, #10444, #10443, #10438, and several on the emitter), and adding a seventh is
the exact class this branch documents. Main is green again as of 9d351d6db0.

— sent from warm-seal-35

gunbc-ci-auto-heal and others added 6 commits September 4, 2026 19:04
…view 60273)

Review 60273 is right on both halves and neither is cosmetic.

THE CEILING JUSTIFICATION WAS SYNTACTIC. The row claimed ceiling 3 because "whether a predicate
reaching an effectful operation carries a clause the pure-observation path does not is a static
question over the same Node tree a lens already reads." An arbitrary or irrelevant conjunct
satisfies that shape while preserving the invalid state exactly, so it establishes no rung above
2. It is the tell DESIGN section 5 names: a check satisfiable by editing the declaration while
the realization still lies. Withdrawn in the row rather than quietly reworded, because a later
reader will reach for the same shape test. What it can honestly do is enumerate CANDIDATES --
effect paths carrying no additional clause at all -- which is a lens, not a wall.

THE TRIGGER NAMED AN ARTIFACT SHAPE, NOT A CAPABILITY. It asked for an actuator carrier built
from a detected member plus an ADMISSIBILITY receipt. But a receipt type whose constructor does
not require anything is vacuously inhabitable and claims admissibility that no constructor
entails, which leaves the class where it was while reading as coverage. Section 4b(3) requires
the trigger to name the capability, and a trigger naming less is satisfied while the capability
stays dead.

The trigger now has both load-bearing halves: the actuator must DECLARE ITS ADMISSIBLE
POPULATION as a predicate over the member, and the effect constructor must require a witness
produced by EVALUATING THAT DECLARED PREDICATE on the member being acted on. Then a detection
result has no constructor into the effect at all and the promotion fails to construct. Ceiling
restated as 4 on that route, since the invalid state loses its constructor rather than being
validated after the fact.

Parsed through a roster-consuming witness.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PGkDt1W1m3U28vBpV6BY9Z
…d-to-actuator

# Conflicts:
#	docs/design-failure-modes.md
…o detection

The trigger already required the actuator to declare its admissible population and the effect
constructor to take a witness from evaluating that predicate on the member. It said a detection
result has no constructor into the effect - which still admits a caller-authored receipt, a bare
member, or a true boolean. Excluding one bad path is not the property.

The requirement is EXCLUSIVITY: a successful evaluation must be the ONLY constructor of the
permit. A permit type carries no safety property of its own; the property lives entirely in an
exclusive constructor that proves the actuator's declared policy over the EXACT subject acted
upon. Any additional constructor returns the class to rung 1 while the type name still reads as
coverage, which is the evidence-strength inflation this repository already files.

Third strengthening of this row today and none of them were mine to see from inside it: the
ceiling justification was syntactic, the receipt was ungrounded, and the constructor was
non-exclusive.

Parsed through a roster-consuming witness.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PGkDt1W1m3U28vBpV6BY9Z
…or' into work/detector-promoted-to-actuator
@gunbai-bot

gunbai-bot Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

Review 60337 was correct and the drift is now resolved at head a9a19176f24. Verified at the
live head rather than assumed:

  • .dag receipt carries the exclusivity clause — 1 occurrence
  • docs/design-failure-modes.md carries it — 1 occurrence
  • the projected paragraph now reads …thought to ask. EXCLUSIVITY IS THE WHOLE OF IT…, so
    "Both halves are load-bearing" has its antecedent back

Cause, which was mine rather than the projector's. heal regenerated the projection at
0307a8ca56b; I pushed the exclusivity clause at 047fa80c4d7. The projector ran before the
authority changed, so the .md lagged the .dag by one commit. This PR has been pushed four
times today — each in response to a real finding — and every push races the regenerator, so a
stale-projection window is the predictable consequence of re-editing an authority that has a
generated artifact, not bad luck. The dangling antecedent was the visible tell, and it is only
visible by reading the two artifacts against each other; neither one alone looks wrong.

On the repair. I regenerated from the authority with the documented actuator
(generated_artifact_gate.dag --function main_wet) rather than hand-editing the markdown, and
it rewrote six docs/plans/ files byte-identically while changing exactly this projection by
one line. heal landed its own regeneration concurrently at a9a19176f24, and my output was
byte-identical to it — so I dropped my commit rather than push a no-op through CI. That
identity is also a small piece of evidence that the projector is deterministic and that local
regeneration was a legitimate route here: the merge driver's "do not regenerate this projection
locally" instruction is scoped to the merge-conflict case, where neither side's bytes derive
from the merged authorities. There was no merge ambiguity here — the .dag was unambiguously
authoritative and the .md simply lagged it.

No further push from me; the finding is discharged by content at the current head.

— sent from warm-seal-35

gunbc-ci-auto-heal added 12 commits September 4, 2026 20:11
…d-to-actuator

# Conflicts:
#	dag/gunbc/recurring_failure_mode/roster.dag
#	docs/design-failure-modes.md
…d-to-actuator

# Conflicts:
#	dag/gunbc/recurring_failure_mode/roster.dag
#	docs/design-failure-modes.md
…d-to-actuator

# Conflicts:
#	dag/gunbc/recurring_failure_mode/roster.dag
#	docs/design-failure-modes.md
Ledger-Repair-Judged: docs/design-failure-modes.md
Ledger-Rows-Repaired: docs/design-failure-modes.md detector_promoted_to_an_actuator_predicate
Ledger-Repair-Judged: docs/design-rung-drops.md
…d-to-actuator

# Conflicts:
#	dag/gunbc/recurring_failure_mode/roster.dag
#	docs/design-failure-modes.md
Ledger-Repair-Judged: docs/design-failure-modes.md
Ledger-Rows-Repaired: docs/design-failure-modes.md detector_promoted_to_an_actuator_predicate
Ledger-Repair-Judged: docs/design-rung-drops.md
…d-to-actuator

# Conflicts:
#	dag/gunbc/recurring_failure_mode/roster.dag
#	docs/design-failure-modes.md
Ledger-Repair-Judged: docs/design-failure-modes.md
Ledger-Rows-Repaired: docs/design-failure-modes.md detector_promoted_to_an_actuator_predicate
Ledger-Repair-Judged: docs/design-rung-drops.md
…d-to-actuator

# Conflicts:
#	dag/gunbc/recurring_failure_mode/roster.dag
#	docs/design-failure-modes.md
Ledger-Repair-Judged: docs/design-failure-modes.md
Ledger-Rows-Repaired: docs/design-failure-modes.md detector_promoted_to_an_actuator_predicate
Ledger-Repair-Judged: docs/design-rung-drops.md
gunbc-ci-auto-heal and others added 2 commits September 5, 2026 03:35
…rated projection

Two conflicting files, opposite remedies, because one is authored and one is derived.

dag/gunbc/recurring_failure_mode/roster.dag is AUTHORED and its order is
load-bearing in the projection, so it is resolved as an ORDERED UNION -- main's
rows first, then this branch's -- and verified as ordered SEQUENCES: imports and
rows are ordered-equal at 125, and main's 124 are all present with their relative
order preserved as a subsequence. A set check would have accepted a reordering
the projection can see.

docs/design-failure-modes.md is GENERATED. The merge driver refused it rather
than answering, leaving the ours bytes in the worktree with no conflict markers
-- so a marker count here reports only that the driver worked, never that its
subject is intact. Per the path's declared repair route the BASE side is taken
verbatim and the difference verified BY ROW IDENTITY, not by count: no row from
base went dark. The result is deliberately one row short, and
heal-generated-artifacts derives it from the merged authorities.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PGkDt1W1m3U28vBpV6BY9Z
Ledger-Repair-Judged: docs/design-failure-modes.md
Ledger-Rows-Repaired: docs/design-failure-modes.md detector_promoted_to_an_actuator_predicate
Ledger-Repair-Judged: docs/design-rung-drops.md
@briansrls
briansrls merged commit 5eedd15 into main Sep 5, 2026
8 checks passed
@briansrls
briansrls deleted the work/detector-promoted-to-actuator branch September 5, 2026 04:51
gunbai-bot Bot pushed a commit that referenced this pull request Sep 5, 2026
…or the projection

#10417's own merge is what conflicted this branch -- both append to the same
recurring_failure_mode roster, and the roster is the busiest file in the repo.

Same pair, same opposite remedies. The authored roster is resolved as an ORDERED
UNION and verified as ordered SEQUENCES: imports and rows ordered-equal at 129,
main's 127 all present with relative order preserved as a subsequence, because
roster order is load-bearing in the projection and a set check would accept a
reordering the projection can see. The generated projection is taken from base
verbatim and verified by ROW IDENTITY rather than count -- the driver refuses
these bytes and leaves them marker-free, so a marker count reports only that the
driver worked. Left deliberately two rows short for heal to derive.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PGkDt1W1m3U28vBpV6BY9Z
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant