Repository navigation
import deletion - #8277
import deletion#8277gunbai-bot[bot] wants to merge 53 commits into
Conversation
#8084 and #8080 were closed as quarry, so the base session/valiant-boar-65 sits at an archived head that will not move again. This is the lane's source contribution replayed onto main: the eleven authored files, with every generated artifact re-derived from the merged authority on this base rather than carried across (plan, stage0, .gitattributes, ci.yml). None of CI2's diff comes with it. Replay proven by execution, not assumed. regen_divergence_count=0 with build exit checked first, and every witness re-run on this base: the declaration matrix 18/18, the import-deletion bridge 3/3, and the four sibling scoped-roster files 13/4/4/7/3 -- zero failures. Contents, in the order they landed: Bridge real authored source into occurrence binding, with exposure grounding a parameter rather than a hardwired assumption. Commit B: pin the parser declaration-category denominator by execution. Every authored declaration form gets a row, including the forms that mint nothing -- the zero rows are the load-bearing half. Commit C: mint type declarations, constructors and fields, role-directed in `stamp_parsed_node_children`. It cannot be shape-inferred per node: a product FIELD node carries `inferred` Present with a Present ident_span, and a payload VARIANT carries the same `connective: Conj` a product declaration does, so a shape-first rule turns fields into type declarations. The member kind is a fact about the parent. A pattern's field position was minted as a FieldOccurrence DECLARATION. It names which field is being destructured, so it is a reference; the binder beside it is the declaration. Two defects at one authored name, discriminated by three rows keyed on exact occurrence identity so neither could green by masking the other. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The heal job failed because it regenerated a workflow path it is not credentialed to push: `.github/workflows/ci.yml` was one derivation pass behind this branch's emit plan. The single changed line is the heal job's own authored-conflict exclude list, which is derived from the plan, so it was missing the three stage0 modules this lane adds (the bridge witness, the declaration matrix, and its support module). Cause was mine and mechanical: `main_wet` derives ci.yml from the plan it reads at START, so a change that adds emitted files needs a second pass. I ran one on this base. Two passes now, and a third pass produces no further change, so the fixed point is shown rather than assumed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Resolutions, by class rather than by file: AUTHORED. `dag/gunbc/explicit_witness_admission.dag` conflicted on one row I had deleted and main still carries — the `english_emit_add_prose_holds` promotion I did as a favour for another lane. Took MAIN's side: that promotion belongs in its own change, not carried inside an import-deletion PR where a wrong call would red this branch for an unrelated reason. GENERATED. `.gitattributes`, `ci.yml`, the emit plan, `lib.rs` and one emitted witness were all regenerated from the merged authority rather than hand-resolved, per the driver's own instruction. Taking main's whole self-consistent stage0 as the bootstrap first mattered: a per-file `--ours` mixture built 44 errors, because half the seed was mine and half was main's. THE KEYSTONE GREENED, and its expect-red admission is deleted. Established in BOTH directions rather than observed once: against main's parser witness_import_less_reference_reaches_the_real_provider FAILS, against commit C's it PASSES, and the provider-withheld control stays green in both — so the pass is the new type-declaration population, not a closure that happens to carry the name. Its file is now enrolled file-grain beside its two controls, uniformly green. Scope unchanged: this is the SUPPLIED-PROVIDER claim, so provider discovery and provider-file edge projection remain open. That deletion also emptied `known_red_probe_v1_envelope_roster`, which fixes the floor's arm-time refusal on the previous push. My keystone admission had enrolled `gunbc_v1_claim_scoped_known_red_probe_batch` — a conditionally-enrolled batch — without the companion positional row the clamp table requires, exactly the ENROLLMENT COUPLING its own note warns about. Un-enrolling restores 6 positional batches against 6 clamp rows, so no clamp row was added, no budget was raised, and no operator signature is owed. INHERITED, NOT MINE: main cannot derive its own generated artifacts. This carries the `HasTrigger` heal (also opened standalone as #8117) because without it no plan can be derived here at all; the second break, `SubstrateLongLaneRow`, is #8116's and is deliberately not duplicated. Until both land, this branch cannot complete its regen chain, and that is an inherited blocker rather than this lane's red. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#8116 carries both halves of main's #8059 skew break and is proven green -- main_wet EXIT=0 with zero drift -- so this branch's copy of the same 81 rows is redundant. Pushing an identical repair from two directions into a merge is the duplication itself, not a safety margin, and the manager owning #8116 asked explicitly that it not happen. CONSEQUENCE, stated rather than hidden: this branch cannot derive an emit plan again until #8116 lands, so its three new emitted stage0 modules stay unregistered and the regen chain stays red. That red is inherited from main, not produced here, and it clears by merging main once #8116 is in. #8115 stays draft until then. Also correcting arithmetic I put in the closed PR and in the merge commit before it: 77 is the POPULATION of SubstrateLongLaneRow literals, not the count of broken fields. The broken field count is 18, out of 21 dissolve_on spellings, three of which are prose inside strings. The number I quoted answered a neighbouring question and read as though it answered this one. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…delete-imports-on-main
#8116 landed both halves of the #8059 dissolution-carrier skew and #8110 was reverted, so main derives its own artifacts again and this branch's inherited red clears. Merged with no conflicts -- the previous commit had already reverted the duplicated heal, which is what left nothing to collide. The three new emitted stage0 modules are now registered by the derived plan rather than by hand: the bridge witness, the declaration matrix, and its support module. `main_wet` reaches a fixed point proven by BYTES rather than by a third quiet pass -- md5 of .gitattributes, ci.yml and the emit plan is unchanged across a further run -- and the two-round seed regen verifies at regen_divergence_count=0 with build exit checked before the verify. Evidence re-run on the merged tree, not carried over: all seven scoped-roster witness files green -- the declaration matrix 18/18, the import-deletion bridge 3/3, and the four siblings 13/4/4/7/3. Zero failures. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The CI auto-heal merged main into this branch and pushed a regeneration (c43a89d) while I was doing the same work locally. Its result is not self-consistent and must not be taken: measured on that exact head, `regen_stage0 --verify` reports regen_divergence_count=5, while this tree reports 0. WHAT IT DROPPED, and how it is visible: the heal's emitted `v1_compiler_parse.rs` contains ZERO occurrences of `parsed_module_item_declares_type_name` while its own `src/v1/02_parse.dag` still contains commit C in full. So the source claims role-directed minting and the seed does not implement it -- a source/artifact divergence, committed. The cause is the heal regenerating with release-bins built from a merge ref that predates commit C; an emitter that does not know about a change cannot emit it, and its output then looks like a clean regeneration. This merge is `-s ours` deliberately rather than a file-by-file resolution. The remote's only unique content is those five stale generated files; its merge of main is the same main commit (4bd738b) this branch already merged, so nothing authored is discarded. Verified after the fact rather than assumed: divergence stays 0 and all seven scoped-roster witness files stay green. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…delete-imports-on-main
Module identity does not delete an import. An import line names a FILE, so the thing replacing it has to produce a file-grain edge -- the loader needs to know which file to pull into the closure, and a claim that only says which MODULE a reference belongs to leaves that unanswered. The keystone proved the reference reaches the right module; these rows prove the same binding projects the provider file, through the production projection rather than a copy of it. Three new rows, and the pair is discriminating in both directions: the consumer names LiveTreeDisposition TWICE (parameter type, return type), so a correct projection yields exactly 2 binding-provenance rows -- derived from the fixture's own text, not read off the parser; both rows name the same provider file, so the file-grain projection must dedupe them to exactly 1 edge, asserted with exact consumer and provider paths and a no-self-edge clause. Two rows becoming two edges is a dedup failure that over-loads the closure; two becoming zero drops the dependency the loader needs. One is the only answer that is right for a reason. a control that a withheld provider projects NO provenance rows and NO edges, distinct from the keystone's own control -- without it both counts above could be satisfied by a projection emitting edges from an empty binding population, which is the direction that silently loads files nothing references. PROVEN DISCRIMINATING, not assumed: perturbing the fixture to a single mention reds both count rows while the dedupe row still reports 1 -- so the counts are load-bearing and the dedupe row measures a different fact rather than echoing them. NO NEW MACHINERY, which item 5 requires: the provenance population and the dedup come from std.occurrence_binding_candidates, and the module-to-file index is built from the closure's own module_files rows. Nothing walks a filesystem, scans names, or builds a second module-to-file map. `ModulePathFileRow` is declared as an import for the same reason as last time -- it appears in a signature, so the emitted seed needs it, and that dependency is invisible in an import list until the build says so. This lane's own subject. regen_divergence_count=0, build exit checked first. 7/7 on this witness, 18/18 on the declaration matrix. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…g it THE DEFECT, which was mine and was the exact class this lane exists to delete. The previous revision reached its 2-provenance/1-edge result through a FAIL-OPEN ADAPTER: it matched only ReferenceBindingProjectionBound, discarded every other outcome, then hand-constructed AllReferencesBound from the survivors. So a population where one reference bound and another did not would have reported a complete closure and emitted a provider-file edge -- the false-skip direction, which silently under-loads. Every refusal arm (assembly, index, walk, binding, file projection, unbound, ambiguous) also collapsed to an empty list, so the withheld-provider control proved only that refusal and legitimate-zero are represented identically. THE REPAIR. One assembly, then the production total path and nothing local: structural_binding_walk_selected_references returns a BoundReferencePopulation that is AllReferencesBound or ReferencePopulationRefused carrying its first failure, and reference_derived_dependency_projection turns that into Ready or a typed binding/file refusal. This file no longer constructs AllReferencesBound, folds projections, or uses [] to mean a stage refused. Outcomes are one coproduct reusing the production refusal types; only two arms are local, and both are about the fixture's own population (unknown candidate path, reference-count mismatch). Not the top-level helper, deliberately: reference_derived_dependency_projection_for_assembled_closure selects through the clause-e filter, which excludes TypeOccurrence, so it would have selected zero references and reported Ready with zero rows -- every count assertion passing vacuously. Moving TypeOccurrence into clause e to reuse that walk is item 4's wrong turn and is not taken here. PROVEN DISCRIMINATING BY REINSTATING THE DEFECT, not argued: with the fail-open put back, exactly two rows red -- the withheld-provider and partial-population controls -- and every other row, INCLUDING the 2-to-1 pair, passed. The positive law never detected the fail-open and never could, because a fabricated success is still a success. Those two controls stay enrolled as the executing evidence that totality holds. The planted partial-population control needed a second repair to have teeth: its first version selected only the subject's references, so the unbindable one never entered the population and the row passed against a Ready projection. Selection now takes a name list and the control requires all three references, one of which cannot bind. Also in this commit, from the same review: idb_source_for's "every non-consumer path is the provider" default is gone, replaced by explicit CandidateSource rows. Under the default, an unrelated candidate silently received the real provider's text, so unrelated and actual provider were not representable as different contents and every later provider-discovery control would have been misleading. An unknown path now refuses. the keystone is renamed from real_provider to supplied_provider and the read-off-disk/live-corpus claims are gone. Both sources are embedded and the provider is handed in, which the note admitted while the name denied. new controls: pool-independence as an EQUALITY on binding identities and the edge set (not merely "still green"), same-file provenance retained with no self-edge, malformed source as a parse refusal, and selection refusal. Item 3 is NOT complete: the ambiguity control needs candidate declaration identities that ReferenceBindingProjectionAmbiguous does not currently carry, and the review requires repairing that carrier rather than working around it here. Missing/conflicting module-file controls are also still owed. 12/12 on this witness, 18/18 on the declaration matrix, divergence 0 with build exit checked first. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… ambiguity and storage controls
ReferenceBindingProjectionAmbiguous named an unbindable reference and threw
away what it was ambiguous BETWEEN. resolve_reference_occurrence_binding
already returned OccurrenceAmbiguous { occurrence, candidates } and the
projection matched `candidates: _`, so a consumer could learn that two
declarations competed and never which two -- and two ambiguity outcomes of
equal cardinality over different declarations are different states with
different remedies. The carrier now carries the upstream
AmbiguousBindingCandidates<OccurrenceId> itself; no new type was minted,
because head+second+tail already makes a one-candidate ambiguity
unconstructible.
The same class was then found one layer out, by tightening a control rather
than by reading code: the bridge witness collapsed
AssembledCrossFileBindingClosureRefused to a bare marker, so a storage
contradiction could not be asserted at its identities. It carries the
refusal through now.
Controls added, each asserting identities rather than counts:
- two admissible providers of one name refuse as ambiguous, naming both
competing declaration OccurrenceIds and joining each against the declaration
population of the assembled transport
- two files claiming ONE module path refuse at closure assembly, naming the
module path and both competing files. Which stage refuses is measured: an
earlier version tolerated assembly OR index refusal, which is a two-answer
assertion that would have passed had the contradiction moved between stages
- a bound population projected against an index with the provider module's row
REMOVED refuses the file projection, so a dropped edge cannot read as success
Stale prose stripped: the offline recipe still named a `real_provider`
function that no longer exists, listed two other functions that no longer
exist, and described an expected red this file no longer carries. The
provider is SUPPLIED by the fixture, and the recipe now says so and stops
enumerating a roster the file-grain enrollment already expands.
15/15 green by execution. Terminal acceptance is unchanged and unreached:
imports, their grammar and their AST are still present, and no production
node is accepted.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…mitter defect Four generated artifacts conflicted (.gitattributes, ci.yml, the emit plan, lib.rs). Resolved by regeneration, not by picking a side: main's bytes predate this branch's three new src/v1 witness modules, so taking either side would have produced a plan that omits or duplicates them. Regeneration surfaced a real defect rather than only bytes. `regen_stage0 --verify` reported divergence 0 while `cargo build` failed with E0308: a nested coproduct pattern through a reference-counted field emits `projection.as_ref()` matched against a bare enum, so the pattern the v1 interpreter accepts does not survive emission. Byte-fidelity is not target-language validity, and the verify step cannot see the difference. The control is authored as two sequential matches -- semantically identical, and the idiom the surrounding corpus already uses. That is not a repair, and `idb_nested_rc_pattern_emitter_defect` says so at the site, names the emitted file, and carries its dissolution: the Rc wrap-decision predicate. Changing a load-bearing emitter is not this control's business. 15/15 green by execution after the split; seed rebuilds clean; verify at 0. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…he measured namespace frontier on the roadmap DECLARATION DENOMINATOR, three forms measured rather than assumed. A probe rendered each population once and was then deleted rather than left behind as a second instrument: - a type ALIAS mints TypeOccurrence. It is the third named-type shape and the one a connective-keyed rule misses, so the whole-population row now asserts THREE named types, not two -- with two the alias hole was undetectable from this file while both existing rows passed. - the pattern field position mints a FieldOccurrence REFERENCE at 394-411 whose containment locates it inside the matching function. This is the positive half of a pair whose other half only says no DECLARATION sits there -- satisfied equally by a correct reference and by nothing being minted at all, which would drop the edge silently. Asserting it required a references-side projection; the support authority now renders references at full identity too. - a module-scope value declaration mints NOTHING. `data name: T = v` binds no name, so a bare cross-file reference to a module datum cannot bind and the derived closure is short by every module-value edge in the corpus. The module-value row is enrolled EXPECTING RED, not written as a zero row in the green file. A zero row asserting the empty population would be green today and would make the defect the contract -- the shape DESIGN section 5 names when it rules that a test asserting a degradation's own string enshrines it. The row asserts population size and not category, because which OccurrenceCategory a module member value binds is a genuinely open modelling question and a red row is not where it gets decided. Cause located at parsed_occurrence_role_for_node, which classifies a bodied node by the ABSENCE of a type annotation -- a defect distinct from the three role-directed minting closed. Its admission carries the companion positional clamp row the enrollment coupling requires. ROADMAP, in this commit because the capability boundary moved. NO FRONTIER ADVANCES and that is the correct reading: the provider is SUPPLIED by the fixture, nothing discovers one from a source root, no ordinary compiled path asks for any of it, and the closing contract's capability rows read Unavailable under the same triggers as before. Editing an admission row to reflect fixture evidence would be the rung inflation namespace_node_recut_note already priced. What lands is the measured state, dated, naming both what fifteen executing controls established and what they did not, plus the module-value hole so it is not discovered late -- and the lane joins the focused page because it is being worked. Imports, their grammar and their AST are all still present; the hand-written edge producer is untouched; nothing is cut over. 20/20 green in the enrolled matrix, the red row red as declared, seed rebuilds clean, verify at 0, and the clamp, focus, frontier, document, execution-contract and roadmap gates all pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ching clause (e) Clause (e) excludes TypeOccurrence for a stated reason -- a parser-produced annotation such as `Int` would enter the population and refuse it whole, because std.types was never given to the closure. Both easy repairs are forbidden. Reclassifying TypeOccurrence as clause-(e)-inducing makes that clause say something its contract does not, and every consumer inherits the change silently. Dropping an unbindable type reference to keep the projection green renders "could not see the provider" as "nothing is depended on" -- the empty-observation narrow, strictly worse than the widen DESIGN section 5 forbids, because a widen is expensive and a narrow is silently uncovered. So dependency-inducement becomes its own authority with one arm per ROUTE, COMPOSED over clause (e) rather than replacing it: it reads the clause-(e) verdict first and inspects the category only inside the NotInducing arm, so clause (e) remains the authority for its own question. Both routes then reach the SAME BoundReferencePopulation through one fold over ONE candidate index -- the N2 typed entry already returns a ReferenceBindingProjection, so no second binding authority was minted. Its index-supplied twin exists because the inputs-taking entry rebuilds a candidate index per call, which is correct for a single-reference API and quadratic inside a walk. Six controls, and the load-bearing pair is measured at the EDGE: - clause (e) STILL excludes type references. Its own row, because the point of adding an authority instead of editing the filter is that this stays true. - the admission table is total over the closed coproduct: fields and methods stay excluded, carrying the category they excluded. Asserting only the type arm would let a change that admitted field references pass, and a field reference admitted as dependency-inducing makes every record access look like a file dependency. - clause (e) ALONE derives ZERO edges for a type-only consumer, and the joined walk derives exactly ONE, to the provider file. An earlier draft asserted clause (e) selects nothing there; measurement refuted it -- it selects one same-file parameter mention, which is why the discriminator is the edge and not the population. The zero row alone is satisfied by a projector that produces nothing; the one-edge row alone by one that always emits an edge to any supplied provider. Together neither passes. - three admitted references reach one population at an EXACT count; a join that dropped a route would satisfy any lower bound. - an unsupplied type reference refuses and NAMES the occurrence. That name is the loader's work list, which is the whole reason the production switch waits. The production projection deliberately does NOT consume this yet, and the authority note says so: admitting type references makes the demand real, and the demand is item 6's fixed point. A surface with no consumer is coverage by illusion, so its dissolution is named -- when the switch lands, reference_derived_dependency_binding_references has no caller and deletes. 21/21 in the bridge witness; the candidates, pool-independence and N2 type binding witnesses stay green; seed rebuilds clean; verify at 0. CI red on this branch remains INHERITED from main and is unchanged by this commit: main @341d320 fails the same check with the same two classes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
module_path_file_row_note has said, since it was written, that v2.compiler.source_authority ModuleStorageBinding / ModuleStorageIndex is the single home for the path-to-module binding and that ModulePathFileRow is a scaffold projection OF it. Nothing performed that projection. Every ModulePathFileRow in the tree was a hand-authored literal inside a witness, so the two representations coexisted with no derivation between them -- the exact parallel-representation state the note claimed to be avoiding, and the reason provider discovery had nothing to consume. This is the missing function, and it lives with the binding because the binding is that module's fact. TWO ARMS, because a module does not always have a file. ParsedFromSource carries an Artifact whose file_path IS the storage realization. ProducedByBehavior means a module the compiler produced: there is no file, and every path the projection could invent -- the module name reshaped, the empty string, the producer's own file -- is indistinguishable downstream from a real one, which is fabricated plausible output. So it refuses, naming the module and its producer. The index projection refuses WHOLE, and that is the load-bearing decision. The tempting arm projects the file-backed bindings and drops the rest: every positive row still passes, and the caller receives a row set it believes is the whole storage picture with a module silently missing -- the empty-observation narrow at set grain. A caller that legitimately wants only the file-backed subset filters the index first, which is a statement of intent rather than a silent loss. Six controls. The positive row pins BOTH derived facts at exact values -- dotted module path and the artifact's own path -- because asserting that some row appeared would pass for a projection that answered about the wrong module or invented a path from the name. The whole-refusal row plants an unprojectable binding BETWEEN two projectable ones, so a dropping implementation would return two rows and read as complete. Multiple failures are all reported, so the deficit is countable rather than first-only. Order is asserted at both ends, which is what a reversed fold swaps. And an empty index is Ready with no rows, stated as its own row so "no input" stays distinguishable from "input whose answer was dropped". This is the first half of item 5: provider discovery needs a derived row population, and until now there was none. The second half consumes the whole-tree parsed manifest discover_source_root_ingest --emit-module-binding-manifest already produces, supplied as modules in an ephemeral overlay source root the way module_binding_supply_transport does -- so no new filesystem walker, name scanner or module-to-file map is required, and the rows arrive as modules rather than through a Filesystem service, which dissolves the placement contradiction stage0_emission_model_gap_note records. 6/6 green by execution; verify at 0; no generated artifact moved. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…delete-imports-on-main
…ur the prose-row wall THE PROSE-ROW GATE WAS RIGHT AND IS NOW GREEN. It refused exactly one row -- module_path_file_row_projection_note in src/v2/compiler/source_authority.dag, a path already migrated to authored // annotations -- and counted 26 further rows as out-of-scope residue it reports without enforcing. The refused rationale moves into a leading // block above the declaration it explains, which is what DESIGN section 4c admits. Nothing was weakened to pass: the argument is unchanged, only its carrier. PROVIDER ROWS NOW COME FROM THE REAL TREE. The new witness projects the whole-source-root PARSED module-to-path manifest through the authority, and the point is what it does NOT add: no filesystem walker, no name scanner, no module-to-file map, no second dependency producer. discover_source_root_ingest --emit-module-binding-manifest already emits those rows via v1_compiler_parse, and module_binding_supply_transport already overlays them as a MODULE in an ephemeral source root, so they arrive as data through carriers that existed. Arriving as a module is also what makes this reachable: a filesystem_read would drag the Filesystem service into the emission closure, which under src/v1 produces an unregisterable dry_run.rs -- the placement contradiction the bridge witness records. Five controls, enrolled in that transport's claim list: the real rows project; EVERY manifest binding projects, asserted as a count identity against the manifest's own count, so a Ready answer is the claim that the whole observed corpus is file-backed; a known module resolves to its known real path; an absent module does not appear; and a REAL module bound to the WRONG file does not appear either -- that last one distinct from the absent-module row because it is the failure a projection reshaping names into paths would produce, and it would pass the absent row. All are vacuity-guarded: run without the overlay, the manifest is the committed Empty stub and an empty index projects to an empty row set that is legitimately Ready, so every claim requires a populated manifest and a non-empty projection first. A PRODUCER DEFECT FOUND BY MEASUREMENT, named rather than routed around. A second known pair in the dag/ root was authored and REMOVED: the manifest cannot carry that root at all. The producer renders the artifact id and textual locus file as a BARE symbol from the file stem, and dag/ holds dag/std/import.dag and dag/extdeps/languages/go/module.dag, so a two-root emit writes `id: ^import` and `file: ^module` -- keywords -- and the whole manifest fails to parse. Measured: 3517 rows written, then `for_each_parsed_module_binding: parse error ... expected identifier or ( after ^, found keyword`. This matters to the lane rather than being trivia: providers live in dag/ as well as src/v2, so reference-derived closure over the real corpus cannot complete while the producer cannot describe half of it. The limit is stated on the consuming witness and on the transport with its dissolution -- the file path is already carried as a String in the same row, so the stem-derived symbol is a second, weaker naming of a fact the row already has. Also corrected here: I had described this manifest as whole-TREE. It is whole-source-ROOT, and the transport passes src/v2 only. 5/5 green through the transport against a real overlay; the sibling consistency and crate-layout claims in the same gate are unaffected. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…d defect instead of restating it TWO FIXES, one of them a correction to the previous commit. THE WITNESS WAS DISCOVERED AND RUN WITHOUT ITS OVERLAY. CI ran six of my rows through the corpus discovery walk -- which picks up any *_test.dag under a source root, not only the three scan dirs -- and outside the transport host_module_bindings is the committed Empty stub, so the vacuity guard refused and every row returned false. The guard was working; the placement was wrong. It now carries a WitnessExclusionRow in exactly the crate_layout posture, which is the same manifest, the same Empty read and the same refusal, and it says explicitly that the refusal is the guard working rather than a defect. The witness keeps its executing consumer: the transport's ClaimRun roster. This is the local-green-is-not-CI-path-green class, and I walked into it: the five rows passed through the transport and passed against a hand-made overlay, and neither of those is the admission path. THE dag/-ROOT DEFECT WAS ALREADY DOCUMENTED, and the previous commit presented it as a find. gunbc.commit_workflow heal_writer_claims_no_fixture_exemptions_note records that discover_source_root_ingest mints edge identifiers from source-file BASENAMES with no keyword escaping, and names all three colliding files -- dag/extdeps/bmc/capability.dag, dag/extdeps/languages/go/module.dag and dag/std/import.dag, minting ^capability, ^module and ^import. I reproduced it independently and found two of the three. The witness now CITES that authority rather than standing a second description of the same fact beside it, which is the nicknaming DESIGN section 3 forbids applied to prose, and it takes that note's dissolution trigger instead of minting another. What is genuinely new is kept and stated: the consequence for this lane, that providers live in dag/ too, so reference-derived closure over the real corpus cannot complete while the producer cannot describe half of it. The exact-witness-admission gate is green with the new row. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…delete-imports-on-main
…brace that broke every gate DISCOVERY/ENROLLMENT REPAIR, and the roster is now one authority. The five overlay-dependent claims were inline in the transport's call, so the function names existed only inside an expression no coverage accounting can read. They are now `module_binding_supply_claims`, one declared List<ClaimRun> the transport consumes, so execution and any consumer census read the SAME rows rather than two lists that can drift. The names are not duplicated anywhere. Discovery no longer executes them. The corpus walk picks up any *_test.dag under a source root -- not only the three scan dirs -- and outside the overlay host_module_bindings is the committed Empty stub, so the vacuity guard refuses and every row returns false. Selection is removed with a WitnessExclusionRow in exactly the crate_layout posture (same manifest, same Empty read, same refusal); scan dirs are NOT broadened and no vacuity guard is weakened. TWO CONTROLS I DELETED, RESTORED. Rewriting the dag/-root comment in the previous commit replaced a span that swallowed msrs_an_absent_module_is_not_projected and msrs_a_real_module_is_not_bound_to_the_wrong_file. It surfaced as `no such function` when the roster called them, which is the one way it could surface -- the file still parsed and the remaining three still passed, so nothing else would have said so. Both are back with their arguments intact. A BRACE THAT BROKE EVERY GATE. #8148 landed a reason string containing `Classified{LocalRead}`, and `{` opens interpolation in .dag, so the module raised `undefined variable 'LocalRead'` and nothing that resolves explicit_witness_admission could run -- which is every gate I needed. Escaped to `\{LocalRead\}`. One character, no semantic change, and it is a base-side defect fixed here only because it blocks this branch entirely. Executed: transport gate true (11 claims, mine among them); bridge witness 21/21; main_wet to a no-op fixed point; regen_stage0 re-emitted and the seed rebuilt clean; regen --verify divergence 0. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…e, third instance of one class dag_compile_clean_gate_passes was the only red left after the discovery repair, and its hard diagnostics were `unresolved type 'ArtifactIdentity'` in three cache witnesses this branch never touched. Cause: two declarations of that name now exist -- dag/std/cache_interface.dag's generic ArtifactIdentity<T>, and a new non-generic ArtifactIdentity in src/v2/compiler/self_host/generation.dag that arrived with main. Adding a name to the flat namespace is a corpus-wide edit, so the generic uses stopped resolving in files unrelated to either. The newer name yields, per single authority, and it is renamed rather than suffixed because the old name was wrong for it: the type is `ArtifactMaterialized | ArtifactNotMaterialized`, which is a MATERIALIZATION STATE and not an identity at all -- the field holding it is already called materialized_artifact. It becomes ArtifactMaterialization, with artifact_identity_agrees following as artifact_materialization_agrees. Eight sites across three files; no behaviour change. THIS IS THE THIRD INSTANCE OF ONE CLASS IN TWO DAYS, and that is the finding rather than the rename. gunbc#8151 fixed the same shape twice (Memory against std.measure's Quantity variant, unsatisfied_capabilities against the auth fold), and both times the break surfaced in a third file that neither PR touched. A flat-namespace homonym is DECIDABLE, so it is a construction wall waiting on the containment authority -- which is this lane's own terminal work -- not a matter of diligence. Until that lands it recurs at roughly the rate two lanes mint the same bare noun, and each recurrence costs an unrelated PR its CI. Executed: the three cache witnesses and both self-host witnesses green; main_wet to a no-op fixed point; regen_stage0 re-emitted; seed rebuilt clean; regen --verify divergence 0. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ecutingConsumer The admission refusal was mine and the classification was wrong: 5 UnclassifiedPathDeferral, "enrolled witness row(s) excluded from discovery name zero executing consumer" (run 31567336073). WHY OfflineLocalRecipe WAS THE WRONG ARM, having copied it from the crate_layout siblings. witness_path_policy_execution_standing_in maps an offline-classified row to LegacyFrozenPathDeferral only when gunbc.witness_deferral_freeze already names it, and otherwise to UnclassifiedPathDeferral, which is refused -- the forward hole the 2026-08-04 ruling opened deliberately. The siblings predate that ruling and are held by the frozen baseline, which may only SHRINK, so a NEW witness cannot join them and must not try. Copying a precedent's classification without checking which arm it reaches is what produced this. FixtureExplicitRoster is the honest arm and reaches WitnessHasExecutingConsumer, because the executing consumer genuinely exists: the declared roster module_binding_supply_claims, which the transport runs. That is the operator's second classification done at the authority rather than as an exemption -- one typed roster read by execution, and a classification the coverage fold already understands, with no second list of function names anywhere. Proven by calling the production fold rather than by rerunning the floor: witness_path_policy_execution_standing for this entry and function now returns WitnessHasExecutingConsumer, where it returned UnclassifiedPathDeferral before. The freeze witness, the exact-admission witness and the exclusion-reconciliation witness are all green; the frozen roster is untouched and did not grow. main_wet to a no-op fixed point; regen_stage0 re-emitted; seed rebuilt clean; regen --verify divergence 0. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…e CI batch witness_live_floor_discovery_batch_count_is_four went red, and measurement said the live count is FIVE. The cause was mine: enrolling the module-value expected-red row is v1-scoped, so it enrolls the conditional gunbc_v1_claim_scoped_known_red_probe_batch, and that is a whole additional corpus batch in the floor plan -- purchased for exactly one assertion, with a companion clamp row to fund it. I did NOT bump the literal to five. Editing an oracle so it agrees with my own change is the change-detector shape, and here the change was not worth defending: a fixture-only red row that moves no production boundary is precisely what the anti-stall constraint rules out, and its cost is a per-PR batch every other lane pays. So the row, its admission and its clamp companion are removed and the count returns to four by construction rather than by re-pinning. THE REQUIREMENT IS NOT LOST, which is the only reason removing evidence is acceptable here: nothing climbed, so there is no rung to protect, and the finding is already carried in gunbc.roadmap_authority namespace_lane_measured_frontier_note -- a module-scope value declaration binds no name, so a bare cross-file reference to a module datum cannot bind and the derived closure is short by every module-value edge, with the cause located at parsed_occurrence_role_for_node. When the loader needs module-value bindings the assertion returns riding the loader's own witness, where it costs no extra batch. Deleting the source also required deleting its emitted seed file: regen refused with `committed generated stage0 file(s) are absent from the derived emission plan`, which is the derivation working -- the plan is derived from the observed module closure, so a removed module leaves its .rs orphaned and the gate says so rather than leaving dead output committed. Executed: witness_live_floor_discovery_batch_count_is_four and ci_floor_plan_witnesses green; witness_floor_appends_one_selected_native_batch green; the clamp-authority and exact-admission gates green; regen_stage0 down to 144 files; seed rebuilt clean; regen --verify divergence 0. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three conflicts, all resolved by taking main rather than by merging text. MY RENAME IS WITHDRAWN. main fixed the flat-namespace collision itself, renaming the self-host type to GeneratedArtifactIdentity where I had renamed it to ArtifactMaterialization. Their name is the one on main, so mine is a second answer to a question already settled -- exactly the fork this repository consolidates later at cost -- and the self-host generation, promotion-admission and both witness files are taken wholesale from origin/main. My reasoning for the other name (the type is a materialization state, not an identity) is not worth a divergence and is not re-litigated here. ci.yml conflicted as a generated artifact and was regenerated from the authority rather than hand-merged, leaving a one-line delta. Executed: both self-host witnesses and the cache witness that the collision originally broke are green; main_wet to a no-op fixed point; regen_stage0 at 144 files; seed rebuilt clean; regen --verify divergence 0. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…left behind compile-clean's only hard diagnostic was `name 'ArtifactIdentity' not found in module 'v2.compiler.self_host.generation'` at dag/gunbc/self_host_artifact_materialization.dag. main's own rename to GeneratedArtifactIdentity did not update that consumer, so the defect is on main and arrived here with the merge -- the same class as the collision the rename was fixing, one step later: a name change is a corpus-wide edit and this one missed a file. Three sites updated in that module. Nothing else needed touching: the remaining ArtifactIdentity references in the tree belong to std.cache_interface's GENERIC ArtifactIdentity<T> and are unrelated -- renaming those would have broken the cache witnesses this class already broke once. A TRAP WORTH RECORDING, because it nearly sent me chasing a phantom: running dag_compile_clean_gate_passes standalone through `gunbc run` returns false regardless of the tree, and its companion receipt says why -- `compile-clean failure detail unavailable: no in-run compile receipt`. The gate CONSUMES the executor's one in-run compile receipt; outside the executor there is none, so the false is the invocation path, not a compile error. Verified the actual fix by resolving the repaired module directly instead: it now reaches a call-contract mismatch on a missing argument, which is proof the import resolved. Executed: dag/gunbc/self_host_artifact_materialization.dag resolves; self_host_artifact_materialization_witness_test green; main_wet to a no-op fixed point; regen_stage0 at 144 files; seed rebuilt clean; regen --verify divergence 0. The real-execution sibling refuses hermetically for wanting a real cargo build, which is its declared posture and not affected by this change. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The loader fixed point: std.reference_derived_closure iterates an entry's file closure out of accepted structural bindings, with no import list and no hand-selected provider path anywhere on the path. TWO POPULATIONS, DELIBERATELY NOT ONE, and this is the correction that made it work. The candidate index is built over the WHOLE ordinary source-file universe; the references BOUND are selected by the file they belong to, from the admitted closure only. Collapsing them deadlocks: with the index over admitted files alone, a bare reference to a not-yet-admitted provider has no declaration to bind to, so it is Unbound, so no provider file is derived, so nothing is ever admitted. A module-to-file index cannot stand in — storage realization says where a module lives, never which module declares a name. Selection is by the reference's own file, derived from its module path through the module/file index, because assembly rekeys every occurrence into one transport and the row boundary is gone by then. A reference whose file cannot be derived REFUSES rather than being dropped; dropped, it would narrow the selected population silently and the step would report the consumer as having fewer dependencies than it has. The executed trace, no provider-path input: consumer admitted -> LiveTreeDisposition binds to the exact declaration -> src/v2/std/live_tree.dag becomes pending -> provider admitted -> pending empty -> Settled with one provider edge, 2 iterations Three outcomes and the absence of a fourth is the point: there is no arm returning the edges for whichever references happened to bind. An unbound or ambiguous reference in the selected population refuses the whole step, because a closure short by one provider is not a smaller closure — every consumer downstream reads it as complete. joined_dependency_binding_walk_selected is factored out of the existing joined walk (which now delegates to it with every reference selected), so clause-(e) and type references still reach ONE population through one fold over one index. No second closure, no second totality law. Witnessed by execution, 8/8, src/v1/tests/claim/reference_derived_closure_witness_test.dag: the keystone trace; the second step settling over the first step's own returned state; the whole fixed point at exactly two iterations; the carrier law that every admitted mention keeps its receipt while edges dedupe to one with no self-edge (measured three provenance rows, not the two I first asserted); the provider outside the universe refusing as Unbound; an entry outside the universe refusing; two declaring files refusing as ambiguous naming both identities; and an unreferenced universe file staying out of the closure, so universe breadth does not become closure breadth. Homed in dag/std beside its carrier rather than under src/v2: the witness needs the v1 seed parser to produce real transports, and a src/v1 witness importing a src/v2 module is invisible to the regen world — regen refused with "function not found in scope" until the file moved. production file/function changed: dag/std/reference_derived_closure.dag reference_derived_closure_step / reference_derived_closure_fixed_point; dag/std/occurrence_binding_candidates.dag joined_dependency_binding_walk_selected focused command executed: claim_batch --entry src/v1/tests/claim/reference_derived_closure_witness_test.dag (8/8 PASS); regen_stage0 --verify regen_divergence_count=0 next production function: v1_compiler.cli_run source_root_ingest_artifact_id_for_path — take the basename out of the identity decision (mint through the string-taking Symbol authority on the repo-relative path the row already carries) so dag/std/import.dag, dag/extdeps/languages/go/module.dag and dag/extdeps/bmc/capability.dag are representable, then run this fixed point over dag + src/v2. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…esentable The module-binding manifest could not describe half the corpus, and the cause was an identity derived from a lossy projection of a fact the row already carries. source_root_ingest_artifact_id_for_path minted a `^stem` symbol literal from the file BASENAME. That is wrong twice. It is UNREPRESENTABLE for any file whose stem is a reserved .dag keyword -- dag/std/import.dag, dag/extdeps/languages/go/module.dag and dag/extdeps/bmc/capability.dag emitted `^import`, `^module` and `^capability`, so any manifest scope touching dag/ wrote a file that does not parse. And it is not an IDENTITY at all: two files with the same basename in different directories minted the same symbol, so the manifest's own rows collided silently -- a defect that was never going to announce itself, since nothing about the output looks wrong. The repair is not keyword escaping. The basename is out of the identity decision entirely: the id interns the repo-relative PATH through the string-taking Symbol authority (symbol_intern_lexeme), exactly the move the module-path half already makes with qualified_name_from_string_segments. Keywords are inert because nothing is ever spelled as an identifier, and same-named files in different directories stop colliding because the path is what distinguishes them. source_root_ingest_symbol_from_stem is deleted rather than fixed; there is no sanitizing pass left to get wrong. Measured end to end, not by reading the emitter: 3533 bindings over dag + src/v2, manifest emitted, PARSED and resolved, and all nine row-supply projections green. The wet gate run_module_binding_supply_gate now emits over BOTH ordinary source roots and returns true, including its parse oracle. The controls are the ones the removed comment promised, ONE PER KEYWORD rather than one for the class -- each is a distinct file whose stem is a distinct keyword, and a repair that fixed only the one a class-level assertion happened to visit would pass it: std.import -> dag/std/import.dag extdeps.languages.go.module -> dag/extdeps/languages/go/module.dag extdeps.bmc.capability -> dag/extdeps/bmc/capability.dag plus a population row asserting the index spans both roots at once, which the per-pair rows cannot state: a producer invoked with one root would satisfy every pair it happened to cover and fail that one. The scope comment on the consuming witness dissolves with the defect (its stated dissolve-on was exactly this), and the "dag/ root is not covered" paragraph is replaced by the assertions rather than by a claim that it is now fine. Why this lane needed it: providers live in dag/ as well as src/v2, so the reference-derived closure landed in the previous commit could not be run over the real corpus while the producer could not name half of it. production file/function changed: v1_compiler.cli_run source_root_ingest_artifact_id_for_path / source_root_ingest_compilation_unit_for_path (source_root_ingest_symbol_from_stem deleted); v2.workflow.module_binding_supply_transport mbs_dag_source_root + mbs_emit_args focused command executed: discover_source_root_ingest --source-root src/v2 --source-root dag --emit-module-binding-manifest (3533 rows, zero `^` literals); claim_batch module_storage_row_supply_test 9/9 PASS over that manifest; run_module_binding_supply_gate = true; regen_stage0 --verify divergence 0 next production function: supply this manifest's rows as the loader's universe and run reference_derived_closure_fixed_point over dag + src/v2, then replace dependency_resolution_facts_live's body and differential its edge set against the import-derived one in both directions with a typed disposition per row. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The loader now runs from the ordinary compile path over a real source-file
universe, and the first thing it produced was a refusal I could not act on. That
is a defect in my carrier, not in the corpus, so it is fixed here rather than
worked around at the reporting end.
A REFUSAL NAMING AN OccurrenceId IS NOT A LOCATED REFUSAL. The id is an allocator
ordinal: it means nothing to a reader, it means nothing to a tool, and assembly
rekeys it, so it is not even stable across universes and cannot be joined back to
the file it came from. The authored name and byte span sit in the assembled
transport's own index, one lookup away. Carrying them is not new observation -- it
is declining to discard what the step already holds.
ClosureBindingRefused now carries first_failure_locus (authored name + span) and
unbound_count. An occurrence the index cannot resolve gets its OWN arm,
ClosureRefusalNotLocatable: a population refusal whose subject cannot be located
is a different failure with a different cause -- the transport and its index
disagreeing -- and collapsing it into a missing locus would hide that.
reference_binding_projection_occurrence lands on the carrier that owns
ReferenceBindingProjection rather than as a private match in the loader (one
authority, ten arms, total over the closed coproduct). It answers Optional rather
than fabricating an id, because the four index-build refusal arms are failures of
the WHOLE transport, not of one occurrence, and answering them with some
particular occurrence would point a reader at an innocent site.
MEASURED, from the ordinary path, no fixture and no provider-path input:
--source-root src/v2/std --entry src/v2/std/live_tree.dag
universe=116 parse_refusals=0
Refused after 2 iteration(s):
ClosureBindingRefused: 'FreeMonoid' at bytes 269-279 unbound
(25 unbound reference(s) in the selected population)
Two facts in that one line. The closure GREW on the real tree -- two iterations
means a provider was discovered from a binding and admitted -- and then it refused
precisely, on a name I can check. The refusal is CORRECT and the fault was my
input: FreeMonoid is declared in dag/std/algebra.dag and merely IMPORTED by
src/v2/std/algebra.dag, so the declaring file was outside the src/v2/std universe I
supplied. A loader that had narrowed instead of refusing would have reported a
closure short by every dag/-rooted provider and every consumer would have read it
as complete.
The witness rows that match this refusal now assert the LOCUS -- authored name
LiveTreeDisposition with a nonzero span and a nonzero unbound count -- so the
located half is proven by execution rather than merely typed. 8/8 green.
The probe entry point is deliberately not enrolled in the release-bins pack: no
transport addresses it, and enrolling a name nothing invokes would be a claim of
enrollment without a consumer.
production file/function changed: dag/std/reference_derived_closure.dag
closure_binding_refusal / locate_occurrence (+ ClosureRefusalNotLocatable);
dag/std/occurrence_binding_candidates.dag reference_binding_projection_occurrence;
v1_compiler.cli_run reference_derived_closure_over_source_roots
focused command executed: reference_derived_closure_probe --source-root src/v2/std
--entry src/v2/std/live_tree.dag (located refusal above);
claim_batch reference_derived_closure_witness_test 8/8 PASS;
regen_stage0 --verify divergence 0
next production function: reference_derived_closure_over_source_roots on the full
dag + src/v2 universe (running; 3533 files parsed into one assembly is the cost
question), then the same-file two-way differential against
import_resolution_facts_live -- which needs this import-blind producer, because
the host reference producer emits nothing for a file that still carries imports
(measured: 800 of 3533 files are import-free, so those two producers partition
the corpus and cannot disagree on any file).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Review findings on d07bc0f, all four accepted. This is its commit 1. THE UNIVERSE IS THE DENOMINATOR FOR EVERY BINDING DECISION, so building it now refuses instead of counting. The previous version pushed unreadable and parse-refused files onto a counter, omitted them, and ran the closure anyway. That is unsound in the exact way this lane keeps closing, and worse than the review stated: a dropped file may hold the ONLY declaration answering a reference, so its omission renders as Unbound -- or, if a homonym survives elsewhere, the reference binds uniquely to the WRONG declaration and nothing reports anything. No caller could distinguish "the complete universe has no provider" from "the provider was in a file we discarded". Universe construction is total: SourceReadFailed, ParseRefused, DuplicateCandidateFile and DuplicateModuleIdentity all refuse BEFORE the fixed point starts. Duplicate file identity mattered independently: partition_universe kept the last matching row while universe_row_for_file returned the first, so overlapping source roots could make the answer depend on which helper read the population. EntryOutsideUniverse is split out of the generic ClosureNoEntryAdmitted -- "the entry is not in the universe" and "no entry was admitted" have different causes. ONLY A SETTLED CLOSURE IS A SUCCESSFUL RUN. Refused and Exhausted both returned Ok(String) and the probe exited zero, which makes it useless as a gate and reads as the ordinary path having ACCEPTED the closure. ReferenceDerivedClosureRun is typed, is_settled() is true for exactly one arm, and the probe exits 0 settled / 1 refused or exhausted / 2 bad invocation. Three further repairs, each removing something that could lie: closure_step_edges answered a refusal with [] and closure_step_state with Absent -- the absorbing fallback this lane removes elsewhere, exported and waiting for a consumer. Both DELETED rather than reworked: a caller reading [] cannot tell "no dependencies" from "the step refused", and matching the three arms is the only reading that cannot lie. The fabricated OccurrenceId { value: 0 } is gone. A population failure with no occurrence-bearing arm is ClosurePopulationFailureHasNoOccurrence carrying the population -- the comment two lines above it forbade inventing an id, and then it invented one. unbound_count is failure_count. population_failure_count counts EVERY projection failure class, including ambiguity and index/category refusals, so the name and my own report of "25 unbound references" both overstated what was measured. CONTROLS EXECUTED, on a real source root through the ordinary host path: malformed candidate -> CandidateUniverseRefused: ParseRefused{malformed.dag}, exit 1 duplicate module identity -> CandidateUniverseRefused: DuplicateModuleIdentity naming both files, exit 1 same root, defect removed -> Settled: 2 files, 1 edge, 3 provenance rows, 2 iterations, exit 0 The positive control is the vacuity guard for the two refusals: without it, a universe builder that refused everything would pass both. One measured correction to my own control, kept because it cost real time: the first positive control refused on 'CtlOne' unbound, and the fault was the fixture, not the loader -- `type CtlSubject = CtlOne` with no leading pipe is an ALIAS whose right side REFERENCES CtlOne, not a one-variant coproduct. The loader was right. RUNG, corrected. What is landed is: the fixed-point mechanism executes over real repository source files through a host entry point. What is NOT landed is ordinary loader or compiler consumption -- dependency_resolution_facts_live is unchanged and nothing on the compile path reads this closure. The in-code claim that said otherwise is rewritten; the probe is an integration instrument, not the cutover. STILL OPEN from the review, in its order, none of them started here: relational exposure (a permanently cross-file-grounded file is wrong once it becomes an admitted consumer with references of its own) with A->B->C and earlier/later-in-B controls; sealing the candidate universe once instead of reassembling 3533 files per iteration; module-value declarations; then the differential. production file/function changed: v1_compiler.cli_run reference_derived_closure_over_source_roots (+ CandidateSourceFailure, ReferenceDerivedClosureRun), bin/reference_derived_closure_probe; dag/std/reference_derived_closure.dag closure_binding_refusal (+ ClosurePopulationFailureHasNoOccurrence, failure_count; closure_step_edges / closure_step_state deleted) focused command executed: the three controls above; claim_batch reference_derived_closure_witness_test 8/8 PASS; regen_stage0 --verify divergence 0 next production function: the candidate-count instrument on the located occurrence -- the whole-corpus run refuses on 'String' with TWO visible module-root declarations, where the honest answer is ambiguous-naming-both, and "exposure rejected both candidates" versus "the name bucket never held them" are different defects with different fixes that no report currently distinguishes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… two of them
A refusal saying a name did not bind means one of two opposite things, and the
report could not tell them apart: EITHER no file in the universe declares that name
(the universe is short and the loader is right) OR declarations exist and candidate
enumeration discarded them (the filter is wrong and the loader hides it behind the
same word). One count separates them, so the refusal now carries it.
Counted from the PRE-ASSEMBLY parses deliberately. The assembled transport is what
the filter read, so a count taken there would inherit whatever the filter did and
could never contradict it.
TWO OPPOSITE VERDICTS FROM ONE INSTRUMENT, which is why it discriminates rather
than confirming whatever I expected:
universe=116 (src/v2/std) 'FreeMonoid' unbound
[universe declares 'FreeMonoid' 0 time(s)]
-> LOADER RIGHT. src/v2/std/algebra.dag IMPORTS FreeMonoid; the only
declaration is dag/std/algebra.dag, outside that universe.
universe=251 (dag/std + src/v2/std) 'String' unbound
[universe declares 'String' 2 time(s) in
dag/std/string_type.dag x1, src/v2/std/text.dag x1]
-> LOADER WRONG. Both are module-root `type String = FreeMonoid<Char>` in files
grounded CrossFileProviderExportedExposure, so the honest answer is
ambiguous-naming-both. Candidate enumeration returned EMPTY against two
present, exported declarations.
That is the review's static-grounding finding confirmed by measurement, and it is
live on the corpus today rather than latent at the second hop. It also retires my
own earlier reading of the whole-corpus run: that refusal was never evidence about
the corpus. The loader was reporting "nothing declares this" while two files
declared it -- not a widen, not a located refusal, but a WRONG VERDICT WEARING A
CORRECT-LOOKING WORD, which is the one failure shape worse than either.
The instrument stays enrolled for exactly that reason: it is what stops the word
from standing unchecked again.
Two process notes kept because both cost real time. The 20-minute whole-corpus rerun
produced nothing readable because I piped it through `tail -4` and the refusal
prints to stderr AHEAD of `time`'s output, so my own pipeline discarded the
measurement; the small two-declarer universe answers the same question in minutes
and is the right instrument size. And `exit=$?` after a pipe reports the LAST stage,
so an earlier `... | tail -2; echo exit=$?` printed 0 for a probe that had correctly
exited 1.
production file/function changed: v1_compiler.cli_run
reference_derived_closure_over_source_roots (pre-assembly declaration count on the
ClosureBindingRefused report)
focused command executed: reference_derived_closure_probe over src/v2/std (declares
0) and over dag/std + src/v2/std (declares 2, reported unbound), exit 1 both
next production function: candidate_occurrence_ids_for_reference, and beneath it
declaration_exposed_on_reference_chain vs ordinal_of_occurrence -- the authored-
order gate is the leading suspect, because authored order is a WITHIN-FILE fact and
assembly places the consumer's references before every provider declaration in one
global ordinal space. Which of the two rejects gets instrumented before either is
changed; that is the review's commit 2, now with a measured failure to satisfy
instead of a hypothetical one.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ot exist RETRACTION, same day. There is no two-declarer defect in the closure. Measured with the population summary this commit adds: universe=3 'CtlAlias' 2 declarers population=Ambiguousx2 correct universe=255 'String' 2 declarers population=Ambiguousx1 correct universe=116 'FreeMonoid' 0 declarers Unbound correct The loader answered correctly in every case. What was wrong was my report and my reading of it: the refusal line printed a located authored name and a failure COUNT but never the projection CLASS. I carried the word Unbound over from the earlier FreeMonoid run -- which genuinely was unbound, its declaration being outside that universe -- and built a nonexistent defect out of the inference, then carried it into a message to another lane as a blocking finding. A count is not a class, and a report that omits the discriminating field invites the reader to supply it from memory. first_failure alone is a head projection; the tail carried the answer. The same class was measured independently in a sibling program the same night (vivid-bat-377): parse_grammar_choice_overlap_residue is a constant prefix prepended to every parse failure by rejected_with_pending, so every .head reading in that program discriminated nothing while looking like a cause. Two labs, two carriers, one shape -- read the head, get a stable-looking answer that was never about the input. So the probe now renders EVERY member of a refused population by class (population=Ambiguousx2), not the head and a count. The specific lie is no longer available to whoever reads it next, including me. WHAT THIS CHANGES ABOUT THE WORK, stated because the retraction moves a lane item rather than merely deleting a note: String is declared in BOTH dag/std/string_type.dag and src/v2/std/text.dag, two authorities for one concept, and the closure refusing it as ambiguous is CORRECT behaviour. So the next unit is not a filter repair -- it is consolidating forks like that one, which is ordinary import-deletion work. The corpus-clean measurement will surface every such fork as an ambiguity, and each is a decision about which authority survives, not a bug to fix. production file/function changed: v1_compiler.cli_run summarize_population_failures (+ the population summary on the ClosureBindingRefused report) focused command executed: the three rows above, each re-run against the rebuilt probe next production function: unchanged in shape but not in content -- relational exposure with an A to B to C receipt, then sealing the candidate universe once. The filter repair I had queued ahead of them is withdrawn; there was nothing to repair. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The reference-derived closure had a property no language may have: the accepted
declarations for one file changed with which file the walk started from. A
provider file's module members were ground to RootExposure, so a premature
same-module reference bound when its file was reached as a provider and refused
when it was the entry. Measured on identical bytes, both directions, before the
repair.
The cause was role-stamped exposure. CrossFileProviderExportedExposure and its
ModuleExposure => RootExposure promotion are deleted; every ordinary module
member grounds ModuleLocalMemberExposure whether its file is entry or provider.
The whole source-root universe stays the declaration DENOMINATOR and stops being
the visibility set: a globally unique leaf no longer binds bare from anywhere.
That closes the hole and needs a replacement naming path, which is the second
half here. A qualified reference is already ONE occurrence whose authored_name is
the whole dotted path -- the parser mints it that way and nothing new is minted --
so what was missing was only the resolution rule: the leaf selects the bucket,
the qualifier selects which declaring module may answer, by segment suffix over
the declaration's own module row. Exposure for a projected reference is
ModuleExposure for exactly that module: RootExposure does not answer a qualified
name, and the source-order gate is not applied, because order is a within-file
relation and two files have no authored order between them. Two modules sharing a
qualifier segment supply both declarations and fold to Ambiguous -- the refusal,
not a nearest-match heuristic.
Controls, green by execution:
- later declaration refuses identically under both entries (same authored name,
same span; OccurrenceId deliberately not compared across assemblies, which
rekey into separate allocator scopes)
- earlier declaration binds under both entries, so the row above is not
satisfiable by refusing everything
- bare cross-file reference is Unbound
- qualified projection reaches the provider and admits its file
- qualified A->B->C settles in 3 iterations, 3 files, 2 edges
- two modules sharing the qualifier segment refuse as Ambiguous naming both
The keystone fixture moved off globally-bare LiveTreeDisposition, which was
testing the deleted model: it passed because exactly one declaration carried that
leaf corpus-wide, which is the reasoning this commit removes.
Corpus state, unresolved and stated rather than worked around: over dag + src/v2
(3533 files) the closure refuses on bare String, declared in both
dag/std/string_type.dag and src/v2/std/text.dag. It refused before this commit
too, as Ambiguous; it now refuses as Unbound. The corpus is authored in the
import-era bare style, so reference-derived closure over real files stays refused
until those references carry a qualifier or the naming authority reaches them.
That is the remaining work for import deletion, not a defect in this change.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…delete-imports-on-main # Conflicts: # .github/workflows/ci.yml # ROADMAP.md
…n merge Both conflicted paths are generated artifacts, so neither side of the conflict was the answer: ci.yml is derived by gunbc ci and ROADMAP.md by the roadmap authority. Resolved by taking main's side to close the merge and then regenerating both, which is the only resolution that cannot encode a hand-picked line. ci.yml picks up this branch's new emitted modules in the heal exclusion list; ROADMAP.md re-derives the namespace lane rows this branch's focus selection adds. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ir subject
Two things, both consequences of the exposure change rather than new direction.
MODULE VALUES. A module-scope `data name: T = v` bound no name at all, so a
reference to any module datum could not bind and the derived closure was short by
every module-value edge in the corpus. The shape classifier cannot be widened
where the hole is -- it sees a node carrying both a body and a type annotation,
which is reachable from nodes that are not module items -- so the arm goes where
the question is decidable: parsed_module_item_role, asked only of a node already
known to be a module item.
The category is new rather than borrowed, and the two obvious reuses are both
wrong. LexicalValueOccurrence is explicitly NOT module-scope exposed, so it would
have declared every module datum unexported; CallableOccurrence would have
nicknamed a value as callable. ModuleValueOccurrence joins the closed coproduct as
module-scope exposed and clause-E dependency inducing, and a value reference is
admissible against it -- an expression variable cannot know whether its target is
a local binder or a module member, and the reference site is not where that is
decided. Measured on a fixture: three failing references become two, the residue
being type references genuinely outside the universe.
THE WITNESSES. Six discovery rows went red in CI that my local run did not catch,
and the reason is worth recording: I filtered a corpus run's output and read the
absence of FAIL from a filtered stream. A count of matched lines is not a count of
witnesses. Re-run unfiltered, with totals compared against the roster.
Of the six, two -- cross_file_provider_exported_exposure_at_{module_root,direct_child}_upgrades_to_root
-- had no subject left: they called the surviving grounding and demanded the
deleted RootExposure answer. They are deleted rather than inverted, because what
they would now regress is a behaviour the model no longer has, and the two
module_local_member_exposure rows beside them already assert the surviving answer
at both containment shapes. The other four are fixture-driven and were authored
bare: the clause-E consumer now references provider.helper, and the pool-
independence decoy moves from app.decoy to other.provider so it still COLLIDES
under a qualifier -- its own note predicted the collision would become a fixture
artifact under projection, and moving the module keeps it the ambiguity control it
was written to be rather than letting it pass for the wrong reason.
Green by execution: assembly 9/9, pool-independence 8/8, candidates 23/23,
closure 10/10, bridge 21/21.
FRONTIER NOTE REWRITTEN. namespace_lane_measured_frontier_note described a
supplied-provider era that no longer exists and would have sent the next reader
back to work already landed. It now records what moved (discovery, transitivity,
relational exposure, qualified type projection) and what is open at the grain that
decides dispatch: expression-position paths, the bare-authored corpus, no ordinary
compile-path consumer, and an unsealed universe. It also carries the obligation it
previously omitted -- rewire_type_env_import_str_binding_identity survives on main,
gunbc#8202 proved it cannot simply move pre-typecheck and gunbc#8227 only optimized
it, so its deletion belongs to the type-env single-authority cutover and must land
before grammar deletion.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… file An expression path arrives as a bare head reference plus a field projection -- p.first is a reference to p, not one dotted occurrence the way a TYPE path is -- and the head had nothing to resolve against: parse_module stamps a module once carrying its WHOLE dotted name, so declarations_by_name, keyed by authored spelling, has no entry for the last segment alone. Measured before: 'p' did not bind, universe declares 'p' 0 times. module_declarations_by_segment buckets NamespaceSegmentOccurrence declarations by their last segment and supplies them to bare references beside the on-chain population. Keyed by segment rather than full path because the ratified migration form is the SHORTEST qualifying container path; two modules ending in the same segment supply two candidates and fold to Ambiguous, which is the refusal rather than a nearest match. The category is read off the DeclarationOccurrence, not inferred from a dot in the text, so an ordinary declaration cannot impersonate a module. Three consequences, each deliberate rather than incidental. A module candidate skips the on-chain exposure filter: module-sibling exposure gates on same-module plus source order, both within-file relations, and a module name is visible across files by design. A value reference is admissible against a namespace segment, because the parser mints the head as ExprVar and the reference site genuinely cannot know whether a bare name is a local binder, a module member or a module. And `data name: T = v` needed its own child-stamping arm beside the lexical binder's: the catch-all stamps every child Unclassified, which loses the reference the value expression carries -- measured, the provider file's own constructor reference stopped binding, so admitting a file did not admit what it declares. Isolated receipt, no type reference to the provider anywhere in the fixture: module tmp.e.c type Str2 = TOne | TTwo data mine: Str2 = p.first -- the only cross-file reference is the head Settled: 2 file(s) admitted, 1 edge(s), 2 iteration(s) WHAT THIS IS NOT. It binds the HEAD, which is what a file dependency needs -- the provider file follows from the module declaration's own file. It does not descend to the member, so a bound head with a missing or ambiguous member is not distinguished here; that belongs to the resolver cutover. The dissolution trigger is the ReferencePath carrier both syntactic positions feed, at which point the parser mints the head as a namespace reference and the category admission above stops being needed. MEASURED AND NOT FIXED, pre-existing: a constructor reference at module scope does not bind. Constructors are module-scope exposed by category, but exposure derived from containment gives them LexicalExposure scoped to the type body, because their ancestors are [module, type] rather than [module]. Cause located at declaration_exposure_from_containment; not repaired here because it changes the exposure authority again and is independent of this commit. parser_sibling_match_arm_pattern_binder_holds fails, and the evidence says it is not from this change: main hardcodes the same ModuleLocalMemberExposure grounding this branch passes explicitly, the fixture contains no module-value item, and no module segment is named x. Its own note parks it, stating the property does not hold from authored source because match-arm bodies stamp as siblings of pattern binders. Green by execution: closure 10/10, bridge 21/21, parser matrix 20/20. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A module-scope reference to a constructor did not bind. Constructors are declared module-scope exposed by the category authority, but exposure derived from containment gave them LexicalExposure scoped to the type body, because their ancestors are [module, type] rather than [module]. So a provider file could be admitted and still fail to supply what it declares. Containment alone cannot decide this. A constructor and a field are both children of a type node and derive identical ancestors, yet one is reachable at module scope and the other is not, so shape carries strictly less information than the question needs. The nested arm now asks occurrence_category_module_scope_exposure_verdict -- the authority that already owns module-scope membership -- rather than guessing. This is not category substituted FOR exposure: the decision site still reads only DeclarationExposure. It is the grounding chosen at derivation, which is what the exposure authority already requires of its callers. The discriminating control is a pair at identical depth: nested ConstructorOccurrence derives ModuleExposure, nested FieldOccurrence stays LexicalExposure scoped to the type body. Same containment, opposite answers, so the rule cannot be satisfied by anything that merely widens exposure. End to end on the realistic shape -- a provider declaring a type with constructors and a datum initialised from one, reached by expression-position projection: module tmp.k.p module tmp.k.c type Str = SOne | STwo type Local = LOne | LTwo data first: Str = SOne data mine: Local = p.first Settled: 2 file(s) admitted, 1 edge(s), 2 iteration(s) THREE FIXTURES WERE MISLABELLING THEIR OWN SUBJECT and are corrected rather than accommodated. nested_containment_derives_lexical_exposure_holds and the seed binary's two_ancestors_lexical_exposure both passed TypeOccurrence while calling their subject a binder; a nested type IS module-scope exposed, so under the new rule they failed for the right reason. Both now carry LexicalValueOccurrence, which is what a binder is. The clause-E fixture's exposure rows now carry the same category its declaration rows already declare. Changing a test because it broke is usually the wrong repair; what distinguishes these is that the fixture disagreed with itself, and the new constructor/field pair is what actually pins the behaviour. Green by execution, each run to completion against its roster: closure 10/10, bridge 21/21, type-ref context 8/8, cross-file assembly 9/9, pool independence 8/8, candidates 23/23, parser matrix 20/20, and the compiled seed witness binary all cases pass. NOT CLAIMED: a clean whole-corpus local run. Two attempts were killed by their timeout mid-file at 545 and 6455 of 7472 witnesses, and I quoted both as totals before checking that either finished. Zero failures across ~5750 executed witnesses is what those runs support. The corpus verdict comes from CI, which completes; gunbc#8244 measures why the local shape does not (1,927s preparing entry-private worlds for 148s of evaluation) and is the reason to stop paying it per push rather than to pay it for longer. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…defect The note this replaces was itself written to supersede a stale one and went stale within a day, so it is now dated and reads as a measurement rather than as a standing description. The substantive change is how expression-position work is characterised. It landed at module-head grain only, and 'descent is unimplemented' understates the consequence: a path whose member is absent or ambiguous still discovers a provider file, so the resulting edge is justified by the module head rather than by an accepted terminal declaration. Named as a provisional loading widen that may not become the final dependency authority, with the ReferencePath carrier as its closing condition. Also records why the corpus migration must be derived rather than hand-judged: the population moves while the lane works — 18,628 import lines across 2,884 files at this measurement, and one in-flight PR moves it by +147. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The head-only widen named in the previous commit is closed, and it was worse than "descent is unimplemented". Reproduced before the repair: module tmp.m.p module tmp.m.c type Other = OOne | OTwo data mine: Local = p.no_such_member Settled: 2 file(s) admitted, 1 edge(s) A file dependency was asserted against a provider that declares no such member. The edge rested on the module head, which names only where to look. THE MECHANISM, which is a category answering a question it was not asked. An expression path is a FieldOccurrence reference whose base is its child, and FieldOccurrence is excluded from clause-E dependency inducement -- correct for a record field access. That exclusion was applied to the DEPENDENCY question and silently answered the BINDING question too: an excluded reference is never added to the required population, so it never had to resolve at all. Exclusion from edge derivation had quietly become exemption from binding. A projection off a module head is not a record field access, and the difference is decidable at resolution rather than at the category: the base resolves to a NamespaceSegmentOccurrence or it does not. When it does, the segment resolves among that module's own members and the dependency is attributed to the TERMINAL declaration, which is what a file dependency actually rests on. Both directions, by execution: member exists -> Settled: 2 files, 1 edge, 5 provenance rows member absent -> Refused: 'no_such_member' Unbound, located at the segment A second gate had to move for the positive case, and finding it required distinguishing two states that look identical. Candidates WERE supplied for the existing member and every one was filtered out by category admissibility -- FieldOccurrence admitted only FieldOccurrence -- which reads as Unbound and is indistinguishable from a member that does not exist. Both fixtures refused for what looked like one reason and was two. The arm now admits module-scope member categories, narrow in effect rather than in statement: candidates for a FieldOccurrence reference are supplied at exactly one site, reached only after the base has resolved to a module, so an ordinary field access supplies nothing and cannot reach a module member by widening. DISSOLVE-ON, for both this and the head admission: the ReferencePath carrier gives a projection segment its own category at derivation, at which point a segment stops impersonating a field and both widenings are deleted rather than kept beside it. This commit closes the fail-open; it does not yet unify the two syntactic positions into one carrier. Green by execution, each run to completion against its roster: closure 10/10, bridge 21/21, candidates 23/23, pool independence 8/8, binding resolve 10/10, cross-file assembly 9/9, type-reference context 8/8. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
parser_sibling_match_arm_pattern_binder_holds cannot pass from authored source, and the parser is the reason rather than the witness. v1.compiler.parse stamps a match-arm pattern binder with the ARM's ancestors, so the binder is a SIBLING of the arm body rather than an ancestor of it; its containment-derived LexicalExposure has an exposing scope that does not contain the arm-body reference, so the isolation asserted here -- each arm's reference binding its own binder and refusing the other arm's -- cannot hold however the exposure authority is written. The file's own note has said this since the B1 rollup. What it lacked was an ADMISSION: a note is not enrollment, so the function stayed an ordinary green-required test that happened to run only when something in its import closure changed. gunbc#8115's dag/std/occurrence_binding_resolve change selected it for the first time in this lane, and it read as this PR's regression. The CI history says otherwise precisely: green at a4f6bbd, whose diff is a roadmap note and selects no src/v1 witness, and red at b4f16ae, whose diff is in that closure. Cost of the missing row, recorded because it is the argument for adding it: two bisect cycles reverting correct code against a subject I had not confirmed was the failing one. The CI log does not carry the failing witness name, so I substituted the one failing locally -- which passes in CI -- and tested theories about it. Identify the failing subject before forming a theory about the cause. The equivalent property is discharged on a hand-built transport where the arm body nests under the binder occurrence, by occurrence_binding_candidates_witness, so this row loses no coverage. It stops a known-structural red from being rediscovered as a new one. Dissolution is the parser nesting change the witness note already names, at which point the row deletes and the function re-enrolls. exact_witness_admission_witness_test 18/18. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…delete-imports-on-main # Conflicts: # .gitattributes # .github/workflows/ci.yml
… main merge Both conflicted paths are generated. The merge driver marked them unmerged without markers, which is its documented behaviour for generated artifacts, so neither side was the answer: resolved to main's side to close the merge, then regenerated. The derived delta is this branch's new emitted modules entering the generated-artifact merge list and the heal exclusion list. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…delete-imports-on-main
…lment owes Enrolling parser_sibling_match_arm_pattern_binder_holds in known_red_probe_v1_envelope_roster appends one RunnableDiscoveryBatch to the floor schedule. It is not a RunnableScopedWitnessBatch, so it carries no scoped clamp and falls to the positional table, which still had six rows for seven batches — claim_executor refused fail-closed at arm time, exactly as v1_claim_scoped_known_red_probe_batch_note's ENROLLMENT COUPLING clause says it would. Row: overhead 240s, per_unit_ms 1000 (the signed family witness coefficient, so a second enrolled probe re-denominates rather than being absorbed). Basis, carried in gunbc_ci_v1_envelope_known_red_probe_clamp_basis_note: 95.6s measured local x86 wall for the single enrolled row, deliberately marked as NOT a fleet observation, which is why the margin is ~2.5x rather than the family's usual 1.6x spread factor. witness_scoped_batch_is_singleton_and_outside_positional_clamps green by execution; no generated-artifact drift. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…nt terms
Landing the clamp row got the walk past arm time, and the corpus then named
three real reds — all the same shape, and all mine: the conditionally-appended
known-red v1-envelope probe batch was visible to the schedule side of three
laws and invisible to their declared side.
- gunbc_ci_floor_enrolled_runnable_labels_for omitted the probe entirely, so
witness_floor_schedule_preserves_enrolled_set saw a set difference. Both
conditional batches now carry a term, mirroring the native arm.
- witness_live_floor_discovery_batch_count_is_four pinned four discovery
batches; the probe is a fifth. Renamed to _is_four_plus_enrolled_probes with
the conditional as a named term, so four still reads as the unconditional
population and an enrollment stays visible rather than folded into a constant.
- witness_floor_appends_one_selected_native_batch counted schedule + scoped + 1;
the probe entered its constant. It now has its own term, so a claim about the
native lane cannot red for a reason belonging to the probe.
ci_floor_plan_witnesses (the 33-member conjunction root),
witness_floor_schedule_preserves_enrolled_set,
witness_floor_batch_clamp_params_cover_schedule,
witness_live_floor_discovery_batch_count_is_four_plus_enrolled_probes,
witness_floor_appends_one_selected_native_batch and
witness_scoped_batch_is_singleton_and_outside_positional_clamps all green by
execution; no generated-artifact drift.
The fourth failure in that run, arrow_body_form_eval_value_vertical_holds
("no field 'root' on type 'Node'"), is inherited main red from #8242 — it is the
sole failure on main's own runs 31824195524 and 31824260033 — and is being
repaired by #8269. Nothing here touches it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…delete-imports-on-main
…producer WORK IN PROGRESS on the terminal draft — the carrier and producer land here with no consumer yet; the resolver and the two call-site cutovers follow in this same commit series, and only then do the two standing widenings dissolve. This PR is not independently mergeable by construction, so this is an in-flight state rather than an unconsumed artifact being landed. ReferencePath is what both spellings of a projection fold into. A type path is one occurrence carrying the whole dotted text; an expression path is a base reference plus one FieldOccurrence node per segment, the base reachable as the segment's containment child. The producer decides the dotted case from the authored text alone and otherwise walks down containment children. Two shapes are deliberate. The head is a NAME plus a span, not an occurrence identity the type spelling never allocated. And a segment carries its own span where the spelling gives it one and the whole path's span where it does not — an honestly coarse location, never a fabricated offset into a blob. Every failure arm refuses: an unindexed occurrence, a branching projection (more than one containment child is a shape this producer does not model, so it names the count rather than picking a child), empty segment text, and an unbounded descent are four distinct typed reasons, not one absorbing "no path". Established by execution before the shape was chosen, since the resolver's descent rests on it: a module member's containment parent IS the module declaration occurrence, so one containment-child step is the uniform segment rule and the qualifier suffix-match becomes removable rather than being generalised. The probe that established it was temporary and is not committed; Commit A's own controls pin it discriminatingly (bound head/bound member, bound head/missing member, bound head/ambiguous member with exact identities). Compiles clean: 0 blocking errors on the dag target. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…uisite The frontier paragraph described the head-only widen as live — p binds and admits its module file while the member never resolves. That has not been true since the one-segment terminal-member resolution landed, and the row went stale inside a day. Recorded as a correction rather than a rewrite, because reading a superseded frontier as current sends the next reader to repair a fail-open that is already closed. Same correction applied to the PR body. What is actually open is restated as a SHAPE obligation, not a safety one: the one-segment repair keeps two temporary representations beside each other — a projection segment impersonating a FieldOccurrence (so binding compatibility was widened at a field reference, held safe by caller behavior rather than by construction) and a module head decided twice, once by the reference fold and again by a segment-index reconstruction. Both dissolve into ReferencePath, which consumes the accepted head binding rather than re-deriving it. Also names a prerequisite the row previously omitted. The match-arm binder known-red admission is not a resolution: the parser stamps the binder and the arm body as siblings, and when the structural resolver becomes the ordinary resolver ordinary local lexical references must still bind. Repair parser containment, green the witness, delete the admission along with the conditional batch and clamp row that exist only to carry it, keep the witness as a permanent control — rather than accumulating more CI accounting around a parser defect. No production node accepted; imports, grammar and AST all still present. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…e first half owed THE CI FAILURE FIRST, because it was mine and the cause is worth stating: dag/std is a stage0 self-compile input, so the carrier commit needed the two-pass regen and I ran only the generated-artifact gate. That gate does not cover stage0 staleness — regen_verify_gate_passes does — so "no artifact drift" was a true statement about the wrong population. Seed regenerated, rebuilt, and verified: regen_divergence_count=0. Rebuilding also surfaced an emitter limitation rather than a modeling one: a fold whose element parameter is discarded emits a by-value closure where the iterator yields a reference (E0631). Replaced with length, which is the simpler spelling anyway; no behavior changes. THE RESOLVER. resolve_reference_path binds the head exactly once through the same on-chain-plus-named-modules rule an ordinary bare reference uses — there is no second segment-index reconstruction of the head beside it, because two derivations of one fact are two chances to disagree silently. Every later segment is a containment-child step, uniform for the first segment and the tenth, which is what the measured module-member containment relation bought: the qualifier suffix-match is removable rather than something to generalise. The cardinality rule is identical at every step and special-cased nowhere: zero children Unbound, one descend, more than one Ambiguous naming every candidate — never a nearest match, never first-wins. WHY THIS IS A WALL AND NOT A CHECK: the only value carrying a terminal declaration is produced by consuming every segment, so a head that bound while a segment did not has no representation as a resolved path. A caller cannot read a provider file off a partial descent, because a partial descent yields a refusal and refusals carry no declaration. CONTROLS, green by execution on real parsed source (not hand-built values), each a different decision so they are not one claim written three ways: - head and terminal both bind -> resolves to the TERMINAL declaration, asserted by the terminal's own authored spelling rather than the module head's; - head binds, segment names nothing -> refuses AT THE SEGMENT. This is the discriminating red for the head-only widen: under that widen this same input produced a ready dependency; - head names nothing -> refuses at the HEAD, before any descent, so head and segment failures stay distinguishable rather than collapsing to one Unbound. The resolved case is the positive control the refusals are read against — without it, refusals are satisfiable by a resolver that refuses everything. Whole file 24/24, denominator checked against the test fn roster. STILL OWED before the widenings can be deleted, stated rather than implied: the expression-spelling parity case, multi-segment a.b.c, ambiguous member and ambiguous head, and the ordinary record x.field negative that must never consult module members. The resolver handles them by construction; nothing has executed them yet, so nothing here claims they pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…delete-imports-on-main # Conflicts: # .gitattributes # .github/workflows/ci.yml # dag/gunbc/ci_layer_roots.dag # dag/gunbc/stage0_emit_plan_generated.dag # src/v1/stage0/src/lib.rs # src/v1/stage0/src/v1_compiler_parse.rs
…dinary field access Three more controls, all green by execution on real parsed source, and one of them forced a carrier change rather than merely passing. THE CARRIER CHANGE. A dot is not always a namespace step. x.field on an ordinary record binder is a field access: it induces no cross-file dependency of its own (the dependency came from the reference to x's TYPE) and its member is a field of a type, not a member of a module. Answering it with SegmentUnbound would report a correct field access as an unresolved name — the fabricated-plausible-output failure aimed at a well-formed program — and answering it with a resolved path would attribute a dependency to whatever declaration happened to match the field spelling. So the head's own category decides: only a NamespaceSegmentOccurrence head descends, and any other head with segments returns ReferencePathNotANamespaceProjection carrying the head declaration. That is deliberately neither success nor error: there are three outcomes, so callers handle three. That control also executes the EXPRESSION spelling of the producer end to end — the field-access occurrence walked down to its base, the base bound, and the category gate answered — which is partial type/expression parity evidence rather than an assertion that parity holds. AMBIGUITY AT BOTH GRAINS, since one is not evidence for the other: - two modules whose last segment is live_tree refuse at the HEAD, before any descent, naming both candidates; - one module declaring the same member twice refuses AT THE SEGMENT, naming both. The duplicate-member fixture is deliberately not a well-formed program: the subject is what the resolver does when a segment has two candidates, and no well-formed provider had to be corrupted to ask it. The control closure takes its provider SET as a parameter because cardinality is a property of the assembled population, not of the reference text — neither case is expressible by editing the consumer. Whole file 27/27 against the roster; seed regenerated and verified at regen_divergence_count=0. STILL OWED: multi-segment a.b.c, and full type/expression parity as its own assertion rather than as a by-product. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…nd two real defects The last commit under the incremental strategy, per the operator ruling: this completes the in-flight carrier work, and no further production resolver or loader architecture lands on this branch. THE SPLIT RULE WAS WRONG, and it looked right against the corpus. The producer split at EVERY dot and took the FIRST segment as the head, which happens to work for the import-era spelling live_tree.LiveTreeDisposition -- one qualifier segment, one member -- and fails for everything else. It is now the LONGEST PREFIX naming a module: segments are consumed into the head while they still name one, and the rest is descent. That CORRECTS what I reported earlier. I said a fully qualified reference could not be represented and that the qualification policy was therefore coupled to the producer. It is not: module_named_by_qualifier already admits both an exact path and a suffix, so one rule answers both spellings. Proven by execution: v2.std.live_tree.LiveTreeDisposition resolves to the same terminal declaration as live_tree.LiveTreeDisposition. Full qualification needs no producer work. MULTI-SEGMENT descent is real rather than synthetic. Modules are flat per file, so a.b.c cannot be module-inside-module -- but a module member can have members, so live_tree.LiveTreeDisposition.ReadsLiveTree is module, then type, then variant: three segments, two containment steps, exact terminal. TWO DEFECTS FOUND BY EXECUTION, neither in the semantics. (1) A pathological rustfmt blowup that would have wedged CI's regen job. Seed regeneration formats the whole stage0 crate, so ONE emitted file can stop it: my fixture used a ~20-term string `+` chain and its emitted file did not format in 600 seconds, against 0.19s for the previous revision. Measured rather than guessed -- I first flattened my own module on suspicion, which changed nothing (0.166s either way), then bisected file by file. Rewritten as a single literal: 0.086s. The rule for emitted code is long binary-operator chains are superlinear in rustfmt; prefer one literal. (2) The known-red probe batch redded CI with FLOOR-BATCH-CLAMP-REFUSED. Its SelectionApplied resolves selection machinery from the BATCH's roots, which are deliberately dag plus src/v1, so affected_set_floor_runner had no provenance and the run refused fail-closed -- the machinery correctly reporting it is outside every root. The sibling scoped batch escapes this only by being a RunnableScopedWitnessBatch with InheritedWalkSourceRoots; a plain discovery batch has no such authority. Flipped to SelectionOff, which costs nothing: the roster is a handful of explicit expect-red entries, so there is no population to skip. Widening the roots was rejected -- it moves the subject envelope and invalidates the measured clamp basis. Bridge witness 29/29 against its roster; floor-plan conjunction green; seed verified at regen_divergence_count=0; no generated-artifact drift. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Closing: this branch was a throwaway measurement probe, not a merge candidate. It was created to answer one question by execution — what breaks if every import is deleted right now — by stripping all 19,173 import statements across 2,955 files and compiling. That measurement is done and its result is recorded (1,142 hard diagnostics, 39 distinct unresolved names, 0 of them declared in the same file as their reference — so the corpus is underspecified rather than the resolver being broken). The strip itself was reverted locally and never pushed. What remains on the branch is a duplicate of the frozen quarry head Per DESIGN §5, branch-local experimentation is fully open and its implementation is deleted before merge — the durable output is the measurement, not the branch. — sent from calm-ram-435 |
Auto-opened by session-dashboard for session
calm-ram-435.Pushing to
session/calm-ram-435-import-deletion-probeadvances this PR.Worker attestation
Before flipping this PR to ready for review, confirm each item:
npm test,cargo test) and the result.Closes #Ndirective.Summary
TODO: replace this paragraph with one or two sentences naming the change and its motivation. Reviewers read this first.
Test plan