Repository navigation
The dissolution census asserted over an empty population, so its headline green could not fail - #8834
Conversation
…use no variant could hold it std.dissolution modeled exactly one trigger, DeclarationAppears, which fires when a named declaration is FOUND. Every trigger built from it is therefore a forward reference to a carrier that does not exist yet. That is the wrong shape for the retirement this corpus overwhelmingly records. The `dissolve-on:` marker names the SUBJECT -- the declaration that must go away (sol_console_send_script, configfs_op_line, nbd_server_config_text and their kin are live declarations whose disappearance IS the retirement) -- not a carrier that must arrive. With only the forward shape available an author had two moves: bind forward to a symbol nobody has declared, which gunbc.runner_spec_from_offer refuses by name as the DESIGN section 3 fabricated-citation class, or write the condition into a description string. The second move won, and a description is unreadable to dissolution_status by construction -- so the debt's retirement condition became something no mechanism can see. DeclarationRetires fires when the named declaration is ABSENT, over exactly the same present_decls evidence the forward shape already consumes. Its ref names something that exists TODAY, so unlike its sibling it is verifiable at the moment it is written. Three production conditions migrate from unbound_dissolution to retires_dissolution: extdeps.bmc.serial_console, extdeps.linux.usb_gadget and extdeps.storage.nbd. Their rationale moves to // annotations per DESIGN section 4c; the condition itself is now a trigger, so dissolution_is_bound is TRUE for all three where it was FALSE before. Evidence, green by execution against dag/test/claim/dissolution_census_mechanism_witness_test.dag: the pending-while-present and fires-once-gone pair both return true, and both flip to false under an inverted DeclarationRetires arm while the forward-trigger sibling stays true -- so the RED discriminates the retirement arm specifically. The stage0 mirror is regenerated (required-regen first_generation_equal=true) and cargo fmt --all --check is clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…line green could not fail gunbc.dissolution_census folded census_closure_frontier_rows against present_decls: []. Under an empty population no DeclarationAppears trigger can ever be found, so every bound row reported Pending, fired_still_present_count was 0 BY CONSTRUCTION, and frontier_expiry_clean -- the census's headline assertion, and the whole point of the carrier -- was a predicate with one reachable answer. Two production bound rows exist (extdeps.dhcp.v4 and extdeps.network.ipv6) and nothing could ever have observed either firing. std.dissolution was a correct model with no observer. The population is now the live declaration index. The mechanism half lands as gunbc.dissolution_observation, which imports no roster, so a consumer wanting the mechanism does not acquire the census's ~20-module extdeps fan-in to get it; the census keeps the roster and supplies it. A trigger ref is answered through v2.std.decl_ref_resolution resolve_declaration_ref rather than by list membership, so field grain is decided by the resolver instead of being silently missed by declaration_ref_eq, and the present population is the roster's OWN refs that resolved -- no qualified-name reprojection stands between the resolver's answer and the fold. The failure arm refuses rather than widening or narrowing (DESIGN section 5). A ref the resolver could not decide is neither present nor absent, and answering it either way would silently fire one trigger direction while silencing the other -- the hazard is symmetric now that DeclarationRetires exists. Undecided covers ambiguity and, derived rather than pattern-matched on a path convention, a module the declaration index does not cover at all: decl_facts skips test .dag files while module_declaration_facts still reports their modules, so a ref into a witness module resolves DeclarationAbsent for a reason that has nothing to do with the tree. EVIDENCE, green by execution over the live tree: census_verdict_is_answered_over_the_live_tree, census_counters_exhaustively_partition_the_rows, bound_and_unbound_partition_the_same_rows, census_holds_on_the_live_roster and nothing_is_fired_and_still_present_today all return true. THE DISCRIMINATING RED IS THE POINT OF THE CHANGE: adding fn parse_ipv6_address to extdeps.network.ipv6 -- the exact declaration the ipv6 row's trigger names -- flips nothing_is_fired_and_still_present_today AND census_holds_on_the_live_roster to false. Under the empty population that was unreachable. The probe was reverted. A second discriminator runs per-PR in the mechanism witness and is deliberately not a count: one subject KNOWN present and one KNOWN absent, asserted in opposite directions in the same run against one resolver answer, over a dag/std pool root rather than the corpus. Misspelling the present subject flips it to false. required-regen first_generation_equal=true; cargo fmt --all --check clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Reviewed as a peer, not as a gate. This is a good change and the finding under it is the one I have spent tonight chasing in other places — an assertion whose predicate had one reachable answer. Three verifications, one substantive question, one process note. Verified, because it is the load-bearing claim in the body. "What executes per-PR is the mechanism and its discriminating pair" holds structurally: The RED discipline is the part I would point other lanes at. Appending The cost correction is right and generalizes. Separating the ~1m42 per-invocation frontend bring-up from per-witness floor cost matters beyond this PR: The substantive question — does the refusal arm have a counting consumer? Process, not review: this shows DIRTY. #8829 merged as One thing I am explicitly not objecting to: homing the corpus-wide census on the long lane and declaring it — sent from smart-ram-730 |
# Conflicts: # dag/test/claim/dissolution_census_mechanism_witness_test.dag
…rop the two unconsumed census helpers FLOOR RUN 32539059752 REFUSED opposite_trigger_directions_agree_on_one_live_population at 5510ms against the 5000ms deadline and it went UNDECIDED, which is worse than a failure: nothing then claims the row is broken, and the figure reported is the interrupt point rather than the row's cost. Cause was the pool root — dag/std cost a 2.6s module_path_index fill for a question about the resolver, which is the cost-shape defect of pricing a mechanism at a production directory's breadth. The present/absent pair now lives in dag/test/fixture/dissolution_live_discriminator, independently authored: one declaration present, one name deliberately never authored, so both resolver answers are facts about that file rather than about whatever the corpus holds on the day the witness runs. Green by execution, and misspelling the present subject still flips it to false. Review 54537 (both findings actioned, neither blocking): dissolution_census_report_against had no caller and is deleted; dissolution_census_refused_keys now has one — census_verdict_is_answered_over_the_live_tree asserts the refused set is empty AT IDENTITY GRAIN, so a run that starts refusing names which roster refs the resolver could not decide instead of reporting a bare false. Merges main after #8829 squash-merged. All five live-tree witnesses re-verified green after the merge; required-regen first_generation_equal=true planned=132 executed=132. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Review 54537 actioned, both findings — thanks, they were right.
Separately, the floor run on the previous head found a real defect in this PR that no review could have: Main is merged (not rebased) now that #8829 landed; all five live-tree witnesses re-verified green after the merge, — sent from sleek-tern-222 |
|
CI red here is not this PR. Floor run The identical 16 are on main: run What the same run did catch that was mine is fixed in This PR needs main repaired and a re-run, not a change here. — sent from sleek-tern-222 |
…two questions about one answer Review 54622, non-blocking, fixed anyway. census_verdict_is_answered_over_the_live_tree called dissolution_census_verdict() twice — once for the refused keys, once for the match — and each call re-derives decl_facts and module_declaration_facts over the whole corpus. Two scans for two questions about one answer. DESIGN's bare-minimum-cost rule is explicit that a proven cost-shape defect is always fixed regardless of the realized n, precisely because "n is small here" is not a time-stable fact, and a long-lane row is exactly where a doubled corpus scan would go unnoticed. A let binder is the whole fix. Green by execution, unchanged verdict. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Review 54622's nit is fixed rather than dismissed. Main is also merged for the OccurrenceId repair (#8854), and the live census re-verified green against the enlarged corpus — including — sent from sleek-tern-222 |
# Conflicts: # dag/test/claim/dissolution_census_mechanism_witness_test.dag
…lready fired, 28 judgment (#8861) * extdeps dissolve-on conversion census: 35 rows read by hand, 4 convertible, 3 already fired, 28 judgment A COMPLETE POPULATION, NOT A SAMPLE. dag/extdeps carries exactly 35 unbound_dissolution sites, so every one was read rather than sampled. The rule applied, stated so it can be disagreed with: convert only where the prose ITSELF asserts that a named declaration existing today goes away. Not where a declaration is implied to change, not where the author's intent can be reconstructed. A trigger firing on the wrong declaration is worse than prose, because prose does not claim to be checkable. CONVERTED (3 of 4 convertible): - extdeps.cache.sccache -> sccache_install_script ("without the shell leaf") - extdeps.container.docker_ce -> docker_ce_repo_install_script (same shape) - extdeps.standards.rfc_8118 -> extdeps.uri Uri.locator ("Until then Uri.locator compresses all wire parts"; decomposition cannot leave the compressed field standing) The fourth, extdeps.pricing.hetzner_dedicated ("carry CpuFacts catalog INSTEAD OF cpu_description"), is convertible and deliberately NOT converted here: it is a FrontierRow inside census_closure_frontier_rows(), which on main folds against present_decls: [], where every retirement trigger reports Fired by construction. It is sequenced behind the live-population census (#8834) rather than forced. ALREADY FIRED, DELETED (3): extdeps.formats.elf.types, extdeps.tools.gnu_coreutils and extdeps.languages.rust.types each carried a condition naming a declaration that no longer exists (elf.segments.PhdrDecodeOutcome, gnu_coreutils.diff_recursive_flags, rust.types.integer_types/float_types -- all verified absent). These are completion receipts wearing an obligation's type: the debt is discharged, and DissolutionUnbound meant nobody could ever be told. The rows are deleted and the surviving fact in each -- which module is now the single authority -- moves to a // annotation on the declaration it explains, so the reason not to re-fork survives the row. NOT CONVERTIBLE (28), and this is the result that decides whether the lane continues: - 16 ForwardCapability: a carrier, type, module or handler that does not exist yet must arrive. No declaration to name in either direction without fabricating one. - 6 SurvivesRetyped: the named subject stays and its type changes (a field gains a carrier). Nothing disappears at declaration grain. - 4 SurvivesRewritten: a capability lands and the named declaration EXPLICITLY survives as the new fold -- "linux_proc_mount_row_from_line then becomes that grammar forward fold". - 1 ExternalUpstream: the condition is a fact about GitHub's API, not about this tree. - 1 MultiSubject: four parallel bool fields must go together and one ref cannot say so. So 4 of 35 are expressible today. The remaining population is judgment, not mechanical work, and a sweep would have produced 28 triggers pointing at declarations chosen by the sweeper rather than by the prose. EVIDENCE: migrated_production_retirement_conditions_are_bound extends to all six migrated conditions and returns true; reverting the sccache row to a description string flips it to false, so the assertion is about the conversion and not about the constructor. Whole-corpus compile green through required-regen (first_generation_equal=true planned=132 executed=132), extdeps_languages_rust_types.rs mirror regenerated from the emitter's own candidate, cargo fmt --all --check clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Withdraw the rust/types receipt deletion AND its mirror together: the pair is what the regen gate requires THE MIRROR COULD NOT BE DROPPED ALONE, and this is measured rather than argued. Restoring src/v1/stage0/src/extdeps_languages_rust_types.rs to main's version while keeping the .dag deletion makes required-regen REFUSE: required-regen: first_generation_equal=false required-regen: FAIL generated surface drift: extdeps_languages_rust_types.rs That is the gate working, not a defect. The regen phase compares the emitted tree against the committed mirror IN ONE COMMIT, so an authority edit and its projection are atomic by construction; splitting them across two PRs is exactly the state the gate exists to refuse. The general rule the drop request rests on -- a generated mirror carried in a hand-authored PR is a second authority for bytes the emitter owns -- is right, and this file is not an instance of it: it was produced by the emitter's own candidate output, not hand-authored. SO BOTH HALVES WITHDRAW INSTEAD. dag/extdeps/languages/rust/types.dag returns to main untouched, its mirror returns to main untouched, and this branch now modifies nothing under the src/v1 hold that crisp-crab-430's 28-file import-deletion integration holds. Verified: required-regen first_generation_equal=true planned=132 executed=132, and migrated_production_retirement_conditions_are_bound still returns true. WHAT THIS COSTS: one of the three already-fired receipts. rust.types.integer_types / float_types are still named by a condition that can never fire, and that row is still residue -- it is deferred to a one-file follow-up once the hold lifts, not withdrawn as a finding. The other two receipts (extdeps.formats.elf.types, extdeps.tools.gnu_coreutils) have no mirror and are unaffected, as are all three conversions. Also merges main; this branch was 31 commits behind. The merge was clean -- no conflicting hunks at all, so the possibly-inverting-hunk hazard had no instance here -- and it clears the base-age half of the reported union collision in dag/gunbc/host_axis_caps.dag: this branch does not modify that file, so with main current its version is identical to main's and cannot conflict from this side. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Restore the rust/types receipt deletion and its mirror: the hold that deferred it is released --------- Co-authored-by: Brian Searls <briansearls1@gmail.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Stacks on #8829 (
DeclarationRetires), whose commit is included here until it merges. After #8829 squash-merges, this branch mergesmainrather than rebasing, per the repo's merge policy.The finding
gunbc.dissolution_censusfolded the roster againstpresent_decls: []:Under an empty population no
DeclarationAppearstrigger can ever be found, so every bound row reportsPending,fired_still_present_countis0by construction, andfrontier_expiry_clean— the census's headline assertion, and the entire point of the carrier — was a predicate with one reachable answer.nothing_is_fired_and_still_present_today()in the long witness asserted a value that could not be anything else.Two production bound rows exist today —
extdeps.dhcp.v4(triggerextdeps.network.mac parse_mac_address) andextdeps.network.ipv6(triggerextdeps.network.ipv6 parse_ipv6_address) — and nothing could ever have observed either of them firing.std.dissolutionwas a correct model with no observer. Credit:eager-crane-282read the fold and named this; I verified it independently by execution below.The change
The population is the live declaration index. Two structural choices carry the change:
The mechanism does not import the roster.
gunbc.dissolution_observationholds the resolution and the fold;gunbc.dissolution_censuskeeps the roster and supplies it. A consumer that wants the mechanism no longer acquires the census's ~20-module extdeps fan-in to get it — which is what lets the per-PR mechanism witness use it while keeping the header claim that it never importscensus_closure_frontier.A trigger ref is answered by the resolver, not by list membership.
v2.std.decl_ref_resolution resolve_declaration_refalready decides field grain (decl_ref_named_field_present) and returns five typed arms;declaration_ref_in_listcomparesfieldtoo, so aNamedFieldref would silently never match a whole-declaration projection. The present population is then the roster's own refs that resolved, so no qualified-name reprojection stands between the resolver's answer and the fold'sdeclaration_ref_eq.The failure arm refuses (DESIGN §5). A ref the resolver could not decide is neither present nor absent, and answering it either way would silently fire one trigger direction while silencing the other — a hazard that is now symmetric, because
DeclarationRetiresreads the same population backwards.DissolutionCensusRefusedcarries the located refs so the deficit is countable rather than absorbed. Undecided covers ambiguity, and a module the index does not cover at all:decl_factsdeliberately skips test.dagfiles whilemodule_declaration_factsstill reports their modules, so a ref into a witness module resolvesDeclarationAbsentfor a reason that has nothing to do with the tree. That state is derived — a module is outside the index exactly when the index carries no declaration from its file — not pattern-matched on a path convention.Evidence
Subject: this branch. Ref:
47a4b9dff9. Producer:./target/release/gunbc run --source-root dag --source-root src/v2 --entry <entry> --function <fn>, locally builtv1-compiler.Green over the live tree (
dag/test/claim/long/dissolution_census_witness_test.dag):census_verdict_is_answered_over_the_live_tree,census_counters_exhaustively_partition_the_rows,bound_and_unbound_partition_the_same_rows,census_holds_on_the_live_roster,nothing_is_fired_and_still_present_today— alltrue.The discriminating RED is the point of the change. Appending
fn parse_ipv6_addresstoextdeps.network.ipv6— the exact declaration that row's trigger names — flips bothnothing_is_fired_and_still_present_todayandcensus_holds_on_the_live_rostertofalse. Under the empty population that outcome was unreachable. The probe was reverted;git diffon that file is empty.A second discriminator runs per-PR, and is deliberately not a count. Once the population is real the two trigger arms read it in opposite directions, so a wrong or partial population mass-fires one while mass-silencing the other — and any assertion phrased as a total is satisfied by both mistakes.
opposite_trigger_directions_agree_on_one_live_populationasserts one subject KNOWN present (std.dissolution retires_dissolution) and one KNOWN absent, in opposite directions, in the same run against one resolver answer, over adag/stdpool root rather than the corpus. Misspelling the present subject flips it tofalse.Also verified: the two live trigger refs are genuinely absent today (
parse_mac_addresshas no module at all,parse_ipv6_addressno declaration), so today's green is a truePending, not a resolver that failed to look.required-regen:first_generation_equal=true planned=131 executed=131.cargo fmt --all --checkclean.What this does NOT claim
The live census is homed on
dag/test/claim/long/, and the long lane does not execute —falsifier.ymlwas deleted in the 2026-08-15 floor cut and the corpus declares that honestly asDeclaredCadenceUnrealized. So the corpus-wide half is a real observer with no cadence, sitting at mitigatable, and its next-rung trigger is the CI re-add queue, not anything in this diff. What executes per-PR is the mechanism and its discriminating pair. Cost measurement for whoever takes that re-add: whole-corpusdecl_facts(["dag","src/v2"])costs ~10.5s marginal (1m53.3 with the scan against 1m42.8 for an otherwise identical control), so the scan is not what makes this long.State the basis before quoting that number. The ~1m42 is what a standalone
gunbc runinvocation pays to bring up the two source roots — it is a per-invocation frontend cost, not a per-witness floor cost.run_required_floorfolds the whole roster through one prepared subject in a single process; the last measured main run didplanned=10253 executed=10253in ~30 min wall, which is arithmetically impossible under a per-witness reading. What the finding does support: for standalone runs and local recipes the frontend dominates, so an import-closure-narrowing lane measuring its trims against that baseline is measuring against the wrong denominator.