Skip to content

Commit 68f7add

Browse files
AdaWorldAPIclaude
andauthored
Waves calcified: dispatch maps for every plan; Ghidra plan from real archaeology (#7)
* Core vertical slice: docs/abi.md contract, native/lgj-abi, Java facade Ships the fully verified core of the Panama x ndarray::simd x Valhalla vertical slice (Phases A-E of the mission plan): - docs/abi.md: the normative Rust<->Java ABI contract, written before either side was implemented so both could be checked against one frozen doc instead of each other. - Five new ndarray::simd primitives (eq_u32_to_mask, gt_i32_to_mask, mask_and/mask_or(_assign), masked_sum_i32), added under ndarray's own W1a consumer contract. - native/lgj-abi: the Rust ABI crate. Generation-checked handle registry, generic SoA fixture, bulk kernels routed exclusively through ndarray::simd, 14-symbol extern "C" surface. 72/72 tests green, clippy/fmt clean, and the registry's core safety check was disable-verified (short-circuited, confirmed exactly the two guarding tests go red, restored). - java/: the Panama membrane (internal/ffm, never exposed publicly) and the public semantic facade (NativePattern/View/Predicate/ Pattern/Mask). 132/132 checks green across 8 suites, including a reflection-enforced ApiSurfaceTest that mechanically proves zero FFM types ever reach a public signature, and a LazinessTest that empirically proves the thesis: building a chain costs zero crossings, evaluating it costs exactly one, independent of row count up to 1,000,000. - .claude/: a 6-agent ensemble, 6 knowledge docs, and a full board (LATEST_STATE/STATUS_BOARD/AGENT_LOG/EPIPHANIES/TECH_DEBT/ISSUES/ PR_ARC_INVENTORY/INTEGRATION_PLANS/CODEX_REVIEW_CHECKLIST), all scoped to this repo's actual seams. A mechanical audit (D-LGJ-AUDIT) found and fixed the one real rule violation before this commit: kernels.rs::simd_popcount was calling the internal ndarray::hpc::bitwise path instead of the sanctioned ndarray::simd re-export. Deliberately NOT included: the Valhalla lab (valhalla-lab/) and the Vector API benchmark harness (bench/) — still in flight, tracked as open STATUS_BOARD.md rows, to land in a follow-up PR once reviewed with the same rigor as this slice. Generated by [Claude Code](https://claude.ai/code) * Valhalla lab: three-truths method, causal isolation, 3 real reproducers Completes D-LGJ-F. One experiment source (src/shared/), compiled twice against real JDKs -- stable JDK 26 GA (record) and the official JEP 401 early-access binary (value record) -- via a self-verifying run.sh that mechanically diffs the two Vocab.java files modulo the 'value' keyword before trusting the A/B is honest. Experiments: IdentityExperiment (semantic truth -- is identity actually unobservable), FootprintExperiment (real per-object/array/field bytes via allocation-delta + JOL where available), FfmAddressingExperiment (is the wrapper free where it touches native memory), ThesisExperiment (the mandatory headline: 65,536 rows as one native lane vs hydrated Java objects, on both platforms). Causal isolation via three additional run.sh passes: escape analysis off, and UseArrayFlattening/UseFieldFlattening toggled independently -- isolates which flag actually drives the measured difference rather than inferring it. Three real Valhalla limitations reproduced and filed under reproducers/, none of which changed the production API: - R1: @NullRestricted field on an identity class is a VerifyError (javac's fault -- no source form expresses the required strict-field init order relative to super()) - R2: array flattening has a hard 8-byte payload cliff, confirmed via -XX:+PrintFlatArrayLayout. LaneId/Ordinal/MaskId (<=8B) flatten; RowRange/Row (16B) do not. This turns "Valhalla helps descriptors, not entities" from a hand-wave into a measured VM cutoff -- and RowRange landing on the wrong side is flagged as the one place the expectation was too optimistic. - R3: the densest null-restricted array form is jdk.internal-only and generics erase flattening entirely; Foo! null-restricted type syntax confirmed not to parse, matching the earlier archaeology finding. One real defect found and fixed before landing: IdentityExperiment and the stable Platform called Class::isValue() directly on four vocabulary types with a comment incorrectly claiming it was "final API on JDK 26" -- it does not exist there at all, confirmed by a real javac failure. Fixed by routing every query through Platform.isValueClass(Class<?>), answered honestly per platform. Generated by [Claude Code](https://claude.ai/code) * Vector API bench: real JMH, cross-checked; the crossing does not always win Completes D-LGJ-G, the mission's mandated "where does execution belong" comparison -- measured, not assumed to favor the Rust crossing. Real JMH 1.37 (fork+warmup+compiler-blackholes confirmed in the log, not a hand-rolled loop -- that lives in valhalla-lab and is labelled as such there). Four components, cost kept strictly separate per the mission brief: A_DowncallOverhead (bare crossing, no work), B_SegmentAccess (raw native-memory read throughput), C_ExecutionBoundary (native fused plan vs Java Vector API vs Java scalar, swept 64 to 4,194,304 rows), E_FusionAndPlanning (fused vs unfused vs the scalar reference kernel vs plan-construction-only, swept 1-8 predicates). 50/50 rows, 0 failures. Data.crossCheck() runs in @setup and throws if the three kernels disagree on count or sum, so a faster-but-wrong Vector kernel could not have won the comparison undetected. The headline complicates the thesis honestly: for a single predicate over one native lane, the Java Vector API -- reading the SAME native MemorySegment zero-copy via IntVector.fromMemorySegment, no byte[], no bounce buffer -- beats the native crossing at EVERY row count tested, 56.4x at small sizes down to 1.3-1.4x at 4M rows. A second crossover is also real: native beats a plain Java scalar loop only past roughly 4,096-16,384 rows. Component E shows why this doesn't overturn the project's premise: SIMD-vs-scalar is the largest lever measured anywhere in this suite (10.8x-31.1x, growing with predicate count), and fused/unfused land within this harness's own ~10% noise floor of each other at 65,536 rows -- the fused plan's real value is the structural one-crossing guarantee (already proven by LazinessTest), not a large measured time saving at this scale. Verdict: the crossing is worth paying for composed, multi-predicate work, not for reading one predicate off one lane, where Java on the same memory is simply faster. RESULTS.md was hand-written from the raw CSV, then independently cross-checked against summarise.sh -- a script the same PR ships that mechanically regenerates every table from results/jmh-results.csv, so a re-run's numbers can never silently drift from a hand-transcribed table. Both productions agreed to 3 decimal places on every cell checked. Generated by [Claude Code](https://claude.ai/code) * Phase I synthesis docs + fusion-sweep 256-row re-run + board hygiene Four synthesis docs close D-LGJ-I: architecture.md (the four layers and what each is provably responsible for), panama.md (manifest-over-header, belt-and-braces ownership, restricted-method discipline), valhalla-lab.md (three-truths synthesis, the 8-byte flattening cliff, zero API adoption), execution-boundary.md (the measured crossover picture + three structural hot-path facts, each checked rather than assumed). The fusion sweep was re-run with a 256-row arm after the first pass's 'fusion does nothing' finding proved true only at 65,536 rows: at 256 rows x 8 predicates unfused/fused reaches 2.99x. RESULTS.md is rewritten from jmh-results-merged.csv; TABLES.md is mechanically generated from the same file so the two cannot drift. MultiLaneColumn (ndarray::simd_soa) evaluated for the fixture kernels and declined on two concrete API mismatches (64-byte-multiple constraint, no u32 lane); earmarked for the future 512-byte row-store slice where it fits by construction. Operator layout reference recorded on the board. PR_ARC_INVENTORY backfilled for merged PRs 1-3; the lapse is owned in the file itself. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Pud4qpxFHwqyqDjSabQbs * Board: PR #4 arc entry (post-merge) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Pud4qpxFHwqyqDjSabQbs * SoA row store: 512B rows, 32 facet lanes, ABI minor 2 (W1+W2) The lance-graph-shaped substrate, wired end to end. The flat three-lane fixture was always scaffolding (docs/abi.md 10, architecture.md said so from PR #1); this is the layout the stack actually converges on: 64K x 512-byte rows, 32 facet lanes of 16 bytes = 4-byte LE classid + 12-byte payload, the V3 content-blind facet. Rust (native/lgj-abi): - rowstore.rs: one Arc<[u8]>, two readings (row-major chunks and strided facet columns), zero copies, normative SplitMix64 generator. - LGJ_RESOURCE_ROWSTORE + lgj_rowstore_open; facet lanes described through the UNCHANGED LgjLaneDesc (stride_bytes carried this since minor 1); lgj_op_eq_classid produces ordinary masks that compose with the existing algebra; lgj_row_facet_match writes per-row 32-bit facet sets into a caller-owned buffer via MultiLaneColumn (Arc refcount bump, no copy). - byte_len tightened to the exact covered span (len-1)*stride + elem_bytes: a full-stride final window would let Java bound a segment past the allocation's end on a facet lane. - ABI minor 1 -> 2; docs/abi.md gains 11 and its symbol count is corrected (the 14 was drift; the list already enumerated 15, and the real number is now 18 per nm -D). Gates: cargo test 84/84, clippy -D warnings clean, fmt clean, release build exports 18/18 symbols. Both new kernels are parity-checked against independent scalar references over 10 row counts x 2 seeds x 4 facets x 4 needles, then cross-checked a third way against RowStore::classid_at; a two-sided falsifier proves payload bytes never satisfy a classid match and that a real match does fire. Docs: .claude/plans/lgj-soa-substrate-v1.md (W1-W5 waves) + one plan per consumer example (world-trades / bricks-analytics / graph-traversal), .claude/knowledge/soa-row-store-layout.md, and the board triple ledger. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Pud4qpxFHwqyqDjSabQbs * Board: PR #5 arc entry (post-merge) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Pud4qpxFHwqyqDjSabQbs * Knowledge: assess the archived layout-bridge discussion; name W6 The operator's pre-build ChatGPT discussion is assessed once, in .claude/knowledge/prior-art-and-the-layout-bridge-claim.md, so it is never re-mined or cited naively. Verdict: it converged independently on the architecture this repo then built and measured. Kept: the callability-vs-shared-executable-layout positioning, the schema-key-as- join-point extractable (now the named W6 consideration: an explicit schema/classid field on the descriptors when ClassView lands), and the baseline-dependent claims discipline for W5 comparisons. Pinned: its page-descriptor sketch has no liveness story (the registry's whole job), its native-always-wins assumption is measured false (Component C), and its ndarray paragraph describes upstream crates.io ndarray, not the AdaWorldAPI fork whose ndarray::simd polyfill this stack mandates. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Pud4qpxFHwqyqDjSabQbs * Plans: OGAR Machine (exploratory) + lance-graph-hydrate dependency note Captures the operator's second archived context as .claude/plans/ogar-machine-v1.md — a genuinely new workload for the shipped substrate, not convergent confirmation: one row = one machine STATE, control flow as population masks over 64K execution contexts, Ghidra P-code as the normalized guest ISA (repo attached and cloned), differential migration testing (legacy XOR replacement across 65,536 worlds) as the killer demo, Lance as the time machine. Strong claim vs weak claim separated per the discussion's own discipline; gated on W3 + one W5 example + Ghidra archaeology + a tiny falsifiable probe (P-M1). Also records lance-graph #957 (merged: lance-graph-hydrate, the generic SoA->S3->volume->Lance hydration crate minted for consumers to inherit) and #958 (its open hardening fast-follow) in the substrate plan: when this repo's persistence slice arrives, hydration is inherited from lance-graph-hydrate, never re-derived here. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Pud4qpxFHwqyqDjSabQbs * Board: PR #6 arc entry (post-merge) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Pud4qpxFHwqyqDjSabQbs * Waves calcified: dispatch maps for every plan; Ghidra plan from real archaeology Operator ruling: calcify, don't execute. .claude/waves/ holds one dispatchable map per plan — README with the standing rules and the verbatim worker preamble, substrate W3+W4 (the only READY wave), three consumer waves stamped DO-NOT-DISPATCH, Ghidra G1+G2, and OGAR-Machine P-M1 (BLOCKED behind a 4-condition gate including an explicit operator go). Each map carries disjoint worker scopes, orchestrator-only steps, exact gate commands, disable-runs, and STOP triggers. ghidra-integration-v1.md is written from archaeology against the real clone, not the sketch: 74 P-code opcodes (CPUI_MAX=75), 12.2 DEV / Java 25+, analyzeHeadless entry, and Ghidra's own PcodeEmulator as the reference-implementation parity oracle (the tesseract-rs method). The ogar-machine plan is cross-updated to cite it. Mapping-time catches that would have burned a dispatch: the graph consumer needs a deliberate edge-bearing generator arm (today's payload is PRNG noise) - a substrate change, flagged in the wave; the hop has a real D1a/D1b design fork with ruling guidance recorded. Muscle memory pinned as E-LGJ-CALCIFY-THEN-DISPATCH-1: the eight earned-this-session rules and the plan->wave->shelf->dispatch rhythm. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Pud4qpxFHwqyqDjSabQbs --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
1 parent 8954e53 commit 68f7add

12 files changed

Lines changed: 723 additions & 4 deletions

.claude/board/EPIPHANIES.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,52 @@
44
> `**Status:**`/`**Confidence:**` line. A correction gets its own new,
55
> dated entry that references the one it corrects — the storno rule.
66
7+
## 2026-08-17 — E-LGJ-CALCIFY-THEN-DISPATCH-1
8+
9+
**Status:** DOCTRINE (operator-ruled: "don't execute the consumer plans yet,
10+
just calcify the insights and make sure the muscle memory of the epiphanies
11+
helps to gain momentum"). **Confidence:** High.
12+
13+
The working rhythm this repo now runs on, made explicit so it compounds
14+
instead of being re-derived:
15+
16+
**plan → wave map → (shelf) → dispatch → gates → merge → arc entry**
17+
18+
A *plan* says what and why. A *wave file* (`.claude/waves/`) says exactly
19+
who edits which file under which verbatim guardrails, with which disable-runs
20+
and gate commands — dispatchable as-is, months later, by a session with zero
21+
shared context. Writing the wave map WITHOUT executing it is not deferral;
22+
it is the calcification step: decisions get made while the context is hot
23+
(worker scopes, D1-style design forks, STOP triggers, the graph wave's
24+
discovery that the fixture payload is PRNG noise and traversal needs a
25+
deliberate edge-bearing generator arm — found at MAPPING time, not
26+
mid-dispatch), and execution later starts from momentum instead of from
27+
archaeology.
28+
29+
**The muscle memory, in one list** (each item earned at least once this
30+
session, provenance in the entries below and in PR bodies #1#6):
31+
32+
1. **Disable-run or it didn't happen.** Green tests prove nothing about a
33+
guard; break the thing, watch exactly the right tests go red, restore.
34+
2. **Scaffolding-vs-target check.** When a proposal doesn't fit the code,
35+
ask which one is the placeholder before declining the proposal.
36+
3. **The membrane never grows from the consumer side.** A needed symbol
37+
goes back through the wave process (now stamped in every consumer wave).
38+
4. **Measure before believing direction** — the Vector API beat the
39+
crossing; fusion was noise at 65K rows and 3× at 256; the doc that
40+
assumed otherwise got corrected by the bench, not vice versa.
41+
5. **Independent recomputation over golden blobs** — parity tests
42+
transcribe the generator; two (better: three) independent paths to one
43+
number.
44+
6. **Assessments happen once, on the record** — archived discussions get
45+
one knowledge-doc verdict (kept/pinned-wrong) so they are never
46+
re-mined or cited naively.
47+
7. **Exact-span over round-up** at every boundary a segment can be built
48+
from (the `byte_len` lesson — the difference between a view and an
49+
out-of-bounds capability).
50+
8. **Board in the same commit** as the work it records; arc entry at
51+
merge; realign after every squash.
52+
753
## 2026-08-17 — E-LGJ-THE-MIDDLE-TIER-IS-DELETED-NOT-WRAPPED-1
854

955
**Status:** DOCTRINE (operator-stated, scope confirmed). **Confidence:** High —

.claude/board/LATEST_STATE.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,29 @@
1+
## 2026-08-17 (latest) — waves calcified, Ghidra plan grounded, NOTHING dispatched
2+
3+
Operator ruling: consumer plans are **calcified, not executed** — insights
4+
locked in while hot, execution starts from momentum later. Rhythm now
5+
explicit in `E-LGJ-CALCIFY-THEN-DISPATCH-1` (plan → wave map → shelf →
6+
dispatch → gates → merge → arc).
7+
8+
- **`.claude/waves/`** created: README (standing rules + the verbatim
9+
worker preamble) + six dispatchable wave maps — substrate W3+W4 (the
10+
only one marked READY; W3 is still the next action), three consumer
11+
waves (DO-NOT-DISPATCH), Ghidra G1+G2 (shelved), OGAR-Machine P-M1
12+
(BLOCKED, 4-condition gate incl. explicit operator go).
13+
- **`ghidra-integration-v1.md`** written from REAL archaeology against
14+
the fresh clone (`/workspace/ghidra`, 12.2 DEV, Java 25+): the true
15+
P-code op set is **74 opcodes** (not the sketch's ~13); Ghidra ships
16+
its own sequential `PcodeEmulator` — upgrading the OGAR-Machine oracle
17+
story to reference-implementation parity (the tesseract-rs method);
18+
`Toy` processor = the minimal lift target; `SymbolicSummaryZ3` = the
19+
in-tree precedent for the far-future branch-population direction.
20+
- **Mapping-time discoveries** (the payoff of calcifying): the graph
21+
consumer needs a deliberate edge-bearing generator arm (fixture payload
22+
is PRNG noise) — a substrate change, flagged before anyone hits it
23+
mid-dispatch; the graph hop has a real design fork (D1a Java-side
24+
scatter via WRITABLE mask words / D1b native `lgj_hop`, minor 3) with
25+
ruling guidance recorded.
26+
127
## 2026-08-17 (Slice 2) — the SoA row store is REAL: ABI minor 2, W1+W2 shipped
228

329
**The reframing that started it** (operator, three directives): the flat

.claude/board/PR_ARC_INVENTORY.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,28 @@
88
> anti-pattern the imported board rules name. Backfilled below in one
99
> pass rather than left stale; PR #4 onward gets its entry at merge time.
1010
11+
## PR #6 — layout-bridge assessment, OGAR Machine plan, hydrate note (merged 2026-08-17, squash `8954e53`)
12+
13+
- **Added:** `.claude/knowledge/prior-art-and-the-layout-bridge-claim.md`
14+
(the first archived ChatGPT context assessed once: convergent
15+
confirmation; kept the callability-vs-shared-executable-layout
16+
positioning + the schema-key extractable + the baseline-dependent
17+
claims discipline; pinned its three errors so it is never cited
18+
naively); `.claude/plans/ogar-machine-v1.md` (the second context — a
19+
genuinely NEW workload: population emulation, one row = one machine
20+
state, Ghidra P-code as guest ISA, differential migration testing;
21+
EXPLORATORY, gated on W3 + a W5 example + archaeology + probe P-M1).
22+
- **Locked:** W6 named in the substrate plan (schema/classid field on the
23+
descriptors when ClassView lands); lance-graph #957's
24+
`lance-graph-hydrate` recorded as the INHERITED hydration path — never
25+
re-derived here (#958 is its hardening fast-follow, owned elsewhere).
26+
- **Deferred:** everything in ogar-machine-v1 (named, not scheduled).
27+
- **Docs:** the PR IS docs; `AdaWorldAPI/ghidra` attached + shallow-cloned
28+
at `/workspace/ghidra` for the future P-code archaeology.
29+
- **Confidence:** High for the assessments (checked against shipped code
30+
and measurements); the OGAR Machine plan is explicitly exploratory.
31+
Both bot reviewers hit usage limits and did not run.
32+
1133
## PR #5 — SoA row store: 512B rows, 32 facet lanes, ABI minor 2 (merged 2026-08-17, squash `78aa60e`)
1234

1335
Companion: **AdaWorldAPI/ndarray#279** (W1), merged first — `lgj-abi`'s
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# ghidra-integration-v1 — the P-code front end and the parity oracle
2+
3+
> **Status: PLANNED** (2026-08-17). Companion to `ogar-machine-v1.md`
4+
> that plan owns the population-execution side; this one owns the Ghidra
5+
> side: how a legacy binary becomes a normalized program image, and how
6+
> Ghidra's own emulator becomes the parity oracle. G0 (archaeology) ran
7+
> against the real clone before this plan was written; every path and
8+
> number below is verified, not sketched.
9+
10+
## G0 — archaeology (DONE 2026-08-17, against `/workspace/ghidra` @ `52bb03d`)
11+
12+
`AdaWorldAPI/ghidra` is a fork of upstream at **12.2 DEV**
13+
(`Ghidra/application.properties`), minimum **Java 25**, Gradle ≥ 9.1 —
14+
our JDK 26 toolchain covers it.
15+
16+
| fact | where | why it matters |
17+
|---|---|---|
18+
| **74 real P-code opcodes** (`CPUI_COPY = 1``CPUI_MAX = 75`) | `Ghidra/Features/Decompiler/src/decompile/cpp/opcodes.hh:37-131` | The discussion's ~13-op sketch was a fraction; the real normalized ISA is 74 ops (arithmetic incl. signed/unsigned compares, zext/sext, FLOAT_* family, MULTIEQUAL/INDIRECT decompiler ops). The population interpreter implements a **subset** and must halt loudly on the rest — see op-set discipline below |
19+
| Java-side opcode mirror | `Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeOp.java` | The lift script reads ops through this surface (`Instruction.getPcode()`) |
20+
| Headless entry point | `Ghidra/RuntimeScripts/support/analyzeHeadless` (+ `analyzeHeadlessREADME.md`) | Lifting is a batch job: import binary → analyze → post-script → exit. No GUI anywhere in the loop |
21+
| **In-tree P-code emulator** | `Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/` (`PcodeEmulator`, `PcodeMachine`, `BytesPcodeThread`, `DefaultPcodeThread`…) | **The parity oracle.** Ghidra ships a sequential reference execution of exactly the IR we will population-execute — the same role libtesseract played for tesseract-rs. We never have to *invent* ground truth |
22+
| `Toy` processor | `Ghidra/Processors/Toy` | A teaching ISA with full SLEIGH spec — the smallest possible lift target for G1, before any real x86 binary |
23+
| ~39 processor modules incl. x86, AARCH64, RISCV, JVM, Z80, 68000 | `Ghidra/Processors/` | The "don't implement x86" promise is real: every guest ISA arrives through SLEIGH, and the OGAR Machine sees only the one 74-op IR |
24+
| Symbolic extension precedent | `Ghidra/Extensions/SymbolicSummaryZ3` | The "carry both branch populations" far-future direction has an in-tree symbolic P-code precedent to study first — do not design that from scratch |
25+
26+
License note: Ghidra core is Apache-2.0 (compatible with everything in
27+
this stack); the `GPL/` subtree (demangler etc.) is not needed for
28+
lifting and stays untouched.
29+
30+
## The integration shape — two roles, both offline
31+
32+
```
33+
legacy binary ──analyzeHeadless + post-script──▶ normalized program image (LE file)
34+
35+
┌────────────────────────┤
36+
▼ ▼
37+
Ghidra PcodeEmulator OGAR Machine population path
38+
(sequential ORACLE) (rows = machine states)
39+
│ │
40+
└──────── parity ────────┘
41+
```
42+
43+
1. **Ghidra as lift-time compiler.** Runs ONCE per binary, offline. Never
44+
at OGAR-Machine runtime — the same footing tesseract-rs gives its C++
45+
oracles ("only the oracle's link deps, never in the Rust path").
46+
2. **Ghidra as parity oracle.** `PcodeEmulator` executes the same program
47+
sequentially; the population path must match it bit-for-bit per world.
48+
This is the tesseract-rs byte-parity method transplanted: we diff
49+
against the reference *implementation*, not against our own scalar
50+
rewrite alone (which stays as the second, independent check).
51+
52+
## Waves
53+
54+
| wave | deliverable | falsifier |
55+
|---|---|---|
56+
| **G1 — lift proof** | An `analyzeHeadless` post-script (lands in the ghidra fork, `ghidra_scripts/`) that walks one function of a **Toy**-ISA test binary and dumps every instruction's `getPcode()` sequence to a deterministic text form | The dump's opcode mnemonics/order for that function match the decompiler's own listing view of the same function — two independent Ghidra surfaces agreeing, not one surface trusted |
57+
| **G2 — the image format** | A versioned LE program-image format (header + op records: `opcode u32` + varnode triples `(space u32, offset u64, size u32)` for output/inputs) + a Rust loader in a NEW crate (NOT `lgj-abi` — the membrane stays lean; the loader is a consumer-tier crate) | Round-trip (emit → load → re-emit byte-identical); a hand-built image of known ops decodes to exactly those ops; a truncated/garbled image is refused with a status, never partially loaded |
58+
| **G3 — probe P-M1** (shared gate with `ogar-machine-v1.md`) | Population interpreter over a deliberately small op subset (COPY, INT_ADD/SUB/AND/OR/XOR, INT_EQUAL/LESS, CBRANCH, BRANCH, RETURN), one hand-written program, 64K input worlds over the row store | Bit-identical final states vs an independent scalar Rust interpreter; planted-divergence two-sided test (one known behavioral difference between two program variants → the divergence mask finds exactly that world set) |
59+
| **G4 — oracle parity** | The same image through Ghidra's `PcodeEmulator` (JVM side, sequential, N sampled worlds) vs the population path | Final machine state bit-identical per sampled world; a deliberately mis-implemented op (disable-run) must be CAUGHT by the oracle diff |
60+
61+
Sequencing: G1 is independently startable (pure Ghidra-side, no OGAR
62+
Machine code). G2 depends on G1's real dump shape. G3/G4 are gated
63+
exactly as `ogar-machine-v1.md` gates them (W3 + one W5 example shipped
64+
first). One wave = one PR, gates central, per house style.
65+
66+
## Op-set discipline (the falsifiability rule applied to an ISA)
67+
68+
74 opcodes exist; the interpreter implements a declared subset. Any op
69+
outside the subset must **halt that world loudly** — a per-row status
70+
lane recording "unimplemented op X at cycle N" — never skip, never
71+
best-effort. A world that halted is excluded from parity comparison *and
72+
counted*, so "we handled the corpus" can never silently mean "we skipped
73+
the hard ops." (Same family as tesseract-rs's "a guard that cannot fire
74+
is the defect one level up.")
75+
76+
## Boundaries (so drift is visible)
77+
78+
- **No Ghidra at OGAR-Machine runtime.** Lift-time + oracle-time only.
79+
- **No second object model.** The image loader emits ops + varnodes into
80+
lanes; it does not grow a Rust AST of P-code (the Core-first lesson —
81+
the substrate is the model).
82+
- **Guest RAM stays dense** (ogar-machine-v1 rule 3); varnode SPACE ids
83+
are how register-lane vs memory-page routing is decided at execute
84+
time.
85+
- **Fork discipline:** the lift script and any exporter live in the
86+
`AdaWorldAPI/ghidra` fork; whether G1 *runs* against a fork build or a
87+
released Ghidra distribution is a cost decision made at G1 time (the
88+
fork build needs JDK 25 + Gradle 9.1 — available here, but a release
89+
binary may be cheaper; the fork remains the source of truth either
90+
way).

.claude/plans/ogar-machine-v1.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,16 @@ CALL, RETURN, INT_ADD/SUB/MULT/AND/OR/XOR, …); the OGAR Machine executes
5151
P-code only. `legacy.exe → Ghidra (once) → normalized program image →
5252
population execution`. This is the same shape as the workspace's
5353
ruff→OGAR harvest arms: an existing analyzer becomes the transcoder
54-
front-end, and the substrate executes the normalized IR. First concrete
55-
archaeology step in the fresh clone: locate the P-code opcode enum and
56-
SLEIGH lifting surface, and size the *real* op set (the list above is the
57-
discussion's sketch, not a verified inventory).
54+
front-end, and the substrate executes the normalized IR.
55+
56+
**The archaeology ran — see `ghidra-integration-v1.md` G0** (2026-08-17):
57+
the real op set is **74 opcodes** (`opcodes.hh`, `CPUI_MAX = 75`), not the
58+
sketch's ~13; the fork is 12.2 DEV / Java 25+; `analyzeHeadless` is the
59+
lift entry; and Ghidra ships its own sequential `PcodeEmulator`
60+
(`Ghidra/Framework/Emulation/.../pcode/emu/`) — which upgrades this plan's
61+
oracle story from "our own scalar reference" to "diff against the
62+
reference implementation," the tesseract-rs method. Wave detail, image
63+
format, and op-set discipline live in that companion plan.
5864

5965
## The four load-bearing design rules captured from the discussion
6066

.claude/waves/README.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Execution waves — agent-dispatch maps, one per plan
2+
3+
> A **plan** says what and why; a **wave file** says exactly who edits
4+
> which file, in what order, under which guardrails, and which command
5+
> gates it. A wave file must be dispatchable as-is: the orchestrator
6+
> copies worker briefs out of it verbatim.
7+
8+
| wave file | plan it executes | dispatch state |
9+
|---|---|---|
10+
| `wave-substrate-w3-w4.md` | `lgj-soa-substrate-v1.md` (W3 Java facade, W4 bench) | **W3 READY** — next action |
11+
| `wave-consumer-trades.md` | `consumer-world-trades-v1.md` | gated on W3 |
12+
| `wave-consumer-bricks.md` | `consumer-bricks-analytics-v1.md` | gated on W3 |
13+
| `wave-consumer-graph.md` | `consumer-graph-traversal-v1.md` | gated on W3 (+ possible W6 ABI wave) |
14+
| `wave-ghidra-g1-g2.md` | `ghidra-integration-v1.md` (G1 lift proof, G2 image format) | G1 independently READY |
15+
| `wave-ogar-machine-pm1.md` | `ogar-machine-v1.md` (probe P-M1 / G3+G4) | BLOCKED (gate list inside) |
16+
17+
## Standing rules, inherited by every wave (do not restate per file)
18+
19+
1. **Model policy:** orchestrator + anything synthesizing across sources =
20+
Opus-tier; bounded one-source-in/one-shape-out work = Sonnet workers.
21+
Never Haiku.
22+
2. **Workers never run build tools or git.** No `cargo`, no `javac`, no
23+
`java`, no `git`, no worktrees. Edit-only. The orchestrator compiles,
24+
tests, lints, commits — centrally, once (`agent-cargo-hygiene.md`).
25+
3. **Disjoint file ownership.** Two workers in one file is a lost-write
26+
race. Shared files (module wires, `AllTests`, build scripts, board
27+
files) are ORCHESTRATOR-ONLY and are edited after workers land.
28+
4. **Every worker brief carries the verbatim preamble** (below) plus its
29+
file scope and STOP triggers. A spawn without it is a protocol
30+
violation.
31+
5. **A wave is done when its gate table is green AND its disable-runs ran
32+
red-then-green** — never when workers report success ("completed" is a
33+
process status, not a quality status).
34+
6. **One wave = one PR** (cross-repo waves: one PR per repo, merge order
35+
stated in the wave file). Board updates land in the same commit.
36+
37+
## The verbatim worker preamble (copy into every brief)
38+
39+
```
40+
You are an edit-only worker. HARD RULES:
41+
- Do NOT run cargo, javac, java, git, gradle, or any build/VCS command
42+
— not once. The orchestrator compiles and tests centrally.
43+
- Do NOT create worktrees, branches, or commits.
44+
- Edit ONLY the files listed under YOUR SCOPE. Files under OTHER AGENTS
45+
or ORCHESTRATOR-ONLY must not be touched, even for a one-line import.
46+
- Do not claim code compiles or tests pass — you did not run them.
47+
Report what you WROTE, plus anything you could not resolve.
48+
- Read .claude/board/LATEST_STATE.md and the knowledge docs named in
49+
your brief BEFORE writing. Do not write to any board file.
50+
- STOP and report instead of improvising if: a symbol/type you need does
51+
not exist; the contract (docs/abi.md) seems to disagree with code; or
52+
your task requires touching a file outside your scope.
53+
```

0 commit comments

Comments
 (0)