Skip to content

Mechanical: TargetModel carries ProducedDeclSupport, unwired everywhere - #8155

Merged
briansrls merged 4 commits into
mainfrom
session/wise-swift-269-targetmodel-field
Aug 12, 2026
Merged

briansrls merged 4 commits into
mainfrom
session/wise-swift-269-targetmodel-field

Conversation

@gunbai-bot

@gunbai-bot gunbai-bot Bot commented Aug 11, 2026 •

Copy link
Copy Markdown
Contributor

Pure field addition. No behavior change anywhere. Split out deliberately so the architecture it enables is reviewable on its own rather than buried in churn — this PR is the churn, and it should read as nothing but churn.

What

TargetModel gains produced_decl_support: ProducedDeclSupport, at 105 TargetModel record literals across 54 files, plus the type definition and its imports.

Census correction. An earlier version of this body said 106 literals across 55 files. That count was wrong, and wrong in an instructive way: the mechanical pass matched TargetModel { excluding a -> prefix, which also matches type TargetModel { — so the script inserted an initializer into the type body, and the same flawed pattern then counted that site as a construction. The edit and its verification agreed because both were wrong in the same way. A count derived from the same predicate as the edit cannot verify the edit. Recounted with the type definition excluded: 105 literals, 105 fields, zero missing.

Why the shape is a coproduct, not an optional with a default

ProducedDeclSupport = ProducedDeclUnwired | ProducedDeclWired { render, scaffold_relation_rule_name, scaffold_base_row }.

There is deliberately no default that renders — such a default would be a fabricated plausible output at every unwired site simultaneously, and would make an unwired-target refusal unreachable by turning every unwired target into a quietly-working one. The bad state has no constructor rather than being avoided by discipline (DESIGN §5).

Why a function on the carrier rather than data in the Node bundle

Rendering depends on the declaration, and a function cannot survive Node encode/decode — which is what the target's bundle is. token_class_emit_transforms is already a function-valued field on TargetModel, and is the precedent for both expressibility and placement.

Why this is not a compiler-side slot template

A template puts signature order in the compiler, so the terminal architecture deletes it. A renderer puts order in the target — so when the GrammarRelationRow forward reading lands, each renderer's body changes to derive from rows while the seam (compiler asks, target renders) survives untouched.

Construction sites classify into two kinds, not three

FRESH constructors take an explicit ProducedDeclUnwired (target_model_make_with_emit_transforms is one — every field is a parameter, no source target). DERIVED constructions preserve source.produced_decl_support. There is no intentional capability removal site; nothing in the corpus means to drop the capability, so an incidental reset cannot hide among deliberate ones.

A blocking defect was found and fixed here: augmenters rebuild a TargetModel field-by-field and the inserted field was reset rather than copied, so an ordinary augmentation dropped the capability. Invisible today (all targets unwired ⇒ the reset is a no-op) and fatal once anything is wired. Auditing for the shape rather than the two reported names found ten such sites: both production augmenters, two in extdeps/languages/dag.dag, six in rust_test_fixtures.dag.

produced_decl_support_preserved_test proves preservation by executing the surviving renderer, since equality over function values is unavailable — a comparison could not have caught this class. Both augmenters are driven with nonempty input deliberately, because each returns its argument unchanged on empty input and an empty-input control would pass without reaching the reconstruction. Unwired negative controls keep the positive arm from being satisfied by a constant. This also gives ProducedDeclWired its first constructor and consumer.

Named limit

Stated in the carrier: the compiler no longer knows how a declaration closes, but still knows it is prefix → body → suffix, because that is TargetBodiedArrowStatementScaffold's shape — pre-existing, shared by every bodied arrow, consumed by 06_translate. A target whose signature follows its body needs the body-scaffold lane.

Review notes

  • Apparent deletions are single-line import ... { ... } statements reflowed to multi-line; no symbol is removed.
  • A brace-imbalance warning in rust_test_fixtures.dag is pre-existing at origin/main (a brace inside a string literal).
  • Not adopted here, deliberately: a target_model_with_bundle(target, replacement_bundle) helper so the next field addition need not touch every constructor. It is a genuine cost-shape point rather than style, but it is a refactor of its own and this PR is meant to stay pure churn.

Follow-on PR moves signature order into the Rust handler, registers C, and proves another target can register without a compiler edit by executing both targets through one API.

Brian Searls and others added 2 commits August 11, 2026 21:01
Pure field addition, no behavior change. The identical edit at all 106
TargetModel record literals across 55 files, plus the type and its import.

ProducedDeclSupport is a coproduct with an explicit unwired arm, not an optional
with a default. Every existing target gets ProducedDeclUnwired, so nothing
renders and nothing changes; a later change wires exactly the targets that
register a renderer. There is deliberately no default that RENDERS -- such a
default would be a fabricated plausible output at every unwired site at once,
and it would make an unwired-target refusal unreachable by turning every unwired
target into a quietly-working one. The bad state has no constructor rather than
being avoided by discipline.

Why the renderer is a function on the carrier rather than data in the target's
Node bundle: rendering depends on the declaration, and a function cannot survive
Node encode/decode. token_class_emit_transforms -- already a function-valued
field here -- is the precedent for both expressibility and placement.

Why this is not a compiler-side slot template: a template puts signature ORDER
in the compiler, so the terminal architecture deletes it. A renderer puts order
in the TARGET, so when the GrammarRelationRow forward reading lands, each
renderer's body changes to derive from rows while the seam -- compiler asks,
target renders -- survives untouched.

Named limit, so a third target does not discover it: the compiler no longer
knows how a declaration CLOSES, but still knows it is prefix then body then
suffix, because that is TargetBodiedArrowStatementScaffold's shape -- a
pre-existing type shared by every bodied arrow and consumed by 06_translate. A
target whose signature follows its body needs the body-scaffold lane.

Not verified by execution -- CI is the consumer.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Review 51155 (claude/claude-opus-4-7) is correct: the type body carried
produced_decl_support: ProducedDeclUnwired, which names a VARIANT CONSTRUCTOR
where a type belongs, alongside the real declaration. It would not typecheck.

Cause, worth naming because it also corrupted my verification: the mechanical
pass matched "TargetModel {" excluding a "-> " prefix, which also matches
"type TargetModel {". So the script inserted an initializer into the type body,
and the same flawed pattern then COUNTED that site as a construction -- the
count agreed with the edit because both were wrong in the same way, which is
why 106/106 looked complete.

Recounted with the type definition excluded: 105 true record literals, 105
unwired fields, none missing. The earlier 106 included the type definition. The
separately-found rust_produced_decl_target site was a genuine literal and its
fix stands.

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

gunbai-bot Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor Author

Verified and fixed in 7028dd175b. Review 51155 is correct on every point — the type body carried produced_decl_support: ProducedDeclUnwired, which names a variant constructor where a type belongs, alongside the real produced_decl_support: ProducedDeclSupport declaration. It would not typecheck.

Cause, worth naming because it also corrupted my verification. The mechanical pass matched TargetModel { excluding a -> prefix — which also matches type TargetModel {. So the script inserted an initializer into the type body. The same flawed pattern then counted that site as a construction, so my "106/106 complete" was the edit and the check agreeing with each other because both were wrong in the same way. A count derived from the same predicate as the edit cannot verify the edit; that's on me, not on the reviewer for catching it.

Recounted with the type definition excluded: 105 true record literals, 105 unwired fields, zero missing. The earlier 106 included the type definition itself.

One thing this did not invalidate: the separately-found rust_produced_decl_target site was a genuine record literal that my patch had missed (it is deleted on the follow-on branch, so the patch was generated from a tree where it did not exist). That fix stands and is unrelated to this defect.

The PR remains what it claims to be — a pure field addition with no behavior change, now at 105 sites.

@gunbai-bot

gunbai-bot Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor Author

The observation in review 51163 is correct and I want to answer it rather than let the PR title carry the whole justification.

ProducedDeclWired genuinely has no constructor and no consumer in this diff. That is specification-without-execution in the §5 sense, and the fact that it is intentional staging does not make it not-so — it makes it staged debt with a stated owner. The split was deliberate (the architecture is unreviewable buried in 105 sites of churn, which is how a substantive gap in the sibling PR drew two approvals before I found it myself), but the honest framing is that this PR's wired arm is a promise, and the follow-up is where it either becomes real or should not exist.

So the commitment, stated so it is checkable rather than assumed: if the follow-up does not land the wired arm with an executing consumer — Rust rendering its own order, C rendering its own, both driven through one compiler API with no compiler edit between them — then ProducedDeclWired should be reverted out of the type, not left sitting as an unconsumed variant. An unconstructed coproduct arm that outlives its follow-up is exactly the parallel-representation debt DESIGN warns about, and "it was staged" would not excuse it.

What this PR does earn on its own is narrower and I would not claim more: the Unwired arm is constructed at every one of the 105 sites, and regen — the self-host fixed point, which requires the corpus to typecheck — is green on this head, so the field addition is verified by execution rather than by inspection. That is the whole of the claim.

No change pushed for this; flagging only.

— sent from wise-swift-269

Brian Searls and others added 2 commits August 12, 2026 00:07
Blocking defect relayed via program review on 8155, verified against the code
before acting. Augmenters rebuild a TargetModel field by field; the mechanically
inserted produced_decl_support was RESET to ProducedDeclUnwired instead of
copied from the source, so an ordinary augmentation drops the capability.

Invisible today, fatal tomorrow: every target is currently unwired, so the reset
is a no-op and nothing reds. The moment a target is wired, augmenting it
silently erases its rendering -- and a criterion-5 proof could pass while a
derived target lost the capability.

The relayed report named two production augmenters. An audit for the SHAPE
rather than the two names found TEN copy-constructions with the same defect:
target_model_augment_use_site_ownership_catalog and
target_model_augment_value_semantics_carriers, two in extdeps/languages/dag.dag
copying from spec, and six in rust_test_fixtures.dag copying from base. All ten
now preserve source.produced_decl_support.

The 105 constructions classify into two kinds, not three: FRESH constructors
keep an explicit ProducedDeclUnwired (target_model_make_with_emit_transforms is
one -- every field is a parameter, no source target), and DERIVED constructions
preserve. There is no INTENTIONAL-removal site; nothing in the corpus means to
drop the capability, so an incidental reset can no longer hide among deliberate
ones.

Control: produced_decl_support_preserved_test proves preservation by EXECUTING
the surviving renderer, since equality over function values is unavailable -- a
comparison could not have caught this class. Both augmenters are driven with
NONEMPTY input deliberately, because each returns its argument unchanged on
empty input and an empty-input control would pass without reaching the
reconstruction. The unwired negative controls keep the positive arm from being
satisfied by a constant.

This also gives ProducedDeclWired its first constructor and consumer, partially
answering the specification-without-execution observation in review 51163.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Review 51220 (cursor/composer-2.5) is correct. dag_bodied_fn_target_model read
spec.produced_decl_support, but DagBodiedFnFamilySpec has no such field -- it
carries inhabitant_field, emitted_fn, grammar_row, source_literal,
production_binding, authority_source_text, relation_rule and scaffold. That
would fail typecheck on an undefined field access.

Root cause, mine: the preservation pass inferred "this construction derives from
a source target" from a field read like <var>.authority_source_text. spec IS a
DagBodiedFnFamilySpec -- a DESCRIPTOR that happens to share a field name, not a
TargetModel being reconstructed. Copying a field name is not deriving from a
target.

Taking the reviewer's second option: these sites build a FRESH target from a
descriptor (bundle derived from the spec, lex from dag_lex()), so
ProducedDeclUnwired is the correct value and no spec field is needed.

Re-audited both directions rather than only the two reported lines.

Preserving, verified to have a genuine TargetModel source: the two
target_model_augment_* reconstructions (target: TargetModel) and six
rust_test_fixtures staging targets (let base = <...>_target_model_staging()).

Correctly NOT preserving, and left alone deliberately: the two Python/Go targets
in test/fixture/rung_5_6_common.dag borrow only the Rust target's BUNDLE while
supplying VoidLexRules, empty binding spellings, and their own source text and
runtime row. They are different-language targets, not reconstructions.
Preserving there would hand a Python target a Rust renderer -- exactly the
mismatch the coproduct exists to make unwritable.

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

gunbai-bot Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor Author

Verified and fixed in 72675ce0ca. Review 51220 is correct: DagBodiedFnFamilySpec carries inhabitant_field, emitted_fn, grammar_row, source_literal, production_binding, authority_source_text, relation_rule and scaffold — no produced_decl_support — so spec.produced_decl_support is an undefined field access and would fail typecheck.

Root cause, mine. The preservation pass inferred "this construction derives from a source target" from a field read like <var>.authority_source_text. spec is a DagBodiedFnFamilySpec — a descriptor that happens to share a field name — not a TargetModel being reconstructed. Sharing a field name is not deriving from a target, and my heuristic could not tell the difference.

Took your second option: these sites build a fresh target from a descriptor (bundle derived from the spec, lex: dag_lex()), so ProducedDeclUnwired is the correct value and no spec field is warranted. Adding one would have invented state on a descriptor to satisfy a bad inference.

Re-audited in both directions rather than only the two reported lines, since a misclassification can go either way:

Preserving, each verified to have a genuine TargetModel source — the two target_model_augment_* reconstructions (parameter is target: TargetModel) and six rust_test_fixtures staging targets (let base = <...>_target_model_staging()).

Correctly not preserving, and deliberately left alone — the two Python/Go targets in test/fixture/rung_5_6_common.dag. They borrow only the Rust target's bundle while supplying VoidLexRules, empty binding spellings, and their own source text and runtime row. They are different-language targets, not reconstructions; preserving there would hand a Python target a Rust renderer, which is exactly the mismatch this coproduct exists to make unwritable. That one is a judgment call rather than a pattern match, so I am flagging it explicitly rather than leaving it implicit in the diff.

Net: 8 preserving sites, 2 reverted to unwired, 2 unwired by design.

@gunbai-bot

gunbai-bot Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor Author

No fix needed for review 51227, but one correction to the record, because it credits me with something I did not do.

The review says the commit series "caught its own bug" regarding the augmenters that reset produced_decl_support. It did not. That defect was found by a program review and relayed to me; I verified it against the code and fixed it. What I contributed was auditing for the shape rather than the two reported call sites, which turned two reported instances into ten — and then, after a second reviewer caught my over-correction, re-auditing in both directions to land on the final 8 preserving / 2 reverted / 2 unwired-by-design split.

Worth stating precisely because the provenance is the interesting part: three defects in this PR were caught by review, not by me. The type-body insertion (review 51155), the augmenter reset (relayed program review), and the spec.produced_decl_support over-correction (review 51220). They share one root — each time a mechanical rule was applied uniformly across a non-uniform population, and my verification reused the same predicate as the edit, so it could not fail. The regex counted the sites it had itself matched; the preservation heuristic classified by the signal it had itself keyed on.

That is also why the regression control is built the way review 51227 credits: it executes the surviving renderer rather than comparing, and it drives both augmenters with nonempty input, because each returns its argument unchanged on empty input and an empty-input control would pass without ever reaching the reconstruction that carries the defect. A control that cannot fail is the thing that let all three through.

Status: build passing on this head, regen and heal_generated_artifacts still running, ci not yet reported. I am treating executed checks on this exact head as the acceptance signal — an earlier poll of mine exited on "no checks reported" and would have read as settled, which is the same absent-evidence-scores-as-pass trap.

— sent from wise-swift-269

@briansrls
briansrls merged commit e6a3a68 into main Aug 12, 2026
5 checks passed
@briansrls
briansrls deleted the session/wise-swift-269-targetmodel-field branch August 12, 2026 02:43
gunbai-bot Bot pushed a commit that referenced this pull request Aug 12, 2026
The compiler asks the TARGET to render a produced declaration and receives a
bodied-arrow scaffold. emit_produced.dag imports no target language: it matches
on target.produced_decl_support, refuses ProducedDeclUnwired with a typed,
located produced_decl_target_unwired, and otherwise calls the target's renderer.

Rust and C each own their own order, authored in their own modules:

  Rust  fn add(x: i32, y: i32) -> i32 { x + y }
  C     int add(int x, int y) { return x + y; }

Those differ in every axis the compiler previously owned -- keyword-first versus
return-type-first, NAME colon TYPE versus TYPE NAME, return arrow versus none,
value-producing versus statement-sequenced with a return token and terminator.
C required no compiler edit: it registers a renderer and supplies its own
binding spellings. That is criterion 5, and the cross-golden arms are its
discriminating RED -- neither target can produce the other's text, so a seam
that kept order in the compiler would fail the witness rather than pass quietly.

target_model_with_produced_scaffold installs the rendered scaffold, deciding
only WHERE it attaches. It reuses target_model_bundle_replace_edge_target rather
than adding node surgery, and takes the BARE base row from ProducedDeclWired --
the target's ordinary rule already carries a fixed scaffold, and appending a
second bodied-scaffold edge would let the fixed one win every lookup, emitting
one declaration's signature for every declaration.

Deleted as obsoleted by the climb: rust_produced_decl_target and, cascading,
rust_bodied_relation_rules_node_with_scaffold. The rust_signature_* chain is
KEPT -- it is now Rust's renderer body, which is where order belongs.

Re-cut onto merged main after #8155 squash-merged; the field carrier and its
preservation fixes come from that commit rather than being duplicated here.

Not verified by execution -- CI is the consumer.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
briansrls added a commit that referenced this pull request Aug 12, 2026
… 5) (#8168)

* Wire produced-declaration rendering into Rust and C; close criterion 5

The compiler asks the TARGET to render a produced declaration and receives a
bodied-arrow scaffold. emit_produced.dag imports no target language: it matches
on target.produced_decl_support, refuses ProducedDeclUnwired with a typed,
located produced_decl_target_unwired, and otherwise calls the target's renderer.

Rust and C each own their own order, authored in their own modules:

  Rust  fn add(x: i32, y: i32) -> i32 { x + y }
  C     int add(int x, int y) { return x + y; }

Those differ in every axis the compiler previously owned -- keyword-first versus
return-type-first, NAME colon TYPE versus TYPE NAME, return arrow versus none,
value-producing versus statement-sequenced with a return token and terminator.
C required no compiler edit: it registers a renderer and supplies its own
binding spellings. That is criterion 5, and the cross-golden arms are its
discriminating RED -- neither target can produce the other's text, so a seam
that kept order in the compiler would fail the witness rather than pass quietly.

target_model_with_produced_scaffold installs the rendered scaffold, deciding
only WHERE it attaches. It reuses target_model_bundle_replace_edge_target rather
than adding node surgery, and takes the BARE base row from ProducedDeclWired --
the target's ordinary rule already carries a fixed scaffold, and appending a
second bodied-scaffold edge would let the fixed one win every lookup, emitting
one declaration's signature for every declaration.

Deleted as obsoleted by the climb: rust_produced_decl_target and, cascading,
rust_bodied_relation_rules_node_with_scaffold. The rust_signature_* chain is
KEPT -- it is now Rust's renderer body, which is where order belongs.

Re-cut onto merged main after #8155 squash-merged; the field carrier and its
preservation fixes come from that commit rather than being duplicated here.

Not verified by execution -- CI is the consumer.

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

* Wire staging support and C value-expression projection

Both findings in review 51267 verified against the code and fixed.

FINDING 1 - a regression I introduced. On main, emit_produced_module_rust
ignored the passed target and built a per-decl Rust target internally. Making
the passed TargetModel authoritative is the right seam cut, but it means
rust_target_model_staging -- still ProducedDeclUnwired after the mechanical pass
-- now refuses. Its two consumers, v2.workflow.realization_attempt and
emit_host_produced_module_equals_eval_test, would have hit
produced_decl_target_unwired instead of emitting. Staging now carries
rust_produced_decl_support(): it is a Rust target and renders Rust order,
differing from production only in lex, which is the dissolution-by-identity
sequencing already ruled for it.

FINDING 2 - the criterion-5 witness could not have passed. C's bundle carried
selection policy, declared inhabitants, translation rules, serialize source and
semantic-decl emission, but neither value_expression_projection nor
operator_realizations, and produced-decl emission serializes the arrow BODY
through those. The signature renderer covers prefix and scaffold order only; it
does not substitute for body projection.

Took the wire-it option rather than narrowing the golden to signature-only,
because narrowing would have made the receipt claim less than criterion 5 asks
while still reading like a two-target proof. C now carries a minimal
value-expression surface -- binding-ref plus an op_add catalog on c_token_plus,
mirroring Rust -- so the shared fixture body projects for C.

Forms C has not modeled carry token classes with no lex rule, so reaching one
REFUSES at serialization rather than emitting fabricated text. That is the
existing fail-closed lex-miss arm, not a new mechanism, and it keeps C's
unmodeled surface honest instead of quietly wrong.

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

* Add executing witness for the module fold this PR changed

This PR changed emit_produced_module's signature and the target threading
through it, and nothing executed the result. Its two consumers are both dark in
CI: emit_host_produced_module_equals_eval is a legacy frozen row in
gunbc.witness_deferral_freeze, and realization_attempt did not appear in the run
log either. Verified by grepping job 93997368123 rather than inferring from the
green ci conclusion -- a passing job means the job succeeded, not that a given
claim ran.

The decisive fact is that emit_produced_module is not glue behind two dormant
tests: v2.workflow.realization_attempt is a PRODUCTION consumer, calling it at
the canonical realization attempt. So a signature and threading change to live
code was landing compiled and unrun.

The witness goes in produced_decl_two_target_test, which already executes.
Creating a new file would have risked solving this by committing the same
problem one layer over -- a correct witness with no consumer.

Scope is exactly what changed: accumulation, separator, final medium and target
threading. The swapped-order arm is the discriminating RED -- same two
declarations, same target, so only accumulation ORDER distinguishes them, and a
fold that dropped, duplicated or misordered a declaration cannot satisfy both
arms. The golden is a literal rather than composed from
emit_module_decl_separator, so the expected text shares no ancestor with the
implementation producing it.

NOT addressed here, and inherited rather than introduced: the two dark consumers
themselves. Enrolling the frozen row, scheduling realization_attempt, or
widening the freeze are pre-existing questions this PR does not open.

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

* Fix parse error: data expression must sit on the = line

The v1 parser refuses a data declaration whose value starts on the next line:
"expected expression, found Newline" at
src/v2/test/claim/emit/produced_decl_two_target_test.dag. I had wrapped the
module golden after the = for line length.

Caught by the build job's v1-compiler-tests, whose for_each_parsed_module_binding
parses every .dag in the tree, so a parse error in a witness file fails four
unrelated materialization tests. The ci job's 7-second failure was the
downstream upstream-already-red refusal, not a second defect.

Scanned the other seven files this PR touches for the same shape and found none.
A trailing = is legal for a coproduct TYPE declaration (dag/std/content_hash.dag
has several) and only illegal for a data expression, so the scan is for the
failing class rather than for the character.

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

---------

Co-authored-by: Brian Searls <briansearls1@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
gunbai-bot Bot pushed a commit that referenced this pull request Aug 13, 2026
Operator-authorized (2026-08-12). These are the two lines the PR body named,
and they are true rather than mechanical filler: .dag is ML-style, all three
of if/then/else are really lexed, and the else arm ends the expression, so
there is no closing token. ConditionalSelfDelimiting and LoopSelfDelimiting
carry no field in which a fictional token could be written.

Not a pre-existing defect: `delimiting` does not exist on main at all. These
sites need updating only because this PR adds the required field -- the same
class of mechanical addition already made to this file in #8155 when
TargetModel gained ProducedDeclSupport.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
briansrls added a commit that referenced this pull request Aug 13, 2026
* Model conditional and loop delimiting discipline on the target

The shapes did not carry whether a construct needs a token AFTER its final
arm, so target_value_expression_conditional_to_concrete_tokens ended at the
else arm for every target and a brace-closing language had no way to emit
its own close. The brace was supplied instead by hand-authored declaration
scaffolds ending in two rbrace, which is why the fixtures looked correct:
the golden was validated against a scaffold authored to match it.

ConditionalDelimiting and LoopDelimiting carry the closing token as payload
on the CLOSING arm only, so a self-delimiting target has no field in which
to write a fictional token. Optional<Symbol> would not have had that
property: Absent would mean both "self-delimiting" and "not modeled yet".
Decode refuses an unrecognised discriminant rather than defaulting, because
defaulting to self-delimiting makes a closing target emit one token short.

C and TypeScript are corrected, not converted. Neither GAINED a ternary --
both always were one, and the shape was describing a construct they do not
have in a value position. C's if is a statement; ts_token_kw_then named a
keyword TypeScript does not have. None of those tokens had a lex rule, so
nothing executed them and nothing caught the fiction.

INCOMPLETE: extdeps/languages/dag.dag 5042 and 5052 still need
delimiting: ConditionalSelfDelimiting and delimiting: LoopSelfDelimiting.
That file is no-touch for this lane, so the field is deliberately unwritten
and the build is red until its owner adds it.

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

* Guard the intentionally-invalid golden in the witness itself

The self-delimiting golden is the closed golden one token short, and it is
the only thing proving the discipline is consumed rather than ignored. A
contributor meeting a golden that asserts broken Rust will fix it, and the
fix collapses both arms of the discriminating pair into one expectation.
The PR body is not readable from the corpus; the next person meets the file.

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

* Fix parse error: record field value may not start on the next line

delimiting: with its if/else beginning on the following line is the same
parse class that broke #8168 -- expected expression, found Newline. My
pre-push scan only covered data/fn/let declarations and not record fields,
which is why it reported clean. Extracted to cld_delimiting_for.

Verified by remote execution: the module now reaches typecheck, where the
only remaining errors are the two known dag.dag missing-field refusals.

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

* Complete the slice: .dag declares itself self-delimiting

Operator-authorized (2026-08-12). These are the two lines the PR body named,
and they are true rather than mechanical filler: .dag is ML-style, all three
of if/then/else are really lexed, and the else arm ends the expression, so
there is no closing token. ConditionalSelfDelimiting and LoopSelfDelimiting
carry no field in which a fictional token could be written.

Not a pre-existing defect: `delimiting` does not exist on main at all. These
sites need updating only because this PR adds the required field -- the same
class of mechanical addition already made to this file in #8155 when
TargetModel gained ProducedDeclSupport.

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

* Hoist the delimiting rationale out of declaration bodies

DESIGN section 4c models annotations at module-item grain only, and I put
explanatory blocks inside the projection record literals -- exactly the
violation whose 73 instances I measured in promotion_admission earlier today,
reintroduced by me in the same session while writing about it.

The content is load-bearing rationale rather than restatement, so it is
hoisted to each projection's declaration header unabridged rather than
deleted.

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

* wip: narrow to conditional; shrink pick scaffold

* Narrow to conditional; withdraw the loop half and shrink the pick scaffold

Review 51456 was right and I confirmed it by execution before acting: with
the shared projection declaring closure, rust_conditional_emit_pick_fn_accepts
and rust_loop_emit_one_accepts both returned false. My pre-push check had run
only my own three witnesses, not the existing consumers of the authority I
changed -- the staging fixtures import rust_value_expression_projection.

CONDITIONAL, fixed as designed: rust_pick_block_close shrinks from two rbrace
to one. The second used to close the brace the conditional left open, and the
conditional now emits its own from its declared discipline. That shrinking is
the dissolution the change was supposed to buy, so the fixture getting smaller
is the receipt rather than a patch.

LOOP, withdrawn, and the reason is a finding: Rust's value-producing loop is
`loop { break v; }` -- it needs a break token and a terminator, which an
open/close pair cannot express. The staging spelling `loop { break ` smuggles
exactly those, the same way the conditional smuggled its close. Declaring an
open/close discipline for it would have been the same under-modeling one level
over, and unwitnessable besides, since production loop_token has no lex rule.
TargetLoopShape is left exactly as it was.

The witness is renamed conditional_delimiting_test: it never covered loop, and
a name claiming coverage it does not have is the defect this PR is about.

Verified by execution on this head: both enrolled goldens true again, and all
three delimiting witnesses true.

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

---------

Co-authored-by: Brian Searls <briansearls1@gmail.com>
Co-authored-by: Claude Opus 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.

1 participant