Repository navigation
The v2 production door was not refusing multi-declaration modules; it was silently truncating them - #10907
Merged
Conversation
… was silently truncating them The lane's premise was that generate_rust_module_emission_candidate's exactly-one-declaration ceiling was the admission wall keeping real corpus modules out, and that removing it meant routing every nonempty population through emit_produced_module. Execution says the ceiling was never the wall. produced_decl_conjs_in_tree asked whether a node was a Conj whose FIRST child is a Named edge into an Arrow, and stopped descending there. A resolved .dag module carries one Named-into-Arrow edge PER DECLARATION on a single shell Conj, so the SHELL satisfied that predicate: the walk recorded it as one declaration, and emit_produced_decl -- which reads children[0] -- emitted declaration 1 and dropped 2..N while reporting Accepted. Measured through observe_provenanced_rust_emission_from_ingest on a two-declaration module: ArtifactProduced, carrying exactly the first declaration. The rust_module_emission_decl_ambiguous surplus arm above it was therefore unreachable from any real module -- the collector never produced a surplus to refuse -- so relaxing the ceiling alone would have widened a silent truncation, not opened a door. This is DESIGN section 5's forbidden case, not a rung. The repair moves the predicate onto the edge that carries the role: a declaration IS the named edge into an arrow, and no per-declaration node exists in the tree to collect. The walk is edge-driven, records one declaration per declaration edge in source order, and never descends into one. The composition above keeps its typed zero-declaration refusal and sends every nonempty population through emit_produced_module. Evidence, green by execution over REAL INGESTED modules rather than planted decl nodes (v2.test.claim.self_host.rust_module_emission_population): the count arm reads 2 for a two-declaration module and 1 for one, measured one stage before emission; both declarations emit in source order and the swapped order is refused; a declarationless module still refuses at emission; a two-declaration module against an unwired target refuses WHOLE, with the same module on a wired target as its positive control. Every existing witness over this fold stayed green through the entire silent-drop period because each handed the fold a declaration list it had built itself -- which is why the specimens here are ingested modules. direct_rust_door_expected_source gains the module authority's declared separator, composed from rust_source_text and emit_module_decl_separator rather than transcribed: a one-declaration module is still a module, and emit_produced_module terminates every declaration including the last, exactly as emit_module does. The direct-door production group closing expectation, the two-target module fold and the produced-module golden all remain green. Class filed at gunbc.recurring_failure_mode.shape_predicate_reads_only_the_head_so_a_container_passes_as_its_element. No production regen selector, workflow/CI routing, roadmap acceptance or native bootstrap touched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QbNFKK8mf6wBeVaCr22iNV
…ceiling can reach a verdict required-witnesses-floor run 34392489718 refused seven of the eight new arms INTERRUPTED-BEFORE-VERDICT at cpu_at_least=501-507ms against the lane's 500ms per-claim CPU ceiling. The eighth, the declarationless refusal, completed at ~341ms -- it is the one specimen whose walk stops at the collector's empty population. The cost is not incidental setup that could be rewritten away: each arm is one ingest -> assemble -> infer -> collect -> emit production walk over one specimen module, and that walk IS what the arms assert over. Two arms walked three specimens to compose their expected text from the single-declaration emissions. So the repair is DESIGN section 2's -- one computation serving several demands is carried once at their shared ancestor rather than recomputed per demand -- using the mechanism v2.workflow.floor_pure_producer_share already models: nullary pure producers, warm-enrolled, forced during strict preparation OUTSIDE every per-claim budget, with each claim serving the landed fill. The production walk is not shortcut by this. The producers ARE the production observation, one per specimen, and their cost is paid in full; what changes is how many times it is paid. The share points are String and Bool, fully portable. Grounds for enrollment stated on the carrier, because three of the seven serve exactly one claim and would fail this roster's cross-claim sharing conjunct if read against it: these earn their rows on the FILL-THAT-CANNOT-LAND ground the roster already records for dag_prepared_grammar -- a fill costing more than one claim's budget can never complete from an in-fold first touch, and sharing is not the question. NOT verified locally, and the carrier says so: the warm path runs at strict preparation, which is the required-floor recipe's step and not a plain claim_batch --entry run. Measured after the refactor, the arms' claim_batch costs are unchanged at 341-1545ms, exactly as expected when the fill is not being forced. The deciding instrument is the required floor's own [floor-shared-fill] ledger: these fills must land at preparation with disposition=Stored and the seven former INTERRUPTED arms must serve hits. All eight arms remain green by execution on their verdicts. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QbNFKK8mf6wBeVaCr22iNV
#10886 landed the stage0 target profile and enrolled two of its own emissions on floor_cross_claim_pure_producers_warm for exactly the reason this branch enrolled seven: a ~700ms nullary fill cannot complete inside a 500ms claim. Both row sets and both carrier notes are kept -- the conflict was additive on both sides, with no disagreement about the mechanism. Worth recording, because it independently confirms a call made on this branch: #10886's note states that enrolling a producer whose only callers declare v2.test.long. would buy a preparation-time fill no planned claim reads, and that homing its claims there "took them off the merge path altogether and made a green floor mean nothing about them". That is the same reason this branch refused the long-home escape for its own arms rather than taking it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QbNFKK8mf6wBeVaCr22iNV
…ual to one Merging #10886 surfaced two regressions this branch caused, both real and both invisible to every witness that existed before that PR landed. Measured against origin/main to establish they are mine and not pre-existing: all three of v2.test.self_host.stage0_production_target's emission claims are GREEN on origin/main and RED with this branch's collector change. FIRST, the join produced the wrong representation of a string. emit_produced_module folded with list_append over an `Empty` FreeMonoid seed, so its result carried the CONS-LIST SPELLING of a string rather than the native one. `==` and `concat` behave identically on it -- which is why every golden over this fold has been green since it was written, and why this branch's own eight arms, which compare with `==`, could not see it either. A builtin that requires a native string refuses it: `split expects a string, got Variant`, from the two stage0 contains-controls, the first consumers ever to read this fold's output through `split`. Nothing on the production door reached the fold until this branch routed every nonempty population through it, so the defect was latent rather than new, and equality-only readers could never have found it. The join is now `concat` over a "" seed. One representation for one concept (DESIGN section 3). The first attempt at this fix changed only the seed and left list_append, and it did not work -- the measurement said so, and it is the reason the diagnosis above names the JOIN rather than the seed. SECOND, the trailing separator moved a golden this branch could not have known about. stage0_boundary_expected_source pinned "pub fn add(x: i64, y: i64) -> i64 { x + y }" against the door's output, which now terminates its last declaration because the door routes through the module authority. The expected text gains the separator, composed from emit_module_decl_separator rather than transcribed, so a future change to the module convention reds there instead of drifting silently. The declaration text stays a literal: it is the fixture golden that claim exists to pin, and reading it from the profile under test would stop it discriminating. Verified on the merged tree: all five stage0_production_target claims, this branch's eight population arms, produced_decl_module_folds_declarations_in_order, produced_decl_unwired_target_still_refuses, produced_module_two_distinct_fns_assemble and direct_rust_door_production_group_closing_expectation_holds. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QbNFKK8mf6wBeVaCr22iNV
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What the brief expected, and what execution found
The brief's first cut was to remove
generate_rust_module_emission_candidate's exactly-one-declaration ceiling, on the reading that it "removes the admission wall excluding almost every real corpus module", and to leave the collector alone "unless execution reveals a real defect in it". Execution revealed one, and it changes what the cut is.v2.compiler.emit_producedproduced_decl_conjs_in_treeasked whether a node was aConjwhose first child is aNamededge into anArrow, and stopped descending there. A resolved.dagmodule carries oneNamed-into-Arrowedge per declaration on a single shellConj— so the shell satisfied that predicate. The walk recorded the shell as one declaration, andemit_produced_decl, which readschildren[0], emitted declaration 1 and dropped 2..N while reportingAccepted.Measured through
observe_provenanced_rust_emission_from_ingestover an ingested two-declaration module, before the repair:ArtifactProduced, carrying exactly the first declaration.Two consequences for the brief's plan:
rust_module_emission_decl_ambiguoussurplus arm was unreachable from any real ingested module — the collector never produced a surplus to refuse. It read as a wall and was a decoration (DESIGN §4b: ask whether the check's RED is authorable before writing it).At N=1 the shell and the declaration are the same node, which is why the one-declaration door was correct by coincidence and every byte-level golden agreed.
The repair
The predicate moves onto the edge that carries the role: a declaration IS the named edge into an arrow, and no per-declaration node exists in the tree to collect. The walk is edge-driven, total, records one declaration per declaration edge in source order, and never descends into one. The single-edge
Conjit projects per declaration is the declaration's canonical node form — the shapeingested_add_decl_nodealready builds — soemit_produced_declandproduced_decl_subject_from_declkeep consuming aNodeand every existing consumer is untouched.Above it, the composition keeps its typed zero-declaration refusal and sends every nonempty population through the one module authority,
emit_produced_module.Evidence (green by execution)
v2.test.claim.self_host.rust_module_emission_population— every specimen is a real ingested.dagmodule walked by the production observation, never a planted decl node:..._collects_two/..._collects_one..._emits_both_in_source_order..._is_not_the_swapped_order..._declarationless_module_refuses_at_emission..._refuses_whole_on_unwired_target..._produces_on_wired_target..._one_declaration_module_emits_terminated_declarationWhy the pre-existing witnesses stayed green through the whole silent-drop period, and why the specimens here are ingested modules: each one handed the fold a declaration list it had built itself (
ingested_add_decl_node,table_fixture_add_decl_node). A fixture that constructs the population cannot discriminate a defect in deriving it, however thoroughly it pins ordering, separators and refusal propagation.No regression:
direct_rust_door_production_group_closing_expectation_holds,produced_decl_module_folds_declarations_in_order,produced_decl_unwired_target_still_refuses,produced_module_two_distinct_fns_assembleall re-run green.The one byte-level change
direct_rust_door_expected_sourcegains the module authority's declared separator, composed fromrust_source_textandemit_module_decl_separatorrather than transcribed. A one-declaration module is still a module, andemit_produced_moduleterminates every declaration including the last, exactly asemit_moduledoes.Second commit: the floor's per-claim ceiling
Required-floor run 34392489718 refused seven of the eight arms INTERRUPTED-BEFORE-VERDICT at
cpu_at_least=501-507msagainst the lane's 500ms per-claim CPU ceiling; the floor was otherwise clean (planned=3583 executed=3583 claims_failed=0 unexpected_failures=0), and the aggregatewitnessesfailure is that lane result being read.Each arm is one full production walk over one specimen, and two arms walked three specimens to compose their expected text — the walk is what the arms assert over, so it cannot be rewritten away. The repair is §2's: one computation serving several demands is carried once at their shared ancestor, via the mechanism
v2.workflow.floor_pure_producer_sharealready models — nullary pure producers, warm-enrolled, forced at strict preparation outside every per-claim budget. The producers are the production observation; their cost is paid in full, only once each.Two things stated on the carrier rather than left to inference:
dag_prepared_grammar's fill-that-cannot-land: a fill costing more than one claim's budget can never complete from an in-fold first touch, so sharing is not the question.claim_batch --entryrun does not execute — measured, the arms'claim_batchcosts are unchanged at 341-1545ms after the refactor, which is what you should see when the fill is not being forced. The deciding instrument is the floor's own[floor-shared-fill]ledger: these fills must land withdisposition=Storedand the seven former INTERRUPTED arms must serve hits. All eight arms remain green on their verdicts — the open question is cost, not correctness.Filed and left open
gunbc.recurring_failure_mode.shape_predicate_reads_only_the_head_so_a_container_passes_as_its_element(rung after repair: 2; ceiling 4; trigger is an absent language capability, at capability grain).v2.workflow.realization_attemptcollect_arrowsanswers this same question for itself and answered it correctly per-edge — the static field tell for this class. Consolidating it walks a resolved tree and searches for the first arrow below an edge rather than requiring the edge to target one; merging without measuring that difference would trade a silent drop for a silent empty population.v2_native_route/ workflow / CI routing, roadmap acceptance, stage0 target profile, native bootstrap or crate partitioning.Scope
This does not complete the
v2-emitter-production-compatible-corpus-moduleroadmap node. It repairs the collector prerequisite that node stands on. The remaining vertical — the stage0 census understage0_production_target, selecting the smallest first-refusal set, driving one real module to compile in a stage0-shaped crate — needs #10886's target profile, and is worth re-running against this head: any pre-#10907Acceptedfor a multi-declaration module was reporting success for a module the collector had mostly discarded.Measured en route, not pursued: a two-declaration module whose second declaration calls the first reaches
ArtifactProduced, and so does one whose second declaration calls an unresolved name — the latter is worth a look by whoever owns name resolution.🤖 Generated with Claude Code
https://claude.ai/code/session_01QbNFKK8mf6wBeVaCr22iNV