Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .claude/board/entries/2026-10-31-r1-native-prepared-successor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# R1 successor: native prepared plans over CompiledProgram

**Date 2026-10-31 · Status MEASURED (lab) · Branch `ccr-85acaff0-oszq40-r1b` (successor to #1892)**

`binding_native::prepare_native` runs a `binding::Prepared` plan once through `FoldDialect`, keeps each fold's program as a mask-risc `CompiledProgram` in step order, and records the output layout. `run_native` checks guards, route and permit exactly as `run` does, then runs the programs with `execute_compiled` and fills a fixed-size receipt. No interpreter runs, nothing is allocated, and there is no per-run `BTreeMap`. Plans that compiling once would freeze are refused by name:
- `FoldResultFeedsFold`: a fold fed by an earlier fold's result;
- `ScalarAfterFold`: a scalar computed after a fold;
- `DataScalarNotPrepared`: a fold-free scalar read from the planes;
- `TooManyOutputs`;
- `PlanesChanged`.

`ExecPermit` stays a lab permission, not a production authorization.

Benchmark (`r1_vs_prebound`, `MPF_REPS=31`, 4 cores). Median ns per call from the clean run of three; the other two were disturbed by VM load, one doubling several arms. Every timed call is checked against the oracle.

| rows | mask-risc prepared | R2IL prepared | R1 run | R1 native |
|---:|---:|---:|---:|---:|
| 64 | 105.5 | 107.2 | 405.8 | 164.2 |
| 512 | 204.0 | 207.0 | 499.1 | 256.0 |
| 4096 | 1086.9 | 1089.1 | 1435.8 | 1139.7 |
| 65536 | 18670.8 | 18812.1 | 19039.0 | 18726.3 |

Allocations per call: R1 native 0, R1 run 5.

R1 native sits about 50–60 ns above prepared mask-risc at every size, so the gap is constant and does not grow with rows. The guard check alone measures about 30 ns (prebound vs prebound+guard), and route lookup and the permit scan are the rest. This split is a working model, not separately timed.

OPEN:
- plans that compute a scalar from a fold result (`ORDER_A`, `ORDER_B`, `SUB_*`) are refused, not executed natively;
- the permit check is a linear scan.
70 changes: 70 additions & 0 deletions .claude/board/entries/2026-10-31-r1-r2il-binding-reading.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# R1: optional R2IL reading of a DT64 MethodBinding (prepare/run)

**Date 2026-10-31 · D-id D-DYNBAG §19.3 R1 (scope corrected by §20) · Status DONE (lab-tested, optional reading)**

`lance_graph_r2il::binding` lets an admitted DT64 `MethodBinding` be run through the existing `FoldDialect`, when a consumer asks for the R2IL reading. It is not the path any consumer must take. Native execution (Quack, mask-risc, `ndarray::simd`) runs its own prepared plan without consulting a codegraph.

Lifecycle, as in the Tract probe (`tract-harvest-probe/src/plan.rs:116-128,171,204`):
- `prepare` (cold), in this order:
- frame/focus/scope guards;
- route at the captured version and now;
- class basis via `BindStore::resolve`;
- `BindStore::admit`;
- refuses WHEN guards and three-valued truth;
- lens concept = OGAR `CONCEPT_R2IL_FOLD` (provisional; lab only);
- per effect: lens, word label resolved to exactly one opcode by OGAR's `R2ILVocabulary` (the position is free; only the name binds), translatable subset (17 opcodes: stack operands only, one pushed result), and OGAR arity = argument count.
- `run` (hot): guards, route and an explicit `ExecPermit` again, then the bound opcodes through `FoldDialect`. No `ClassView`, vocabulary or lens lookup.
- `Receipt`: plan id (FNV-1a over method ref, lens, steps), frame revisions, steps executed, mask-risc programs run, output. Bounded by step count. It is not a row trace, not rewrite history, not logical provenance.

MEASURED (`cargo test -p lance-graph-r2il`): 14 new tests, the existing 9 + 22, and `r2il-mask-abi-probe`'s parity suite all green. 11 disable runs, each red:

| guard | red |
|---|---|
| D1 label binds, not word number | 12 tests |
| D2 OGAR arity = argument count | an_arity_the_lens_declares_against_ogar_refuses |
| D3 run needs a permit | execution_needs_a_permit_beyond_admission |
| D4 run rechecks guards | a_moved_frame_focus_or_scope_is_never_reused |
| D5 run rechecks route | a_changed_route_is_never_reused |
| D6 lane constant fits lane type | operands_that_do_not_translate_faithfully_refuse_at_run |
| D7 WHEN refused | what_the_reading_cannot_honour_is_refused_at_prepare |
| D8 lens is the R2IL reading | a_word_under_another_lens_or_reading_refuses |
| D9 executable subset | what_the_reading_cannot_honour_is_refused_at_prepare |
| D10 effect names the R2IL lens | a_word_under_another_lens_or_reading_refuses |
| D11 three-valued refused | what_the_reading_cannot_honour_is_refused_at_prepare |

A first disable of "OGAR arity vs declared arity" stayed green: `BindStore::register` already ties argument count to the declared arity, so that comparison could never fire on its own. It was removed; the refusal still reports the declared arity.

**Found:** `FoldDialect`'s lane compares narrow an `i64` constant with `unwrap_or(i32::MAX)` / `unwrap_or(u32::MAX)` (`lance-graph-r2il/src/lib.rs` `INT_EQUAL`, `INT_S_LESS` arms), which changes the predicate for an out-of-range constant. The seam refuses such a constant first; the dialect's own saturation is unchanged (OPEN, owner: lance-graph-r2il).

OPEN:
- who issues `ExecPermit`;
- `0xC401` is provisional in OGAR;
- `GROUP_SUM`/`KEEP` (sinks) and immediates (`NUMBER`, `LOAD`, `RANGE`) are refused, not translated.

**D-DYNBAG-0 does not depend on R1** (§20). Its seam is binding → prepared native (Quack) plan.

## Benchmark result and decision (2026-10-31)

`crates/math-polyfill-probe/examples/r1_vs_prebound.rs` times seven arms over `SUM{a : a < t}`, with identical inputs and outputs. Every timed call of every arm is checked against the oracle, and the run fails if the count of correct answers differs from the call count. An error or a wrong value is a failed sample, never a skipped one. This replaces an `if let Ok(r)` in the R1 arm that discarded failures.

Median ns per call, `MPF_REPS=31`, 4 cores. Three runs: one was disturbed by load and is not used, and the other two agree.

| rows | direct | prebound | +guard | mask-risc prepared | R2IL prepared | dialect | R1 run |
|---:|---:|---:|---:|---:|---:|---:|---:|
| 64 | 31.5 | 31.8 | 62.3 | 103.3 | 109.8 | 211.0 | 384.1 |
| 512 | 123.4 | 123.9 | 150.6 | 212.3 | 215.3 | 352.1 | 497.3 |
| 4096 | 947.2 | 945.9 | 996.3 | 1043.8 | 1049.4 | 1140.6 | 1320.9 |
| 65536 | 18158.6 | 18078.9 | 18504.9 | 18301.8 | 18338.4 | 18509.1 | 18663.0 |

Allocations per call: 0 for every prepared arm, 1 for the dialect run, and 5 for R1. At 65,536 rows the row scan dominates, and the arms differ by less than the run-to-run variance.

**Conclusion.**
- Prepared R2IL and prepared mask-risc have equivalent measured runtime.
- R1's extra ~280 ns at 64 rows and its five allocations per invocation belong to its current implementation, not to R2IL. That implementation interprets every step through `FoldDialect` on each run, keeps per-run `BTreeMap` field values, rebuilds the stack, and allocates the receipt.

**Decision (operator).** Keep #1892 intact as measured, historical evidence until a clean successor replaces it. Its OGAR-body rationale is superseded and is not a merge requirement. The successor:
- prepares executable mask-risc programs once, through the `FoldDialect` compilation and `CompiledProgram` machinery D-MPF-4 proved;
- keeps multi-fold ordering and every refusal;
- keeps frame, route, focus, scope and permit validation independent.

`ExecPermit` remains a lab permission, not a production authorization claim.
4 changes: 3 additions & 1 deletion .claude/board/entries/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,13 @@ index row, (3) no duplicate entry id. Checks 1 and 2 are deliberately
opposite directions; the stranding this convention prevents shows up in
exactly one of them, never both.

531 entries, 2026-08-06 .. 2026-10-31.
533 entries, 2026-08-06 .. 2026-10-31.

| date | entry id | finding | file |
|---|---|---|---|
| 2026-10-31 | `x2-dt64-admission-policy` | | [2026-10-31-x2-dt64-admission-policy.md](2026-10-31-x2-dt64-admission-policy.md) |
| 2026-10-31 | `r1-r2il-binding-reading` | | [2026-10-31-r1-r2il-binding-reading.md](2026-10-31-r1-r2il-binding-reading.md) |
| 2026-10-31 | `r1-native-prepared-successor` | | [2026-10-31-r1-native-prepared-successor.md](2026-10-31-r1-native-prepared-successor.md) |
| 2026-10-31 | `ogar-behavior-probe-p2-p3-p9` | | [2026-10-31-ogar-behavior-probe-p2-p3-p9.md](2026-10-31-ogar-behavior-probe-p2-p3-p9.md) |
| 2026-10-31 | `D-SAG-6` | | [2026-10-31-kythe-bridge-ra-facts.md](2026-10-31-kythe-bridge-ra-facts.md) |
| 2026-10-31 | `D-XLOG-NATIVE-6` | | [2026-10-31-d-xlog-native-6-ndarray-simd-execution.md](2026-10-31-d-xlog-native-6-ndarray-simd-execution.md) |
Expand Down
4 changes: 4 additions & 0 deletions crates/lance-graph-r2il/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,7 @@ lance-graph-mask-risc = { path = "../lance-graph-mask-risc" }
# the contract from git `main`; the root `[patch]` maps that to the member.
ogar-loco = { git = "https://github.com/AdaWorldAPI/OGAR", branch = "main" }
ogar-r2il = { git = "https://github.com/AdaWorldAPI/OGAR", branch = "main" }
# The execution-binding seam (`binding`): DT64 resolves and admits a method,
# this crate executes it. Both are zero-dep beyond the contract.
lance-graph-dt64 = { path = "../lance-graph-dt64" }
lance-graph-contract = { path = "../lance-graph-contract" }
Loading
Loading