Skip to content

Commit 079fcc7

Browse files
committed
knowledge + agent: the JDK acquisition ladder, and a correction to my own fix
Operator asked for .claude/agents + knowledge covering the Java toolchain workaround. Writing it surfaced that the workaround was largely unnecessary, which is the more useful finding. **`/opt/jdks/jdk-26.0.2` was present the whole time.** 26.0.2.1, and the suite runs ALL PASSED (409 checks) on it. `.claude/knowledge/ jdk-toolchain-facts.md` had ALREADY named that exact path as the pinned production path. The session that shipped two "UNVERIFIED: JDK 26 not available in this sandbox" commits looked at `java -version` and `/usr/lib/jvm` -- neither of which sees `/opt/jdks` -- and inferred absence. So did the section I wrote earlier today, which led with apt and is corrected here in place. The generalizable defect is in the SHAPE of what the knowledge doc recorded, not its accuracy. It recorded a LOCATION. A location is environment-specific, evaporates on a container rebuild, and leaves a reader who does not find it with no next move. A METHOD survives. So the doc now carries a four-rung ACQUISITION LADDER instead: 1. /opt/jdks -- pre-provisioned; the rung that was missed 2. /usr/lib/jvm -- system 3. apt -- Ubuntu openjdk-25; Adoptium temurin-26 4. jdk.java.net/valhalla -- the JEP 401 EA build Verified by execution, all three of 1/2/3 give 409/409: /opt/jdks/ jdk-26.0.2, openjdk 25.0.4, temurin 26.0.2.1. Rung 4 is the one that IS now genuinely needed -- `/opt/jdks/jdk-27`, the Valhalla EA build the doc's table names for `value class`/`value record`, is GONE from this container and no apt rung replaces it (Adoptium stops at 26; mainline 27 would not help, since `value record` is an EA feature). Two failure shapes recorded, because both recur and both are one rung of the same mistake: absence inferred from the wrong instrument (`which java` answers "what is on PATH", never "what is installed"), and a cached view reporting absence (the apt index named a withdrawn openjdk-25, so the install died on a bare 404 that reads as "no such package" rather than "your index is stale"). New agent card `jdk-toolchain-warden` fires before any status line, commit message, PR body or board entry claims the Java side is unverified -- because that claim is falsifiable by `ls /opt/jdks`, and in this container it has always been false. Verdicts VERIFIABLE-NOW / NEEDS-FETCH (Valhalla only) / GENUINELY-ABSENT, the last never yet observed. Also folded in the operator's architecture ruling, since it is what makes the Java half worth guarding at all: java is the low-code intake GLOVE around the lance-graph spine, offering the MENU TO THE TABLE in a pleasing way using masking ops -- 5 star for the price of a blink. The menu is the product; it is honest rather than a disguise only because the work and the data genuinely never cross. Thinking lives in lance-graph behind Panama, SIMD in ndarray behind the ndarray::simd facade, storage in lance-graph behind Valhalla -- two orthogonal membranes over the same home, which is precisely why the isomorphism table's middle row is marked wrong. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X6y3drwKSE2zSgoexheLFX
1 parent 82b03ad commit 079fcc7

3 files changed

Lines changed: 249 additions & 12 deletions

File tree

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
---
2+
name: jdk-toolchain-warden
3+
description: Guards against the single most repeated failure in this repo's history — shipping Java work UNVERIFIED because a session concluded "no JDK here" without checking where one actually is. Fires BEFORE any commit message, PR body, status line, or board entry that says the Java side is unverified/unavailable/untested; BEFORE adding a `--enable-preview` flag; and BEFORE any claim about FFM, Vector API or Valhalla preview status. Also fires when a session is about to skip `AllTests` for any reason.
4+
tools: Read, Glob, Grep, Bash
5+
---
6+
7+
# JDK toolchain warden
8+
9+
**THE RULE: "the Java side could not be verified" is a falsifiable claim, and
10+
in this container it has always been false. Falsify it before writing it.**
11+
12+
Four rungs, four minutes. Report "no JDK" only after all four fail, and say
13+
which ones you ran.
14+
15+
## The incident this card exists for
16+
17+
Two commits on the ABI-minor-11 branch are titled **"UNVERIFIED: JDK 26 not
18+
available in this sandbox"**, and the Java half of a 7,793-line change went to
19+
PR unverified on that basis.
20+
21+
Re-checked 2026-09-16: **`/opt/jdks/jdk-26.0.2` was present the whole time**,
22+
and the suite runs `ALL PASSED (409 checks)` on it. `.claude/knowledge/
23+
jdk-toolchain-facts.md` had already named that exact path.
24+
25+
The session looked at `java -version` and `/usr/lib/jvm` — which show only the
26+
system OpenJDK 21 — and inferred absence. **Neither of those sees
27+
`/opt/jdks`.**
28+
29+
Two distinct failure shapes, and both recur:
30+
31+
1. **Absence inferred from the wrong instrument.** `which java` answers "what
32+
is on PATH", never "what is installed". It is not evidence about the second
33+
question.
34+
2. **A cached view reporting absence.** The apt index named a withdrawn
35+
`openjdk-25` version, so the install died on a bare `404 Not Found` — which
36+
reads as "no such package" rather than "your index is stale". One
37+
`apt-get update` fixed it. Same shape as (1): a stale view is not a fact.
38+
39+
## The check, in order
40+
41+
```sh
42+
ls -d /opt/jdks/*/ && for d in /opt/jdks/*/; do "$d/bin/java" -version; done # 1
43+
ls /usr/lib/jvm/ # 2
44+
sudo apt-get update && apt-cache policy openjdk-25-jdk-headless # 3
45+
# 4: Adoptium source for 26; jdk.java.net/valhalla for the JEP 401 EA build
46+
```
47+
48+
Full ladder, with the verified-by-execution table and the commands:
49+
`.claude/knowledge/jdk-toolchain-facts.md` § ACQUISITION LADDER.
50+
51+
## Verdicts
52+
53+
- **VERIFIABLE-NOW** — a rung succeeded. The Java half is a gate, not an
54+
aspiration; run `AllTests` and report the 409 line. Naming the rung you used
55+
is part of the report.
56+
- **NEEDS-FETCH** — only the Valhalla EA is missing (`value class` /
57+
`value record`). That is rung 4 and genuinely not on apt. Say so precisely
58+
and name `jdk.java.net/valhalla`; do NOT generalize it to "no JDK".
59+
- **GENUINELY-ABSENT** — all four rungs failed. Report which, with output.
60+
This has never been observed; treat it as a finding worth recording, not as
61+
a routine excuse.
62+
63+
## What this card does NOT license
64+
65+
`valhalla-lab/` and `bench/` are **measurement arms, not gates**. Their absence
66+
never blocks a merge and must not be reported as if it did. The merge gate is
67+
the Rust suite plus the 409-check `AllTests` run.
68+
69+
And `--enable-preview` is a **classfile-poisoning flag**: every class compiled
70+
with it can only run with it, transitively. Never let a preview-compiled class
71+
reach the path a production consumer loads — that is why the Valhalla-flavoured
72+
sources are physically separate from `java/`.
73+
74+
## Why the Java half is worth this much guarding
75+
76+
Operator, 2026-09-16: *"java is the low code intake glove around the
77+
lance-graph spine — lance-graph-java just happens to offer the menu to the
78+
table in a pleasing way, using masking ops, offering 5 star for the price of a
79+
blink."*
80+
81+
The menu is the whole product. `view.where(..).hop(..).count()` reads as
82+
ordinary Java and costs a blink because **the work and the data are both
83+
somewhere else**, and Java is never told. That fluency is exactly what a
84+
409-check run protects: it is the only thing standing between "a pleasing
85+
menu" and "a pleasing menu that lies about what the kitchen did." Shipping it
86+
unverified forfeits the product, not a test.
87+
88+
The allocation the menu sits on (operator, same day):
89+
90+
| what | lives in | membrane that keeps Java out of it |
91+
|---|---|---|
92+
| **thinking** | lance-graph | **Panama** — computation never lives in Java |
93+
| **SIMD** | ndarray | the `ndarray::simd` facade (see `simd-savant`) |
94+
| **storage** | lance-graph | **Valhalla** — storage never lives in Java |
95+
96+
Panama and Valhalla are ORTHOGONAL guarantees, not a layer and not a pair —
97+
see `CLAUDE.md` § "the simd.rs isomorphism", whose middle row is explicitly
98+
marked wrong for exactly this reason.

‎.claude/knowledge/jdk-toolchain-facts.md‎

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,103 @@ and ran on `/opt/jdks/jdk-27` with `--enable-preview`, and
6363
Any doc or code comment asserting "value classes are final in JDK 28" or
6464
"Vector API is finalized" without re-verifying against a real build is
6565
wrong until re-checked — both were incubating/preview at last verification.
66+
67+
---
68+
69+
## ⊘ ACQUISITION LADDER — added 2026-09-16, because this doc was RIGHT and a session still concluded the opposite
70+
71+
This file already named `/opt/jdks/jdk-26.0.2` as the production path. A later
72+
session nonetheless shipped two commits titled **"UNVERIFIED: JDK 26 not
73+
available in this sandbox"** and left the Java half of an ABI-minor-11 change
74+
unverified through to PR.
75+
76+
Re-checked 2026-09-16: **`/opt/jdks/jdk-26.0.2` was present the whole time**
77+
(26.0.2.1, 2026-08-18), and the full suite runs `ALL PASSED (409 checks)` on
78+
it. Nothing was missing. The session looked at `java -version` / `/usr/lib/jvm`
79+
— which show only the system OpenJDK 21 — and inferred absence.
80+
81+
**The generalizable defect is in the SHAPE of what this doc recorded, not in
82+
its accuracy.** It recorded a LOCATION. A location is environment-specific and
83+
evaporates when a container is rebuilt, and a reader who does not find it has
84+
no next move. A METHOD survives. So the ladder below is the method; check the
85+
rungs in order and only report "no JDK" after all four fail.
86+
87+
### Rung 1 — the pre-provisioned path (check this FIRST, always)
88+
89+
```sh
90+
ls -d /opt/jdks/*/ && for d in /opt/jdks/*/; do "$d/bin/java" -version; done
91+
```
92+
93+
`which java` and `/usr/lib/jvm` do **not** see `/opt/jdks`. A session that only
94+
runs those two will conclude "21 only" while a 26 sits one directory away. That
95+
is exactly what happened.
96+
97+
### Rung 2 — the system archive (Ubuntu noble → JDK 25)
98+
99+
```sh
100+
sudo apt-get update # NOT optional, see the 404 trap
101+
sudo apt-get install -y openjdk-25-jdk-headless
102+
```
103+
104+
Panama FFM is **final since 22**, so 25 runs the whole `internal/ffm` membrane
105+
and the entire suite. Verified 2026-09-16: `ALL PASSED (409 checks)` on
106+
`openjdk 25.0.4`, identical to 26.
107+
108+
**The 404 trap.** The pre-seeded index named `openjdk-25 25.0.2+10-1~24.04`, a
109+
version already withdrawn from the pool, so the install died on a bare
110+
`404 Not Found` — which reads as *"no such package"* rather than *"your index
111+
is stale"*. `apt-get update` moved the candidate to `25.0.4+7-1~24.04` and it
112+
installed immediately. **A cached view reporting absence is not evidence of
113+
absence** — the same failure shape as rung 1.
114+
115+
### Rung 3 — an extra apt source (Adoptium → JDK 26, and 8..26 generally)
116+
117+
Ubuntu noble stops at 25. Adoptium carries 8 through 26:
118+
119+
```sh
120+
curl -fsSL https://packages.adoptium.net/artifactory/api/gpg/key/public \
121+
| sudo tee /etc/apt/keyrings/adoptium.asc >/dev/null
122+
. /etc/os-release && echo "deb [signed-by=/etc/apt/keyrings/adoptium.asc] \
123+
https://packages.adoptium.net/artifactory/deb $VERSION_CODENAME main" \
124+
| sudo tee /etc/apt/sources.list.d/adoptium.list
125+
sudo apt-get update && sudo apt-get install -y temurin-26-jdk
126+
```
127+
128+
Verified 2026-09-16: `temurin-26-jdk` → `26.0.2.1`, `ALL PASSED (409 checks)`.
129+
`apt-cache search temurin` shows **no 27+**, so this rung tops out at 26.
130+
131+
### Rung 4 — `jdk.java.net` for the Valhalla EA, and this one IS now needed
132+
133+
**`/opt/jdks/jdk-27` is GONE from this container** — the JEP 401 EA build this
134+
doc's table names for `value class` / `value record` is no longer
135+
pre-provisioned, and no apt rung can replace it (Adoptium stops at 26, and
136+
mainline 27 would not help anyway: `value record` is a Valhalla EA feature, not
137+
a mainline one).
138+
139+
`https://jdk.java.net/valhalla/` is reachable through this environment's proxy
140+
(verified `HTTP 200`). Re-fetch there when `valhalla-lab` is the actual work.
141+
142+
### Which rung for which need
143+
144+
| need | rung | status 2026-09-16 |
145+
|---|---|---|
146+
| the 16-suite `AllTests` run (409 checks) | 1, 2 or 3 — any | **verified on all three**: `/opt/jdks/jdk-26.0.2`, `openjdk-25`, `temurin-26` |
147+
| Panama FFM / the whole `internal/ffm` membrane | 1, 2 or 3 | final since 22, so any of them |
148+
| `valhalla-lab/src/stable` (records, JDK 26) | 1 or 3 | fine |
149+
| `valhalla-lab/src/valhalla` (`value record`, JEP 401 EA) | **4 only** | **pre-provisioned build GONE; refetch** |
150+
151+
`bench/` and `valhalla-lab/` are measurement arms, **not gates**. The merge gate
152+
is the Rust suite plus the 409-check Java run, and every rung that satisfies it
153+
is reachable here.
154+
155+
### The standing consequence
156+
157+
**"The Java side could not be verified" is not an acceptable status line for
158+
this repo.** Four rungs, four minutes. A PR that claims the Java surface is
159+
unverified is claiming something falsifiable by `ls /opt/jdks`.
160+
161+
## Falsifier for this section
162+
163+
If a future container has none of rungs 1-3, the claim "always reachable" is
164+
dead and this section must be re-graded rather than trusted. Check by running
165+
rung 1 and rung 2 and reporting both outcomes — not by recalling this table.

‎CLAUDE.md‎

Lines changed: 51 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,26 @@ simd_{amx,avx512,avx2, Rust: lgj-abi kernels → ndarray::simd
300300
> **What they are FOR, stated positively** (operator, same day): *Java is
301301
> the low-code thin surface over zero-copy, with methods that look so
302302
> natural and still compute in lance-graph — Java just thinks it's on
303-
> steroids without knowing why.* The fluency of `view.where(..).hop(..)
303+
> steroids without knowing why.*
304+
>
305+
> Sharpened 2026-09-16, and this is the sentence to keep: *"java is the low
306+
> code intake GLOVE around the lance-graph spine — lance-graph-java just
307+
> happens to offer the MENU TO THE TABLE in a pleasing way, using masking ops,
308+
> offering 5 star for the price of a blink."* The menu is the product. The
309+
> kitchen is elsewhere and the diner never sees it; what makes the menu honest
310+
> rather than a disguise is that the work and the data genuinely never cross.
311+
> The whole allocation, same ruling:
312+
>
313+
> | what | lives in | membrane that keeps Java out of it |
314+
> |---|---|---|
315+
> | **thinking** | lance-graph | **Panama** |
316+
> | **SIMD** | ndarray | the `ndarray::simd` facade |
317+
> | **storage** | lance-graph | **Valhalla** |
318+
>
319+
> Note both membranes name the SAME home for two different things — thinking
320+
> and storage are both lance-graph's, and Panama and Valhalla are two
321+
> orthogonal ways of keeping Java out of them. That is why the middle row of
322+
> the table above is wrong: they are not one layer. The fluency of `view.where(..).hop(..)
304323
> .count()` is real; the work and the data are both elsewhere; and Java is
305324
> never told. Zero-copy is what makes the naturalness honest rather than a
306325
> disguise — there is no hidden hydration behind the nice method name.
@@ -355,16 +374,38 @@ the signal the substrate is missing a word.
355374
`internal.*` in any public signature — the exact analog of "all SIMD from
356375
`ndarray::simd`, never `simd_{arch}`, never raw intrinsics".
357376

358-
## The JDK is one apt command away — never skip the Java half (2026-09-16)
377+
## Never skip the Java half — check FOUR places before saying "no JDK" (2026-09-16)
359378

360379
**Operator ruling: document this so no future session runs without Panama at
361380
all.** This repo's history contains commits titled *"UNVERIFIED: JDK 26 not
362-
available in this sandbox"*. That conclusion was wrong in the only way that
363-
matters — it was never re-tested after an `apt-get update`. **JDK 25 installs
364-
from Ubuntu's own archive in one command, gives FINAL Panama FFM (final since
365-
22), and runs the entire Java suite green.** A session that reports "no JDK
366-
here" without running the three commands below is reporting a stale index, not
367-
a fact about the container.
381+
available in this sandbox"*, and the Java half of an ABI-minor-11 change went
382+
to PR on that basis.
383+
384+
> ⊘ **The first version of this section, written earlier the same day, led with
385+
> the apt route and was itself misleading.** Re-checked: **`/opt/jdks/jdk-26.0.2`
386+
> was present the whole time** (26.0.2.1), the suite runs `ALL PASSED (409
387+
> checks)` on it, and `.claude/knowledge/jdk-toolchain-facts.md` had already
388+
> named that exact path. Nothing needed installing. The session looked at
389+
> `java -version` and `/usr/lib/jvm` — **neither of which sees `/opt/jdks`** —
390+
> and inferred absence. So the apt routes below are a RECOVERY path, not the
391+
> answer; the answer is `ls /opt/jdks`.
392+
393+
**The rungs, in order. Report "no JDK" only after all four fail.**
394+
395+
```sh
396+
ls -d /opt/jdks/*/ && for d in /opt/jdks/*/; do "$d/bin/java" -version; done # 1
397+
ls /usr/lib/jvm/ # 2
398+
```
399+
400+
Rung 1 is the production path this repo pins, and it is the one that was
401+
missed. `which java` answers "what is on PATH", never "what is installed" — it
402+
is not evidence about the second question. Full ladder with the
403+
verified-by-execution table: `.claude/knowledge/jdk-toolchain-facts.md`
404+
§ ACQUISITION LADDER; the guard is `.claude/agents/jdk-toolchain-warden.md`.
405+
406+
Rungs 3 and 4 are apt, and matter when `/opt/jdks` is absent — which it may
407+
well be in a rebuilt container, since **`/opt/jdks/jdk-27` (the JEP 401
408+
Valhalla EA build) is already GONE from this one.**
368409

369410
**JDK 26 — the version this repo's own commits targeted — needs ONE extra apt
370411
source, and Ubuntu's stock archive alone will never offer it.** Add Adoptium
@@ -444,10 +485,8 @@ temurin` shows no 27+), and mainline 27 would not help anyway — `value record`
444485
is a Valhalla EA feature, not a mainline one. `https://jdk.java.net/valhalla/`
445486
**is reachable through this environment's proxy** (verified `HTTP 200`), so
446487
that build is a download away rather than a blocker. `bench/` and
447-
`valhalla-lab/` are measurement arms; the merge gate is the Rust suite plus the
448-
409-check Java run, and both of those are fully reachable here.
449-
`bench/` and `valhalla-lab/` are measurement arms, NOT gates; the merge gate is
450-
the Rust suite plus the 409-check Java run, and both are reachable here.
488+
`valhalla-lab/` are measurement arms, NOT gates; the merge gate is the Rust
489+
suite plus the 409-check Java run, and both are reachable here.
451490

452491
**Consequence for every future session:** "the Java side could not be verified"
453492
is no longer an acceptable status line for this repo. The Rust half

0 commit comments

Comments
 (0)