Skip to content

v2 grammar: a keyword is not a value name; module and data are contextual value names (ROOT E) - #13168

Merged
gunbai-bot[bot] merged 52 commits into
mainfrom
session/tidy-raven-393-kwbinder
Oct 5, 2026
Merged

gunbai-bot[bot] merged 52 commits into
mainfrom
session/tidy-raven-393-kwbinder

Conversation

@gunbai-bot

@gunbai-bot gunbai-bot Bot commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor

ROOT E of the native seven: a keyword used as a value name. The v2 grammar admitted keywords as binding names (dag_binding_name_keyword_tokens at every parameter, let, node NAME / NAME = and field-pattern binder) but only an identifier plus module/data as a reference (dag_grammar_primary_name_expr). So a name could be declared and never used. The refusal then landed at the first use, as the generic parse_g0_tokens_remain.

On the N7 path (measured at main a0a5c62161b / 9446711d348):

  • src/v2/compiler/body_lowering_fold.dag: body_lower_exit_first(.., then: Node, ..), refused at node: then.
  • src/v2/compiler/service_realization.dag: realization_require_conj(.., then: fn() ..), refused at then().

Ruling implemented (operator, via calm-boar-904, 2026-10-03: contextual-module-data)

  1. One declared set, dag_contextual_value_name_keywords = module, data, is consumed by both ends. A value reference reads dag_grammar_primary_name_expr. Every value-binder position reads that same production behind a guard, and so does the first segment of qualified_name, which is where a pattern's bare binder comes from. A name is declarable exactly when it is referenceable. The criterion for the set is recorded as precedent on the RFM row: a keyword qualifies only if it heads nothing that can begin or continue an expression.
  2. Every other keyword at a value binder refuses as KeywordAsValueName (new closed reason in v2.std.parse_refusal_reason), located at the keyword. Field labels still admit keywords (x.module, Rec { module: e }, { module: m }), and so do dag grammar: expr/stmt/let/if/arg/match_arm overlap rows to zero by restructuring #13056's statement and argument guards, which read dag_binding_name_terminal and are untouched.
  3. The keyword binders in the corpus are renamed, iterated to zero by the native census:
    • then: ts_if plus its 137 then: call labels (exactly one per ts_if( call; the only record field named then is the If constructor's own), body_lower_exit_first, realization_require_conj.
    • alias: the llama_cpp_rpc_construct parameter and its four call labels, nine let alias binders, and one pattern binder.
    • as: the belt_refusal_ids parameter and one pattern binder.
    • java.dag's let type.

Shape constraints kept

  • The guard is a LEADING not_predicate(refuse_on_match(..)) in sequence, never a choice arm. v2.compiler.parse parse_sequence_capture answers the other side, so the binder still captures as one atom (the shape silent-crane-798's readers take as pair.left) and no overlap row is added.
  • It fires only where the keyword can only be a binder, because patterns and the node sugar are probed ahead of statements and RefuseOnMatch stops the parse:
    • a parameter before :;
    • node NAME before : / =;
    • a field-pattern binder before , / }, excluding the literal patterns true / false;
    • let, unconditionally.

Declared rung limit (measured, recorded on the RFM row)

The bare NAME = statement sugar still refuses a keyword binder, but unnamed (parse_g0_tokens_remain at the keyword). Statement alternatives dispatch by FIRST set, and a not-predicate guard is transparent to that set, so the sugar alternative is never reached for a keyword: loop = 1 and then = 1 were measured with the locate probe. The unreachable guard was removed rather than left as dead code. A floor claim holds that the keyword is never accepted there.

Next-rung trigger (the capability): FIRST-set computation that sees through a refusal guard.

Evidence (remote, on this branch)

  • grammar_choice_ambiguity_roster(root: dag_grammar_root()): empty, so no overlap rows.
  • v2.test.parse.keyword_value_name_parse, 11/11 PASS:
    • Located KeywordAsValueName subjects (5): parameter, let, node NAME =, field-pattern value, punned field pattern.
    • Bare-form floor (1).
    • Probed-position controls (5): keyword labels, contextual module/data declared and used (including { module: module }), true/false literal patterns, a match arm led by true, and a node value followed by an if.
  • v2.test.parse.keyword_path_segment_parse: 3/3 PASS, unchanged.
  • Native census over dag + src/v2 (the if-arm-reader-differential refused-file list, located per file by a single-file native emit), at the pre-merge head:
    • refusals 113 → 96, and 0 parse_keyword_as_value_name remain;
    • body_lowering_fold.dag and service_realization.dag no longer refuse;
    • no regression: node.dag, model_core.dag and 00_compile are clean.
    • The 96 that remain are other known classes (unbraced-let lambda bodies, XL-2 service argv/paths, match guard, refinement).
  • Discriminating red: each located subject refuses KeywordAsValueName at its byte range. On main the same keyword binder is admitted, so the parse accepts or refuses only at a later use, and g_fatal_at is false. The claim module itself can't run on main, because the reason variant doesn't exist there.

Not covered

The last merge of main (11 commits) came after the census. A keyword binder newly added there would surface on the next census run, not in this PR's checks.

RFM: seed_admits_a_keyword_as_a_pattern_binder is extended with this specimen, the repair, the contextual-set precedent and the per-position rung.

Witness restructure (supplied token streams)

The floor's enrolment margin (302ms) refused seven kvn claims: each re-ran tokenize, which is not its subject. Per DESIGN §3 (a witness discriminates at one interface), 11 of 12 claims now parse a supplied token stream through v2.test.parse.supplied_token_stream_support supplied_stream_parse. kvn_param_refuses_at_the_keyword_holds stays on the real text route as the inhabitance claim, and it asserts the route as well as the answer: the fatal has to be KeywordAsValueName at the keyword's bytes. Each supplied stream has a *_stream_is_the_tokenizer_output_holds claim that compares class, lexeme, start, end and the layout digest against the real tokenizer.

Dropped: the two floor_cost_debt rows (floor_cost_debt_proven_chunk_22, ruled by sharp-raven-357). Both claims are now under the new-witness step budget, so the file is back to main's bytes.

🤖 Generated with Claude Code

gunbc-ci-auto-heal and others added 30 commits October 2, 2026 22:20
…pr row

body_lower_if_else_capture_optional searched the if's capture and, at any left
element that was not `else`, recursed LEFT before right. For
`if b { if c { X } else { Y } } else { Z }` it entered the then-arm and answered
the nested if's `else { Y }` as the outer else: Z was dropped, Y lowered twice,
and normalize ACCEPTED the module (silent wrongness, DESIGN §5). It was loud only
when Y held a function value, whose binder occurrence then appeared twice:
dag/std/materialization_ladder.dag value_materialization refused as
normalize_reason_minted_occurrence_duplicated, one of the native seven's file
refusals. body_lower_if_then_arm_optional searched the same way.

Both arm readers are now projections of body_lower_if_row_arms_optional, which
reads the two if_expr grammar rows by position (block: if cond fn_body else
(if_expr | fn_body); then-form: if cond then expr else expr) and answers Absent
for any other capture. The dead searching helper
body_lower_if_fn_body_capture_optional is deleted. The arm contract is
unchanged: an `else if` is answered as its if_expr shell, any other arm as its
production's captured child.

Claims: v2.test.claim.body_lowering.if_arm_position (an atom count over the
normalized tree, plus the function-value forms). Row: the capture side is filed
on gunbc.recurring_failure_mode else_arm_lowered_as_an_if_nested_inside_it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The first floor run (37073686904) refused all four claims over the new-witness
enrolment margin (353k-687k eval steps against 72300): each claim frame paid its
own tokenize -> parse -> normalize. iap_verdicts computes the four verdicts once
into four Bools (portable: no Node, no closure), each claim reads its field, and
the producer is enrolled WARM in v2.workflow.floor_pure_producer_share beside
eam_outcomes, the same ground.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
`if false { if true {1} else {2} } else {3}` is 3. The searching else reader took
the nested `else { 2 }` as the outer else, so a body lowered through it answers 2.
The mirror (`if true { if false {1} else {2} } else {3}` is 2) holds under either
reader and is the control. The module is import-free: the floor evaluates it
through the seed, and the native lane through the emitted compiler's own body
lowering, where it is the discriminating red. It is not yet merge-blocking
evidence for the v2 reader, since the native lane is not a required lane.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…dicate guards

A FIRST-overlap row is now removed only when the declared lookahead
(k=2) proves the alternatives disjoint. A leading not-predicate subtracts
the prefixes it is proved to cover, and only when the guard is plain (a
finite token-string set whose PEG success is membership). A guard naming
one word of a class narrows that position to class-less-word (the k=1
op_requires shape). Every undecidable arm keeps the row.

Roster 51 -> 48: stmt return-vs-bare-assignment, let_expr let-vs-bare,
let_sugar node-vs-bare are disjoint at k=2.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… retire its debt row

Touching the file put its bare references under the unimported-provider
gate. Both names are now imported, so the SubstrateInputsOnly row moves
ActiveDebt -> Retired { ImportsFixed }.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…fix; no wildcard arms

The ruling's direction: a pair of prefixes is disjoint when one side's
prefix is covered by the other side's proved-plain guard, whatever length
the guarded side's own prefix has (name: e | !(name :) e, whose positional
side can be one token). Guards ride the prefixes they head and survive
extension. The three GrammarExpr matches are written out arm by arm
(NonFoldResidueRosterDiverged). Two controls added for the short-side case.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…in not-predicates

arg: positional is !(name :) expr. stmt: the expression alternative is
!(name = | 'node' name) expr. expr: binary_expr is !(name =>) binary.
Each guard captures nothing, so trees are unchanged; the overlap proof
clears dag_production_arg and stmt's ident-led row.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The expression body was a second alternative sharing every
expression-led input with the statement body. It is the one-statement
case; body_lower_match_arm_body_lowered already lowers a statement body
through body_lower_stmt_spine, whose last statement is the value.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ator ruling 2026-10-03, this case only)

if_expr := if expr ( then expr else expr | block (else (if|block))? ).
The else_less_if_stmt production is deleted; stmt no longer offers it.
With-else trees are unchanged (a present optional and a choice pass
through). Lowering reads an else-less if through one positional reader,
body_lower_else_less_if_parts_optional: at statement position a block
ending in return is the admitted guard; the exit walk and the if value
reader refuse every other else-less if as else_less_if_unlowered, at it.

Controls (v2.test.parse.else_less_if_value_category): the guard is
accepted; the value position refuses at the if; both parse to the one
else-less if_expr route.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…(LetInExpressionDropped)

Per the 2026-10-02 zero-ambiguity decisions (statement let only, let-in
dropped). The in-form alternative shared 'let name = value' with the
statement form. A statement let's value followed by 'in' now refuses by
name at the 'in' (a not-predicate over refuse_on_match, so a statement
let's tree is unchanged). The six in-form lowering readers are deleted;
no non-test source used the form. Test sources that used it are moved to
the statement form or dropped where the statement form was already
covered; let_in_form_refuses_at_the_in_holds is the refusal control.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…guard (base 2, cap 3)

Every candidate site is read at depth 2; one that still overlaps is read
again only as deep as its longest proved-plain leading guard, capped at
3. Measured on the dag grammar: a global k=3 tripled the roster run
(114s -> 371s); guard-driven depth costs 138s. Controls: a three-token
guard clears its row at its own depth; the near miss keeps it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… empty arm body is its own production

expr: block_expr is !('{' '}' | '{' name ':' | '{' string ':') block_expr,
so '{}' in expression position is the empty brace literal (operator ruling
2026-10-03, A); binary_expr is !(name '=>' | '(' name ',') binary. Both
three-token heads are read at their guards' own depth.
match arm: body is empty_block_body ('{' '}') | !('{' '}') stmt body, so
the forty 'X => {}' arms stay empty blocks; it lowers as an empty block
always did (statement_spine_headless, at the body).
Route controls: v2.test.parse.brace_and_lambda_head_route.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…e slot as IfRowNoElse

On #13029's positional reader. The block form's else slot is seq(else, ..)
or the empty node of an absent optional; the empty node answers
IfRowNoElse, said rather than searched for or read as a value. The
else-less guard reads its condition and arm from the same row reader.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…0-p2

# Conflicts:
#	src/v2/compiler/body_lowering_fold.dag
…he parsed node

DESIGN section 3 witness rule (floor refused five new claims on cost).
else_less_if_value_category: the conjunction is split into one claim per
input; the guard and value claims parse once and hand the node to
body_lower_admitted_guard_optional / body_lower_if_expr instead of a whole
normalize. The real path stays executed by closure_parse_batch_two
an_early_return_guard_parses_and_lowers_holds.
brace_and_lambda_head_route: one data declaration per source, one parse
per claim.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ly at a site's own alternatives

Review 74631: the ingested bind fixtures now use statement let; the
round-trip instrument's let_in case is retired (let_statement covers the
form), and its derived drop population and projection follow. The bind
family spec waits on the target-model separator change (its own PR).

Cost (floor fill 26.5s against main's ~5s): a not-predicate narrows
prefixes only where it heads one of the site's own alternatives; nested
guards inside expanded productions are read as the empty prefix they
consume, which only keeps rows. Measured remotely: the proof was ~8s of
~9s preparation.

Also fixes the if-arm Optional typing in else_less_if_value_category.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…0-p2

# Conflicts:
#	docs/design-rung-drops.md
…101k against 72.3k)

The value position is a one-line data initializer; the guard keeps the
shortest fn with a statement after it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…0-p2

# Conflicts:
#	docs/design-rung-drops.md
#	src/v2/extdeps/languages/dag.dag
#	src/v2/std/parse_refusal_reason.dag
#	src/v2/test/claim/parse/newline_dual_role_operator_mutation_test.dag
…okens

Floor fill was 26s against main's ~5s. Measured remotely (startup
subtracted): grammar preparation 8.6s at depth cap 3, 3.5s at cap 2,
2.3s with the proof off -- the escalation re-walked every head of the
site, so the expr site re-expanded every identifier-led binary
expression at depth 3. A prefix whose first token begins no pair that
overlapped at the shallower depth cannot overlap deeper, so the deeper
read is restricted to those tokens. After: 2.5s. Roster at this head: 0
rows. All 12 analyzer controls and 8 route claims pass.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Floor: the two guard claims were 89-91k against 72.3k; parsing the body
as the fn_body production drops the module and signature tokens.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
elif_verdicts and bhr_verdicts compute every claim's Bool from one parse
per source and are enrolled in floor_pure_producer_share; each claim
reads its field (the claims had measured at the new-witness budget's
edge, refused on enrolment margin).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Measured with claim_batch (prepare wall, 3 runs each, spread <0.3s): main
grammar 2.5s; this head before 6.8s; depth cap 2 3.3s; proof off 2.2s. The
depth-3 read of the expr site's '{' row walked every statement form after
the brace with depth 2 left. A prefix whose first two tokens begin no pair
that overlapped at the shallower depth cannot overlap deeper, so the
second position is restricted to those pairs' second tokens (unless a
pair has a prefix shorter than two). After: 5.0s. Roster 0; all 12
analyzer controls pass.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ough IfRowElse

#13051 (now on main) read IfRowArms.else_part, which this branch replaced
with else_slot: IfRowElse. The census's new-reader else now answers
through body_lower_if_row_else_part_optional (IfRowNoElse -> Absent), the
same explicit no-else the lowering readers use. emit-build at 7668231
refused 'no field else_part on type IfRowArms'.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…0-p2

# Conflicts:
#	src/v2/workflow/floor_pure_producer_share.dag
gunbc-ci-auto-heal and others added 6 commits October 4, 2026 01:13
…rim two arms

The node-then-if control is the same fixture as #13133's enrolled
v2.test.parse.bare_node_statement_route bare_node_statement_before_an_if_takes_the_expression_route;
repeating it here was duplicated evidence (and the claim the floor priced highest,
95797 steps). The bool-literal and contextual pattern-binder controls now end their
single arm in '=> 0'. The floor's own measurement on 49d1886 put only these three over
the 72300 new-witness budget.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…rp-raven-357

kvn_bool_literal_pattern_control_holds (76109 steps) and
kvn_contextual_pattern_binder_control_holds (77441 steps) on the floor of record, run
37167363297 at head 2c91990, against the 72300 new-witness budget. Two rows, not
folded. Reason: each subject is already the minimal construct and the cost is the
interpreted tokenize/parse floor. Trigger: per-source tokenize/parse cost below the
budget, or parse claims evaluated on the native route.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…'s parse

The chunk list was already balanced (its closing brace is the fn's own line); the extra
'}' ended floor_cost_debt_proven_chunks early and every lane refused the module as
unparseable (src/v2/workflow/floor_cost_debt.dag:122723). v1_src_dag_parse: 7601 files
parse-clean.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ance claim

The floor's enrolment margin (302ms) refused seven kvn claims: each re-ran
tokenize for a subject that is the grammar's binder guard. Per the §3
witness rule, every claim but kvn_param now parses a supplied stream;
kvn_param stays on the real text route and asserts the located
KeywordAsValueName fatal (the route, not only the answer). Each supplied
stream owes a fidelity claim against the real tokenizer, comparing class,
lexeme, positions and layout digest.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ness budget on supplied streams

Floor at 0b89edf measured kvn_bool_literal_pattern_control_holds at 48755
steps (was 76109) and kvn_contextual_pattern_binder_control_holds at 49815
(was 77441), both admitted inside the enrolment margin, so the ruled debt
rows no longer apply. File restored to origin/main's bytes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…rdAsValueName

dag_grammar_field_pattern_expr is 'name [":" pattern]', so R { true } is an
implicit value binder named true, not a literal pattern. The field-pattern
guard now takes admit_bool_literals: false at the pun/outer-name position
and true after ':', where a pattern reads true/false as literals. Claims:
R { true } and R { false } refuse at the keyword; R { true: x } and
R { ok: true } still parse.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@gunbai-bot
gunbai-bot Bot added this pull request to the merge queue Oct 4, 2026
roadmap_component: take main's widened account-slot condition with the
then_stmt label, and rename main's two new ts_if then: labels to then_stmt.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@gunbai-bot
gunbai-bot Bot removed this pull request from the merge queue due to a manual request Oct 4, 2026
@gunbai-bot

gunbai-bot Bot commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor Author

Floor refusal at b72b994: runner speed, not the change. Eval steps are the same as on the green floor at 41d5a63. Only wall time moved, and a test already on main moved by the same factor.

identity steps, green floor ms steps @b72b994 (run 37197430363) ms
main peer test.claim.parse_test_fn_decl_return_clause.nfbcp_eq_body_specimen_parses_holds 48754 240 48760 426
kvn_bool_literal_pattern_control_holds 48755 267 48811 435
kvn_contextual_pattern_binder_control_holds 49815 241 49871 435
kvn_match_arm_true_control_holds 41709 260 41715 416
kvn_contextual_module_data_control_holds 37857 247 37863 402
  • Green-floor sources: the peer, match_arm_true and contextual_module_data are from the 0b89edf floor (run 37172055960); bool_literal and contextual_pattern_binder ms are from the 41d5a63 floor (run 37180490606). The green-floor steps are from 0b89edf.
  • At b72b994, the 15 kvn parse claims ran 346–438 ms against budget_ms=302. Every claim's step count is within 56 of its green run.
  • The only content the merge brought in was the roadmap_component label conflict, which is outside these claims' closure.
  • The margin judges a host-dependent quantity, so the floor is being re-run once. The margin rule itself is routed to its owner via calm-boar-904.

— sent from tidy-raven-393

@gunbai-bot

gunbai-bot Bot commented Oct 4, 2026

Copy link
Copy Markdown
Contributor Author

Correction to the comment above: the rerun reproduced the refusal, so this is not runner variance. The rerun (job 111431173772) read the main peer nfbcp_eq_body_specimen_parses_holds at 430 ms / 48760 steps, matching the first b72b994 run. The kvn parse claims read 347–499 ms, again at unchanged steps. The tokenizer-only claims got faster (95 → 59–73 ms). So the per-step cost rose only on the prepared-parse path, by about 1.75×. It rose on a test already on main too, which places it in main's range after 41d5a63, not in this PR. It is being bisected or routed via calm-boar-904; this PR's floor stays refused until that lands or the margin owner rules.

— sent from tidy-raven-393

gunbc-ci-auto-heal and others added 2 commits October 4, 2026 18:38
parse_refusal_reason: keep both new variants (KeywordAsValueName from this
branch, TokenStartsNotIncreasing from main) in the comment, the coproduct
and the symbol match; the mutation test's exhaustive match keeps both arms.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…t_alias

alias is a keyword; this PR refuses it as a value binder (KeywordAsValueName).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@gunbai-bot

gunbai-bot Bot commented Oct 4, 2026

Copy link
Copy Markdown
Contributor Author

Floor at cfd9538 fails for the same known reason, and nothing in this PR can fix it. The same 15 kvn parse claims are refused on enrolment_measured_over_margin (365–508 ms against a 302 ms margin). All 14 tokenizer checks are admitted, and the merge added no new identity. The peer already on main, nfbcp_eq_body_specimen_parses_holds, reads 450 ms at 47603 steps in this run. The cause is the interpreter's per-context content hash of the PreparedGrammar argument (see the comment above). royal-deer-478 owns the repair, sequenced after #13043, so this PR waits on that with no budget rows.

— sent from tidy-raven-393

gunbc-ci-auto-heal and others added 2 commits October 4, 2026 21:43
roadmap_allocation_client: take main's rewritten intent block. Main's new
js_branch parameter 'then' is renamed then_arm (a keyword is not a value
binder), and its ts_if then: labels become then_stmt, matching ts_if's
renamed parameter.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
#13305 landed the service_realization 'then' -> 'proceed' rename, so this
branch's hunk there is dropped (file is main's bytes). The RFM row keeps
main's THIRD SPECIMEN (#13305's parameter shape) and renumbers this
branch's binder/reference receipt to FOURTH.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
roadmap_component / roadmap_inline_assignment_client / roadmap_issue_sort:
this branch's change there was only the ts_if then: -> then_stmt: label
rename (verified: identical modulo that rename), so take main's rewritten
files and reapply it. Main's new dag/gunbc/site/{controls,choice_client,
mention_client}.dag ts_if call sites get the same label rename.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@gunbai-bot
gunbai-bot Bot added this pull request to the merge queue Oct 5, 2026
Merged via the queue into main with commit 59c3845 Oct 5, 2026
4 checks passed
@gunbai-bot
gunbai-bot Bot deleted the session/tidy-raven-393-kwbinder branch October 5, 2026 17:23
gunbai-bot Bot pushed a commit that referenced this pull request Oct 6, 2026
…et route

The claim predates #13168, which ruled that a keyword is not a value name. Gating the module ran it for
the first time and it returned false. It now asserts the refusal. The sibling claim
return_statement_takes_the_return_route stays the positive control on the same parse helper.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants