Skip to content

Gen-two door PR-c: census every file, emit the entry's reference-derived closure member by member - #12218

Merged
gunbai-bot[bot] merged 17 commits into
mainfrom
session/stern-moth-549-door-closure
Sep 26, 2026
Merged

gunbai-bot[bot] merged 17 commits into
mainfrom
session/stern-moth-549-door-closure

Conversation

@gunbai-bot

@gunbai-bot gunbai-bot Bot commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

PR-c of the gen-two door fix (self-host closure frontier lane, stern-moth-549). It builds on #12186 (PR-a, reference-derived closure) and #12187 (PR-b, census mode). Design agreed with neat-boar-16.

Why the door changes

Two defects, both measured:

  • The door emitted the entry module only. compiler_closure_emit resolved the admission subject, and resolution answers for the subject alone; it reads other modules only through the symbol index. The old "closure" was one module.
  • The door body-lowered every file under the roots. So gen-one stopped at body_lowering_reason_call_argument_unread in dag/examples/weather/weather.dag, a file outside v2.compiler.compile's closure (srv1 receipt at Keep the generation-one v2-native-cli executable past its probe root (declared seed growth) #12182's head).

What

v2.compiler.self_host.closure_emission emit_closure_from_ingest_located(ingest, entry, entry_locus, target) does the following:

  1. Census (total). Tokenize, parse, then normalize_census every file, threading one allocator. A parse or census refusal refuses the emission: an unreadable declaration might be the one a reference binds, so dropping the file would be the section 5 absorbing arm.
  2. Closure. reference_derived_closure over the census index of every file. resolve_member fully normalizes and resolves one member in a context whose only root is that member; NamespaceOnlyY never admits imports through other roots. The normalized tree comes back as the visit payload (the fold is now generic in P), so each member is normalized once.
  3. Emit, per member. Resolve against the members' full projections plus the non-members' census_tree_binding_source (the PR-b handoff condition), check that the member reaches only closure members (closure_emission_member_reaches_outside_closure otherwise), then infer and emit_for_target. The outcome is one typed arm, ClosureMemberEmitted or ClosureMemberRefused.
  4. Carrier and codec. ClosureEmission { entry, members (reach order), modules } renders through extdeps.languages.json, the one declared codec, via closure_emission_json. The parse direction reads it back; see the round-trip control.

In v2.cli.compile_cli v2_cli_run, stdout is the carrier's JSON rendering. CliEmitted { text, closure_size, refused_count }. v2_cli_exit fails the process if any member refused; every arm is still written.

Why two resolutions per member. The closure phase can only use the census index, since membership is not yet known. The emission index differs where the unique-variant-alias scan counts a Disj inside a body the census never lowered. The reach check turns any divergence into a located refusal.

Which refusals stop everything. A member whose body does not lower refuses the emission, because its reach is unknown and emitting the rest would be a silently smaller closure. Only refusals after membership is known (resolve, infer, translate, emit, e.g. #12207's body refusal) become that member's arm.

Evidence

v2.test.claim.self_host.closure_emission, 4/4 PASS locally:

  • Direction 1: a module outside the closure whose body does not lower does not stop the emission. The closure is exact, the outsider has no arm, and every member has exactly one arm.
  • Direction 2: a module outside the closure that does not parse refuses the emission.
  • Direction 3: a closure member whose body does not lower refuses the emission at body_lowering_reason_else_less_if_unlowered.
  • Codec: the rendering parses back through extdeps.languages.json.parse with the same entry, member identities and module count.

Each scenario is a shared nullary producer in floor_pure_producer_share. v2_native_cli_test (18/18) and occurrence_file_attribution_test (7/7) also pass with the new CliEmitted shape. reference_closure_test has been updated for the generic payload.

On this tree every fixture member's arm is translate_rejected_grounding_not_derived, which is blocker #2 (infer grounding, warm-cat-318's lane), carried as an arm rather than stopping the others.

Route pin (PR-b handoff (b)). Nothing after resolve reads a SymbolIndex entry: 04_infer mentions it only in a comment, and eval, translate, emit and partition not at all. Resolution mints references as declaring-path spines, and CensusTree has no route to any of those stages. So a non-member's signature-only Arrow is unreachable by construction.

Owed, NOT discharged

  • The 189 set-equality. Gen-one's closure of v2.compiler.compile, read from this door's members, must be set-equal at module identity with the seed's reference-derived closure (v1_compiler.cli_run.entry_resolve load_sources_for_entry_with_index, 189 at 2b1ff2a). It runs on srv1 through the built door (no floor witness can ingest the corpus). Trigger: this PR's head built by //gunbc/instruments:v2-native-cli and run over dag + src/v2.

Ordering dependency (do not land first)

The v2-native-cli instrument's emit probe (adjudicate_cli_emit_probe, seed side) requires EMPTY stdout. With this PR the door writes the carrier and exits 1, because the fixture member refuses on grounding. warm-cat-318's branch flips that probe to admit the emission and makes the member emit. This PR must land after it, or rebase onto it. I am not editing that function in parallel.

🤖 Generated with Claude Code

Seed census (hand-written Rust, before/after)

Measured at merge-base a1d5db9518a (origin/main) vs head 212a846b9e. Generated mirrors (// Generated by v1 compiler) are excluded; the only one touched, v1_compiler_emit_rust.rs, is regenerated from src/v1/05_emit_rust.dag (fixed point verified).

file before after Δ (numstat)
src/v1/stage0/src/cli_run/native_lane_runner.rs 2841 3103 +262 net (+286 / −24)

No other hand-written seed file changes. What the new lines are:

  • Production (host decode + adjudication): CliDoorMemberArm (Emitted / Refused) and cli_door_member_arm, which read the fixture member's arm out of the door's ClosureEmission carrier. There are also two new CliEmitProbeVerdict arms, CarrierContradictsRendering and MemberRefusalCarrierMissing, plus their refusal renderings in cli_emit_probe_refusal.
  • Test controls (#[cfg(test)]): carrier, carrier_refused helpers; a_member_refusal_carrying_the_carrier_is_judged_by_its_rendered_reason, a_carried_reason_that_differs_from_the_rendered_one_fails, an_emitted_arm_under_a_refusal_fails, a_body_refusal_without_its_carrier_is_not_the_pinned_arm, the_cli_main_writes_the_fold_text_once_before_the_status_dispatch, exit_zero_with_the_fixture_arm_refused_fails, and (adoption, lively-lynx-749) an_adjudicate_marker_without_frontier_refuses. The adoption also added "frontier" to five pre-existing fixtures, which Native v2 frontier ratchet: identity-grain debt roster, in-binary ratchet, nightly srv job #12184 had left red on main.

All production additions are enumerated in gunbc.source_root_eval_driver_seed_growth (reason and current_boundary) under that row's existing trigger: a .dag driver that receives a process termination as a value.

Brian Searls and others added 3 commits September 24, 2026 07:56
…ved closure member by member

v2.compiler.self_host.closure_emission emit_closure_from_ingest_located:
- Phase one parses every file and censuses it (normalize_census, #12187),
  threading one occurrence allocator. A parse or census refusal refuses
  the emission, because the census must be total.
- The closure is v2.compiler.reference_closure reference_derived_closure
  (#12186) over the census index. Each member is normalized once, and its
  tree is handed back as the fold's visit payload (the fold is now generic
  in that payload).
- Every member is resolved again against members' full projections plus
  non-members' census projections, then checked to reach only closure
  members, then inferred and emitted. Each outcome is one typed arm of the
  ClosureEmission carrier.
- The carrier renders through extdeps.languages.json, the one codec.

v2.cli.compile_cli v2_cli_run emits that rendering. CliEmitted carries
closure_size and refused_count, and v2_cli_exit fails the process if any
member refused.

Why the door changes: resolution answers for the admission subject alone,
so the old door emitted the entry module only. And it lowered every
file's bodies, so dag/examples/weather stopped gen-one.

Witness v2.test.claim.self_host.closure_emission covers three refusal
directions plus a codec round trip at identity grain. Each scenario is a
shared nullary producer.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ed it inside the body)

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

emit-build on #12218 refused the door fixture with
reference_closure_target_ownerless. The integer literal 606060 lowers to a
cons list of digit atoms, the same shape as a qualified-name spine, so
declaration_reference_path_optional read it as the path
integer_tag_digit_6.integer_tag_digit_0... . The resolver mints a spine only
for a path the symbol index answers, so the fold now takes the index and
counts a spine only when the index declares its path. An undeclared spine is
neither a reference nor descended into.

Regression: reference_closure_test's entry now declares
data rc_entry_literal: Int = 606060. A mutant that reverts the check turns
set-equality and three membership rows red (measured locally).

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

emit-build: NativeCliDoorRefusalNotRendered. When any member refused, the
door exited 1 with a one-line summary, but a failing status owes the
located cause (a 'diagnostic chain:' section plus '| FATAL AT <locus>'),
the same as a closure-level refusal. CliEmitted now carries
refusal_detail, rendered by the one v2_cli_refusal_detail over the first
refused member in reach order (closure_emission_first_refusal) and the
ingest's spans. Every refusal is still its member's arm in the carrier.

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

gunbai-bot Bot commented Sep 24, 2026

Copy link
Copy Markdown
Contributor Author

emit-build status (a non-required detector lane), as of 3956ea4:

— sent from stern-moth-549

Brian Searls and others added 2 commits September 25, 2026 02:37
…er; drop the redundant index guard

- adjudicate_cli_emit_probe judges the fixture member's arm, decoded from
  the door's ClosureEmission carrier by the new cli_door_member_arm, never
  stdout as a whole. On exit 0 the emitted arm must pass the existing
  name + 606060 + rustc oracle; a refused arm under exit 0 is
  EmittedWithoutSubstance. On a refusal, stdout may be the carrier or empty
  and nothing else. The reason still comes from the rendered chain on
  stderr: only CLI_DOOR_EMIT_BODY_REFUSAL is BodyRefusalReturned, and any
  other reason is DeterminingReasonDiffers.
  Unit tests: stdout now takes the carrier shape, plus two new rows.
  native_lane_runner 32/32.
- #12220's resolver marker makes declaration_reference_path_optional read
  a literal as no reference, so the SymbolIndex-membership guard
  (cc68171) and the fold's index parameter are deleted. The 606060
  regression row in reference_closure_test stays.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@gunbai-bot
gunbai-bot Bot marked this pull request as ready for review September 25, 2026 03:01
…ent trigger (review 71141)

v2.compiler.self_host.compiler_closure_emit carries
compiler_closure_emit_frozen_dissolve_on. It enumerates the six remaining
consumers by name, forbids new ones, and names the retiring event: the
cutover change that moves them to closure_emission and deletes the
module. The new module's header states that it replaces the old one and
points at the row.

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

gunbai-bot Bot commented Sep 25, 2026

Copy link
Copy Markdown
Contributor Author

Addressing review 71141 in 0d98ba4. The finding is correct: two emitters answered one question, and the PR never said how they relate.

I chose the review's second option, a declared frozen-X statement, rather than cutting over in this PR. The old emitter's remaining consumers are six, in other lanes' subsystems:

  • compiler_closure_emit_driver, and through it v2.workflow.product_receipt_stage
  • frontier_probe
  • the long emit-derisk test
  • the execution test
  • keen-cat-459's closure_emit_arrow_body_refusal control
  • two rows that cite the route by name

Cutting them over is its own replacement migration, so it gets a PR of its own rather than riding in the door change (§3: for a root outside the gate, enumerate the consumers by name before you delete).

What this commit does:

  • v2.compiler.self_host.compiler_closure_emit now carries compiler_closure_emit_frozen_dissolve_on. It says the module is replaced by closure_emission emit_closure_from_ingest_located, and says why: the old route emits the entry module alone and lowers every file. It names all six consumers, states that adding a consumer before the cutover is a defect, and gives the trigger: the cutover change landing, which moves them and deletes the module.
  • The new module's header states the replacement and points at that row, so the two are never presented as alternatives.

I'm offering the cutover to neat-boar-16 as a follow-up; I can take it.

— sent from stern-moth-549

…iew 71151)

The row enumerates the CLI door harness by name, and its own standard is
that a function added beside them without a row entry is the defect.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
gunbai-bot Bot pushed a commit that referenced this pull request Sep 25, 2026
Brian Searls and others added 2 commits September 25, 2026 07:25
…nsus; identity-grained carrier witnesses; the probe verifies the carried cause; exact frozen-consumer census

1. Fail-open fixed. The census refuses the second file that declares a
   module identity (closure_census_duplicate_module, located at its
   module node). closure_file_of returns exactly one file or a typed
   refusal and never drops a tail. Each member resolves in a singleton
   context where resolve_duplicate_module_name cannot fire, so the wall
   lives in the census, which sees the whole population. Witness: two
   files, one 'module v2.test.ce_duplicate', independent declarations.
2. The direction-1 witness requires EXACTLY ONE arm for the entry, the
   member and v2.std.logic, and none for the outsider. The codec round
   trip compares the ordered member identities, and row by row each
   module identity plus exactly one of emitted/refused matching its arm.
3. The probe reads the fixture arm's typed reason from the carrier and
   requires it to equal the stderr rendering (new
   CarrierContradictsRendering). The verdict is decided from the carried
   reason, and an emitted arm under a refusal also contradicts.
   native_lane_runner 34/34.
4. The frozen declaration now splits direct consumers (driver,
   frontier_probe, derisk test, body-refusal test, product_receipt_stage
   by type), transitive consumers (product_receipt_stage via the driver)
   and live citations (the failure-mode and rung-drop rows). It drops
   v2.test.execution.self_host_compiler_closure_emit, which reads only a
   static record. namespace_graft's consumer audit and normalized_tree's
   frontier comment now name the new route.

closure_emission witness 5/5 locally.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…im no longer decides when stdout is written

v2.cli.compile_cli v2_cli_outcome_text decides the text: empty for a
plain refusal, the ClosureEmission carrier when a member refused, the
census refusal set when the census refused. The rendered main printed it
only on ExitSuccess, so the failing-arm carrier never reached stdout
(measured on srv1: 0 bytes on the first full gen-two census run). That
was the shim making a decision the fold owns. Both arms now write the
text. Edited in src/v1/05_emit_rust.dag and in its generated stage0 copy
together.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
gunbai-bot Bot pushed a commit that referenced this pull request Sep 25, 2026
…ure-arm stdout) into the census refusal set

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…fold's text once, before the status dispatch

Side-chat REQUEST_CHANGES at 751416d:
1. adjudicate_cli_emit_probe: once stderr renders CLI_DOOR_EMIT_BODY_REFUSAL,
   an empty stdout (no carrier) is the new MemberRefusalCarrierMissing and
   never BodyRefusalReturned. A member refusal is CliEmitted, so it comes
   with every member's arm on stdout.
2. Control a_body_refusal_without_its_carrier_is_not_the_pinned_arm
   (status 1, empty stdout, located body-refusal stderr). The existing
   regression row now supplies the carrier.
3. The generated main (src/v1/05_emit_rust.dag
   emit_native_cli_driver_main_rs and its stage0 copy) writes
   v2_cli_outcome_text once, unconditionally, before matching
   v2_cli_exit. The fold alone decides emptiness, and no per-status write
   can drift.
4. Control the_cli_main_writes_the_fold_text_once_before_the_status_dispatch
   over the generated source: exactly one write, preceding the dispatch.
   Measured red with the write moved into the success arm.
native_lane_runner 36/36.

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 Sep 25, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Sep 25, 2026
…door

Resolved: the door emits through cli_emit_target_model(t: target); the
imports are unioned; CliUsageRefused sits beside CliEmitted in the CLI
and its witness; the generated main keeps the single text write before
the status dispatch. native_lane_runner 36/36, v2_native_cli 26/26,
closure_emission 5/5.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@briansrls
briansrls added this pull request to the merge queue Sep 25, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Sep 25, 2026
gunbc-ci-auto-heal and others added 5 commits September 25, 2026 19:45
…ain's frontier-ratchet enumeration with cli_door_member_arm; stage0 mirror taken from main pending regen)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ote --required-regen; second pass regen2=0, fixed point fp=0)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…the emitted main prints (#12184 made it required); control that a marker without it refuses

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

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… ce_member_emitted/ce_member_refused (review, §3c)

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 Sep 26, 2026
Merged via the queue into main with commit 4039815 Sep 26, 2026
5 checks passed
@gunbai-bot
gunbai-bot Bot deleted the session/stern-moth-549-door-closure branch September 26, 2026 19:42
gunbai-bot Bot pushed a commit that referenced this pull request Sep 26, 2026
…l set

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
gunbai-bot Bot pushed a commit that referenced this pull request Sep 26, 2026
…r mirror taken from main pending regen

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