Repository navigation
dag target: tokens are written separated, so emitted text reads back as the tokens emitted - #13113
Merged
Merged
Conversation
…scoped, { let k = (v) { b } }
Operator/parent ruling (stern-bear-500, ruling A): ahead of the dag
grammar dropping 'let x = e in body' (#13056), TargetBindLetShape.in_token
becomes Optional<Symbol>. C/Rust/TypeScript keep Present(';'); dag
declares Absent and a core Bind is emitted as a block whose first
statement is the binding and whose last is the body: the value is
parenthesised and the body braced so no continuation of the value can
absorb the body, and the outer braces give the binding a statement
position wherever the Bind stands. Absent travels in the projection
bundle as its own atom, so a bundle missing the field still refuses.
The dag bind family's tokens and authority text move to that spelling.
Controls (v2.test.emit.dag_bind_let_block_scoped): a Bind nested as the
value of a Bind, and bodies that are a bare name, an empty brace literal,
a '-'-led expression and a nested Bind, each emitted through the dag
projection and reparsed by the dag grammar to the same binds.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…model per claim Floor refused the five round-trip claims on cost (74-112k against 72.3k). Each now renders with the dag lex rules and the two spellings its tokens bind instead of building dag_bind_target_model, and parses the emitted text as the dag grammar's expr production over the shared prepared grammar instead of a module around it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…quence, not text
Floor: the round trips were still 72-105k against 72.3k. The emitter's
product is a token sequence, so each claim now hands those tokens to the
expr production directly (no render, no lexer). The previous text route
also passed for the wrong reason: dag text rendering puts no separation
between tokens ({letx=(a){x}}), and 'letx = (a)' re-read as a bare
assignment let. a_single_bind_emits_block_scoped_tokens pins the exact
token sequence instead; the rendering defect predates this change and is
reported separately.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… token fold reads them Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… into session/swift-lark-785
…itance on the real route; failure-mode row Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…rm rows (verilog) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… once (warm producer) The nested-value and nested-body claims parsed ~21 tokens each (87-105k steps). They are replaced by two composition claims (the emitter places a Bind's tokens verbatim in the value group and the body block) and two slot parses (a Bind inside a value group, a Bind as a body statement), which with the lone-Bind parse cover nesting in a context-free grammar. The five parses run once in dbl_verdicts, enrolled warm in floor_pure_producer_share; claims read stored Bools. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…join (its parse alone is over the new-witness budget) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… into session/swift-lark-785
…bodies and a Bind value; token join then one parse, shared once (warm producer) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… warm row) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
gunbai-bot Bot
pushed a commit
that referenced
this pull request
Oct 3, 2026
Conflict in v2.workflow.floor_pure_producer_share: main (#13113, #13056) added three hand warm rows (elif_verdicts, bhr_verdicts, trt_verdicts) to the roster this PR deletes; resolved to this PR's side and recorded for disposition. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This was referenced Oct 4, 2026
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.
#13099 (eager-crab-610) has merged; this branch has main merged in and the diff is now only this change's eight files.
Coordination with #13043 (royal-deer-478). This PR adds one hand-roster row,
v2.test.emit.dag_target_text_round_trip.trt_verdicts, tov2.workflow.floor_pure_producer_sharefloor_cross_claim_pure_producers_warm. #13043 deletes that roster in favour of derived cross-claim demand. If #13043 lands first, I drop the row here and confirm the six parses are derived as shared, with the floor at 0 over cost. If this lands first, royal-deer-478 dispositions the row.Defect
The dag target wrote its tokens with nothing between them.
letxcame out asletx, one identifier;if true then 1 else xcame out asiftruethen1elsex, which the dag grammar'sexprproduction accepts as a single name. Text round trips through the dag target were reading back a different construct and passing.There were two causes, not one:
dag_target_model(and its three sibling constructors) declaredtoken_class_emit_transformsempty.bound_tokens_source_text, whichserialize_concrete_syntax_tokens_to_source_stringcalls for every value-expression body, never read that map at all. Only the relation-row route did. So rows on the dag model alone would not have fixed a Bind.Change
EmitSpellingWrap { prefix: "", suffix: " " }); no new mechanism.dag_token_class_emit_transformsderives one row per token class fromdag_lex_rules, so the lex rule set stays the one list of classes.>then=unseparated is the one token>=(claimed below), and//opens an annotation. Whitespace is trivia in the dag lexer, so the space cannot change the tree.bound_tokens_source_text_with_transformsis the fold;bound_tokens_source_textis that fold with the empty map, so its direct callers (bash) are byte-identical.serialize_concrete_syntax_tokens_to_source_stringpasses the target's map. Both folds now reach onelayout_or_atom_in.Controls
v2.test.emit.dag_target_text_round_trip. Every round trip is: tokens from the real emitter -> text through the dag rows -> lexed withdag_lex-> the lexed token classes equal the emitted ones, in order -> those lexed tokens parsed once atexprto one tree of the expected shape. The join comes first; a let count is never asked in its place.{ let k = (v) { b } }at text grain, one claim per body: a bare name, an empty brace literal{}, a--led expression, and a Bind. Plus a Bind as the value.let_exprfor the first three; two for the nested ones. A Bind in the value group sits under the outer let; a Bind in the body block does not (the block is its own statement after the let). The claims state which.if true then 1 else x: the join, and oneif_expr.[ident].>=reads back as two tokens with the rows and as>=without.serialize_concrete_syntax_tokens_to_source_stringoverdag_target_model()writes a Bind that lexes back as emitted. Dropping the rows from the model, or the map from the serializer, fails it.The six parsing round trips are computed once, in
trt_verdicts, a nullary pure producer enrolled WARM inv2.workflow.floor_pure_producer_share(the same arrangement #13099 uses fordbl_verdicts); each claim reads its Bool. See Cost for why.One thing I got wrong first, and it is the class itself. My first red asserted that the unseparated nested Bind does not parse to two lets. It returned false: that text does parse to two nested lets, because
letx = (..)reads as a bare-assignment binding. So a let count cannot tell the good text from the bad one; only the token join does. The red is now the token join, and the receipt is in the failure-mode row.Recorded red on main (a0a5c62), by scratch probe, not landed:
iftruethen1elsexparses atexprwith one token and noif_expr; spaced, six tokens and oneif_expr. Nestedlet … intext is rejected atexpron main in either spelling, which is why (a) needs #13099's block form.What ran
All by
gunbc run --functionlocally, one claim per process. The binary was borrowed from another worktree and is one commit behind main in the seed, so CI is the authority; none of this is a floor run.dag_target_text_round_trip: true. The six parsing round trips were re-run after the rewrite; the two reds, the punctuation claim and the inhabitance claim were run before it and their code is unchangeddag_bind_let_block_scoped(nested_bind_emitted_block_scoped_reparses_to_the_same_binds,a_single_bind_emits_block_scoped_tokens): truerust_bind_let_emit_holdsandts_bind_let_iife_emit_exact_holds(exact text): true. Rust and TypeScript models keep the empty map.bind_emit_holds(dag emit equals serialize): trueOther targets on the flat route (asked by stern-bear-500)
Precisely what changed:
bound_tokens_source_textitself still passes the empty map, so its direct callers (bash,bash_orch_if) cannot change. Onlyserialize_concrete_syntax_tokens_to_source_stringnow passes the target's map. It is reached from four places: a bodied arrow's value-expression tokens, an import projection, a produced declaration, and a semantic declaration. The last builds its own serialize target with the empty map (semantic_decl_serialize_target), so it cannot change either.No target's emitted bytes changed. None of verilog, swift rows, sql, gha or bash declares a value-expression projection, an import projection, a bodied scaffold or produced-declaration support, so their own emission never reaches that serializer. One exact-text claim per target, executed after the change, all true:
w_interlock_emits_expected_bytes_through_the_generic_foldbash_fold_command_var_ref_x_holds,bash_fold_command_arbitrary_lit_quote_escape_holdsbash_emit_embedded_quote_holds,bash_emit_bound_words_holdssql_fold_create_table_with_pk_holdsgha_fold_pilot_job_byte_identical_holdsstring_literals_escape_statically_and_keep_extended_text_verbatim,a_member_with_a_body_is_blank_separated_and_nestedemit_directive_rust_escapes_string_literal_holdsemit_directive_bash_message_flows_holdsThese claims run each target's own route, which is the point: that route is not the flat serializer. I did not run
sql_target(only its fold) or every claim in each file.What does change is a direct call to the flat serializer with a transformed class, which no production path makes today. New claim
v2.test.emit.target_text_layoutthe_flat_token_route_applies_the_targets_transform_rowspins it on verilog:assign=;now render"assign = ;\n". Before, that fold wrote the bare lex literals with no rows (assign=;, no line break); I derive the before text from the old fold, I did not execute it. This is a fix: it is the spelling the relation-row route already gives those classes.Cost, read from the floor run on head b8ace9b (budget 72,300 eval steps per new witness)
That run executed 677 claims, 667 passed, 5 known-red held, 0 failed, and refused on cost alone:
One expression parse is at or past a new witness's budget, and the nested one is far past it. The second floor run (head 5097bbc, nested claim cut to the token join only) passed. The text-grain controls then required (sharp-raven-357, via stern-bear-500) put a parse back in every Bind case, so the parsing round trips now run once each in the warm producer
trt_verdictsand no claim pays a parse. The floor run on head d475a8a passed: 683 claims executed, 673 passed, 5 known-red held, 0 failed, 0 over the cost requirement, and none of this PR's claims on the over-cost line.Ledger
New row
gunbc.recurring_failure_mode.round_trip_claim_passes_on_emitted_text_reread_as_another_construct. Rung now: mechanically preventable. Next-rung trigger: the serializer checks, from the target's own lex rules, that each adjacent pair it writes lexes back as itself, for every target.dag_target_emits_only_its_fixture_rowstill says the dag model's transform map is empty. That was true at the commit it cites; I left the receipt alone.🤖 Generated with Claude Code