Repository navigation
Conversation
…ng, keep the name through the graft
A locally-declared record type was unresolvable anywhere in v2. Two defects in
series, each measured rather than inferred, and neither of them the generics
story the symptom first suggested.
FIRST, LOWERING NEVER NAMED IT. body_lower_type_decl gated the construction of
its Named edge on body_lower_find_where_host_conj -- a search for a `where`
refinement clause. dag_grammar_type_decl_expr admits `field_decl_block | "="
type_alias_rhs` and the where clause lives inside the alias rhs, so a record
CANNOT carry one: the guard was unsatisfiable rather than merely narrow, and
every record returned the unlowered shell. The body is reachable by emitted
identity, which is how this file already finds param lists and fn decls
(body_lower_param_binding_symbols_from_fn_captured), so the Absent arm now
looks there before giving up.
SECOND, THE GRAFT DISSOLVED THE NAME IT WAS HANDED.
namespace_graft_collect_body_edge descended through a Named edge whose target
is a "transparent sequence spine" -- a Conj whose children are all projections
-- and the label was lost in the descent. A record's field block is raw parse
structure, so it is transparent; a fn body is an arrow, so it is opaque. That
asymmetry, not the where clause, is what made alias-with-where survive while
records did not. The descent stays on the Positional arm, which is where an
anonymous spine belongs; a Named edge that is not metadata, not a projection
and not a module header is a declaration, and keeping it is what
namespace_graft_body_edges_survive_holds already specifies by asserting that
NgPilotType survives with its name.
MEASURED ON THE FULL NATIVE FOLD, 723 modules, against the same fold before the
change:
accepted 139 -> 159 (+20)
resolve_refused 540 -> 519 (-21)
context_refused 43 -> 43 (unchanged, as predicted: neither fix touches
the parse or body-lowering walls)
infer_refused 1 -> 2 (+1)
Twenty-one modules changed outcome; twenty reached accepted and one
(v2.test.claim.local_binding_shadow) now passes resolve and fails later at
infer, which is forward motion rather than displacement. Nothing regressed from
accepted to refused.
Unit evidence, each with a control that must not move: RecordP goes absent ->
PRESENT as a named edge before the graft while control_fn stays PRESENT; the
symbol index goes RecordP:MISS -> HIT while AliasW:HIT and control_fn:HIT hold.
namespace_graft_body_edges_survive_holds and
coproduct_declaration_interior_is_not_retained_holds both stay true -- the
second constrains the collector in the opposite direction, so this is not a
blanket loosening. The emitted crate builds with zero warnings under
RUSTFLAGS="-D warnings", and v2 still typechecks itself (reconcile 84s against
an 82s baseline).
WHAT THIS DOES NOT CLOSE: 519 modules still refuse at resolve, so this is one
cause among several rather than the whole wall.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…'s already does v2 hardcoded ONE pairing for the contextual test marker -- dag_grammar_test_fn_decl_expr is `ident + fn_decl` -- so `test data` matched the ident, demanded `fn`, found `data`, and failed. No other top-level alternative starts with an identifier, so the item failed inside the module's `repeat`, the repeat simply ended, and the module surfaced as `parse_g0_tokens_remain` pointing at leftover tokens rather than at a form the grammar has no rule for. v1 does not hardcode the pairing: drop_leading_test_marker strips a leading `test` ident before ANY keyword. So `test data` is corpus syntax v2 never caught up to rather than a form being invented here, and the corpus uses exactly two spellings -- `test fn` (19839) and `test data` (40). The lowering side already handles it. body_lower_test_fn_decl_to_member is marker-driven, not shape-driven: it checks the left child is `test` and returns the right child whatever it is, and still refuses a non-test marker with body_lowering_reason_test_marker_not_test. Only the grammar was narrow, so this widens the production's right side to `fn_decl | data_decl` with fn_decl first, leaving every one of the 19839 `test fn` declarations on the identical path. MEASURED ON THE FULL NATIVE FOLD, against the same fold without this change: accepted 159 -> 161 context_refused 43 -> 34 (-9) resolve_refused 519 -> 526 infer_refused 2 -> 2 file_refusals 224 -> 215 (-9) parse_g0_tokens_remain 397 -> 355 (-42 identities) Nine modules left context_refused, all of them `test data` files: two reached accepted and seven progressed to resolve_refused, where they meet the separate resolve wall. Zero modules regressed from accepted to refused, and context_refused FELL rather than rose, so the added alternative introduced no grammar ambiguity for declarations that previously parsed. The emitted crate still builds with zero warnings under RUSTFLAGS="-D warnings", and v2 typechecks itself in 82s against an 82s baseline. WHAT THIS DOES NOT CLOSE, and it is a real incompleteness rather than a caveat: a new reason appears, normalize_reason_post_normalize_not_well_formed, at 33 identities where there were none. Those sit in modules that were already failing at parse and now fail slightly later, so nothing regressed -- but it means `test data` is admitted by the grammar without its lowering always producing a well-formed tree. The form parses; it is not yet fully modeled. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ype fix withdrawn for shipping without one THE STEP. `claim_executor --self-host` runs the generation this repository can perform today: the seed emits v2's compiler closure, assembles it as a crate, and builds it. Pass or fail is the whole verdict and where it stops is the status, so there is no roster of known-good entries to widen -- a roster is a second representation of what this process demonstrates by running (DESIGN sections 2 and 3), and it drifts exactly when nobody updates it. `required_v2_emission_entries` has changed once in repository history, and that change was a directory move. It is a PREFIX of --v2-native-route rather than a second path: it calls the same prepare_emitted_compiler and returns on its verdict, so the two modes cannot disagree about whether the seed can build v2. The native route then spends roughly nine further minutes executing the v2.test.* universe, which is a different claim -- what the emitted compiler ANSWERS -- and bundling it would price the self-host question at the cost of a question nobody asked. Measured on this tree: emit 4 minutes, write 182 files, cargo build, exit_status=0 warning_count=0 under RUSTFLAGS="-D warnings". The interpreted alternative -- running v2.workflow.product_receipt_stage's seven boundaries through `gunbc run` -- was killed at twenty minutes still inside boundary 3, so the modelled boundaries are the right shape on the wrong engine. It is a flag and not a --required-ci phase deliberately. A phase is a standing claim on a paid runner for every push and every pull request, and that enrolment is the operator's to make after the wall time has been watched on real heads. THE WITNESS, which is the other half of what this commit is for. v2.test.claim.parse.test_data_decl_parse carries the treatment (a `test data` declaration parses), the control (a `test fn` declaration still parses, so the widened choice did not disturb the path 19,839 declarations already take), and a standing red (the marker followed by a form the top level does not admit still refuses, so the choice was not widened into an absorbing accept). Discriminating by execution: with the grammar fix the treatment returns true and without it false, while both controls hold either way. THE WITHDRAWAL, and the reason is the point rather than the diff. b6373b9 landed two changes -- naming a record type at lowering, and keeping that name through the namespace graft -- with NO witness of their own. The evidence was a probe under target/ and the fold's aggregate counts. The graft half then broke all four v2.test.native_decl_selection claims, including the plain positive `the_requested_declaration_is_selected_and_evaluated`: removing the transparent-sequence-spine descent let graft SPINE SEGMENTS be collected as body edges, and that fixture's module exists precisely because its only collision-named edge is a spine segment. The branch I deleted was load-bearing and I had inferred its purpose rather than established it. The lowering half is withdrawn with it, because measurement says it does not stand alone: with only that change RecordP becomes a named edge before the graft and is still absent from the grafted tree, so it names a declaration without reaching the index. Landing it would leave a half-repair whose green means less than it appears to. What the withdrawn work established stays on the record and is not re-derived by the next reader: record types never reach the symbol index while functions do; the declaration is dropped rather than merely unnamed; and the `where` clause that appeared to discriminate is not causal -- it incidentally puts a non-projection named child under the host, which is what makes the host opaque to a structural test three stages later. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ed binary `gunbc test //gunbc/instruments:self-host` runs the generation this repository can perform today: the seed emits v2's compiler closure, assembles it as a crate, and builds it. Measured here at 553s with exit_status=0 warning_count=0 under RUSTFLAGS="-D warnings". WHY THE ROUTE MOVED. The step first landed as `claim_executor --self-host`, and that binary is the wrong home by construction: its ~20 flags are hand-parsed in a 2783-line main over 44,451 lines of cli_run.rs with NO .dag authority at all, while `gunbc`'s own surface IS modeled (gunbc.cli_dispatch_surface -> gunbc_cli_dispatch_generated.rs). The seam that already exists for exactly this is gunbc.target_binding TargetProducer bound to a gunbc.instrument_targets label, and its host note states the extension shape outright: adding one is a row in instrument_registry and an arm here; it is not a new route. The flag is WITHDRAWN in this change rather than left beside the target, because two routes to one fact is the section 3 fork this seam exists to prevent. WHAT THE SEAM BUYS, and it is not tidiness. The label carries its own refusal vocabulary, so an unknown or pattern operand REFUSES rather than reporting a pass, and the three terminations stay distinct -- 0 held, 1 an observation that did not hold, 2 no observation -- for every instrument at once rather than per flag. That split is load-bearing here: a refusal from prepare_emitted_compiler is the subject never having been reached (the emit refused, the crate would not write, cargo could not run) and NOT an observation that the seed cannot build v2. Collapsing them would report a broken bench as a broken compiler, which is the absorbing answer DESIGN section 5 forbids. run_self_host returns SelfHostHeld rather than printing and returning unit, so the instrument seam decides the termination once. A producer that only printed would make every caller re-derive the verdict from stderr, which is the second representation section 3 forbids. WHERE IT STOPS IS THE STATUS, which is why no roster accompanies it. A list of known-good entries beside this instrument would be a second representation of what it demonstrates by running, and it drifts exactly when nobody updates it: required_v2_emission_entries has changed once in repository history and that change was a directory move. A green here is EMISSION AND COMPILATION and not behavioural equivalence -- that half lives in the three behavioral-receipt instruments beside it -- and it is not the second generation: the built binary emitting the same closure is the v2 -> v2 boundary, and it refuses today because v2.compiler.compile declares SourceRootEvalDriver, whose rendered main answers `census` and `adjudicate` and has no compile mode. That refusal is the honest next position and belongs in this instrument when a driver can reach it. THE ROSTER DOCUMENTS ITSELF. An unknown-target refusal now lists every rostered instrument, read from the same instrument_registry rows the lookup just failed against -- so a reader who has forgotten the label is handed it by the refusal, and a listing cannot disagree with what is invocable. A prose page naming them would be the second representation sections 2 and 3 price, stale the first time someone adds a row and does not think to edit prose. DESIGN's Building & checks carries the RULE (the seam, and that a new instrument is a row rather than a flag); the roster itself is never restated there. THE WITNESS, with its red measured rather than asserted. Removing self_host_binding while keeping its label -- the half-added row a registry actually suffers -- turns the_self_host_operand_routes_through_the_live_registry and every_rostered_label_carries_a_binding red while the subject invariant stays green, because that row asserts a different property. The routing claim asserts the PRODUCER ARM and not merely the absence of a refusal: a binding naming the wrong producer routes successfully and then measures something else while reporting this target's standing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
gunbc test //gunbc/instruments:self-hostruns the generation this repository can perform today — the seed emits v2's compiler closure, assembles it as a crate, and builds it. Measured here at 553s,exit_status=0 warning_count=0underRUSTFLAGS="-D warnings".Stacked on #11456 (in the merge queue), which introduced
run_self_host. This PR moves it to the modeled route and withdraws the flag.Why the route moved
The step first landed as
claim_executor --self-host. That binary is the wrong home by construction: ~20 flags hand-parsed in a 2783-linemainover 44,451 lines ofcli_run.rswith no.dagauthority at all — whilegunbc's own surface is modeled (gunbc.cli_dispatch_surface→gunbc_cli_dispatch_generated.rs).The seam that already exists for this is
gunbc.target_bindingTargetProducerbound to agunbc.instrument_targetslabel, and its host note states the extension shape outright:The flag is withdrawn in this change rather than left beside the target — two routes to one fact is the §3 fork the seam exists to prevent.
What the seam buys
The label carries its own refusal vocabulary, so an unknown or pattern operand refuses rather than reporting a pass, and the three terminations stay distinct —
0held,1observation did not hold,2no observation — for every instrument at once rather than per flag.That split is load-bearing here: a refusal from
prepare_emitted_compiler(emit refused, crate wouldn't write, cargo couldn't run) isSubjectUnreached, not an observation that the seed cannot build v2. Collapsing them would report a broken bench as a broken compiler.run_self_hostnow returnsSelfHostHeldrather than printing and returning unit, so the seam decides the termination once instead of every caller re-deriving it from stderr.Where it stops is the status
No roster accompanies it, deliberately. A list of known-good entries would be a second representation of what the instrument demonstrates by running — and it drifts exactly when nobody updates it:
required_v2_emission_entrieshas changed once in repository history, and that change was a directory move.A green is emission and compilation, not behavioural equivalence (that half lives in the three
behavioral-receipt-*instruments beside it). It is not the second generation either: the built binary emitting the same closure is the v2 → v2 boundary, and it refuses today —— because
v2.compiler.compiledeclaresSourceRootEvalDriver, whose rendered main has no compile mode. That refusal is the honest next position and belongs in this instrument when a driver can reach it.The roster documents itself
An unknown-target refusal now lists every rostered instrument, read from the same
instrument_registryrows the lookup just failed against:A listing therefore cannot disagree with what is invocable. DESIGN's Building & checks carries the rule (the seam; a new instrument is a row rather than a flag); the roster itself is never restated there, because a prose page naming them goes stale the first time someone adds a row and doesn't edit prose.
Witness, with the red measured
dag/test/claim/self_host_instrument_binding_witness_test.dag. Removingself_host_binding()while keeping its label — the half-added row a registry actually suffers:truefalsetruefalsetruetrueThe routing claim asserts the producer arm, not merely the absence of a refusal — a binding naming the wrong producer routes successfully and then measures something else while reporting this target's standing.
🤖 Generated with Claude Code