Repository navigation
The systemctl state filter on two host-hygiene observers binds a FUNCTION instead of a string, because the compiler silently accepts duplicate declarations: the wall, the repair, and a fixture-boundary RED - #9452
Conversation
…use them, with a fixture-boundary RED and two live specimens repaired A module declaring one name twice was ACCEPTED with zero diagnostics, and every reference bound whichever declaration was written last. `v1.compiler.env` `symbol_index_insert` is a `map_insert` keyed on the qualified name, so the second declaration REPLACED the first in the symbol index -- two authorities for one fact (DESIGN section 3) with no refusal anywhere. Names resolving is the ordinary compiler floor, so this sat BELOW it (DESIGN section 4b): silent wrongness, outside the ladder rather than a weak rung on it. MEASURED BY EXECUTION, instrument named. Binary built from source at ddb7f53 (main tip), sha256 1b2eb218e6725f76..., mtime 2026-08-27T13:31:07Z -- not the installed /usr/local/bin/gunbc, which is two days stale and proves nothing about current main: fn probe_dup() -> Int { 1 } fn probe_dup() -> Int { 1 } fn probe_dup() -> Int { 2 } fn probe_dup_caller() -> Int { probe_dup() } fn probe_dup_caller() ... => 2 (the LAST definition) => 1 (control) The collision is NOT fn/fn only, which the original finding did not cover: a `data` and a `fn` of one name share the same key, and a bare reference to the data evaluated to `<fn thing>`. LIVE EXPOSURE, denominator stated: 4163 tracked .dag files scanned, 2 modules carried it, both repaired here. * dag/gunbc/systemctl_list_units.dag -- `data systemctl_list_units_all_states: String` shadowed by `fn systemctl_list_units_all_states(..)`, so the ListUnits `state:` argument bound the FUNCTION rather than "active,inactive,failed". A production module with two importers. The data row is renamed `systemctl_all_unit_states_selector`; the function keeps its name, which is what both importers call. * dag/test/claim/spark_managed_grant_reconcile_witness_test.dag -- one `data` row declared twice with an identical value; the second is deleted. The deletion IS the census: with the dotted-name form of the scan (`service shell.Find` is one name, not `shell` repeated) zero duplicates remain. THE WALL. `v1.compiler.resolve` `check_duplicate_declarations` folds each module's OWN item list and emits a blocking `DuplicateDeclaration` on the second declaration of one name. It reads nothing outside the module it judges -- no corpus walk, no import resolution, no type information -- so the class is decidable from one module's source, which is what makes it a wall rather than a ratchet (DESIGN section 5). Rung: R2, mechanically preventable. The R4 climb is a keyed declaration roster at ingestion that cannot HOLD two entries for one name, and that is the row's next-rung trigger. THE EVIDENCE, and where it does NOT live. The first cut of this change put the RED in `v1.compiler.compiler_tests_rust`. That carrier is DEAD: its output is `#[cfg(test)] mod compiler_tests;` inside the v1-compiler crate, while the only test package that runs is the separate v1-compiler-tests, so cfg(test) is never set for it and the block is not compiled -- a mechanism `src/v1/05_emit_rust.dag` `checkpoint_table_bypasses_identity_note` already records a prior lane discovering the same way. It was reverted, and the evidence is authored where an equivalent witness demonstrably executes: dag/test/claim/duplicate_declaration_wall_witness_test.dag, `SubstrateInputsOnly`, driving the real v1 compile over inline fixtures through `compile_dag_rust_emit_check`, following `checkpoint_identity_keying_witness_test.dag`. Two REDs (fn/fn and data/fn) each paired with a control that differs from it by exactly the duplicated declaration and nothing else -- `compile_dag_rust_emit_check` answers false for ANY hard diagnostic, so the discrimination is carried by the PAIR, not by either row alone. All four measured `true` on the post-wall binary (sha256 1659e35605...), and per DESIGN 4b(4) the controls stay enrolled: they are the executing evidence that the rung is real. THE BOUND THAT MATTERS MOST IS NOT THE COMPILER'S, and it is written into both the witness and the gap-analysis row: where two definitions of one name are semantically EQUIVALENT, no behavioural witness can discriminate them whichever wins. Green-by-execution proves BEHAVIOUR and is structurally blind to duplicate AUTHORSHIP -- which this repository leans on everywhere, and which is why the live specimens above passed every witness over them. Also here: the DESIGN 4b row in docs/plans/compiler-guarantee-recovery-gap-analysis.md (invalid state, harm, distinguishing facts vs the cross-module last-import-wins row, rung, ceiling with reason, next trigger); a prose correction on dag/test/claim/duplicate_definition_binding_probe.dag, which asserted this refusal before it existed and is still `DeclinedLiveTree`, so what flipped is its SUBJECT and not its reach; and the fourth-lane receipt in `v1.std.core` `compiler_diagnostic_seed_projection_note`, which its own text obliges whenever the diagnostic coproduct grows -- cli_run.rs fn census FLAT at 895 on both sides, 2 added / 0 removed, 0 added lines declaring a fn. v1 SEED PURPOSE, since the change touches the seed: the wall is a floor repair on the name-resolution path the v2 self-host program compiles through, and the two repaired specimens are corpus modules that self-host must emit correctly. Nothing here grows the seed's capability -- the two cli_run.rs lines are the mechanical consequence of the coproduct gaining a member. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…n/main moves under other merges, so the named command reported a diff nobody authored (review 56644)
The receipt in `v1.std.core` `compiler_diagnostic_seed_projection_note` names three
commands and requires that each figure be re-derivable from them. All three were keyed
to `origin/main`. THAT REF MOVES. Measured on this branch, hours apart, with no edit to
the change being certified:
git diff --numstat origin/main -- src/v1/stage0/src/cli_run.rs
when authored: 2 added / 0 removed
at review: 6 added / 64 removed
The 64 removals are MAIN's work, not this branch's. A diff has two sides and only one of
them is this branch, so a receipt keyed to the moving side reports a figure that changes
without anyone touching the diff it describes -- which is exactly the rot this row exists
to make visible, arriving through the REF rather than through the NUMBER. That is why it
survived three prior lanes: each author re-measured honestly and the command was already
wrong.
REPAIR: every figure is now measured against `$(git merge-base origin/main HEAD)`, this
branch's own fork point, which does not move; and the base commit is NAMED (ddb7f53)
so the reading stays reproducible after a later merge. Re-measured against it -- fn
census FLAT at 895 on both sides, 2 added / 0 removed, 0 added lines declaring a fn.
THE EARLIER LANES' FIGURES ARE NOT RE-BASED OR AMENDED. They were correct against the
main they named at the time, and rewriting them would destroy the evidence that a receipt
keyed to a moving ref decays -- which is the finding. What changes is the command the
NEXT lane runs.
v1_std_core.rs is the regenerated mirror of the edited note; required-regen reported it
as the only drifting surface and it is installed from the candidate tree.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Fixed in a57a9ff. Review 56644 is correct: Both readings are true, and the difference is which side moved. Measured just now:
The 64 removals are main's work — main deleted them from The finding is bigger than this PR, which is why the repair is in the note and not just in the numbers. Every prior lane wrote Repair: all three figures now use Deliberately not done: the earlier lanes' figures are not re-based or amended. They were correct against the main they named at the time, and rewriting them would destroy the evidence that a receipt keyed to a moving ref decays. What changes is the command the next lane runs.
— sent from sleek-gull-113 |
The production specimen, and it is what this PR leads with
dag/gunbc/systemctl_list_units.dagdeclared one name twice, and thestate:filter on asystemd.Systemctl.ListUnitscall bound the function instead of the string it was written tobind:
The use at line 60 sits BETWEEN the two declarations. A reader going top-to-bottom has seen only
the
datarow when they reach line 60, and is right about everything they have read. That is whyreview did not catch it, and it is the argument for a wall rather than diligence.
Blast radius, measured rather than inferred — and narrower than first reported. Two modules
import the collided name:
gunbc.host_hygiene_liveness_observeandgunbc.host_hygiene_reaper_observe, both host hygiene observers, both using it to ask systemd whichunits exist in a non-default state.
gunbc.host_converge_slice1imports this module too, but itimports
systemctl_list_units_active_services, a different declaration that reachessystemctl_list_units_localand passes nostate:argument at all — so host convergence isnot on this path. An earlier relay of this finding said three consumers including host
convergence; that is corrected here at the source rather than left to travel.
What is NOT claimed: the emitted argv is not measured. What is established is that the reference
bound the function rather than the string, by the mechanism below and by direct execution of the
same shape.
The compiler defect underneath it
v1.compiler.envsymbol_index_insertis amap_insertkeyed on the qualified name, so a seconddeclaration of one name replaced the first and every reference bound whichever was written last
— with zero diagnostics at any severity. Two authorities for one fact (DESIGN §3), and since
names resolving is the ordinary compiler floor, this sat below it (DESIGN §4b): silent
wrongness, outside the ladder rather than a weak rung on it.
Measured by execution, instrument named. Binary built from source at
ddb7f533e4(main tip),sha256
1b2eb218e6725f76…, mtime 2026-08-27T13:31:07Z — not the installed/usr/local/bin/gunbc,which is two days stale and proves nothing about current main:
fn probe_dup() -> Int { 1 }thenfn probe_dup() -> Int { 2 }, caller callsprobe_dup()2— the last definition1data thing: String+fn thing(), bare reference tothing<fn thing>The third row is the shape the production specimen has, and it is not fn/fn — so a
per-declaration-kind wall would have missed it.
Live exposure, denominator stated
4163 tracked
.dagfiles scanned; 2 modules carried it, both repaired here:dag/gunbc/systemctl_list_units.dag— above. Thedatarow is renamedsystemctl_all_unit_states_selector; the function keeps its name, which is what both importers call.dag/test/claim/spark_managed_grant_reconcile_witness_test.dag— onedatarow declared twicewith an identical value; the second is deleted.
A first pass also flagged 9
extdepsfiles. That was my own regex truncatingservice shell.Findto
shell; the real service names are distinct. With full dotted names, 0 remain beyond the two.The wall
v1.compiler.resolvecheck_duplicate_declarationsfolds each module's own item list and emitsa blocking
DuplicateDeclarationon the second declaration of one name. It reads nothing outside themodule it judges — no corpus walk, no import resolution, no type information — so membership is
decidable from one module's source, which is what makes this a wall rather than a ratchet (DESIGN §5).
Rung R2 (mechanically preventable). The R4 climb is a keyed declaration roster at ingestion that
cannot hold two entries for one name, and that is the row's next-rung trigger.
Post-wall binary (sha256
1659e35605…, mtime 2026-08-27T13:33:11Z) refuses both shapes with a typed,located diagnostic; the single-definition control is unchanged at
1.The evidence, and where it deliberately does NOT live
The first cut put the RED in
v1.compiler.compiler_tests_rust. That carrier is dead: its outputis
#[cfg(test)] mod compiler_tests;inside thev1-compilercrate, while the only test package thatruns is the separate
v1-compiler-tests, socfg(test)is never set for it and the block is notcompiled — a mechanism
src/v1/05_emit_rust.dagcheckpoint_table_bypasses_identity_notealreadyrecords a prior lane discovering the same way. It was reverted.
The evidence is authored where an equivalent witness demonstrably executes:
dag/test/claim/duplicate_declaration_wall_witness_test.dag,SubstrateInputsOnly, driving the realv1 compile over inline fixtures through
compile_dag_rust_emit_check, followingcheckpoint_identity_keying_witness_test.dag. Two REDs (fn/fn and data/fn), each paired with acontrol differing from it by exactly the duplicated declaration and nothing else —
compile_dag_rust_emit_checkanswers false for any hard diagnostic, so the discrimination iscarried by the pair, not by either row alone. All four measured
trueon the post-wall binary,and per DESIGN §4b(4) the controls stay enrolled: they are the executing evidence that the rung is real.
The bound that matters most, and it is not the compiler's
Where two definitions of one name are semantically equivalent, no behavioural witness can
discriminate them whichever one wins. Green-by-execution proves behaviour and is structurally
blind to duplicate authorship — which this repository leans on everywhere, and which is why the
live specimens above passed every witness over them. That bound is written into both the witness and
the gap-analysis row.
Also here
docs/plans/compiler-guarantee-recovery-gap-analysis.md: invalid state, harm,distinguishing facts against the cross-module last-import-wins row, rung, ceiling with reason, next
trigger.
dag/test/claim/duplicate_definition_binding_probe.dag, which asserted thisrefusal before it existed and is still
DeclinedLiveTree— so what flipped is its subject, notits reach.
v1.std.corecompiler_diagnostic_seed_projection_note, which its owntext obliges whenever the diagnostic coproduct grows:
cli_run.rsfn census flat at 895 on bothsides, 2 added / 0 removed, 0 added lines declaring a fn.
Two
--required-regenpasses ran the full seed emit with the wall armed and completed, so the seedclosure carries no duplicate declarations either.
v1 seed purpose
The wall is a floor repair on the name-resolution path the v2 self-host program compiles through, and
the two repaired specimens are corpus modules that self-host must emit correctly. Nothing here grows
the seed's capability — the two
cli_run.rslines are the mechanical consequence of the coproductgaining a member.
🤖 Generated with Claude Code