Skip to content

Module addition is one action: refuse when the partition authorities are incomplete - #8111

Merged
briansrls merged 1 commit into
mainfrom
gate/module-addition-one-action
Aug 10, 2026
Merged

briansrls merged 1 commit into
mainfrom
gate/module-addition-one-action

Conversation

@gunbai-bot

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

Copy link
Copy Markdown
Contributor

Starting standing

Adding one extdeps module to the tree required three separate hand commits, each discovered by its own CI round trip:

  1. ci.yml's heal exclusion list
  2. the stage0 seed .rs
  3. the crate partition roster plus a lib.rs mod line

Throughout all three, generated_artifact_gate main_wet reported zero drift while the crate would not compile. Precedent for the same class: #7865.

The defect is not what "the gate lies" first suggested

The gate does not miss drift. Within its registered roster it regenerates every artifact and compares bytes, and it is complete. Its roster simply does not cover the emitted stage0 seed or the partition-crate lib.rs files — those belong to regen_stage0, a different binary.

So zero drift never meant the tree is consistent. It meant the artifacts I am registered for match — a scope statement read as a tree statement. That is why every verdict here carries its own extent: a clean answer that omits its denominator invites exactly that misreading, and the misreading is not a reader error when the verdict withheld the population.

The invariant took three wrong candidates

Each is recorded in the carrier, because each would have shipped a gate that refuses correct rows:

Candidate Why it is wrong
every emitted module is in some crate roster 75 of 138 emit-plan modules are legitimately unassigned and live in the monolith crate
a crate's modules reference only roster-mates use crate::NonEmptyVec names a type, not a module
...reference only its own pub mod set crates reach each other through glob re-exports (pub use v1_stage0_std_core::*)

What survives: reference closure over the reexport-transitive set — a module owned by crate C may only reference modules reachable from C, where reachable is C's own modules plus, transitively, the modules of packages C re-exports.

Both reference sources are imprecise, in opposite directions — measured, not assumed

  • Import lists UNDER-approximate. dag/std/syntax.dag, coercion.dag and http_path.dag declare zero imports while referencing std_types. An empty import list is the maximal under-approximation, so an import-list check finds nothing unreachable and passes while the crate cannot resolve — the same fail-open this gate closes, one authority over.
  • The .dag binding graph OVER-approximates the emitted crate's needs. It proposed four unreachable targets on clean main and all four are false: three name modules the emitter produces no file for, and std_pareto -> std_logic is a .dag reference the emitter never turned into a use crate:: line.

Neither alone is the emitted crate's requirement. So the binding graph is a candidate generator and the emitted artifact confirms exactly. This is a narrowing by an exact check, not a widening failure arm — a confirmed defect is still typed, located and counted.

Reading emitted Rust is a declared realization-side observation, admitted because the structure is genuinely absent upstream: which .dag references become emitted use lines is the emitter's decision and is not recoverable from the .dag graph. Dissolve-on: the emitter exposes its per-module emitted reference set, at which point the confirmation reads that fact and the text scan deletes.

Authorities consumed, not forked

  • Edges: dependency_resolution_facts_live, whose own contract states "do not add a second edge producer beside it; the swap replaces its body" — so this is correct today (import ∪ reference facts) and after the accepted-binding cutover, with no edit here.
  • Emitted spelling: stage0_module_emit_basename. Hand-spelling dots-to-underscores would have been a second naming scheme for something an authority already names (§3), and a per-character walk over a quadratic primitive besides.

Evidence — green by execution, all seven

Positive controls

  • w_live_corpus_partition_closure_holds — the live corpus HOLDS. This is what reds if any of the three discarded candidates is reintroduced.
  • w_transitive_reexport_is_reachable — std-surface reaches std-core modules.
  • w_reachability_is_not_universal — a crate re-exporting nothing does not see another crate's modules, so reachability cannot be vacuously true.
  • w_verdict_carries_its_extent — both arms report crates, modules and edges considered.

RED controls (a roster row removed from a copy of the live rows — the incident shape, not a synthetic)

  • red_missing_roster_row_refuses — a missing roster row REFUSES. If this holds, the gate is the lie it replaces.
  • red_refusal_names_the_incomplete_authority — the refusal names the importer, the owning package, the target, and which of the two hand authorities is short a row.
  • red_reports_every_incomplete_authority_in_one_run — two missing rows are BOTH reported in one run. A refusal that stops at the first would re-serialise exactly the round trips this gate exists to collapse.

Displaced cost

before after
CI round trips to add one module 3 0 — one local run names every incomplete authority
gate verdict on a non-compiling tree zero drift typed, located refusal naming module, crate, target and authority

Bounds, stated rather than left to be discovered

  • The verdict costs ~5m20s wall, dominated by the corpus-wide resolve the edge producer performs. That is the same order as the measured 5m35s front-door cost that closed a per-PR consumer, so this belongs on the regen/gate path that already resolves the tree, not as a new per-PR step. Not enrolled in the per-PR floor in this PR.
  • Scope is the partition-closure question only. It does not verify unique crate ownership — v1_compiler_artifact currently appears in two crate rows, so a uniqueness assertion would refuse main today. Naming that as unmeasured rather than implying coverage.
  • The crate-layout derivation program (DESIGN's emitted-crate-partition thread) is untouched; assignment stays hand-listed.

…are incomplete

The generated-artifact gate reported ZERO DRIFT on a tree whose partition
crates would not compile. It was not missing drift - within its registered
roster it regenerates every artifact and compares bytes. Its roster simply
does not cover the emitted stage0 seed or the partition crate lib.rs files.
So 'clean' meant 'the artifacts I am registered for match' and was read as
'the tree is consistent': a SCOPE statement read as a TREE statement. Every
verdict here therefore carries its own extent.

The invariant took three wrong candidates, each recorded in the carrier
because each would have shipped a gate that refuses CORRECT rows:
roster-completeness (75 emit-plan modules legitimately live in the monolith
crate), roster-mates-only (use crate::NonEmptyVec names a type), and
own-pub-mod-only (crates reach each other by glob reexport). What survives is
reference closure over the reexport-transitive set.

Both available reference sources are imprecise, and both directions were
MEASURED rather than assumed. Import lists UNDER-approximate: std/syntax.dag,
std/coercion.dag and std/http_path.dag declare zero imports while referencing
std_types, and an empty import list is the maximal under-approximation. The
.dag binding graph OVER-approximates the emitted crate's needs: it proposed
four unreachable targets on clean main and all four are false. So the binding
graph generates candidates and the emitted artifact confirms them exactly.
That is a narrowing by an exact check, not a widening failure arm.

Consumes dependency_resolution_facts_live, whose own contract says the swap
replaces its body rather than adding a producer - so this is correct today and
after the accepted-binding cutover with no edit here. Emitted basenames are
projected through stage0_module_emit_basename, never respelled.

Green by execution, all seven: live corpus holds; transitive reexport
reachable; reachability not universal; verdict carries extent; and three REDs
- a missing roster row refuses, the refusal names which authority is
incomplete, and two missing rows are both reported in ONE run rather than
serialising the round trips this gate exists to collapse.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@briansrls
briansrls merged commit d842be6 into main Aug 10, 2026
3 of 4 checks passed
@briansrls
briansrls deleted the gate/module-addition-one-action branch August 10, 2026 20:07
briansrls pushed a commit that referenced this pull request Aug 11, 2026
… witnesses that could not execute

#8122 removed the floor's first structural stop, and the run then reached
work it had not observed since 2026-08-10 03:12: 10 of 8610 discovery
witnesses failed where the previous run reported 1 of 8562. None of the
ten was caused by that change. They are three clusters of pre-existing
breakage, all merged during the window when CI could not reach them.

Nine are fixed here, each verified by execution. The tenth is named at
the bottom and is NOT fixed.

CLUSTER 1 — DOC REACHABILITY (3 rows). Two docs landed bound to nothing:
docs/plans/four-lane-closeout-and-root.md (#8116) and
docs/plans/v2-frontend-std-ingestion-frontier.md (#8114). #8116's own
message records it FIXING an orphan while introducing one. Both now carry
a HandAuthoredDocBind anchored to the work they document — the three v2
frontend stages the frontier receipt measures, and the CI-gate program
note plus concurrency-key expression the closeout reports on.
  PASS doc_graph_is_clean, PASS doc_graph_has_no_orphan_docs

CLUSTER 2 — ROADMAP (2 rows). witness_five_minute_ci_gate_program_chain_is_explicit
asserted an edge five-minute-ci-gate -> native-selected-witness-bundle
that the 2026-08-10 CI2-0 STOP DELETED ("THE NATIVE ROW NO LONGER
PRECEDES THIS GATE"). #8116 implemented the ruling in the authority and
left the witness asserting the removed edge, so the witness read main as
broken while main followed the ruling. The clause is now a negative on
both directions, and the 2026-08-07 note that required the old edge
records its own supersession rather than silently contradicting it.
  PASS witness_five_minute_ci_gate_program_chain_is_explicit

witness_ticket_brief_budget_holds_and_reds: two briefs exceeded the
100-word page budget — native-selected-witness-bundle at 177 and
v2-lens-suite-execution at 203, both authored by #8116 under a budget
witness that had never executed. Trimmed to 97 and 99 words with EVERY
authored fact preserved verbatim in two cited notes in the same module,
the pattern this file already uses; the brief names the symbol instead of
restating it. Nothing operator-authored is deleted.
  PASS witness_ticket_brief_budget_holds_and_reds

CLUSTER 3 — stage0_partition_closure (5 rows), from #8111, which merged
on a CANCELLED check so its own new witnesses never executed once.

THE BUDGET NUMBER WAS NOT THE COST. CI reported "5040ms > 5000ms", which
is where the budget INTERRUPTS; measured here, one live verdict costs
about 175 SECONDS of thread CPU. Five rows that each re-derived the whole
corpus presented as five witnesses slightly over budget.

Decomposed per the 2026-08-04 witness-cost ruling: small discriminating
mechanism fixtures, plus the irreducible census as its own row.
partition_closure_verdict_over is extracted as the pure core over
supplied facts; the three REDs and the extent row run on a fixture of one
crate, one importer and two targets. Importer and targets are REAL
emitted modules (std_measure genuinely references std_types and std_nat),
so the confirmation step still reads a real file and cannot be satisfied
by invented content. RED 2 is now strictly more discriminating: it uses a
roster where only std_nat is missing, so it cannot pass by restating
RED 1, and it pins importer and owning package rather than asserting
non-emptiness.
  4ms / 1ms / 1ms / 1ms, all PASS — against a ~175,000ms baseline.

TWO COST-SHAPE DEFECTS ARE ALSO FIXED, AND THEY DID NOT DELIVER THE WIN.
emitted_reference_confirms re-read the importer's emitted .rs once per
(importer, target) pair though the read depends only on the importer, and
the verdict computed module_declaration_facts_live twice and
dependency_resolution_facts_live twice. Both are genuine redundant work
and both are removed. MEASURED: 177.6s after, against a 173.6s baseline —
no improvement, within noise. The live producers dominate and are already
host-memoized. Stated because the hypothesis was mine and it was wrong.

NOT FIXED, AND IT WILL STILL RED CI:
w_live_corpus_partition_closure_holds keeps the live roster as its
subject at ~175s. It cannot be decomposed — its subject IS the corpus —
and narrowing pool_roots would under-approximate the edges, the exact
fail-open this module's own authority note warns against. It needs a
cadence home with a declared ceiling, which is the operator-owned half of
the 2026-08-04 ruling.

The module comment claiming the REDs run against live facts is corrected
in the same change; leaving it would describe a property the tree stopped
having.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XxLzhuMisV3GAPpP2mBtSG
briansrls pushed a commit that referenced this pull request Aug 11, 2026
…eet, partition-closure census, doc/roadmap authorities (#8122)

* Total the converge-verdict meet, the last unrostered non-fold residue

`non_fold_residue_no_unrostered_or_stale` has been red on main since the
15:45 run on 2026-08-10 — it is the failure on ca64dba (the #8055
revert) and on every main run since. It was the second of two
independent causes of main being red; #8120 closed the first.

Located by execution, not by reading the scanner. A probe over the same
host census the witness consumes named exactly one site:

  live=167 roster=166
  unrostered (1): dag/gunbc/host_converge.dag::converge_verdict_meet
  stale (0)

Introduced by #8101 (fbba81e), which merged at 15:45 that day — the
run in which the witness first went red.

The site:

  Drifted => match b { Absent => Absent  _ => Drifted }

a wildcard over the closed coproduct
ConvergeVerdict = VerdictConverged | Drifted | Absent.

FIXED BY TOTALLING THE FOLD, NOT BY A ROSTER ROW. The roster is
detection-only debt for residues that cannot yet be totalled; this one
can, and the three arms are semantically identical to the wildcard
(Absent -> Absent, Drifted -> Drifted, VerdictConverged -> Drifted, all
correct for a meet). A row here would be counted debt standing where
construction was available. Same probe after the change: live=166
roster=166, unrostered 0, stale 0.

The witness that comes with it is not padding. The existing
witness_self_receipt_verdict_is_joint_never_false_converged drives only
the a=VerdictConverged row, so the a=Drifted row — the one carrying the
wildcard, the one this change edits — had NO executing consumer at all.
Enumerating a wildcard preserves behaviour only if something asserts
what the behaviour is, so the full 3x3 meet table lands with it.

Green by execution (claim_batch, the entrypoint CI uses):
  PASS witness_converge_verdict_meet_is_total_over_every_pair
  PASS witness_self_receipt_verdict_is_joint_never_false_converged

NOT CLAIMED: that main now goes green. Two causes are closed; the floor
short-circuits at its first structural failure, so anything it never
reached remains unobserved. Main's last green ci run was 888321e.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XxLzhuMisV3GAPpP2mBtSG

* Heal the nine floor failures behind the first stop, and decompose the witnesses that could not execute

#8122 removed the floor's first structural stop, and the run then reached
work it had not observed since 2026-08-10 03:12: 10 of 8610 discovery
witnesses failed where the previous run reported 1 of 8562. None of the
ten was caused by that change. They are three clusters of pre-existing
breakage, all merged during the window when CI could not reach them.

Nine are fixed here, each verified by execution. The tenth is named at
the bottom and is NOT fixed.

CLUSTER 1 — DOC REACHABILITY (3 rows). Two docs landed bound to nothing:
docs/plans/four-lane-closeout-and-root.md (#8116) and
docs/plans/v2-frontend-std-ingestion-frontier.md (#8114). #8116's own
message records it FIXING an orphan while introducing one. Both now carry
a HandAuthoredDocBind anchored to the work they document — the three v2
frontend stages the frontier receipt measures, and the CI-gate program
note plus concurrency-key expression the closeout reports on.
  PASS doc_graph_is_clean, PASS doc_graph_has_no_orphan_docs

CLUSTER 2 — ROADMAP (2 rows). witness_five_minute_ci_gate_program_chain_is_explicit
asserted an edge five-minute-ci-gate -> native-selected-witness-bundle
that the 2026-08-10 CI2-0 STOP DELETED ("THE NATIVE ROW NO LONGER
PRECEDES THIS GATE"). #8116 implemented the ruling in the authority and
left the witness asserting the removed edge, so the witness read main as
broken while main followed the ruling. The clause is now a negative on
both directions, and the 2026-08-07 note that required the old edge
records its own supersession rather than silently contradicting it.
  PASS witness_five_minute_ci_gate_program_chain_is_explicit

witness_ticket_brief_budget_holds_and_reds: two briefs exceeded the
100-word page budget — native-selected-witness-bundle at 177 and
v2-lens-suite-execution at 203, both authored by #8116 under a budget
witness that had never executed. Trimmed to 97 and 99 words with EVERY
authored fact preserved verbatim in two cited notes in the same module,
the pattern this file already uses; the brief names the symbol instead of
restating it. Nothing operator-authored is deleted.
  PASS witness_ticket_brief_budget_holds_and_reds

CLUSTER 3 — stage0_partition_closure (5 rows), from #8111, which merged
on a CANCELLED check so its own new witnesses never executed once.

THE BUDGET NUMBER WAS NOT THE COST. CI reported "5040ms > 5000ms", which
is where the budget INTERRUPTS; measured here, one live verdict costs
about 175 SECONDS of thread CPU. Five rows that each re-derived the whole
corpus presented as five witnesses slightly over budget.

Decomposed per the 2026-08-04 witness-cost ruling: small discriminating
mechanism fixtures, plus the irreducible census as its own row.
partition_closure_verdict_over is extracted as the pure core over
supplied facts; the three REDs and the extent row run on a fixture of one
crate, one importer and two targets. Importer and targets are REAL
emitted modules (std_measure genuinely references std_types and std_nat),
so the confirmation step still reads a real file and cannot be satisfied
by invented content. RED 2 is now strictly more discriminating: it uses a
roster where only std_nat is missing, so it cannot pass by restating
RED 1, and it pins importer and owning package rather than asserting
non-emptiness.
  4ms / 1ms / 1ms / 1ms, all PASS — against a ~175,000ms baseline.

TWO COST-SHAPE DEFECTS ARE ALSO FIXED, AND THEY DID NOT DELIVER THE WIN.
emitted_reference_confirms re-read the importer's emitted .rs once per
(importer, target) pair though the read depends only on the importer, and
the verdict computed module_declaration_facts_live twice and
dependency_resolution_facts_live twice. Both are genuine redundant work
and both are removed. MEASURED: 177.6s after, against a 173.6s baseline —
no improvement, within noise. The live producers dominate and are already
host-memoized. Stated because the hypothesis was mine and it was wrong.

NOT FIXED, AND IT WILL STILL RED CI:
w_live_corpus_partition_closure_holds keeps the live roster as its
subject at ~175s. It cannot be decomposed — its subject IS the corpus —
and narrowing pool_roots would under-approximate the edges, the exact
fail-open this module's own authority note warns against. It needs a
cadence home with a declared ceiling, which is the operator-owned half of
the 2026-08-04 ruling.

The module comment claiming the REDs run against live facts is corrected
in the same change; leaving it would describe a property the tree stopped
having.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XxLzhuMisV3GAPpP2mBtSG

* chore: regenerate drifted generated artifacts (ci auto-heal)

* Say plainly that the read-hoist delivered no measured win

The first review of #8122 described the hoist as "a real measured cost
fix". It is not, and the note invited that reading by citing the 173.6s
BASELINE without ever stating the after figure.

Measured after: 177.6s — no improvement, inside noise. The live fact
producers dominate and are host-memoized, so the redundancy removed was
never the dominant term. The hoist stays because re-reading one file per
outbound edge is redundant work regardless of realized n, not because it
bought anything here. What made the mechanism witnesses affordable was
moving them off the live verdict.

Comment-only; no behavior change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XxLzhuMisV3GAPpP2mBtSG

* Split the partition-closure census onto the long lane, keeping its fixtures per-PR

The last of the ten floor failures. w_live_corpus_partition_closure_holds
costs ~175s of thread CPU against the 5s fast-lane budget, so it redded
CI on its own after the other nine were fixed.

IT CANNOT BE DECOMPOSED. Its subject IS the corpus: partition_closure_verdict
derives dependency_resolution_facts_live and module_declaration_facts_live
over pool roots dag and src/v2. Narrowing those roots would
UNDER-APPROXIMATE the edges, which is the fail-open this module's own
partition_closure_authority_note exists to refuse. So it goes to the
falsifier substrate long lane, per the 2026-08-04 ruling's second remedy:
a lane that declares its own ceiling and names the row as an executing
consumer.

WHY A NEW FILE. The long lane admits by FILE-GRAIN path pattern, so
enrolling the census where it sat would have dragged the four 1-4ms
fixtures off per-PR discovery with it — deleting the coverage the
decomposition in the previous commit exists to create. Splitting is what
lifecycle_survivor_corpus_census_test already does for the same reason,
and it needs no new machinery. Each file points at the other, because the
halves are a pair: a mechanism that never refuses fails the REDs, one
that always refuses fails the census.

A WRONG ROUTE WAS TRIED FIRST AND IS RECORDED because the reason
generalises. An exact function-grain ExplicitWitnessAdmission would avoid
the file-grain problem, and it is the direction that carrier's own
separation note names as its migration frontier. But every roster
every_admission_is_projected_into_exactly_one_cadence_roster counts is a
KNOWN-RED probe roster, and this witness is expected to PASS, so a green
long-lane row has no projection there — two walls said so by execution
before anything was pushed. Wiring one is that carrier's work, not this
repair's. Reverting it also restored
every_declared_long_budget_row_is_also_expected_red_today, correctly: with
no green long-budget row in the tree, that claim stands and must not be
retired.

THE MARGIN IS THIN AND IS ON BOTH ROWS, not hidden: 177.6s sits about 1.3
percent under the lane's declared 180000ms ceiling, measured on a
contended session container, so a slower runner may exceed it. That
surfaces as a counted cadence failure rather than a blocked merge, which
is why enrolling still beats the fast lane. Both dissolution conditions
name cost reduction first and self-host second, and both record that the
two cost-shape defects already removed here measurably did NOT move the
total (177.6s against a 173.6s baseline) — so the next attempt starts at
the live producers rather than repeating that.

Green by execution: 6 fixture rows PASS, the 3 admission walls PASS,
witness_exclusion_frontier_reconciliation_holds PASS with its unbacked-row
RED control PASS.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XxLzhuMisV3GAPpP2mBtSG

---------

Co-authored-by: Brian Searls <briansearls1@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: gunbc-ci-auto-heal <gunbc-ci-auto-heal@users.noreply.github.com>
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