Skip to content

An unreadable subject is not a clean one: carry the cause instead of collapsing it - #9188

Merged
briansrls merged 10 commits into
mainfrom
session/source-standing-not-clean
Aug 25, 2026
Merged

briansrls merged 10 commits into
mainfrom
session/source-standing-not-clean

Conversation

@gunbai-bot

@gunbai-bot gunbai-bot Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

#9179 MUST MERGE BEFORE THIS. This branch is cut from #9179's head 17773533eed and therefore contains it. GitHub shows no dependency because both target main. If this merges first, #9179 becomes a no-op and its squash produces an empty commit — the same silent loss that produced the dangling citation repaired in #9177 this morning.


source_has_suspect answered false when the analysis could not be established — and false is the value that means "no suspect". An instrument that could not read its subject reported the clean verdict, indistinguishable from one that read it and found nothing. Worse than the sentinel #9179 removed one function over: a sentinel never compares equal to the passing value; false is the passing value.

Reachable from a fixture, not only from an outage. Three of source_findings' five not-established causes are source-dependent (tokenization, parse, normalization), so ill-formed text alone produces it. Measured on main before this change: source_has_suspect over a snippet that does not tokenize answered false, beside a positive control answering true.

What was actually exposed is not what the shape suggests: source_has_suspect has no production callers. Its consumers are test claims, and the ones at risk are the == false green controls that pin these lenses against false positives. Had their snippet ever stopped parsing, they would have asserted "not flagged" about a source nobody read — every control still green, the guarantee gone. Controls that stop controlling, rather than a lens that misses suspects.

The hoist

FindingStanding moves wholesale into v2.lens.common.source_analysis_standing, beside the cause #9179 put there. It carries no lens-specific payload, so this is a move, not a widening. A cross-lens import would have made one lens the authority for the other's standings; a second copy would have been one concept with two homes.

Two total predicates go with it, and they are not each other's negation — finding_standing_established_clean and finding_standing_observed_suspect are both false for not-established, because an unread subject is neither clean nor an observed suspect. That is why the red control changed shape: it now asserts suspect observed rather than "not clean". "Not clean" is satisfied by an unread subject, so the old form would have reported the planted defect as still alarming on a build that analysed nothing.

Evidence

not-established-rather-than-clean readable-clean planted-copy
as landed PASS PASS PASS
collapse restored (pre-change program) RED PASS PASS

The mutation kills only its own claim. royal-cat's five standing tests pass unchanged over the hoist.

Scope, deliberate

The identical defect in v2.lens.identity_captured_navigation.roster_gate is not repaired here. That module does not typecheck to the point of executing any control — three fail with a type cascade on plain main c271b758293, before #9179 and before this branch — so a repair there could not be verified by execution, and landing it blind is the specification-without-execution failure. The work is written and parked on parked/twin-source-standing-blocked, including the grammar-first fix that stops a void grammar arriving disguised as a parse rejection. It lands when that module executes. Filed separately: that lens's CI exclusion rests on a local recipe that does not run.

— sent from gentle-eagle-360

gunbc-ci-auto-heal and others added 8 commits August 25, 2026 07:07
…collapsing it

source_has_suspect answered `false` when the analysis could not be established,
and `false` is the value that MEANS "no suspect". An instrument that could not
read its subject reported the CLEAN verdict, indistinguishable from one that read
it and found nothing. That is the empty-observation narrow, and it is worse than
the sentinel #9179 removed one function over: a sentinel never compares equal to
the passing value, `false` IS the passing value.

REACHABLE FROM A FIXTURE, NOT ONLY FROM AN OUTAGE. Three of source_findings' five
not-established causes are SOURCE-DEPENDENT -- tokenization, parse, normalization
-- so ill-formed TEXT alone produces it, with no grammar outage anywhere.
Measured on main before this change: source_has_suspect over a snippet that does
not tokenize answered false, beside a positive control answering true.

WHAT WAS ACTUALLY EXPOSED, since it is not what the shape suggests:
source_has_suspect has NO production callers. Its consumers are test claims, and
the ones at risk are the `== false` GREEN controls that pin these lenses against
FALSE POSITIVES. Had their snippet ever stopped parsing, they would have asserted
"not flagged" about a source nobody read, and the anti-false-positive guarantee
would have evaporated with every control still green. Controls that stop
controlling, rather than a lens that misses suspects.

FindingStanding is HOISTED WHOLESALE into v2.lens.common.source_analysis_standing
beside the cause #9179 put there. It carries no lens-specific payload, so this is
a move rather than a widening. A cross-lens import would have made one lens the
authority for the other's standings; a second copy would have been one concept
with two homes. Two total predicates go with it, and they are NOT each other's
negation -- finding_standing_established_clean and finding_standing_observed_suspect
are BOTH false for not-established, because an unread subject is neither a clean
one nor an observed suspect.

That distinction is why the red control changed shape. It now asserts SUSPECT
OBSERVED rather than "not clean": "not clean" is satisfied by an unread subject,
so the old form would have reported the planted defect as still alarming on a
build that analysed nothing at all.

EVIDENCE. New discriminating witness plus a positive control, driven both ways:

  as landed          not-established-rather-than-clean PASS, readable-clean PASS,
                     planted-copy PASS
  collapse restored  not-established-rather-than-clean RED, other two still PASS

The mutation is the pre-change program, and it kills only its own claim.
royal-cat's five standing tests pass unchanged over the hoist.

SCOPE, DELIBERATE: the identical defect in
v2.lens.identity_captured_navigation.roster_gate is NOT repaired here. That
module does not typecheck to the point of executing any control -- three fail
with a type cascade on plain main c271b75, before #9179 and before this
branch -- so a repair there could not be verified by execution. The work is
written and parked on parked/twin-source-standing-blocked, including the
grammar-first fix that stops a void grammar arriving disguised as a parse
rejection. It lands when that module executes.
The hoist moved FindingStanding and its predicates to v2.lens.common.source_analysis_standing
and left three references to finding_standing_holds with no definition anywhere. The receipt
witness now calls finding_standing_established_clean and imports it from its declaring module.
This is a rename, not an alias: the two bodies are byte-identical (NoSuspect => true, the other
two arms => false), so "zero poly2 suspects" asserts exactly what it asserted before -- an
established analysis with no suspect, with an unreadable source still refused rather than
counted clean.

The review named one file; the sweep found a second. accumulator_copy_roster_standing_test
still imported FindingNotEstablished, NoSuspect and SuspectObserved from roster_gate, which
no longer declares them. Its five witnesses passed anyway, by whole-pool type-position
resolution -- the same false-edge class, and one execution is structurally blind to. Both
blocks are corrected against the declaring module, and merged so the module is imported once.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@gunbai-bot

gunbai-bot Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor Author

Pushed deea34c8ca3.

The cited defect. All three finding_standing_holds references are renamed to finding_standing_established_clean and the import is re-homed to v2.lens.common.source_analysis_standing, which declares it. No alias.

I checked the two receipt assertions rather than just making the name resolve. This is a pure rename: the old finding_standing_holds body and finding_standing_established_clean are byte-identical — NoSuspect => true, SuspectObserved { suspects: _ } => false, FindingNotEstablished { cause: _ } => false. So "zero poly2 suspects" still asserts what it asserted before, and specifically still asserts the established reading: an unreadable source refuses rather than counting clean, which is the whole point of the hoist. Had the rename picked finding_standing_observed_suspect's negation instead, the assertion would have silently widened to admit FindingNotEstablished — the two predicates are deliberately not each other's negation.

A second false edge, not named in the review. Sweeping the six hoisted names corpus-wide found src/v2/test/claim/complexity/accumulator_copy_roster_standing_test.dag still importing FindingNotEstablished, NoSuspect and SuspectObserved from v2.lens.complexity_accumulator_copy.roster_gate, which no longer declares any of them. Its five witnesses passed anyway, before and after — whole-pool type-position resolution finds them because an unrelated import elsewhere in the closure drags them into the pool. That is the same false-edge class as the one on #9174, and it is structurally invisible to execution, so the green was not evidence. Both blocks now name the declaring module, and the two source_analysis_standing blocks are merged so the module is imported once.

Evidence.

  • accumulator_copy_roster_standing_test — 5/5 PASS.
  • symbol_index_scaling_receipt_test (long home, declined from execution but must still resolve — this is the one that broke floor PREPARATION) — resolves clean, 0 unresolved/undefined/refused lines, and both witnesses PASS: symbol_index_fill_zero_poly2_suspects_holds, name_resolve_fill_path_zero_poly2_suspects_holds.
  • §4c grain sweep clean on all four touched and declaring files.
  • No remaining reference to any of the six hoisted names against roster_gate anywhere in src/v2.

— sent from gentle-eagle-360

@gunbai-bot

gunbai-bot Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor Author

Correction to the evidence line above, in the stronger direction. The full run of symbol_index_scaling_receipt_test finished at exit 0 with 4/4 PASS, not two — I reported the two I had watched land and implied that was the roster:

PASS symbol_index_fill_zero_poly2_suspects_holds
PASS name_resolve_fill_path_zero_poly2_suspects_holds
PASS symbol_index_fill_refusal_count_within_ratchet_holds
PASS name_resolve_fill_path_refusal_count_within_ratchet_holds

The two ratchet witnesses are the slow ones (46s and 257s wall), which is why they were still evaluating when I posted. Nothing else in that comment changes.

— sent from gentle-eagle-360

@gunbai-bot

gunbai-bot Bot commented Aug 25, 2026 •

Copy link
Copy Markdown
Contributor Author

Declining review 55749's REQUEST_CHANGES, with the measurement rather than an assertion. I have not pushed a change for it.

The cited rule does not exist. The finding invokes "DESIGN's predicate-dissolution rule for reusable .dag/substrate concepts". DESIGN.md contains four occurrences of the word predicate — the sole_constructor caller's-own-predicate clause, std.interval's lying le, §4b's "does not lift arbitrary predicates to proof", and the authority-substitution entry's "the guard must carry the narrow predicate" — and none of them states it. Zero matches for "predicate-dissolution" or "predicate dissolution". §4b's line is about what refinement typing can prove, not a ban on Bool-returning functions. The nearest real rule is §4's example, and it points the opposite way: "idempotency dissolved from an idempotent: Bool flag into the EffectShape variant". That dissolves a Bool field standing in for a coproduct. It says nothing against a Bool query over one — and the two are inverses.

This PR is that rule being obeyed, not broken. What main carried was:

fn source_has_suspect(source_text: String) -> Bool {
  match source_findings(source_text: source_text) {
    Absent => false
    ...

A Bool where the state space has three members — an unreadable source rendered as the clean verdict. The change replaces it with the FindingStanding coproduct carrying its cause, which is exactly the §4 move the finding cites. Note the shape it objects to was already present on main and is strictly less structured there.

The corpus census. Bool-returning functions whose body is a match over a coproduct: 2486 sites across 937 files. If the rule were real it would make every one of them a blocking modeling violation, including the pre-existing analysis_standing_holds-shaped gates in v2.lens.roster_registry, v2.lens.duplicate_computation, v2.lens.non_fold_residue and v2.lens.enforcement.lens_module_gate. The *_holds() -> Bool terminal gate is the established lens idiom and my three functions follow it exactly.

The remedy names no referent for this subject. The finding asks me to "route these checks through the canonical fold/query surface". v2.std.node_query exists and is a canonical query surface — for Nodes: coproduct_arms, coproduct_arm_keys, node_is_coproduct(node: Node) -> Bool. It reads the source structure of a declared type. Routing a value-level FindingStanding through it is a category error, and nothing in v2.std or v2.lens.common is a canonical surface for coproduct predicates over values. Authoring one to satisfy this review would mint a new authority for a concept that already has one — §2's net-concepts-must-not-grow-by-re-invention — and it would be a scaffold with no other consumer. Worth noting that node_query itself carries node_is_coproduct(node: Node) -> Bool: the very idiom the finding calls a blocking violation, in std.

On the substantive kernel, which I think is what the reviewer half-saw. A Bool over a three-arm coproduct does collapse two distinct arms to false, and losing a cause at a boundary is a real §5 concern. It is handled here deliberately: the cause lives on FindingStanding, which is what is computed, stored and passed; the Bool appears only as the terminal assertion value at a witness. And the two predicates are deliberately not each other's negation — finding_standing_established_clean and finding_standing_observed_suspect both return false on FindingNotEstablished, so a caller must choose which reading it means and cannot get the unreadable case for free in either direction. That is the distinction the old Bool destroyed.

One process note, not a defect in the verdict: the finding cites source_analysis_standing.dag:29 and roster_gate.dag:115, where both subjects have symbols. §3's standing rule is cite the symbol, not the position.

Happy to be shown the rule if it exists somewhere I did not look.

— sent from gentle-eagle-360

…ng-not-clean

# Conflicts:
#	src/v2/lens/common/source_analysis_standing.dag
#	src/v2/lens/complexity_accumulator_copy/roster_gate.dag
#	src/v2/test/claim/complexity/accumulator_copy_roster_standing_test.dag
#	src/v2/test/claim/long/symbol_index_scaling_receipt_test.dag
…askable

source_findings already returns a typed SourceAnalysisNotEstablishedCause. source_has_suspect
matched `SourceFindingsNotEstablished { cause: _ }` and answered false -- and false is the value
that means NO SUSPECT, so a subject the lens could not read was reported as the CLEAN verdict,
indistinguishable from one it read and found clean. The cause was not missing; it was present at
the site and discarded. It is replaced by source_finding_standing, which returns the standing.

RosterGateDisposition and file_gate_disposition give the same treatment to the gate: clean,
suspect-observed, refusal-budget-exceeded, and not-established-with-its-cause are four different
answers with four different remedies, and file_gate collapsed all but the first onto `false`.
file_gate still answers Bool for the callers that want "did it come back clean", but the collapse
is now written out arm by arm in one place, so a fifth disposition fails to compile there rather
than inheriting whichever arm it lands beside.

finding_standing_holds and analysis_standing_holds are RETAINED. A Bool projection over a typed
standing is legitimate exactly while the typed standing stays reachable by callers; it is the
defect only when it is the sole surface. The disposition is that surface.

MEASURED, and this is a live red on main rather than a hypothetical. The roster gate over
src/v2/lens/complexity_accumulator_copy/analyze.dag fails identically on unmodified origin/main
and on this head -- pre-existing, and that file has not changed since #8283. Arm-checked through
the new disposition, four probes one per arm: not clean, not suspect, not budget-exceeded,
GateNotEstablished, cause SourceParseRejected. The complexity lens cannot parse its own analyzer.
Under the old Bool that presents as `false`, the same value a genuine copied accumulator produces,
so the witness is not silent -- it is red, and its red points the reader at a defect that is not
there. Filed separately; not repaired here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@gunbai-bot

gunbai-bot Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor Author

Recomposed against current main and pushed as 716aca436ed. The candidate is current-head, not the historical one.

What this now is. source_findings already returns a typed SourceAnalysisNotEstablishedCause. source_has_suspect matched SourceFindingsNotEstablished { cause: _ } and answered false — and false is the value that means no suspect, so a subject the lens could not read was reported as the CLEAN verdict. The cause was never missing; it was present at the site and discarded. It is replaced by source_finding_standing, which returns the standing.

RosterGateDisposition / file_gate_disposition give the gate the same treatment: clean, suspect-observed, refusal-budget-exceeded, and not-established-with-its-cause are four answers with four different remedies, and file_gate collapsed all but the first onto false. file_gate still answers Bool, but the collapse is written out arm-by-arm in one place, so a fifth disposition fails to compile there rather than inheriting whichever arm it lands beside.

finding_standing_holds and analysis_standing_holds are retained. A Bool projection over a typed standing is legitimate exactly while the typed standing stays reachable by callers; it is the defect only when it is the sole surface. The disposition is that surface.

The live proof, which is not hypothetical. The roster gate over src/v2/lens/complexity_accumulator_copy/analyze.dag fails identically on unmodified origin/main and on this head — same binary, same single test fn — so it is pre-existing. Arm-checked through the new disposition, four probes one per arm:

arm_is_clean            FAIL
arm_is_suspect          FAIL
arm_is_budget_exceeded  FAIL
arm_is_not_established  PASS

then five more, one per cause: SourceParseRejected. The complexity lens cannot parse its own analyzer. Under the old Bool that presents as false — the same value a genuine copied accumulator produces — so the witness is not silent, it is red, and its red points the reader at a defect that is not there. Filed separately; not repaired here.

Receipt, at a real denominator. Nine of nine answered: 8 PASS, 1 pre-existing red (roster_lens_traversal, above).

red_control_planted_copy_still_alarms                        PASS
an_unreadable_subject_is_not_established_rather_than_clean   PASS
a_readable_clean_subject_is_established_clean                PASS
roster_machine_code / compile_stage / glob_discovery         PASS
roster_lens_cost_model                                       PASS
roster_std_algebra                                           PASS
roster_lens_traversal                                        RED (pre-existing on main)

Run in small batches deliberately. An earlier single invocation of all nine timed out with only four reporting, and a truncated run's non-reports are indistinguishable from passes in the output — a reader counting "no failures among the nine" would get a fabricated denominator. That run is not cited as evidence for anything.

— sent from gentle-eagle-360

@briansrls
briansrls merged commit 46b7a05 into main Aug 25, 2026
1 check passed
@briansrls
briansrls deleted the session/source-standing-not-clean branch August 25, 2026 17:50
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