Skip to content

02_parse interpreted-cost forensics: refute memo-table-carry, fix 3 O(n)/O(n^2) drivers, name honest residual - #6362

Merged
briansrls merged 11 commits into
mainfrom
session/neat-stag-780
Jul 7, 2026
Merged

briansrls merged 11 commits into
mainfrom
session/neat-stag-780

Conversation

@gunbai-bot

@gunbai-bot gunbai-bot Bot commented Jul 7, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Work item node://adhoc-c328b166-bca: discriminate interpretation-tax vs algorithmic residue in v2 02_parse.dag run under the v1 seed interpreter.

Prime-suspect hypothesis refuted. The packrat memo table (ParseTable) was suspected of O(table-size) carry cost under the interpreter's persistent-value semantics. Direct read of v1_interpreter.rs's Value enum shows ParseTableRealization's record fields are Rc-shared (cheap rebuild) and its entries: Map is backed by a genuine im_rc::HashMap HAMT (O(log n) insert/lookup), not a full clone. The memo mechanism itself is fine.

Same-file compiled-vs-interpreted timing (v1 seed's own generated parser, native, vs v1_interpreter.rs tree-walking the same 02_parse.dag): the compiled realization scales linearly across 43w-6173w files (~350-450 words/ms, no complexity-class break). The interpreted realization showed a genuine complexity-class break, not just the constant-factor tax a tree-walker should honestly pay.

Three real O(n)/O(n^2) drivers found and fixed:

  1. length/count/size fell back to free_monoid_to_vec's O(n) clone even for Value::List/Map/Set, which already track their length natively. parse_current_position (02_parse.dag) calls length() on the full token stream on every parse attempt -- an O(n) tax per attempt the compiled realization never pays. Fix: native_len O(1) fast path in v1_interpreter.rs, checked before the free_monoid_to_vec fallback.
  2. parse_token_first used list_at_optional(xs, 0) (itself fold_list-based, no short-circuit) to peek the first token on every lookahead/terminal-check. Swapped to the already-existing list_head (direct Empty/Cons pattern match, genuine O(1)) -- verified empirically first that native Value::List correctly matches Empty/Cons before editing this load-bearing file.
  3. parse_match_arm_stmt_body folded over the entire remaining token list per match-arm-body parse via fold_list -- because fold_list has no short-circuit, even after the accumulator's done flag flipped true it kept no-op-iterating every remaining token to EOF: an O(remaining-tokens-to-EOF) cost per match-arm body, i.e. O(n^2) across a file full of match expressions (which real .dag source is). Rewritten as parse_match_arm_stmt_step, a genuine short-circuiting recursion that stops as soon as done is true.

Discriminating agreement test (native_list_length_equals_freemonoid_cons_chain_length, src/v1/tests/src/list_free_monoid_chokepoint_test.rs): pins the new native_len fast path and the free_monoid_to_vec fallback equal on an equivalent native-list vs Cons/Empty-chain -- this repo's cross-representation-equality precedent (numeric tower, #5428).

regen_stage0 --verify: divergence 0 -- the hand-maintained v1_interpreter.rs edit is consistent with the committed stage0.

Per-fix verification receipts (merge-bar demand):

  • Fix 1 (native_len fast path, v1_interpreter.rs): native_list_length_equals_freemonoid_cons_chain_length, a plain non-ignored #[test] in src/v1/tests/src/list_free_monoid_chokepoint_test.rs -- pins the fast path and the free_monoid_to_vec fallback equal. cargo test -p v1-compiler-tests list_free_monoid_chokepoint -- 9/9 passed.

  • Fix 2 (parse_token_first: list_at_optional(xs, 0) -> list_head): src/v2/test/claim/parse/parse_token_first_empty_semantics_test.dag, two test fn claims pinning the two implementations equal on the empty-token-stream boundary case that motivated the rewrite (Absent == Absent) and the ordinary present case (same lexeme). Both PASS via claim_batch.

  • Fix 3 (parse_match_arm_stmt_body: fold_list -> parse_match_arm_stmt_step, a rewrite inside DESIGN-named load-bearing 02_parse.dag): src/v2/test/manual/parse_tree_content_hash_witness.dag computes content_hash(ParseTree) (v2/std/node.dag's structural node hash) over the tiny/small corpus sample, run once against the true PR base (0f98a264dc, built as its own isolated release binary in a separate worktree -- the local main ref was stale at 21fb5d1b2d; using it produced a false grammar_invalid rejection from an unrelated grammar-ambiguity fix, parse: nullable-only ambiguity rejection; non-nullable FIRST overlap becomes counted residue (fixes main parse refusing all input) #6337, that landed between the two) and once against this branch's HEAD. Hashes are byte-identical both ways:

    • witness.dag: 9afb740ee43f9e61 == 9afb740ee43f9e61
    • execution_mode.dag: ed071cb2baeaeb61 == ed071cb2baeaeb61

    This proves fix 2 and fix 3 together produce the exact same parse tree pre/post -- not just "no test regressions" on a load-bearing rewrite.

Honest residual, named with numbers, not absorbed into "fixed": datetime.dag (1956 words) still does not finish after all three fixes -- DNF at 1200s+, RSS ~4.3GB at ~15min elapsed, essentially unchanged from the pre-fix baseline (DNF at 900s, RSS 4.35GB). The compiled-vs-interpreted gap is not yet flat across sizes; a further, unidentified superlinear driver remains in 02_parse.dag and/or v1_interpreter.rs beyond the three closed here. tiny/small witness files (43w/75w) complete correctly and unchanged both before and after (~20-25s each -- the interpreter's real combinator-heavy base tax, a separate, expected constant-factor cost, not this bug).

Verdict (layered, per the brief): engine fix (#2, #3 in 02_parse.dag) + interpreter fix (#1 in v1_interpreter.rs) landed and verified individually correct; full resolution for realistic file sizes (~2000+ words) remains an open residual -- grammar_coverage_p8_wall_clock_blocker (src/v2/lens/enforcement/grammar_coverage.dag:68) updated with full receipts. P8 full-corpus census stays blocked, narrowed but not closed.

Emit-on-demand feasibility (deliverable 4): attempted gunbc compile --source-root src/v2 --source-root dag --target rust as a same-realization double-check. There is no scoped/closure-only compile entry point today -- it always compiles the whole reachable transitive-import closure (indexed 1867 modules, resolved 1201 sources) and was OOM-killed during reconcile after ~67s / RSS 667MB+. Emit-on-demand is not viable via the existing CLI without a scoped compile mode; that is itself the answer, not something fixed in this PR.

Test plan

  • cargo test -p v1-compiler-tests list_free_monoid_chokepoint -- 9 passed, including the new agreement test.
  • cargo fmt --all --check -- clean.
  • cargo clippy --all-targets -- -D warnings -- clean.
  • regen_stage0 --verify -- regen_divergence_count=0.
  • claim_batch --entry src/v2/test/claim/parse/parse_token_first_empty_semantics_test.dag --function <both> -- PASS/PASS (fix 2 empty-list semantics pin).
  • gunbc run --entry src/v2/test/manual/parse_tree_content_hash_witness.dag against the true PR base (0f98a264dc, isolated worktree + binary) and HEAD -- byte-identical content_hash(ParseTree) on both corpus files (fix 2+3 byte-equal parse-output witness).
  • Before/after interpreted timing on the 5-file scaling witness (src/v2/test/manual/parse_forensics_scaling_witness.dag): tiny/small unchanged and correct; medium (datetime.dag) still DNF, receipts and numbers above and in the updated blocker marker.
  • Compiled-realization calibration harness: src/v1/stage0/examples/parse_compiled_scale.rs (committed receipt harness, not wired into any build/CI gate).

Closes node://adhoc-c328b166-bca

@gunbai-bot gunbai-bot Bot changed the title interpreted-parse cost forensics: discriminate interpretation-tax vs algorithmic residue in v2 02_parse under the v1 interpreter - same-file compiled-vs-interpreted timing, packrat memo effectiveness + table-carry cost interpreted, verdict = engine fix / interpreter fix / honest no-op-until-S2 (upda 02_parse interpreted-cost forensics: refute memo-table-carry, fix 3 O(n)/O(n^2) drivers, name honest residual Jul 7, 2026
@gunbai-bot
gunbai-bot Bot marked this pull request as ready for review July 7, 2026 21:31
briansrls added 3 commits July 7, 2026 21:47
…+ empty-list semantics pin (fix 2)

Manager-mandated merge-bar demands on #6362, addressed with durable,
re-runnable witnesses rather than a one-off manual diff:

- parse_tree_content_hash_witness.dag: content_hash(ParseTree) over the
  tiny/small corpus sample, computed once against the true PR base
  (0f98a26, built as its own release binary in an isolated worktree)
  and once against HEAD. Both hashes are byte-identical
  (witness.dag=9afb740ee43f9e61, execution_mode.dag=ed071cb2baeaeb61),
  proving fix 2 (list_at_optional -> list_head) and fix 3
  (fold_list -> parse_match_arm_stmt_step) preserve parse semantics
  exactly, not just "no test regressions."
- parse_token_first_empty_semantics_test.dag: two `test fn` claims
  pinning parse_token_first (post-fix, list_head-based) equal to
  list_at_optional(xs, index: 0) (pre-fix arm) on both the empty-list
  boundary case that motivated the rewrite and the ordinary present
  case. Both PASS via claim_batch.

Note: the local `main` ref was stale (21fb5d1); the true PR base is
origin/main's 0f98a26 (merge-base origin/main HEAD) — using the
stale ref for the pre-fix binary produced a false "grammar_invalid"
rejection unrelated to this PR (fixed upstream by #6337, landed after
21fb5d1 but before 0f98a26).
@gunbai-bot

gunbai-bot Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor Author

Re: dashboard "CI FAILING" notification for the `ci` check — this is the known era-4 10-minute job-kill (`##[error]The action 'gunbc ci (.dag witnesses + gates)' has timed out after 10 minutes.`), not a regression from this PR's changes. Per the current CI regime ruling (#6323), a cancelled/timed-out `ci` job is expected; merge basis is dashboard approvals + `rust_tests` + `mergeable`, all of which are green (2/2 fresh approvals, rust_tests pass, ci_regen pass, mergeable=CLEAN). No fix pushed — nothing to fix here.

— sent from neat-stag-780

@briansrls
briansrls merged commit 4908bb2 into main Jul 7, 2026
3 of 4 checks passed
@briansrls
briansrls deleted the session/neat-stag-780 branch July 7, 2026 22:44
briansrls pushed a commit that referenced this pull request Jul 7, 2026
The `compile` command took all `.dag` files in the first --source-root as
entries, so `--source-root src/v2 --source-root dag` compiled the whole
1867-module tree — reconcile-OOM before emit (the missing scoped-compile
mode #6362 flagged). `--entry <file>` makes exactly that one module the
entry; its transitive imports resolve from the full index as before, so a
small closure emits without a whole-tree pass. Mirrors `run --entry`.

Receipt (local): `gunbc compile --source-root src/v2 --source-root dag
--entry src/v2/test/claim/manual/s1_opt_probe.dag --target rust` indexes
1873 modules, resolves 61 (transitive closure), emits 66 files in 10s, no
OOM — vs whole-tree OOM at ~67s. HAND file (main.rs); regen unaffected
(not in the GENERATED roster). fmt + clippy -D warnings clean.

Unlocks compiled-parse iteration for S1 (v2 parser emitted native and run
in ~ms, vs ~278s/file interpreted) and scoped emit-on-demand generally.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZSaWfysRfg28bNVS7ueX
briansrls added a commit that referenced this pull request Jul 7, 2026
…tion (#6359)

* emit_host: wire run_host_process to a real host transport (kernel-D S0 slice)

The fail-closed run_host_process stub becomes a real typed-argv host tap:

- v1_interpreter.rs gains the emit_host_run_transport builtin (wet-only;
  hermetic mode refuses -- no mock arm): materialize workspace files under
  a temp workspace (path-escape refused), run the build argvs then the run
  argv via std::process::Command (typed argv, no shell), return
  exit/stdout/stderr octets + build log as data.
- 04_method.dag + generated v1_compiler_infer_method.rs register the
  builtin name (in-sync pair, the #6270 backfill pattern).
- emit_host.dag folds HostTransportDescriptor -> builtin call -> typed
  EmitHostRunReceipt; unknown host tool is a typed refusal; the not-wired
  diagnostic is deleted (wire-or-delete).
- rust.dag MVP1 descriptor gains the runnable workspace its codec always
  implied: Cargo.toml (FixedText) + main harness (EmittedWithSuffix,
  prints add(2,3) as the 5-byte codec value) + --quiet flags.
- rung_3_4_common fixture repaired: per-node-coherent InferredFacts
  (grounding.node == queried node, structured algebra evidence) and the
  real rust_mvp1_target_model_staging() instead of a hollow TargetModel.

Receipts (local, wet):
- NEW emit_host_transport_wire_test.dag: green run (stdout == [0,5,0,0,0],
  runtime_value_parse Accepted) + 2 REDs (nonzero exit -> logical_run
  Rejected; 4-byte stdout -> codec width Rejected):
  gunbc run --claim-run emit_host_transport_wire_all_holds -> true.
- rust_add_emit_add_fn_accepts_holds -> true (emit side unchanged).
- rung3 roundtrip -> Pass (unchanged); rung4 emit-vs-eval -> Fail at the
  pre-existing target_use_site_ownership_lookup_miss (unchanged; the
  claim subject has never had a translate-accepted shape).

Phase pin stays ModelAndWitnessOnly: the kernel-D flip criterion is the
rung-4 EqualsClaim green-not-Deferred, which now waits only on the emit
side accepting a shared subject, not on transport.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZSaWfysRfg28bNVS7ueX

* emit_host: close S0 — genuine emit(add-fn) host-run == eval end-to-end; flip kernel-D pin

Completes the kernel-D flip criterion (doc §4: run_test_claim_emit_vs_eval
GREEN on a rust EqualsClaim, no Deferred). A dedicated end-to-end witness
now composes the full pure-eval-emit path and passes by structural equality,
not fabrication:

  eval(add-subgraph, octet-interpretation)  ->  RuntimePrimitive{P, octets(5)}
  emit(add-fn) -> cargo build -> cargo run   ->  stdout 5 bytes
  runtime_value_parse(stdout)                ->  RuntimePrimitive{P, octets(5)}
  runtime_value_eq (==)                      ->  Pass

The two sides meet only because they share the host row's primitive_type
node P (rust_emit_host_runtime_primitive_type_node) AND the octet byte
encoding (emit_host_octets_byte_string) — reconciling the value-representation
fork that made the pre-existing nat_semiring rung-4 subject uncomparable.

New witness (src/v2/test/claim/manual/emit_host_add_equals_eval_test.dag):
- GREEN: emit_host_add_equals_eval_holds (Pass).
- RED control: emit_host_add_wrong_oracle_refuses_holds — a wrong eval oracle
  (returns 6) must NOT Pass, proving the comparison discriminates rather than
  always-greening.
- one subject.tree serves both stages: emit walks tree.root (the rust add-fn,
  real rust grounding facts); eval walks claim.rhs (the computation subgraph,
  generic coherent facts) via a composed facts lookup (rust-detail then
  generic fallback whose algebra == P to satisfy eval's resolved_type check).

Interpreter fix (v1_interpreter.rs): emit_host_run_transport now flattens
rope-shaped strings (free_monoid_to_string) for workspace path/text and argv
elements — emit(...).carried materializes as a FreeMonoid Cons-chain, not a
flat Value::Str, so the earlier expect_str rejected it (the transport wire
test passed only because it fed a String literal).

Phase pin: interpreter_kernel_d_current_phase ModelAndWitnessOnly ->
PureEvalEmitWired (transport is wired and proven), with a new
interpreter_kernel_d_phase_is_pure_eval_emit_wired predicate; the kernel-D
witness updated to assert the advanced phase.

Receipts (local, wet):
- emit_host_add_equals_eval_all_holds -> true (green + wrong-oracle RED)
- emit_host_transport_wire_all_holds -> true (unchanged)
- interpreter_kernel_model_witnesses -> true (phase now PureEvalEmitWired)

Not claimed: the nat_semiring rung-4 subject still Fails at translate's
target_use_site_ownership_lookup_miss — that is a translate limitation for
law-shaped subjects, a separate open item, not an emit_host/transport gap.
The kernel-D criterion is met by the dedicated witness above.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZSaWfysRfg28bNVS7ueX

* fmt: rustfmt v1_interpreter emit_host_run_transport builtin (rust gate)

Multi-line format! wrapping to satisfy cargo fmt --check on the HAND
interpreter file. Fixes the rust_tests fmt-diff failure on #6359.

The ci_regen failure was pre-existing divergence on the stale base
(pre-#6357 seed convergence), resolved by merging origin/main; regen
--verify now reports regen_divergence_count=0 and the generated
v1_compiler_infer_method.rs (carrying the emit_host_run_transport row)
is byte-identical to a fresh self-compile.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZSaWfysRfg28bNVS7ueX

* S1 grammar: `?` optional sugar + keyword-as-name (field/param), toward v2 parsing its own closure

Two additive wave1-grammar extensions so v2's parser accepts constructs its
own compiler source already uses (verified by interpreted execution; no Rust
rebuild — grammar is .dag, read at interpret time):

- `?` optional type sugar: `^dag_token_question` lexer rule + trailing
  `optional(?)` on `qualified_type_expr`. `Int?`, `Outcome<Symbol?>` parse
  CLEAN; `Optional<T>` is the emit-ready lowering target (v2 Node has no
  cardinality field — Cardinality connective already has translate support).
  Stash-verified additive: the deeper rejects it unmasks pre-date this change.

- keyword-as-name: `dag_grammar_binding_name_terminal` = ident | any wave1
  keyword, wired into `field_decl` + `typed_param` name positions. v2 source
  already uses `module:`/`loop:`/`match:` as field/param names (11+2+2 sites
  across 5 closure files); the grammar just didn't accept existing valid code.
  Name positions are type-context (name `:` type), so keyword tokens there
  carry no expression-parse ambiguity — grammar validates clean, disjoint
  FIRST sets.

Receipts (interpreted, inline via grammar_coverage_ingest):
- Int? / Outcome<Symbol?> / nested generics / `type X = A|B` → CLEAN
- `type Scope = ScopeRoot { module: Namespace } | ScopeFrame { locals: Map<Symbol, Symbol>, outer: Scope }` (the 03_resolve blocker) → CLEAN
- keyword field/param names (module/loop) → CLEAN
- regression: plain record type / coproduct / fn-with-params → CLEAN

S1 is not complete: full 74-file closure parse still iterating (masked layers
surface as each tokenize/parse gap clears). s1_closure_parse_probe.dag is the
closure receipt harness; s1_opt_probe.dag is the inline bisect tool.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZSaWfysRfg28bNVS7ueX

* S1 diag: no-op probe to isolate interpreted-parse startup (3s) from parse cost

Startup (load+compile probe closure interpreted) measured at 3s via s1_noop;
so the ~5min/file S1 cycle is the interpreted parse itself, not startup.
Manual diagnostic harness (test/claim/manual, non-CI).

* cli: add `compile --entry` to scope emit to a subtree's import closure

The `compile` command took all `.dag` files in the first --source-root as
entries, so `--source-root src/v2 --source-root dag` compiled the whole
1867-module tree — reconcile-OOM before emit (the missing scoped-compile
mode #6362 flagged). `--entry <file>` makes exactly that one module the
entry; its transitive imports resolve from the full index as before, so a
small closure emits without a whole-tree pass. Mirrors `run --entry`.

Receipt (local): `gunbc compile --source-root src/v2 --source-root dag
--entry src/v2/test/claim/manual/s1_opt_probe.dag --target rust` indexes
1873 modules, resolves 61 (transitive closure), emits 66 files in 10s, no
OOM — vs whole-tree OOM at ~67s. HAND file (main.rs); regen unaffected
(not in the GENERATED roster). fmt + clippy -D warnings clean.

Unlocks compiled-parse iteration for S1 (v2 parser emitted native and run
in ~ms, vs ~278s/file interpreted) and scoped emit-on-demand generally.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZSaWfysRfg28bNVS7ueX

* test: add s1_parse_harness (clean scoped parse entry), drop scratch probes

s1_parse_harness.dag calls tokenize -> parse_module -> normalize directly
(no grammar_coverage wrapper), so `compile --entry` emits its closure with
0 compiler diagnostics — the minimal entry for probing v2-parser emit health.

Receipt (local, via compile --entry): the v2 parse closure resolves to 41
sources, emits 45 Rust files in 8s. But the emitted Rust is NOT cargo-green:
4259 rustc errors, 84% (3567) a single E0308 type-mismatch class — the same
shape as the v1 seed's fresh-emit (1667 -> ~2 emitter root causes). So a
runnable compiled parser is blocked on emitter restoration for v2 code (the
S2 self-emit lane), not on scoping. `compile --entry` (prior commit) makes
this emit-health measurable cheaply; the near-term S1 iteration unlock stays
the interpreted-parse fix (list_snoc_item O(n^2) accumulation), owned in the
parse-perf lane.

Removes s1_opt_probe.dag / s1_closure_parse_probe.dag (bisect scratch with
hardcoded temp paths).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZSaWfysRfg28bNVS7ueX

---------

Co-authored-by: Claude <noreply@anthropic.com>
briansrls added a commit that referenced this pull request Jul 8, 2026
…algorithmic residue in v2 02_parse under the v1 interpreter - same-file compiled-vs-interpreted timing, packrat memo effectiveness + table-carry cost interpreted, verdict = engine fix / interpreter fix / honest no-op-until-S2 (upda (#6365)

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* 02_parse verification receipts: byte-equal parse-tree hash (fix 2+3) + empty-list semantics pin (fix 2)

Manager-mandated merge-bar demands on #6362, addressed with durable,
re-runnable witnesses rather than a one-off manual diff:

- parse_tree_content_hash_witness.dag: content_hash(ParseTree) over the
  tiny/small corpus sample, computed once against the true PR base
  (0f98a26, built as its own release binary in an isolated worktree)
  and once against HEAD. Both hashes are byte-identical
  (witness.dag=9afb740ee43f9e61, execution_mode.dag=ed071cb2baeaeb61),
  proving fix 2 (list_at_optional -> list_head) and fix 3
  (fold_list -> parse_match_arm_stmt_step) preserve parse semantics
  exactly, not just "no test regressions."
- parse_token_first_empty_semantics_test.dag: two `test fn` claims
  pinning parse_token_first (post-fix, list_head-based) equal to
  list_at_optional(xs, index: 0) (pre-fix arm) on both the empty-list
  boundary case that motivated the rewrite and the ordinary present
  case. Both PASS via claim_batch.

Note: the local `main` ref was stale (21fb5d1); the true PR base is
origin/main's 0f98a26 (merge-base origin/main HEAD) — using the
stale ref for the pre-fix binary produced a false "grammar_invalid"
rejection unrelated to this PR (fixed upstream by #6337, landed after
21fb5d1 but before 0f98a26).

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* adhoc-c328b166-bca: 01_tokenize O(n^2) fix + full parse-pipeline census + token-stream witness

lex_walk_step accumulated tokens via list_snoc_item every token (O(n^2)
before parsing even starts) -- 4th instance of the same shape, this time
in a different pipeline stage than the three already-landed 02_parse.dag
fixes. Rewritten as reverse-cons + single .reverse() at tokenize()'s exit.

Verified byte-equal via a new order-sensitive token-stream content hash
(class+lexeme, in stream order) run against independently-built pre-fix
and post-fix binaries on tiny/small corpus samples -- identical both
sides, confirming a pure algorithmic rewrite.

Full read-based census of 02_parse.dag's 9 list_snoc_item call sites,
each dispositioned (dead/test-only, bounded grammar-analysis cost, or
already-fixed) -- none explain the file-size-scaling growth still seen
deeper in live parsing on datetime.dag. That residual is not resolved by
this PR; grammar_coverage_p8_wall_clock_blocker records the honest state
(tokenize confirmed non-bottleneck, DNF persists past grammar-validation,
driver not yet pinned) so the marker does not claim a false completion.

Added parse_table_record_hit/miss/lookup_call and
parse_choice_residue_backtrack to the interpreter's call-frequency
watchlist for the continuing in-session hunt.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* fixup: complete the auto-committed partial ProductionFirstRow removal

The auto-committer snapshotted a mid-edit state (b16a357) that deleted
the ProductionFirstRow type but left its consumers referencing it,
breaking typecheck. This completes that edit -- not a new change.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Revert "fixup: complete the auto-committed partial ProductionFirstRow removal"

This reverts commit dae6602.

* Revert "Revert "fixup: complete the auto-committed partial ProductionFirstRow removal""

This reverts commit e2a24c3.

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* adhoc-c328b166-bca: 2 more diag-accumulator sites fixed, parse-memo distinct-key discriminator

parse_match_arm_stmt_step and parse_expr_repeat_step both accumulated
per-iteration diagnostics via list_append(left: diags.reverse(), right:
acc.acc_diags) -- the acc_nodes field in the same accumulators was
already reverse-cons fixed, but acc_diags was missed by the prior
list_snoc_item-scoped census (this is a different primitive). Converted
to List<List<Diagnostic>> batches (Cons-prepended, O(1) per step) plus a
single flatten pass at the end (parse_flatten_diag_batches). Verified
byte-equal via parse_tree_content_hash_witness.dag on tiny/small
(9afb740ee43f9e61 / ed071cb2baeaeb61, unchanged).

Honest caveat: list_append(left: small, right: large) is O(len(left))
per the native fold_list_right implementation (it walks `left`, not
`right`), so this may not be the O(n^2) site the growing list_append
call count suggested -- kept as a correctness-neutral cleanup bringing
acc_diags in line with the already-safe acc_nodes pattern, not claimed
as a proven complexity fix.

Removed the temporary witness_forensics_scale_datetime_half_report
(scratch-path fixture, not for the repo).

Added a cross-thread parse-memo effectiveness discriminator
(PARSE_MEMO_LOOKUPS/HITS/DISTINCT_KEYS globals + record_parse_memo_lookup,
wired into try_parse_table_memo_dispatch's parse_table_lookup arm) per
the manager's decisive-discriminator request: lookups >> distinct with
hits == 0 would prove the native memo cache never serves a re-attempted
span. Result on the datetime.dag half-truncation: lookups=0 hits=0
distinct_keys=0 -- the native fast-path memo cache never engages at all
on this run, a separate real finding (not proven to be the DNF driver,
since the .dag-level table's own stats are never reached either, given
the run still doesn't finish).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* adhoc-c328b166-bca: gate residual-hunt recorders behind GUNBC_FLATTEN_SITE_DUMP_SECS, drop debug eprintln

Addresses cursor/composer-2.5's REQUEST_CHANGES on #6365: record_call_frequency,
record_flatten_site, record_list_cons_tail_split, and record_parse_memo_lookup
ran unconditionally on hot paths (eval_call, every free_monoid_to_vec
materialization, every native List Cons match, every packrat memo lookup) --
only the stderr dump was env-gated, not the recording itself. Added a
residual_hunt_forensics_enabled() OnceLock check (same env var the dump
already uses) and an early return at the top of each recorder, so the
default/production path pays one relaxed load per call site instead of a
mutex lock + HashMap/HashSet write. Added a SCAFFOLD/dissolve-on marker per
DESIGN Sec6 (dissolve when adhoc-c328b166-bca closes).

Also removed the ADHOC-DEBUG eprintln arms in parse_table_memo_scope_and_key
-- a local one-off diagnostic (auto-committer captured a mid-investigation
snapshot before I could revert it) that already served its purpose: it
proved the extraction logic is sound (zero branches fire on a working file,
lookups/hits/distinct_keys populate correctly), so it's not needed going
forward and was correctly flagged as ambient unconditional stderr noise on a
production code path.

Re-verified byte-equal (9afb740ee43f9e61) and forensics gating with/without
GUNBC_FLATTEN_SITE_DUMP_SECS set. cargo fmt --all applied (pre-existing
formatting drift on this file, unrelated to this change, caught by the
--check gate).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Brian Searls <briansrls@gunb.ai>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
briansrls added a commit that referenced this pull request Jul 8, 2026
* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* 02_parse verification receipts: byte-equal parse-tree hash (fix 2+3) + empty-list semantics pin (fix 2)

Manager-mandated merge-bar demands on #6362, addressed with durable,
re-runnable witnesses rather than a one-off manual diff:

- parse_tree_content_hash_witness.dag: content_hash(ParseTree) over the
  tiny/small corpus sample, computed once against the true PR base
  (0f98a26, built as its own release binary in an isolated worktree)
  and once against HEAD. Both hashes are byte-identical
  (witness.dag=9afb740ee43f9e61, execution_mode.dag=ed071cb2baeaeb61),
  proving fix 2 (list_at_optional -> list_head) and fix 3
  (fold_list -> parse_match_arm_stmt_step) preserve parse semantics
  exactly, not just "no test regressions."
- parse_token_first_empty_semantics_test.dag: two `test fn` claims
  pinning parse_token_first (post-fix, list_head-based) equal to
  list_at_optional(xs, index: 0) (pre-fix arm) on both the empty-list
  boundary case that motivated the rewrite and the ordinary present
  case. Both PASS via claim_batch.

Note: the local `main` ref was stale (21fb5d1); the true PR base is
origin/main's 0f98a26 (merge-base origin/main HEAD) — using the
stale ref for the pre-fix binary produced a false "grammar_invalid"
rejection unrelated to this PR (fixed upstream by #6337, landed after
21fb5d1 but before 0f98a26).

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* adhoc-c328b166-bca: 01_tokenize O(n^2) fix + full parse-pipeline census + token-stream witness

lex_walk_step accumulated tokens via list_snoc_item every token (O(n^2)
before parsing even starts) -- 4th instance of the same shape, this time
in a different pipeline stage than the three already-landed 02_parse.dag
fixes. Rewritten as reverse-cons + single .reverse() at tokenize()'s exit.

Verified byte-equal via a new order-sensitive token-stream content hash
(class+lexeme, in stream order) run against independently-built pre-fix
and post-fix binaries on tiny/small corpus samples -- identical both
sides, confirming a pure algorithmic rewrite.

Full read-based census of 02_parse.dag's 9 list_snoc_item call sites,
each dispositioned (dead/test-only, bounded grammar-analysis cost, or
already-fixed) -- none explain the file-size-scaling growth still seen
deeper in live parsing on datetime.dag. That residual is not resolved by
this PR; grammar_coverage_p8_wall_clock_blocker records the honest state
(tokenize confirmed non-bottleneck, DNF persists past grammar-validation,
driver not yet pinned) so the marker does not claim a false completion.

Added parse_table_record_hit/miss/lookup_call and
parse_choice_residue_backtrack to the interpreter's call-frequency
watchlist for the continuing in-session hunt.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* fixup: complete the auto-committed partial ProductionFirstRow removal

The auto-committer snapshotted a mid-edit state (b16a357) that deleted
the ProductionFirstRow type but left its consumers referencing it,
breaking typecheck. This completes that edit -- not a new change.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Revert "fixup: complete the auto-committed partial ProductionFirstRow removal"

This reverts commit dae6602.

* Revert "Revert "fixup: complete the auto-committed partial ProductionFirstRow removal""

This reverts commit e2a24c3.

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* adhoc-c328b166-bca: 2 more diag-accumulator sites fixed, parse-memo distinct-key discriminator

parse_match_arm_stmt_step and parse_expr_repeat_step both accumulated
per-iteration diagnostics via list_append(left: diags.reverse(), right:
acc.acc_diags) -- the acc_nodes field in the same accumulators was
already reverse-cons fixed, but acc_diags was missed by the prior
list_snoc_item-scoped census (this is a different primitive). Converted
to List<List<Diagnostic>> batches (Cons-prepended, O(1) per step) plus a
single flatten pass at the end (parse_flatten_diag_batches). Verified
byte-equal via parse_tree_content_hash_witness.dag on tiny/small
(9afb740ee43f9e61 / ed071cb2baeaeb61, unchanged).

Honest caveat: list_append(left: small, right: large) is O(len(left))
per the native fold_list_right implementation (it walks `left`, not
`right`), so this may not be the O(n^2) site the growing list_append
call count suggested -- kept as a correctness-neutral cleanup bringing
acc_diags in line with the already-safe acc_nodes pattern, not claimed
as a proven complexity fix.

Removed the temporary witness_forensics_scale_datetime_half_report
(scratch-path fixture, not for the repo).

Added a cross-thread parse-memo effectiveness discriminator
(PARSE_MEMO_LOOKUPS/HITS/DISTINCT_KEYS globals + record_parse_memo_lookup,
wired into try_parse_table_memo_dispatch's parse_table_lookup arm) per
the manager's decisive-discriminator request: lookups >> distinct with
hits == 0 would prove the native memo cache never serves a re-attempted
span. Result on the datetime.dag half-truncation: lookups=0 hits=0
distinct_keys=0 -- the native fast-path memo cache never engages at all
on this run, a separate real finding (not proven to be the DNF driver,
since the .dag-level table's own stats are never reached either, given
the run still doesn't finish).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* adhoc-c328b166-bca: gate residual-hunt recorders behind GUNBC_FLATTEN_SITE_DUMP_SECS, drop debug eprintln

Addresses cursor/composer-2.5's REQUEST_CHANGES on #6365: record_call_frequency,
record_flatten_site, record_list_cons_tail_split, and record_parse_memo_lookup
ran unconditionally on hot paths (eval_call, every free_monoid_to_vec
materialization, every native List Cons match, every packrat memo lookup) --
only the stderr dump was env-gated, not the recording itself. Added a
residual_hunt_forensics_enabled() OnceLock check (same env var the dump
already uses) and an early return at the top of each recorder, so the
default/production path pays one relaxed load per call site instead of a
mutex lock + HashMap/HashSet write. Added a SCAFFOLD/dissolve-on marker per
DESIGN Sec6 (dissolve when adhoc-c328b166-bca closes).

Also removed the ADHOC-DEBUG eprintln arms in parse_table_memo_scope_and_key
-- a local one-off diagnostic (auto-committer captured a mid-investigation
snapshot before I could revert it) that already served its purpose: it
proved the extraction logic is sound (zero branches fire on a working file,
lookups/hits/distinct_keys populate correctly), so it's not needed going
forward and was correctly flagged as ambient unconditional stderr noise on a
production code path.

Re-verified byte-equal (9afb740ee43f9e61) and forensics gating with/without
GUNBC_FLATTEN_SITE_DUMP_SECS set. cargo fmt --all applied (pre-existing
formatting drift on this file, unrelated to this change, caught by the
--check gate).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

---------

Co-authored-by: Brian Searls <briansrls@gunb.ai>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
briansrls added a commit that referenced this pull request Jul 8, 2026
* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* 02_parse verification receipts: byte-equal parse-tree hash (fix 2+3) + empty-list semantics pin (fix 2)

Manager-mandated merge-bar demands on #6362, addressed with durable,
re-runnable witnesses rather than a one-off manual diff:

- parse_tree_content_hash_witness.dag: content_hash(ParseTree) over the
  tiny/small corpus sample, computed once against the true PR base
  (0f98a26, built as its own release binary in an isolated worktree)
  and once against HEAD. Both hashes are byte-identical
  (witness.dag=9afb740ee43f9e61, execution_mode.dag=ed071cb2baeaeb61),
  proving fix 2 (list_at_optional -> list_head) and fix 3
  (fold_list -> parse_match_arm_stmt_step) preserve parse semantics
  exactly, not just "no test regressions."
- parse_token_first_empty_semantics_test.dag: two `test fn` claims
  pinning parse_token_first (post-fix, list_head-based) equal to
  list_at_optional(xs, index: 0) (pre-fix arm) on both the empty-list
  boundary case that motivated the rewrite and the ordinary present
  case. Both PASS via claim_batch.

Note: the local `main` ref was stale (21fb5d1); the true PR base is
origin/main's 0f98a26 (merge-base origin/main HEAD) — using the
stale ref for the pre-fix binary produced a false "grammar_invalid"
rejection unrelated to this PR (fixed upstream by #6337, landed after
21fb5d1 but before 0f98a26).

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* adhoc-c328b166-bca: 01_tokenize O(n^2) fix + full parse-pipeline census + token-stream witness

lex_walk_step accumulated tokens via list_snoc_item every token (O(n^2)
before parsing even starts) -- 4th instance of the same shape, this time
in a different pipeline stage than the three already-landed 02_parse.dag
fixes. Rewritten as reverse-cons + single .reverse() at tokenize()'s exit.

Verified byte-equal via a new order-sensitive token-stream content hash
(class+lexeme, in stream order) run against independently-built pre-fix
and post-fix binaries on tiny/small corpus samples -- identical both
sides, confirming a pure algorithmic rewrite.

Full read-based census of 02_parse.dag's 9 list_snoc_item call sites,
each dispositioned (dead/test-only, bounded grammar-analysis cost, or
already-fixed) -- none explain the file-size-scaling growth still seen
deeper in live parsing on datetime.dag. That residual is not resolved by
this PR; grammar_coverage_p8_wall_clock_blocker records the honest state
(tokenize confirmed non-bottleneck, DNF persists past grammar-validation,
driver not yet pinned) so the marker does not claim a false completion.

Added parse_table_record_hit/miss/lookup_call and
parse_choice_residue_backtrack to the interpreter's call-frequency
watchlist for the continuing in-session hunt.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* fixup: complete the auto-committed partial ProductionFirstRow removal

The auto-committer snapshotted a mid-edit state (b16a357) that deleted
the ProductionFirstRow type but left its consumers referencing it,
breaking typecheck. This completes that edit -- not a new change.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Revert "fixup: complete the auto-committed partial ProductionFirstRow removal"

This reverts commit dae6602.

* Revert "Revert "fixup: complete the auto-committed partial ProductionFirstRow removal""

This reverts commit e2a24c3.

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* adhoc-c328b166-bca: 2 more diag-accumulator sites fixed, parse-memo distinct-key discriminator

parse_match_arm_stmt_step and parse_expr_repeat_step both accumulated
per-iteration diagnostics via list_append(left: diags.reverse(), right:
acc.acc_diags) -- the acc_nodes field in the same accumulators was
already reverse-cons fixed, but acc_diags was missed by the prior
list_snoc_item-scoped census (this is a different primitive). Converted
to List<List<Diagnostic>> batches (Cons-prepended, O(1) per step) plus a
single flatten pass at the end (parse_flatten_diag_batches). Verified
byte-equal via parse_tree_content_hash_witness.dag on tiny/small
(9afb740ee43f9e61 / ed071cb2baeaeb61, unchanged).

Honest caveat: list_append(left: small, right: large) is O(len(left))
per the native fold_list_right implementation (it walks `left`, not
`right`), so this may not be the O(n^2) site the growing list_append
call count suggested -- kept as a correctness-neutral cleanup bringing
acc_diags in line with the already-safe acc_nodes pattern, not claimed
as a proven complexity fix.

Removed the temporary witness_forensics_scale_datetime_half_report
(scratch-path fixture, not for the repo).

Added a cross-thread parse-memo effectiveness discriminator
(PARSE_MEMO_LOOKUPS/HITS/DISTINCT_KEYS globals + record_parse_memo_lookup,
wired into try_parse_table_memo_dispatch's parse_table_lookup arm) per
the manager's decisive-discriminator request: lookups >> distinct with
hits == 0 would prove the native memo cache never serves a re-attempted
span. Result on the datetime.dag half-truncation: lookups=0 hits=0
distinct_keys=0 -- the native fast-path memo cache never engages at all
on this run, a separate real finding (not proven to be the DNF driver,
since the .dag-level table's own stats are never reached either, given
the run still doesn't finish).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* adhoc-c328b166-bca: gate residual-hunt recorders behind GUNBC_FLATTEN_SITE_DUMP_SECS, drop debug eprintln

Addresses cursor/composer-2.5's REQUEST_CHANGES on #6365: record_call_frequency,
record_flatten_site, record_list_cons_tail_split, and record_parse_memo_lookup
ran unconditionally on hot paths (eval_call, every free_monoid_to_vec
materialization, every native List Cons match, every packrat memo lookup) --
only the stderr dump was env-gated, not the recording itself. Added a
residual_hunt_forensics_enabled() OnceLock check (same env var the dump
already uses) and an early return at the top of each recorder, so the
default/production path pays one relaxed load per call site instead of a
mutex lock + HashMap/HashSet write. Added a SCAFFOLD/dissolve-on marker per
DESIGN Sec6 (dissolve when adhoc-c328b166-bca closes).

Also removed the ADHOC-DEBUG eprintln arms in parse_table_memo_scope_and_key
-- a local one-off diagnostic (auto-committer captured a mid-investigation
snapshot before I could revert it) that already served its purpose: it
proved the extraction logic is sound (zero branches fire on a working file,
lookups/hits/distinct_keys populate correctly), so it's not needed going
forward and was correctly flagged as ambient unconditional stderr noise on a
production code path.

Re-verified byte-equal (9afb740ee43f9e61) and forensics gating with/without
GUNBC_FLATTEN_SITE_DUMP_SECS set. cargo fmt --all applied (pre-existing
formatting drift on this file, unrelated to this change, caught by the
--check gate).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* parse forensics: pin datetime DNF root cause (lex_repeat_loop O(n²)) + measured-zero on interpreter fold-site cut

Continuation of adhoc-c328b166-bca residual-driver hunt (interpreter-side
free_monoid_to_vec fold chokepoint; .dag-side fixes stay in #6365).

Root cause PINNED via a new caller-attribution probe: the datetime interpreted-
parse DNF is lex_repeat_loop (src/v2/compiler/01_tokenize.dag:158) folding the
whole remaining source char-list (max_len=19021=file char count, elem=Int) once
per repeat-token => O(n²) in source length. A .dag tokenizer dataflow bug, routed
to the tokenize lane; this receipt is its acceptance baseline.

Interpreter fold-site cut = MEASURED CLEAN ZERO, dropped: a streaming left-fold
(no intermediate Vec) was built, proven byte-identical (parse-tree hash equal,
tiny+small, two independently-built binaries), and moved neither wall-clock (~20s
both) nor peak RSS (~168 MiB both) on small — elem=Int makes the removed alloc
churn negligible and the Vec is transient (no peak-RSS contribution). Kept
free_monoid_to_vec + a permanent note (DESIGN §6: a no-op displaces nothing).

Lands: caller-attribution probe (DagFnGuard + record_fold_caller, gated behind
GUNBC_FLATTEN_SITE_DUMP_SECS, dissolve-on the hunt) extending neat-stag's
flatten-site instrument with the .dag-caller axis; + the measured-zero note.

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

* Merge origin/main (post-#6365 squash) into parse-forensics probe branch

Reconciles the caller-attribution probe + measured-zero note onto the
squash-merged #6365 tokenizer/parse fixes. Conflicts were purely additive
(my probe blocks sit adjacent to neat-stag's flatten-site instrument); kept
both sides. Diff vs main is now only v1_interpreter.rs + cli_run.rs.

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

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: Brian Searls <briansearls1@gmail.com>
briansrls added a commit that referenced this pull request Jul 8, 2026
…arness (#6371)

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* 02_parse verification receipts: byte-equal parse-tree hash (fix 2+3) + empty-list semantics pin (fix 2)

Manager-mandated merge-bar demands on #6362, addressed with durable,
re-runnable witnesses rather than a one-off manual diff:

- parse_tree_content_hash_witness.dag: content_hash(ParseTree) over the
  tiny/small corpus sample, computed once against the true PR base
  (0f98a26, built as its own release binary in an isolated worktree)
  and once against HEAD. Both hashes are byte-identical
  (witness.dag=9afb740ee43f9e61, execution_mode.dag=ed071cb2baeaeb61),
  proving fix 2 (list_at_optional -> list_head) and fix 3
  (fold_list -> parse_match_arm_stmt_step) preserve parse semantics
  exactly, not just "no test regressions."
- parse_token_first_empty_semantics_test.dag: two `test fn` claims
  pinning parse_token_first (post-fix, list_head-based) equal to
  list_at_optional(xs, index: 0) (pre-fix arm) on both the empty-list
  boundary case that motivated the rewrite and the ordinary present
  case. Both PASS via claim_batch.

Note: the local `main` ref was stale (21fb5d1); the true PR base is
origin/main's 0f98a26 (merge-base origin/main HEAD) — using the
stale ref for the pre-fix binary produced a false "grammar_invalid"
rejection unrelated to this PR (fixed upstream by #6337, landed after
21fb5d1 but before 0f98a26).

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* WIP: interpreted-parse cost forensics: discriminate interpretation-tax vs alg

* Interpreted-parse superlinear drivers closed: medium fixture DNF(1200s+) -> 88s

Continuation of neat-stag's adhoc-c328b166-bca forensics (fixes 1-3 merged
from session/neat-stag-780). Three new env-gated instruments in the v1
interpreter named each residual driver; each was then killed at its single
authority in v2 source:

- 02_parse: memo position key O(1) via head-token start+1 (was
  length(all) - length(remaining) per nonterminal attempt; v2's length()
  is a .dag fold_list body, so the seed's native_len fast path never fired)
- 01_tokenize: lex_repeat_loop + delimited combinator short-circuit
  recursion (were done-flag no-op folds over the entire remaining source
  per attempt); lex rule matcher thunks compiled once per tokenize
  (CompiledLexRule) instead of fold_lex_pattern per (rule, position)
- std/nat: nat_compare grounded on the realization's native < / ==
  (ordering side of the #5428 numeric-tower grounding; the Peano walk cost
  ~66 frames per char-class range check, 5.2M frames/min)
- std/provenance: SpanIndex assoc list -> Map<OccurrenceId, OriginEvent>
  (ParseTable.entries carrier pattern; lookup was 13.4M frames / 242s
  self-time on one medium parse; merge stays base-wins)
- v1 interpreter instruments (recording gated on GUNBC_FLATTEN_SITE_DUMP_SECS,
  off = one atomic load): big-fold .dag-site attribution, per-builtin
  inclusive wall time, per-.dag-fn self-time profile

Receipts: datetime.dag DNF>1200s/4.3GB RSS -> 88s; node.dag 116s;
grammar.dag 201s (class break closed, ~linear in content); tiny/small
packrat stats byte-identical (0/43/43, 0/200/200); 13 targeted claims
green; compile --entry 0 diagnostics; regen divergence 0; fmt/clippy green.
P8 wall-clock blocker ledger updated with the closure and the two named,
priced residuals (delimited lexeme O(k^2) accumulation, per-file
grammar-analysis base).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZSaWfysRfg28bNVS7ueX

* S1 groundwork: closure receipt harness + dissolve the one v1-only construct in the parser's closure

- dag/std/algebra.dag: kernel_algebra_profile's map literal (the only
  string-keyed brace literal in the parse pipeline's transitive closure)
  becomes an ordinary builder fn over empty_map/map_insert -- same map
  value, one dialect construct fewer; std_algebra.rs regenerated
  (regen_stage0, divergence 0 after)
- s1_closure_receipt_test.dag: S1 self-hosting receipt harness. Subject =
  the parse pipeline's transitive import closure (40 files rooted at
  01_tokenize/02_parse/03_normalize/extdeps-languages-dag), which is
  "v2's parser parsing itself"; the harness's own filesystem plumbing is
  test infrastructure and deliberately outside the subject (note in file).
  Grammar validated once per run; per-file tokenize -> parse_production ->
  normalize; failures collected by path, claim = failures == [].
  Full-closure census run in flight; receipt lands with its result.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZSaWfysRfg28bNVS7ueX

* S2 strategic-lane worker brief + target_model parse probe

- docs/plans/s2-v2-self-emit-brief.md: hand-off brief for the parallel
  v2-emits-v2 lane (receipt ladder, measured landmines incl. the Symbol
  carrier seam = 1,790 of 3,560 E0308s on fresh v1-emit of the closure,
  file-ownership split vs the tactical lane)
- tm_probe.dag: profiled single-file parse probe for target_model.dag
  (census residency diagnosis; forensics-enabled run)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZSaWfysRfg28bNVS7ueX

* S1: dissolve target_model's two pipeline-operator sites + stall-offset diagnosis probe

- target_model.dag: the closure's only live |> uses migrate to the v2
  surface (length(xs:), any(xs:, predicate: fn)) -- v1 frontend 0 diags;
  these were two of the census-named parse failures' construct class
- s1_stall_probe.dag: parses a file at the start production via
  parse_expr directly and reports the byte offset of the first token the
  parse could not consume (greedy repeat => Accepted-with-remaining
  pinpoints the first uncovered construct); batch fns for the remaining
  census failures (runtime, refinement, node, grammar, languages/dag,
  algebra post-migration re-verify)

Census receipt (interpreted, 68 min, pre-fix tree): 35/45 parsed clean;
subject-relevant failures under diagnosis: runtime, refinement, node,
grammar, target_model (pipes, fixed here), languages/dag.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZSaWfysRfg28bNVS7ueX

* S1: keyword-after-dot grammar row + dissolve negative-literal sites

- languages/dag.dag: the postfix projection name after '.' uses
  dag_grammar_binding_name_terminal() (the soft-keyword choice already
  used for field/param names), so interpretation.loop / .match parse --
  the runtime.dag census failure's construct class
- refinement.dag, grammar.dag: the subject's three negative int literals
  (-1/-2) become parenthesized binary minus (0 - 1); the wave1 grammar
  has no negative-literal lexeme and three sites do not justify the
  lexer/normalize lever yet -- class noted for the tree-wide census

All three v1-frontend green (0 diagnostics). Stall probes re-verifying
under the new grammar; node.dag + languages/dag.dag offsets pending.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZSaWfysRfg28bNVS7ueX

* S1: dissolve node.dag's nine arrow-lambda sites to fn-lambdas

The content-hash canonicalizer block used v1 arrow lambdas
(e => match ...) with comma-separated inline match arms at two sites --
node.dag's census failure class (stall byte 13535 = the first such fn).
All nine sites become fn(e) { ... } with newline arms; pure syntax
migration, identical behavior, v1 frontend 0 diagnostics.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZSaWfysRfg28bNVS7ueX

* S1: dquote lex rule via CharPattern + node.dag's last arrow lambda

- languages/dag.dag: the string-literal rule's own delimiters were
  LiteralPattern { text: "\"" } -- an escaped-quote string the wave1
  lexer cannot tokenize when reading this file itself (the stream
  garbles silently and the parse dies ~2,400 lines later; stall probe
  receipt). Now CharPattern { char: 34 }, the exact idiom the
  string-BODY escape element two rules up already uses.
- node.dag: sort_by(xs, h => h) -> fn(h) { h } (the census's ninth-plus
  arrow site, hidden earlier by a head-truncated grep).

Both v1-frontend 0 diagnostics. Full 40-file receipt run in flight.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZSaWfysRfg28bNVS7ueX

* S1: add target_model stall probe fn

Merged-tree receipt: 38/40 subject files parse clean; the two giants
(target_model.dag, languages/dag.dag) each have a residual construct
behind the first fix -- stall probes running to name them.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZSaWfysRfg28bNVS7ueX

* S1: rename dag.dag's keyword let-binding + explicit field puns in target_model

- languages/dag.dag: `let module = ...` used the bare `module` keyword as
  a let-binding name (lexes as kw_module, so the whole
  dag_wave1_grammar_root decl failed); renamed to module_prod -- a local
  binding, so a rename beats extending keywords into let-name/expr
  positions where they would collide with the match/if productions
- target_model.dag: NamedMetadataFoldOk field puns made explicit
  (name: name); NOTE the sweep shows shorthand patterns parse fine in
  green files (integer.dag's Cons { head, tail }), so this may be
  hygiene rather than the poison -- fresh stall probes running to
  arbitrate

Both v1-frontend 0 diagnostics.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZSaWfysRfg28bNVS7ueX

---------

Co-authored-by: Claude <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