Skip to content

v1 parser carries the test marker on the declaration instead of dropping it - #11478

Merged
briansrls merged 3 commits into
mainfrom
session/proud-tern-736
Sep 17, 2026
Merged

briansrls merged 3 commits into
mainfrom
session/proud-tern-736

Conversation

@gunbai-bot

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

Copy link
Copy Markdown
Contributor

Why

Step 1 of making test code enforceably separate from serving code: no code may call or reference a test fn, and serving code may not depend on test code. The v1 parser discarded the test marker (drop_leading_test_marker), so nothing downstream of parse could see it; test-ness was re-read from source text by line scanners.

What

  • v1.std.core Node gains declaration_marker: DeclarationMarker = Unmarked | TestMarked. The marker is sugar on an ordinary fn/data item: module_item_kind is unchanged, so no emitter or kind consumer is touched.
  • v1.compiler.parse test_marked_item_result sets TestMarked. Every other construction is Unmarked, and rebuilds of a node copy the marker from the node they rebuild (~440 mechanical sites across .dag and Rust).
  • One behavior change: test before any item form other than fn/data is now refused; it used to be silently dropped. No corpus row uses that form.
  • Stage0 regenerated; claim_executor --required-regen reports first_generation_equal=true.

Consumption (§3c): declared frontier

Nothing reads the marker in this PR. Its consumer is the next PR, the v1 resolve-stage refusal of a reference to a TestMarked declaration and of a serving-to-test dependency, with a shrink-only identity-grain debt ledger for the existing violations (118 same-module callers in 102 files, 3 cross-module imports). That refusal cannot be written until the parse carries the marker, and it is what retires this frontier. The annotation on DeclarationMarker says so. The text scanners are not the named consumer: the v2 scanner is blocked on v2's grammar, not on v1.

Evidence

  • compiler_tests::test_marker_selects_the_test_item_kind: marked and unmarked fn/data keep their kind and carry the right marker.
  • compiler_tests::test_marker_on_a_type_declaration_is_refused: the discriminating red; the old parser accepted test type.
  • Both pass locally. Rung: these are cargo test --lib, which no CI step runs (gunbc.rung_drop rust_unit_tests_off_the_merge_path). The CI floor exercises the new parse path on every marked declaration in the corpus.

🤖 Generated with Claude Code

gunbc-ci-auto-heal and others added 2 commits September 16, 2026 19:16
`test fn` / `test data` now parse to ModuleItemTestFunction / ModuleItemTestDataValue
rather than being stripped by drop_leading_test_marker. A `test` marker before any
other item form is refused instead of silently ignored. Consumers that only ask what
shape an item has read module_item_kind_shape, so emission and extdeps data reading
are unchanged. Prerequisite for refusing references to a test fn at resolve.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A `test` marker is sugar on an ordinary fn/data item that segregates test code from
serving code, so it no longer changes module_item_kind. Node carries
declaration_marker: Unmarked | TestMarked; rebuilds copy it, everything else is
Unmarked. Emitters and cli_run.rs are back to main. The annotation now states the
resolver refusal as a declared frontier instead of an existing behavior (review 66985).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@gunbai-bot gunbai-bot Bot changed the title v1 parser carries the test marker as an item kind instead of dropping it v1 parser carries the test marker on the declaration instead of dropping it Sep 16, 2026
@gunbai-bot

gunbai-bot Bot commented Sep 16, 2026

Copy link
Copy Markdown
Contributor Author

Addressing review 66985 in 07f21fd:

  1. Annotation asserting a resolver refusal. Correct, it didn't exist. The design also changed: the marker is now Node.declaration_marker, not two new item kinds, so the annotated variants and module_item_kind_shape are gone. The new annotation on v1.std.core DeclarationMarker says in the present tense that no resolver reads it yet.
  2. No consumer. Also correct. Rather than claim the text scanners, whose v2 copy is blocked on v2's grammar as you said, this is stated as a declared frontier. Its consumer is the v1 resolve-stage refusal of references to TestMarked declarations and of serving-to-test dependencies. That refusal needs the parse to carry the marker, lands in the next PR, and retires the frontier. The trigger is named in the annotation and in the PR body.

On the floor failure at a059c5c: the only blocker was corpus_cargo_build_defused_holds completing over its 8000ms wall budget (9365ms); 3961 claims, 0 failures. This head re-measures it, and I'll treat a repeat as a real parse-path cost rather than noise.

— sent from proud-tern-736

@briansrls briansrls left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One blocking parser defect at this exact head.

test_marked_item_result decides whether the authored form was fn/data from n.module_item_kind, but that identity has already been collapsed by parsing. In the live DAG grammar, both pattern and interface use BlockBody; parse_item_by_form sends every BlockBody through parse_block_body_from_prefix, which stamps the result ModuleItemFunction. Consequently, test pattern … and test interface … take the ModuleItemFunction => true arm and are accepted as TestMarked, despite the PR's stated rule that every form other than fn/data refuses. The existing test type negative control cannot detect this class.

Please decide marker admissibility from the authored ItemForm/keyword before that fact is erased (exactly fn or data), and add discriminating negative controls for the block-bodied non-function forms.

I am not blocking on the staged absence of the resolver consumer: the PR names that §3c frontier clearly, and the marker is a reasonable v1 carrier toward the later non-callability refusal. The mechanical propagation/regeneration otherwise looks coherent, and the witnesses workflow is green on this head.

Comment thread src/v1/02_parse.dag Outdated
fn test_marked_item_result(r: ItemResult) -> ItemResult {
if has_err(err: r.err) { return r }
let n = r.item
let markable = match n.module_item_kind {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This predicate reads the already-collapsed module_item_kind, not the authored item form. Both pattern and interface are BlockBody in dag_item_forms, and parse_block_body_from_prefix assigns every such result ModuleItemFunction; therefore test pattern … and test interface … are admitted here as TestMarked. Gate on the exact authored form (fn/data) before parsing erases it, and add reds for these two aliases.

…ed kind

`pattern` and `interface` are BlockBody forms whose constructor stamps
ModuleItemFunction, so checking the parsed kind admitted `test pattern` and
`test interface`. Admissibility now reads the ItemForm body_kind before the item
is parsed (ExprBody = fn, ValueBody = data); the refusal test covers both forms.

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

gunbai-bot Bot commented Sep 16, 2026

Copy link
Copy Markdown
Contributor Author

Fixed the blocking finding from the ChatGPT review in c2b9bd3: test pattern and test interface were admitted because both are BlockBody forms stamped ModuleItemFunction, and the check read the parsed kind. v1.compiler.parse test_marker_admits_form now decides from the authored ItemForm.body_kind before the item is parsed (ExprBody = fn, ValueBody = data; every other body kind refuses), and test_marker_on_a_type_declaration_is_refused now also asserts the refusal for test pattern and test interface, the two rows the kind-based check passes. Regeneration is at its fixed point (first_generation_equal=true) and both marker tests pass locally.

On the non-blocking design note (runner-only test fn in v2): the owner's stated intent is that test is a marker segregating test code from serving code; the resolve-stage refusal in the next PR enforces that. Whether v2 goes further is a separate decision, not this PR's.

— sent from proud-tern-736

@briansrls
briansrls added this pull request to the merge queue Sep 17, 2026
Merged via the queue into main with commit c4e4ea7 Sep 17, 2026
8 checks passed
@briansrls
briansrls deleted the session/proud-tern-736 branch September 17, 2026 21:33
gunbai-bot Bot pushed a commit that referenced this pull request Sep 17, 2026
Two generated files conflicted and the generated-artifact merge driver refused
both rather than picking a side, which is what it exists to do. Neither was
hand-merged:

- `.github/workflows/fleet-converge.yml` regenerated from
  `gunbc.fleet_converge_workflow` via `main_wet_one`.
- `src/v1/stage0/src/v1_compiler_emit_rust.rs` could not be regenerated while it
  was the blocker: my side predated main's `declaration_marker` field (#11478),
  so the crate carrying the regenerator did not compile. Took main's mirror to
  bootstrap the build -- it already carries #11531's squashed content -- then
  re-derived every mirror from the merged `.dag`.

Receipt: `claim_executor --required-regen` reports first_generation_equal=true,
157 planned / 157 executed / 157 adjudicated, so the mirrors on this branch are
what the merged authorities emit rather than what a text merge produced.

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