Skip to content

Commit 6ca9c29

Browse files
committed
P0 implemented: JDK 28 + Valhalla value classes + Panama, 409 checks green
The six vocabulary types (LaneId, MaskId, Ordinal, FacetId, RowRange, WideFieldMask) are now public value record; bench/run.sh pins jdk-28+16 and carries --release 28 --enable-preview. Measured on Temurin 28+16-ea: the full gate suite is 409 checks / 0 failures both unflipped and flipped, all six types report isValue() == true at runtime, and FFM stays final and flag-free. Also fixes a cross-repo break that blocked the build entirely: lance-graph's mask_risc::ExecError gained RangeOutOfBounds with the Pred::Range work and exec_error_to_status maps it exhaustively, so lgj-abi did not compile against lance-graph main. The new variant joins the documented bug-in-this-file family and the doc enumeration is extended with it. Native suite: 187 tests green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HScwwezRdMxFfTs3WLG19d
1 parent 6a5ccda commit 6ca9c29

11 files changed

Lines changed: 68 additions & 19 deletions

File tree

‎.claude/board/EPIPHANIES.md‎

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,44 @@
1+
## E-THE-VALHALLA-FLIP-COST-ONE-WORD-SIX-TIMES-AND-THE-GATES-DID-NOT-MOVE-1 (2026-09-19)
2+
3+
**Finding, measured end to end today.** The P0 mandate (JDK 28 + Valhalla +
4+
Panama) is implemented. `/opt/jdks/jdk-28+16`, Temurin `28+16-ea`.
5+
6+
| arm | result |
7+
|---|---|
8+
| JDK 28, sources unchanged, no preview | compiles; **409 checks, 0 failed** |
9+
| JDK 28, six types `public value record`, `--enable-preview` | compiles; **409 checks, 0 failed** |
10+
| `isValue()` on all six at runtime | **true**; `new LaneId(7) == new LaneId(7)` → true |
11+
| FFM without any preview flag | final; `SysVx64Linker`; `MemorySegment` round-trip clean |
12+
13+
**The lab's central claim is now demonstrated on production sources:** the
14+
migration is one word, six times, and the gate suite does not move. It was
15+
previously proven only on `valhalla-lab`'s parallel vocabulary.
16+
17+
**A cross-repo break found on the way, and it was blocking everything.**
18+
`lgj-abi` did not compile against current lance-graph `main`:
19+
`mask_risc::ExecError` gained `RangeOutOfBounds { lo, hi, n_rows }` with the
20+
`Pred::Range` work, and `exports.rs`'s `exec_error_to_status` maps `ExecError`
21+
**exhaustively**. Fixed by joining the documented *"would be a bug in THIS
22+
file, not in a caller's plan"* family (`LGJ_ERR_ALLOCATION_FAILED`) — the ABI
23+
lowering emits no `Pred::Range`, exactly as it emits no sum terminal and no
24+
blend — and the doc comment's enumeration was extended so the list stays
25+
exhaustive in prose too. **An exhaustive match across a repo boundary is a
26+
tripwire, not a safety net:** it converts an upstream additive change into a
27+
downstream build failure, and nothing in either repo's CI noticed, because no
28+
CI job here compiles the native crate.
29+
30+
**Two false failures I nearly reported, both mine, both caught by reading.**
31+
The flipped suite showed `1 FAILED` twice. Both times it was
32+
`DoctrineFenceTest` refusing to run — it requires `java/src/main/java` AND
33+
`native/lgj-abi/src` relative to CWD, and I had run the flipped build from a
34+
scratch directory that had neither. The fence is *correct*: its first check is
35+
*"a fence that cannot find its corpus must fail, not skip."* Running the flip
36+
in the real tree (committed first, restored with `git checkout`) gave the true
37+
result. **A harness artifact and a regression look identical in a summary
38+
line** — this is the ruff trap "a disable that does not apply is
39+
indistinguishable from a guard that is not load-bearing", met from the other
40+
direction.
41+
142
## E-VALHALLA-IS-THE-STORAGE-MEMBRANE-AND-IT-IS-MANDATORY-1 (2026-09-19)
243

344
**Operator ruling.** *LGJ MUST use JDK 28 and MUST use Valhalla and Panama.*

‎CLAUDE.md‎

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,16 @@ indistinguishable from owning the thing.
6161
Value classes change how cheaply a descriptor is carried; they never make a
6262
population crossable.
6363

64-
**Status: MANDATED, NOT YET IMPLEMENTED.** No JDK 28 is installed in the
65-
current container (`/opt/jdks` does not exist; system `java` is 21.0.10) and
66-
`jdk.java.net` is blocked by the egress proxy, so the migration cannot be
67-
executed from here. Tracked with its concrete steps in
68-
`.claude/board/ISSUES.md` `ISS-LGJ-TOOLCHAIN-MUST-BE-JDK28-VALHALLA-PANAMA`.
69-
A session that finds a JDK 28 available does the migration; a session that
70-
does not must not restate the superseded JDK-26 framing as current.
64+
**Status: IMPLEMENTED 2026-09-19, gates green.** `/opt/jdks/jdk-28+16`
65+
(Temurin `28+16-ea`). The six vocabulary types — `LaneId`, `MaskId`,
66+
`Ordinal`, `FacetId`, `RowRange`, `WideFieldMask` — are `public value record`,
67+
all report `isValue() == true` at runtime, and the full suite is **409 checks,
68+
0 failures** under `--release 28 --enable-preview` against a freshly built
69+
`liblgj_abi.so` (abi 0.11, `ndarray::simd avx512`) — byte-for-byte the same
70+
409 as the unflipped baseline on the same JDK. `bench/run.sh` carries the pin
71+
and the flag. Measured facts and the obtain route (most JDK hosts are
72+
egress-blocked here; GitHub release *download* paths are not) live in
73+
`.claude/knowledge/jdk-toolchain-facts.md`.
7174

7275
## The mask-native invariant (operator-ruled, 2026-08-18)
7376

‎bench/run.sh‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ set -euo pipefail
1010
BENCH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
1111
ROOT="$(cd "$BENCH/.." && pwd)"
1212

13-
JDK="${JDK:-/opt/jdks/jdk-26.0.2}"
13+
JDK="${JDK:-/opt/jdks/jdk-28+16}" # P0: JDK 28 + Valhalla (JEP 401, preview) + Panama FFM
1414
LIB_DIR="${LIB_DIR:-$ROOT/target/release}"
1515

1616
OUT="$BENCH/out"
@@ -29,13 +29,14 @@ fi
2929

3030
echo "== compiling the library API"
3131
rm -rf "$OUT"; mkdir -p "$OUT/api" "$OUT/bench" "$BENCH/results"
32-
"$JDK/bin/javac" -d "$OUT/api" $(find "$ROOT/java/src/main/java" -name '*.java')
32+
"$JDK/bin/javac" --release 28 --enable-preview -d "$OUT/api" $(find "$ROOT/java/src/main/java" -name '*.java')
3333

3434
echo "== compiling the benchmarks (annotation processing ON: JMH needs it to emit BenchmarkList)"
3535
# -proc:full is REQUIRED on JDK 23+: annotation processing is off by default there, and without it
3636
# JMH's generator never runs and the harness fails at startup with "Unable to find the resource:
3737
# /META-INF/BenchmarkList" — which looks like a classpath problem and is not one.
3838
"$JDK/bin/javac" \
39+
--release 28 --enable-preview \
3940
-proc:full \
4041
--add-modules jdk.incubator.vector \
4142
-cp "$CP_LIBS$OUT/api" \
@@ -45,6 +46,7 @@ echo "== compiling the benchmarks (annotation processing ON: JMH needs it to emi
4546
echo "== running"
4647
cd "$BENCH"
4748
JAVA_TOOL_OPTIONS= "$JDK/bin/java" \
49+
--enable-preview \
4850
--enable-native-access=ALL-UNNAMED \
4951
--add-modules jdk.incubator.vector \
5052
-Dstdout.encoding=UTF-8 \

‎java/src/main/java/com/adaworldapi/lancegraph/FacetId.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
*
1919
* @param index zero-based facet index, {@code 0..32} exclusive (a row has exactly 32 facets)
2020
*/
21-
public record FacetId(int index) {
21+
public value record FacetId(int index) {
2222

2323
/** Facets per row — 32, the canonical `32 × 16 B = 512 B` row (abi.md §11). */
2424
public static final int COUNT = 32;

‎java/src/main/java/com/adaworldapi/lancegraph/LaneId.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
*
2929
* @param index zero-based lane index within its resource
3030
*/
31-
public record LaneId(int index) {
31+
public value record LaneId(int index) {
3232

3333
public LaneId {
3434
if (index < 0) {

‎java/src/main/java/com/adaworldapi/lancegraph/MaskId.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
*
2121
* @param token the opaque registry token
2222
*/
23-
public record MaskId(long token) {
23+
public value record MaskId(long token) {
2424

2525
/** Which registry slot. Diagnostic only. */
2626
public int slot() {

‎java/src/main/java/com/adaworldapi/lancegraph/Ordinal.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
*
1717
* @param value zero-based position
1818
*/
19-
public record Ordinal(int value) {
19+
public value record Ordinal(int value) {
2020

2121
public Ordinal {
2222
if (value < 0) {

‎java/src/main/java/com/adaworldapi/lancegraph/RowRange.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
* @param start first row, inclusive
1717
* @param endExclusive one past the last row
1818
*/
19-
public record RowRange(long start, long endExclusive) {
19+
public value record RowRange(long start, long endExclusive) {
2020

2121
public RowRange {
2222
if (start < 0) {

‎java/src/main/java/com/adaworldapi/lancegraph/WideFieldMask.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
* when their bits are equal, never because they are the same object — nothing here relies on
3232
* reference equality, so flattening changes no observable behaviour.
3333
*/
34-
public record WideFieldMask(long value) {
34+
public value record WideFieldMask(long value) {
3535

3636
private static final int FACET_COUNT = 32;
3737

‎native/lgj-abi/Cargo.lock‎

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)