Skip to content

Multi-entry claim_batch + lens-gate rewiring (extends #4719 pattern): src/v2/stage0/src/bin/claim_batch.rs currently resolves ONE --entry closure and loops functions; the v4 lens gate (scripts/v4-lens-ci-gate.sh:87) still does a cold 'gunbc run --claim-run' per row because each lens row has its own - #4783

Merged
briansrls merged 12 commits into
mainfrom
session/jolly-fox-125
Jun 13, 2026

Conversation

@briansrls

@briansrls briansrls commented Jun 12, 2026 •

Copy link
Copy Markdown
Contributor

Worker attestation

  • Title describes the change (not the session id or branch).
  • PR body summarises what and why (replace the TODO below).
  • Tests run: name the command (e.g. npm test, cargo test) and the result.
  • If this closes a work item, the body contains a Closes #N directive.
  • No commits on this branch are surprises (no fork/cherry-pick I did not make).
  • No secrets / credentials / large binaries staged.

Summary

Extends claim_batch to accept multiple --entry flags (each with its own --function/--functions), then rewires scripts/v4-lens-ci-gate.sh to issue one claim_batch call for its entire 8-row GREEN pass instead of 8 separate gunbc run --claim-run invocations. Motivation: each lens gate row has a unique entry file, so the existing batch_green_pass grouping-by-shared-entry pattern did not help it — true multi-entry support in the binary was required.

The multi-entry CLI surface is fully additive. Single-entry invocations (--entry e --functions f1,f2) take the original code path unchanged. The perturb pass stays per-row through gunbc run since each row mutates a different function in its own temp source root.

Caching model

MultiEntryIndex holds:

  • source_files: ModuleSourceIndex — filesystem scan done once (HashMap<module_path, Rc>)
  • intern_table: RefCell<Rc<InternTable>> — lazily-accumulated; grows as new files are first parsed
  • parse_cache: RefCell<HashMap<path, (Rc<ParseResult>, Rc<NewlineIndex>)>> — lazily-populated parse trees

What is shared: filesystem scan, process startup, Rc<SourceFile> text, and — the main lever — tokenise+parse for every .dag file. Each file is tokenised and parsed at most once per claim_batch session. Shared std/ files (~75) are parsed during the first entry's resolve and reused by all subsequent entries.

How: cache misses call parse_with_table(tokens, si, current_table) (which pre-interns internally), update intern_table with the returned parsed.intern_table, and insert into parse_cache. The intern table only grows, so cached ParseResult IDs remain valid for all future entries.

P3 fail-closed parity: resolve_entry_with_parse_cache mirrors compile_to_resolved exactly: resolve_modules → normalize_graph → reconcile → extract_ownership_proofs → ownership_diagnostics, with error checks at every stage.

BEFORE / AFTER timing (local, release binary, warm disk cache)

BEFORE — baseline: 8 separate gunbc run --claim-run invocations:

real    ~5m36s

AFTER — one claim_batch with all 8 --entry/--function pairs + lazy parse cache:

real    3m11.207s
user    3m7.493s
sys     0m1.610s

2m25s / 43% reduction. Parse-tree reuse across 8 entries (75 shared std/ files parsed once instead of 8×75=600) is the dominant lever.

Deferred follow-on: union-closure / typed-module sharing

A further lever exists: sharing reconcile output (typed modules) across entries whose closures overlap. This would amortize type inference for shared std/ files in addition to parsing. Not in this PR — #4777 (Optional generic instantiation fix, a13aa4b) only just landed; let that fix soak before leaning on the generic closure it affects. The program-level next lens-gate win is also frontier-selection (shrinking the witness set), not deeper batching. Union-closure is unblocked by #4777 and tracked as a follow-on.

Test plan

  • cargo test --workspace — passed
  • cargo clippy --all-targets -- -D warnings — passed
  • cargo fmt --all --check — passed (fmt applied)
  • scripts/v4-lens-ci-gate.sh GREEN pass: all 8 witnesses PASS, row-count check passes, 3m11s
  • Backward compatibility: single-entry claim_batch --entry e --functions f1,f2 path unchanged

@briansrls
briansrls marked this pull request as ready for review June 12, 2026 23:04
v4-lens-ci-gate.sh now requires claim_batch (multi-entry green pass
added in this PR), but the v4_lens_gate CI job only cached gunbc +
discover_owned_data. On a cache hit, claim_batch would be missing and
the gate would exit 2 (E-10 fail-closed regression).

Fix: add claim_batch to the cache path, bump the key namespace to
gunbc-lens-gate2-bin- (so pre-PR caches without claim_batch are never
restored), and add an Ensure claim_batch fallback step matching the
existing discover_owned_data ensure pattern.

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

Copy link
Copy Markdown
Contributor Author

Fixed in 9b87859: added claim_batch to the v4_lens_gate binary cache path, bumped the cache namespace to gunbc-lens-gate2-bin- (so pre-PR caches without claim_batch are never restored), and added an Ensure claim_batch fallback step matching the existing discover_owned_data pattern. — sent from jolly-fox-125

briansrls pushed a commit that referenced this pull request Jun 13, 2026
…+ frontier-selection fusion ladder sketch (Part 3)

Design/scoping only — no substrate lands. Part 2 designs the producer that derives
NodeArtifactProvenance from the compiler/source-authority ingest (closing RR-K §7's
real-input blocker and the tidy-wolf source_authority API gap: a whole-ingest enumerator
+ fold to FreeMonoid<NodeArtifactProvenance>, file-grain v1, fail-closed coverage law).
This is the durable replacement for Part 1's drifting affects_v4 allowlist (detector
closure ≡ compile closure by construction). Part 3 sketches the frontier-selection +
affected_testgen_ci_runner fusion over multi-entry claim_batch (#4783), design-only and
gated on #4785 + the affects_v4 fix; safe-slice gating stays HELD per coordinator.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Brian Searls and others added 2 commits June 13, 2026 00:14
…s 5m36s baseline

Replace eager-parse-all approach (pre-parses all 475 .dag files at index
build time — regressed to 6m33s) with demand-driven caching via
RefCell<HashMap>. Each file is tokenised and parsed at most once per
claim_batch session; the accumulated intern table grows monotonically so
cached ParseResult IDs remain valid for all subsequent entries.

Lens gate GREEN pass BEFORE/AFTER:
  baseline (no cache, cold gunbc per row): ~5m36s
  eager-parse-all (all 475 files upfront):  6m33s  [regression, not shipped]
  lazy-cache (parse on first access):        3m11s  [this commit]

~43% wall-clock reduction (2m25s off) for the 8-entry lens GREEN pass.

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

Copy link
Copy Markdown
Contributor Author

All three findings from the cursor/composer-2.5 REQUEST_CHANGES were against the eager-parse-all implementation that was in the prior commits and was a performance regression (6m33s vs 5m36s baseline). That approach was replaced wholesale in the latest commit (f0e24c6).

Current state (f0e24c6) addresses all three findings:

  1. index.global_intern_table field — removed. MultiEntryIndex now declares intern_table: RefCell<Rc<InternTable>>. No stale field reference.

  2. .get() on RefCell<HashMap> — fixed. The cache lookup is index.parse_cache.borrow().get(&source.path).cloned(), releasing the immutable borrow before any mutation.

  3. Empty cache / unimplemented path — fixed. On cache miss, the function tokenises the file, calls parse_with_table with the current accumulated intern table, advances intern_table via *index.intern_table.borrow_mut() = parsed.intern_table.clone(), and inserts into the cache via index.parse_cache.borrow_mut().insert(...). The cache fills lazily; shared std/ files are parsed once and reused by all subsequent entries.

Timing receipts:

  • Baseline (cold gunbc run per row): ~5m36s
  • Eager-parse-all (regressing approach, not shipped): 6m33s
  • Lazy cache (this commit): 3m11s — 2m25s / 43% improvement

The cargo check, clippy (-D warnings), and fmt checks all pass locally. — sent from jolly-fox-125

briansrls pushed a commit that referenced this pull request Jun 13, 2026
…rule (RCA: structural perturb growth + DRAM-bandwidth contention)

v4_lens_gate + v4_lens_ci timeout-minutes 20->35 and matching
check_ci_timeout_budgets ceilings. Uncontended wall ~16m x2 contention
headroom = 32, rounded to 35. Interim: dissolves when the perturb-phase
split and/or #4783 parse-cache adoption shrink the uncontended wall;
re-derive under the budget rule then.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
briansrls pushed a commit that referenced this pull request Jun 13, 2026
Manager ruling (msg_26e8bc1a): spot-perturb (1–2 rows, GITHUB_RUN_NUMBER
rotation) drops Phase B to ~1m → ~8.7m uncontended total → 2.6× ≈ 22.6m ≤ 35m;
shard unnecessary. Reverts a149eba row-shard split; keeps #4791 re-anchor.

dissolve-on-arrival: #4783 multi-entry claim_batch; perturb-phase split if spot
coverage proves insufficient.

Co-authored-by: Cursor <cursoragent@cursor.com>
Brian Searls and others added 2 commits June 13, 2026 01:34
The binary cache key changes whenever src/v2/stage0 source files change.
Previously, if the v4_lens_gate job timed out (before cache-save ran),
the next run would get another cold build — perpetuating the timeout.

save-always: true ensures the compiled binaries are cached regardless of
job outcome, so any subsequent run on the same source tree gets a cache
hit and skips the ~4-minute cold build.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace actions/cache@v5 (restore+save in post-job phase) with an explicit
actions/cache/restore@v5 at the top and actions/cache/save@v5 immediately
after the build step. This ensures binaries are cached before the long-running
lens tests begin — so if the job is cancelled by timeout, the next run still
gets a cache hit and skips the ~4-minute cold build.

The save step is guarded by cache-hit != 'true' to skip when binaries were
already restored.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
briansrls added a commit that referenced this pull request Jun 13, 2026
…documented-baseline budget rule (#4800)

* WIP: ci investigation

* ci: interim lens budget 20->35m under the documented-baseline budget rule (RCA: structural perturb growth + DRAM-bandwidth contention)

v4_lens_gate + v4_lens_ci timeout-minutes 20->35 and matching
check_ci_timeout_budgets ceilings. Uncontended wall ~16m x2 contention
headroom = 32, rounded to 35. Interim: dissolves when the perturb-phase
split and/or #4783 parse-cache adoption shrink the uncontended wall;
re-derive under the budget rule then.

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

* ci: break out C9 per-step baseline + promotion-PR re-measure note in lens budget comment

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

* ci: derive perturb-check plant target from the ceilings table (fixes self-check rot when budgets change)

The perturb self-check hardcoded planting 'timeout-minutes: 20'->21;
raising the last 20m budgets removed every such line, breaking the
check on its own PR. Plant now derives from CEILINGS['ci.yml'] (first
job whose declared value appears verbatim, raised 1 past its ceiling),
so the self-check survives any future budget change.

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

---------

Co-authored-by: Brian Searls <briansearls1@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
briansrls added a commit that referenced this pull request Jun 13, 2026
…tion (P1) + diff→NodeArtifactProvenance producer design (P2) + frontier-selection fusion sketch (P3) (#4795)

* docs(planning): affected-set CI gating recommendation — ci_floor kill-criterion aggregate (1988 receipts, Jun 2–12)

Aggregates the Wave-1 §11.7 emit-affected-set-ci-receipt corpus into a written
gating recommendation (NOT a gating flip; coordination via snappy-crab-849 required).

Findings: 13.8% per-run / 22.1% per-PR floor skip rate; 0 fail-closed and 0
diff-attributable floor false-negatives across 274 would-skip runs; one structural
coverage gap (affects_v4 allowlist narrower than the --source-root src/v4 emit-probe
closure: program.dag, program/program.dag, workflow/runtime_run.dag, lens_ci_gate.dag).
saved_minutes still unmeasured in v1. Recommends: close the allowlist gap fail-closed,
wire the job-timings aggregator, then gate only the safe slice (docs-only + v3-only),
shadow→canary→enforce, narrowing the #4785 corpus-gate witness set rather than racing it.

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

* WIP: CI consolidation scoping (operator un-parked #g-fusion, 2026-06-12): (1)

* docs(planning): diff→NodeArtifactProvenance producer design (Part 2) + frontier-selection fusion ladder sketch (Part 3)

Design/scoping only — no substrate lands. Part 2 designs the producer that derives
NodeArtifactProvenance from the compiler/source-authority ingest (closing RR-K §7's
real-input blocker and the tidy-wolf source_authority API gap: a whole-ingest enumerator
+ fold to FreeMonoid<NodeArtifactProvenance>, file-grain v1, fail-closed coverage law).
This is the durable replacement for Part 1's drifting affects_v4 allowlist (detector
closure ≡ compile closure by construction). Part 3 sketches the frontier-selection +
affected_testgen_ci_runner fusion over multi-entry claim_batch (#4783), design-only and
gated on #4785 + the affects_v4 fix; safe-slice gating stays HELD per coordinator.

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

* docs(planning): ground Part 2 producer design in verified substrate — per-read wire already PROVEN, gap is the closure fold

edit_locus_resolver.dag already proves the single-read producer→affected-set wire
(:192) and the fail-closed reject on malformed source (:259). So the producer is
assembly of proven parts; the only new design is the SourceRootIngest carrier
(FreeMonoid<DagSourceReadWitness>) + monoid fold short-circuiting to reject. Answers
open-question Q2: no source-root ingest set-carrier exists yet.

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

* docs(planning): Part 2 — M9 attach SourceRootIngest to discovery concept family + tidy-wolf constraints + affects_v4 one-arc linkage

Per CI-investigation manager relay (2026-06-13): DFS the concept DAG before minting
SourceRootIngest — the discovery lane (discovery_types.dag OwnedDataDeclRecord/
OwnedDataDiscoveryReceipt + glob_discovery_law) already has the whole-tree-enumeration →
per-item-record → scalar-receipt shape; reuse/mirror it (and flag promoting it from
test/claim/workflow to the compiler layer so producer + discovery share one enumeration
authority). Add tidy-wolf constraints: real changed-paths→GitDiffNameOnly, NodeArtifactProvenance
from parsed/resolved real nodes (no synthetic snapshot), Rust scanner forbidden (second parser
authority). Record that the producer's fail-closed coverage law SUBSUMES the affects_v4 tripwire —
tripwire→producer→retire is one arc, not competing detectors.

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

* docs(planning): record coordinator ruling on Part 2 home question — promotion approved, 4 binding conditions

snappy-crab-849 ruled (2026-06-13): promote the whole-tree enumeration + discovery-receipt
shape to the compiler/source-authority layer (one enumeration authority, attach-don't-mint),
gated by four binding conditions folded into §home-question: (1) sequencing tripwire→#4785
merge→relocation→producer; (2) standalone model PR migrating discovery consumers in the same PR
with M9 std/-DFS home justification; (3) load-bearing design-sign + perturb receipt before
ready-flip (rewires v4_lens_gate equivalence law); (4) STOP on the #4741-frozen five. Design-only;
no implementation until tripwire + #4785 clear.

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

* WIP: CI consolidation scoping (operator un-parked #g-fusion, 2026-06-12): (1)

* docs(planning): add corpus-manifest dissolution as a NAMED CONSUMER + acceptance criterion (coordinator directive)

Coordinator/operator directive (2026-06-13): the producer is load-bearing on a concrete endpoint.
#4785's hand-enumerated ClaimWitnessCorpusClaimRunRow manifest dissolves discovery-driven off the
SAME single enumeration authority the producer shares. Two layers kept separate: enrollment
(discovery, every Bool witness ExpectPass-by-default, manifest dissolves to ExpectFail
exceptions-only — judgments are declarations, never dissolve; add-a-witness-file auto-enrolls, zero
manifest edits) vs selection (producer per-diff frontier narrows which ENROLLED witnesses RUN).
Acceptance criterion is anti-cement same-PR: dissolve cites the hand-list it deletes, discovery
reproduces the 15/5 ExpectPass/ExpectFail baseline by execution FIRST, then the enumeration rows die
same-PR (exceptions remain). Sequencing extended: #4785 merges w/ hand manifest → promotion →
producer → manifest-dissolve.

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

* docs(planning): add live docs-only exhibit (#4797) to Part 1 (coordinator directive)

Coordinator directive (2026-06-13): pair the receipts with tonight's live exhibit. PR gunbc#4797
(one file, docs/demo/add-walkthrough.md, zero .dag/src) ran the full heavy suite — ci_floor 14.7m,
ci_floor_parity 11.6m, lens jobs pending — because the affected job classifies but gates nothing.
A pure-markdown PR consumed lens capacity in the contention squeeze and could fail-closed on a
cancelled dependency despite changing no code. Same phenomenon as the 219 docs-only would-skip runs
/ 0 diff-attributable false-negatives. States the binding prerequisite: affects_v4 gap closure
(#4796, fail-closed, at finish line) must land before any enforce flip.

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

---------

Co-authored-by: Brian Searls <briansearls1@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@briansrls

Copy link
Copy Markdown
Contributor Author

Re: finding 2 — single-entry branch dispatch duplication (claim_batch.rs:271-309)

Valid observation. The single-entry branch duplicates the run_claim match arms from run_witnesses because it needs to keep ctx alive past the witness loop for print_interp_stats(&ctx, ...) — which reports per-context mutation counters that are only meaningful on the live InterpContext. Calling run_witnesses instead would drop ctx before the stats call.

The refactoring path the review suggests (returning ctx from run_witnesses, or hoisting just the match arms) would change run_witnesses's signature in a way that bleeds the stats concern into the shared path. Given that the finding is explicitly non-blocking and this is a CI-perf-only PR, I'm leaving the dispatch inline rather than adding scope creep. The dissolution TODO added for finding 1 (resolve_entry_with_parse_cache) will apply here indirectly when the single-entry branch can call through the shared path.

— sent from jolly-fox-125

@briansrls
briansrls merged commit 6996322 into main Jun 13, 2026
10 of 11 checks passed
@briansrls
briansrls deleted the session/jolly-fox-125 branch June 13, 2026 02:33
briansrls pushed a commit that referenced this pull request Jun 13, 2026
briansrls pushed a commit that referenced this pull request Jun 13, 2026
CI latency attack profile A: break the v4_lens_gate / v4_lens_ci ~32m
wall into its green / perturb / C9 / discovery phases, plus per-witness
resolve accounting — all NON-GATING measurement (pass/fail unchanged).

claim_batch (the green-pass runner): every entry resolve now reports its
wall + closure size ([resolve] <entry>: <ms>ms (<n> modules, <n> resolved
items)), every witness reports its eval wall ([witness]), and a
[resolve-summary] line confirms resolve-vs-witness split at a glance.
Shared outcome reporter factored out of the single-/multi-entry paths.

Gate shells: phase_begin/phase_end wall-time notices (same pattern the
C9 substrate-equivalence gate already uses) around the green and perturb
phases of v4-lens-ci-gate.sh and v4-affected-set-node-frontier-gate.sh.

ci.yml: GUNBC_INTERP_STATS=1 on the two claim_batch-driven witness steps
so the interp flatten/RSS report lands in the job log (non-gating).

Local green-pass run already shows the headline: resolve dominates ~18x
(187s resolve vs 10.5s witness across 8 rows) and the cost is front-loaded
cold parse — first resolve 108s for a 31-module closure vs 2.9s for an
identically-sized warm resolve. Supports the #4783 parse-cache dissolve-on.

Co-Authored-By: Claude Opus 4.8 (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