Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
546b17a
Core vertical slice: docs/abi.md contract, native/lgj-abi, Java facade
AdaWorldAPI Aug 17, 2026
a1e65cc
Merge main (bootstrap commit) into dev branch to establish shared his…
AdaWorldAPI Aug 17, 2026
bdd40b2
Valhalla lab: three-truths method, causal isolation, 3 real reproducers
AdaWorldAPI Aug 17, 2026
317ee83
Merge main (squash of PR #1) to fix history divergence for PR #2
AdaWorldAPI Aug 17, 2026
ea4753c
Merge main (squash of PR #2) to keep dev branch history aligned
AdaWorldAPI Aug 17, 2026
4ab0da8
Vector API bench: real JMH, cross-checked; the crossing does not alwa…
AdaWorldAPI Aug 17, 2026
96eccff
Merge main (squash of PR #3) to keep dev branch history aligned
AdaWorldAPI Aug 17, 2026
cd7782b
Phase I synthesis docs + fusion-sweep 256-row re-run + board hygiene
AdaWorldAPI Aug 17, 2026
ddeaecb
Merge main (squash of PR #4) to keep dev branch history aligned
AdaWorldAPI Aug 17, 2026
e4463cb
Board: PR #4 arc entry (post-merge)
AdaWorldAPI Aug 17, 2026
e8b6dc2
SoA row store: 512B rows, 32 facet lanes, ABI minor 2 (W1+W2)
AdaWorldAPI Aug 17, 2026
aff72f3
Merge main (squash of PR #5) to keep dev branch history aligned
AdaWorldAPI Aug 17, 2026
8fceaca
Board: PR #5 arc entry (post-merge)
AdaWorldAPI Aug 17, 2026
91e22f3
Knowledge: assess the archived layout-bridge discussion; name W6
AdaWorldAPI Aug 17, 2026
6927961
Plans: OGAR Machine (exploratory) + lance-graph-hydrate dependency note
AdaWorldAPI Aug 17, 2026
7e218ed
Merge main (squash of PR #6) to keep dev branch history aligned
AdaWorldAPI Aug 17, 2026
9cc85ad
Board: PR #6 arc entry (post-merge)
AdaWorldAPI Aug 17, 2026
f3bbab7
Waves calcified: dispatch maps for every plan; Ghidra plan from real …
AdaWorldAPI Aug 17, 2026
dac0f0b
Merge main (squash of PR #7) to keep dev branch history aligned
AdaWorldAPI Aug 17, 2026
57a7e48
Board: PR #7 arc entry (post-merge)
AdaWorldAPI Aug 17, 2026
83b8994
Plan: lance-graph #958 merged (was open at last check)
AdaWorldAPI Aug 17, 2026
572f865
Java RowStore facade: W3 shipped (185/185, one bug found+fixed)
AdaWorldAPI Aug 17, 2026
b138382
Merge main (squash of PR #8) to keep dev branch history aligned
AdaWorldAPI Aug 17, 2026
bc6f91a
Board: PR #8 arc entry (post-merge)
AdaWorldAPI Aug 17, 2026
619b3de
Plan: r2sleigh recorded as third lift path + decompiler candidate
AdaWorldAPI Aug 17, 2026
84e73ca
Bench Component F: the boundary re-asked on the real row-store layout…
AdaWorldAPI Aug 17, 2026
897fd92
Merge main (squash of PR #9) to keep dev branch history aligned
AdaWorldAPI Aug 17, 2026
201a626
Board: PR #9 arc entry (post-merge)
AdaWorldAPI Aug 17, 2026
ed4fb7c
Parity: the third independent read path (ROW_LAYOUT segment reads)
AdaWorldAPI Aug 17, 2026
6c4afe4
Merge main (squash of PR #10) to keep dev branch history aligned
AdaWorldAPI Aug 17, 2026
9717cda
Board: PR #10 arc entry (post-merge)
AdaWorldAPI Aug 17, 2026
8eecb3b
Consumer example: World/Trades — the zero-object fluent domain API (W5a)
AdaWorldAPI Aug 17, 2026
74e5b6b
Merge main (squash of PR #11) to keep dev branch history aligned
AdaWorldAPI Aug 17, 2026
9088dc9
Board: PR #11 arc entry (post-merge)
AdaWorldAPI Aug 17, 2026
f7974da
Consumer example: Bricks — mask-first authorization, fail-closed, agg…
AdaWorldAPI Aug 17, 2026
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
28 changes: 28 additions & 0 deletions .claude/board/LATEST_STATE.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,31 @@
## 2026-08-17 (dispatch 4) — W5b bricks shipped: authorization IS a mask, measured

`wave-consumer-bricks.md` executed (2 Sonnet workers K1/K2, disjoint main/test
scopes, orchestrator-gated). `consumers/bricks/` is the second consumer proof:
**RBAC as a natively-evaluated predicate in the same lazy chain as `where(...)`**
— `Role.EU_ONLY` folds `REGION.eq(EU)` into the plan, `DENY_ALL` is a real
impossible predicate (`REGION.eq(0xFFFF)`) that pays a real crossing and counts
0, and an unauthorized chain throws `UnauthorizedQueryException` BEFORE any
native crossing (fail-closed; no default-allow path exists in the package).
Aggregate-only egress is structural: every public method returns
`BricksQuery`/`long`/`Map` — no row-shaped public type exists to leak.

- **BricksAuthTest 62/62.** Parity vs the transcribed generator at 1K+64K rows;
EU_ONLY == GLOBAL+explicit-where equivalence; crossing arithmetic measured.
- **A real finding, not just a green suite: a sum terminal costs 2 crossings**
(plan evaluation into the mask + `lgj_reduce_sum_i32`), unlike `count()`
whose plan eval returns the count and pays 1. `sumBy()` therefore measures
**32 crossings (16 groups × 2) — IDENTICAL at 1K and 64K rows**, which is the
thesis (crossings ∝ groups, never rows). K1's Javadoc claimed "one crossing
per group"; the measurement corrected the doc, not the other way round.
- **Disable-run:** `requireAuthorized` short-circuited → exactly the 3
can-fire fail-closed checks red (59 green), restored, 62/62. Core suite
untouched at 188/188.
- Board-hygiene note, owned: W5a (trades, PR #11) shipped without a
LATEST_STATE entry — STATUS_BOARD D-LGJ-W5 carried it; both consumers are
now recorded there in full. W5c (graph) stays SHELVED on the D1 ruling +
the edge-bearing generator substrate change.

## 2026-08-17 (dispatch 2) — W4 measured: the boundary re-asked on the REAL layout

`wave-substrate-w3-w4.md` Dispatch 2 executed: one Sonnet worker
Expand Down
19 changes: 19 additions & 0 deletions .claude/board/PR_ARC_INVENTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,25 @@
> anti-pattern the imported board rules name. Backfilled below in one
> pass rather than left stale; PR #4 onward gets its entry at merge time.

## PR #11 — consumer example: World/Trades (merged 2026-08-17, squash `db7bdf1`)

- **Added:** `consumers/trades/` — own compile unit, core consumed as a
third party would. `Trade` (schema-not-entity, reflection-proven),
`World.open` → the existing lazy `View` under domain names;
`TradesParityTest` 12/12, `TradesAllocationTest` 3/3.
- **Locked:** the poster's number, measured — **240 bytes allocated per
query, IDENTICAL at 64K and 1M rows** (row-count independence is the
thesis assertion); 0 crossings composing / 1 at terminal, through the
domain vocabulary; the membrane's own `LANE_KIND_MISMATCH` catches a
misbound schema (disable-run green-red-green). Zero new membrane
surface, zero core changes — the consumer iron rule held on its first
real test.
- **Deferred:** QUANTITY (honestly absent — arrives with ClassView/W6);
bricks + graph consumer waves (still shelved).
- **Docs:** STATUS_BOARD W5 row → trades DONE.
- **Confidence:** High — every falsifier two-sided or anti-vacuity
guarded; no `java/` file changed. Bot reviewers at usage limits.

## PR #10 — third parity read path + R2IL handoff boundary (merged 2026-08-17, squash `4114c4e`)

- **Added:** `RowStoreParityTest` section reading all 32,000 classids of a
Expand Down
2 changes: 1 addition & 1 deletion .claude/board/STATUS_BOARD.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,4 @@ layout wired end to end. Doctrine: `E-LGJ-THE-MIDDLE-TIER-IS-DELETED-NOT-WRAPPED
| D-LGJ-W2 | lgj-abi row store: `rowstore.rs`, `LGJ_RESOURCE_ROWSTORE`, `lgj_rowstore_open`, strided facet lanes through the unchanged `LgjLaneDesc`, `lgj_op_eq_classid`, `lgj_row_facet_match`, ABI minor 1→2, `docs/abi.md` §11 | **DONE 2026-08-17** — `cargo test` **84/84**, clippy/fmt clean, release build exports **18/18** symbols (`nm -D`). Parity: both kernels vs independent scalar references over 10 row counts × 2 seeds × 4 facets × 4 needles, cross-checked a THIRD way against `RowStore::classid_at`. Two-sided payload-vs-classid falsifier. End-to-end membrane test covers describe → predicate → mask algebra → count → facet-match → lifecycle |
| D-LGJ-W3 | Java `RowStore` facade: structured `MemoryLayout`, minor-≥2 gate, `FacetMatchView`, parity test transcribing the generator | **DONE 2026-08-17** — dispatched per `.claude/waves/wave-substrate-w3-w4.md` (3 Sonnet workers, disjoint scopes: FFM membrane extension / public facade / tests), orchestrator-integrated. `javac -Xlint:all` clean (same 7 pre-existing `[restricted]` warnings, zero new). `AllTests` **185/185** (was 132; +53 new checks: 29 parity + 24 lifetime). **One real bug caught by the suite and fixed**: `FacetMatchView.rowCount()` was missing the closed-store guard `matchesOf`/`cardinality` both had — a stale row count was readable after the owning store closed. Fixed, re-verified. Both mandated disable-runs ran red-then-green: (1) `Abi.requireMinor` inflated by 1 → exactly `RowStoreParityTest`+`RowStoreLifetimeTest` failed, all 8 other suites stayed green; (2) the pure-Java generator's a/b draw order swapped in `RowStoreParityTest` → exactly that suite broke (17/29), `RowStoreLifetimeTest` (generator-independent) stayed green — confirming the parity test is a real falsifier, not decorative. `Mask.source()` retyped `NativePattern → NativeResource` (new interface) so a `Mask` can parent onto either a `NativePattern` or a `RowStore` — zero call-site breakage (verified: no existing caller bound the narrower type) |
| D-LGJ-W4 | Bench Component F: Vector API facet scan vs the crossing, on the REAL layout | **DONE 2026-08-17** — 1 Sonnet worker (F_RowStoreFacetScan + RowStoreData + Kernels facet-match arms, cross-check-in-@Setup discipline), orchestrator-run JMH: 9/9 combos, cross-checks green at every row count. **Finding: Component C's direction survives, its margin collapses** — Vector API wins the 32-facet strided scan at every row count but by 2.51×/1.92×/1.14× (4K/65K/1M rows) vs C's 56×; at 512 MiB traversed all three arms converge on memory bandwidth. Native arm's per-call allocation asymmetry disclosed in §F with a named follow-up (`facetMatchesInto`), not hidden. summarise.sh extended with the F table (and the old 'E/F' section retitled 'E' — a real naming collision); tables regenerated from the merged CSV |
| D-LGJ-W5 | Three consumer examples (trades / bricks / graph) — one plan file each | **trades DONE 2026-08-17** — `consumers/trades/` (own compile unit, core consumed as a third-party would): `Trade` (schema-not-entity: zero public ctors, zero instance fields, reflection-forced construction still throws), `World.open` → the existing lazy `View` under domain names, zero new membrane surface. TradesParityTest 12/12 (chain vs transcribed-generator recomputation at 1K+64K rows; 0 crossings composing / 1 at terminal THROUGH the domain vocabulary; reflection guard). TradesAllocationTest 3/3 — **the poster's number, measured: 240 bytes/query, IDENTICAL at 64K and 1M rows** (row-count independence is the thesis assertion; 64 KiB absolute backstop). Disable-run: VENUE pointed at the wrong lane → the membrane's own LANE_KIND_MISMATCH rejected it (the binding is checked, not trusted); restored green. bricks + graph still shelved |
| D-LGJ-W5 | Three consumer examples (trades / bricks / graph) — one plan file each | **trades DONE 2026-08-17** — `consumers/trades/` (own compile unit, core consumed as a third-party would): `Trade` (schema-not-entity: zero public ctors, zero instance fields, reflection-forced construction still throws), `World.open` → the existing lazy `View` under domain names, zero new membrane surface. TradesParityTest 12/12 (chain vs transcribed-generator recomputation at 1K+64K rows; 0 crossings composing / 1 at terminal THROUGH the domain vocabulary; reflection guard). TradesAllocationTest 3/3 — **the poster's number, measured: 240 bytes/query, IDENTICAL at 64K and 1M rows** (row-count independence is the thesis assertion; 64 KiB absolute backstop). Disable-run: VENUE pointed at the wrong lane → the membrane's own LANE_KIND_MISMATCH rejected it (the binding is checked, not trusted); restored green. **bricks DONE 2026-08-17** — `consumers/bricks/` (2 Sonnet workers K1/K2 per `.claude/waves/wave-consumer-bricks.md`): mask-first RBAC where `authorize(Role)` is a real natively-evaluated predicate in the SAME lazy chain as `where(...)` (`Role.EU_ONLY` = `REGION.eq(EU)`, `DENY_ALL` = `REGION.eq(0xFFFF)` — a genuine impossible predicate, not a Java branch), fail-closed (`UnauthorizedQueryException` BEFORE any crossing; no default-allow path exists), aggregate-only egress (every public method returns `BricksQuery`/`long`/`Map` — structurally no row-shaped type). BricksAuthTest **62/62**: parity vs transcribed generator at 1K+64K; RBAC-as-predicate equivalence (EU_ONLY result == GLOBAL+explicit-where); DENY_ALL counts 0 while paying a real crossing; crossing arithmetic — count()=1, sumBy()=**32 crossings (16 groups × 2: plan_eval + lgj_reduce_sum_i32), IDENTICAL at both row counts** (the thesis: crossings ∝ groups, never rows — the measured 32 corrected K1's "1 per group" Javadoc claim, a real finding about sum-terminal cost); reflection guards. Disable-run: `requireAuthorized` short-circuited → **exactly the 3 can-fire fail-closed checks red, 59 green**; restored, 62/62. Core suite unaffected (188/188). graph still shelved on the D1 ruling + edge-generator substrate change |
42 changes: 42 additions & 0 deletions consumers/bricks/src/main/java/com/adaworldapi/bricks/Bricks.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.adaworldapi.bricks;

import com.adaworldapi.lancegraph.NativePattern;

/**
* Entry point for the OGAR-Bricks poster example: security as a mask composed <em>before</em>
* execution, never a post-filter over already-fetched data.
*
* <h2>Zero membrane growth — the iron rule</h2>
*
* <p>This package adds no ABI symbol, no native call, no query engine, and no row-shaped public
* type. It consumes {@code com.adaworldapi.lancegraph} exactly as a third-party developer would:
* {@link NativePattern#open} to obtain rows, {@link NativePattern#view()} (via {@link
* BricksSession#query()}) to obtain the lazy description, {@link
* com.adaworldapi.lancegraph.View#where} (via {@link BricksQuery#where} and {@link
* BricksQuery#authorize}) to narrow it.
*
* <pre>{@code
* try (var session = Bricks.open(1_000_000, Bricks.DEFAULT_SEED)) {
* long euOrderCount = session.query()
* .authorize(Role.EU_ONLY)
* .where(Orders.REVENUE.gt(0))
* .count();
* }
* }</pre>
*/
public final class Bricks {

private Bricks() {}

/** The default seed, re-exported from {@link NativePattern#DEFAULT_SEED}. */
public static final long DEFAULT_SEED = NativePattern.DEFAULT_SEED;

/**
* Open {@code nRows} orders generated deterministically from {@code seed}.
*
* @throws IllegalArgumentException if {@code nRows} is negative
*/
public static BricksSession open(long nRows, long seed) {
return new BricksSession(NativePattern.open(nRows, seed));
}
}
144 changes: 144 additions & 0 deletions consumers/bricks/src/main/java/com/adaworldapi/bricks/BricksQuery.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
package com.adaworldapi.bricks;

import com.adaworldapi.lancegraph.Predicate;
import com.adaworldapi.lancegraph.View;

import java.util.LinkedHashMap;
import java.util.Map;

/**
* An immutable, lazy, fail-closed description of a set of order rows.
*
* <h2>Security is a mask composed before execution, never a post-filter</h2>
*
* <p>{@link #where} and {@link #authorize(Role)} are the <em>same kind of operation</em>: both
* return a new {@code BricksQuery} wrapping a {@link View} narrowed by one more {@link Predicate},
* via {@link View#where}. There is no separate enforcement pass here that fetches rows and then
* throws some away — authorization is a predicate in the identical lazy chain a caller's own {@code
* where(...)} conditions live in, fused into the same single native crossing a terminal operation
* pays for. See {@link Role} for how a role's constraint is itself a real, natively-evaluated
* predicate rather than a Java-side branch.
*
* <h2>Fail-closed</h2>
*
* <p>Every terminal operation ({@link #count()}, {@link #sum}, {@link #sumBy}) checks first whether
* {@link #authorize(Role)} was ever called on this chain, and throws {@link
* UnauthorizedQueryException} if not — <strong>before</strong> touching the native side at all.
* There is no default-allow path: a chain that never called {@code authorize} cannot be executed,
* full stop.
*
* <h2>Aggregate-only egress</h2>
*
* <p>Every public method on this class returns exactly one of {@code BricksQuery}, {@code long}, or
* {@code Map<Integer, Long>}. No method here returns, nor could return, anything row-shaped — there
* is no public type in this consumer that represents a single order. A caller can learn how many
* rows matched, or a sum, or a per-group breakdown of sums; a caller cannot ever get a row, a field
* value from one row, or an iterator over rows. That is a structural fact about this package's
* public surface, not a convention someone could accidentally violate from outside it.
*/
public final class BricksQuery {

private final View view;
private final boolean authorized;

BricksQuery(View view, boolean authorized) {
this.view = view;
this.authorized = authorized;
}

/**
* A new query narrowed by one more condition.
*
* <p>Crosses the membrane zero times, exactly as {@link View#where} does not. Does not affect
* authorization: a chain built entirely from {@code where(...)} calls is still unauthorized
* until {@link #authorize(Role)} is called.
*/
public BricksQuery where(Predicate predicate) {
return new BricksQuery(view.where(predicate), authorized);
}

/**
* A new query, authorized under {@code role}.
*
* <p>If {@code role} carries an additional constraint (see {@link Role}), it is folded into the
* same lazy chain via {@link View#where} — a real predicate, composed once, evaluated natively
* alongside everything else. This is the single point in this package where a role's constraint
* enters the chain; there is no other place authorization happens.
*/
public BricksQuery authorize(Role role) {
java.util.Objects.requireNonNull(role, "role");
View next = role.constraint().map(view::where).orElse(view);
return new BricksQuery(next, true);
}

/**
* How many rows this query selects.
*
* @throws UnauthorizedQueryException if {@link #authorize(Role)} was never called on this chain
*/
public long count() {
requireAuthorized("count()");
return view.count();
}

/**
* Sum a signed 32-bit column over the rows this query selects.
*
* @throws UnauthorizedQueryException if {@link #authorize(Role)} was never called on this chain
*/
public long sum(com.adaworldapi.lancegraph.I32Field field) {
requireAuthorized("sum()");
return view.sumOf(field);
}

/**
* Sum {@code value} grouped by every possible value of {@code group}.
*
* <p>{@code group} is a {@link com.adaworldapi.lancegraph.U32Field}, and this consumer's fixture
* gives such fields exactly 16 distinct values ({@code 0..15}) — see {@link Orders#REGION}. This
* method issues one fused native query per group value (16 total), each narrowing this query's
* already-authorized chain by one more {@code group.eq(v)} condition and summing {@code value}
* over the result. Each of those 16 sums costs <em>two</em> native crossings — plan evaluation
* into the selection mask, then the {@code lgj_reduce_sum_i32} reduction — so the measured
* total is 32 crossings (unlike {@link #count()}, whose plan evaluation returns the count and
* pays one). <strong>The crossing count scales with the number of groups, never with the
* number of rows</strong> — the same laziness guarantee every other terminal operation in this
* codebase carries, just paid per group instead of once.
*
* <p>Every group value {@code 0..15} appears as a key in the returned map, including groups with
* zero matching rows (mapped to a sum of {@code 0L}): a group's absence from a real dataset is
* itself a legitimate aggregate fact, not something to hide by omitting the key.
*
* <p>If measurement ever shows this 32-crossing loop is a bottleneck, a native grouped-aggregate
* kernel (one crossing, sixteen output buckets) is the natural W6-tier follow-up — not built
* here, because nothing has measured a need for it yet.
*
* @throws UnauthorizedQueryException if {@link #authorize(Role)} was never called on this chain
*/
public Map<Integer, Long> sumBy(
com.adaworldapi.lancegraph.U32Field group, com.adaworldapi.lancegraph.I32Field value) {
requireAuthorized("sumBy()");
java.util.Objects.requireNonNull(group, "group");
java.util.Objects.requireNonNull(value, "value");
Map<Integer, Long> result = new LinkedHashMap<>(16);
for (int v = 0; v < 16; v++) {
result.put(v, view.where(group.eq(v)).sumOf(value));
}
return result;
}

private void requireAuthorized(String what) {
if (!authorized) {
throw new UnauthorizedQueryException(
what + " was called on a query that was never authorize()'d. This is fail-closed"
+ " by design: a missing role mask never falls back to emitting"
+ " everything. Call .authorize(Role.GLOBAL) (or a narrower role) before"
+ " any terminal operation, even when every row should be visible.");
}
}

@Override
public String toString() {
return "BricksQuery[" + (authorized ? "authorized" : "UNAUTHORIZED") + ", " + view + "]";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.adaworldapi.bricks;

import com.adaworldapi.lancegraph.NativePattern;

/**
* A set of order rows held natively, opened once and closed once — the RBAC-poster sibling of
* {@code com.adaworldapi.trades.World}.
*
* <p>Owns exactly one {@link NativePattern}. {@link #query()} returns a fresh, <strong>unauthorized
* </strong> {@link BricksQuery} over that pattern's rows every time it is called — see {@link
* BricksQuery} for why every chain must call {@code authorize(...)} before any terminal operation,
* with no exceptions for {@link Role#GLOBAL}.
*
* <pre>{@code
* try (var session = Bricks.open(1_000_000, Bricks.DEFAULT_SEED)) {
* long euRevenue = session.query()
* .authorize(Role.EU_ONLY)
* .sum(Orders.REVENUE);
* }
* }</pre>
*/
public final class BricksSession implements AutoCloseable {

private final NativePattern pattern;

BricksSession(NativePattern pattern) {
this.pattern = pattern;
}

/**
* A fresh, unauthorized query over every row in this session.
*
* <p>Crosses the membrane zero times to build, exactly as {@link NativePattern#view()} does not.
* The returned query throws {@link UnauthorizedQueryException} from any terminal operation until
* {@link BricksQuery#authorize(Role)} is called on it.
*/
public BricksQuery query() {
return new BricksQuery(pattern.view(), false);
}

/**
* Release the native storage.
*
* <p>Delegates directly to {@link NativePattern#close()} — same double-close semantics: a second
* call throws {@link com.adaworldapi.lancegraph.ClosedResourceException} rather than being a
* silent no-op, and every {@link BricksQuery} derived from this session's rows stops working
* once this returns.
*/
@Override
public void close() {
pattern.close();
}
}
Loading