Skip to content

Commit 555e69a

Browse files
committed
board: D-LGJ-FOLD-5 done, the stale owed-clause struck, and the gap that hid a red main
Hygiene for the two commits before it. LATEST_STATE gains the dated section (the measured table, the group-count arm's reason, the four disable arms); STATUS_BOARD moves D-LGJ-FOLD-5 off Queued with its numbers. The minor-12 section's "Still owed" clause said the bricks consumer still runs the 32-crossing path. It no longer does, so it is struck IN PLACE with the discharge date rather than deleted -- it was true when written, and this board is append-only. Its second clause (`lgj_hop` holding mask-sized Vecs) stands and is still owed. ISS-LGJ-CONSUMERS-HAVE-NO-CI-LINE is the real finding. `main` was red on a consumer check and nothing reported it: the one workflow gates `lgj-abi` only, so no Java compiles in CI at all, and the 612-check core suite plus the three consumer suites are local gates run by whoever remembers. Same shape as the r2il probe step that sat absent from lance-graph CI -- a gate that is never dispatched is indistinguishable from no gate; there the absence was deliberate and documented, here it is neither. The entry names why it is not merely "add a job" (JDK 28 is preview-gated and an EA build apt does not carry, obtained here from a GitHub release download path because the distribution hosts are gateway-blocked -- a runner's network posture is its own question), and carries a falsifier: re-introduce the stale pin and the job must go red, since a job that builds the consumers without running their mains would pass and be no-gate-with-extra-steps. Per the board's own termination clause this commit is hygiene-only and generates no further obligations. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GXUahz73MZxtxWcfpHp9dG
1 parent cee012c commit 555e69a

3 files changed

Lines changed: 120 additions & 4 deletions

File tree

‎.claude/board/ISSUES.md‎

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,43 @@
1+
## ISS-LGJ-CONSUMERS-HAVE-NO-CI-LINE (2026-09-22) — OPEN
2+
3+
`main` was RED and nothing said so. `GraphHopTest` reported 1 FAILED / 65
4+
passed at `bb81d80`, established by running it from a clean worktree, not
5+
inferred. The defect itself was small — a pin asserting a second
6+
`materializeRows()` costs zero crossings, written before `Mask.words()` began
7+
re-validating its cached window per facade call — and is fixed
8+
(D-LGJ-FOLD-5's second commit). **The issue is that it survived a merge.**
9+
10+
`.github/workflows/` contains exactly one workflow and it gates `lgj-abi`
11+
only: `cargo fmt --check`, `cargo clippy -D warnings`, `cargo test`. Nothing
12+
compiles a single line of Java. The **612-check core suite and all three
13+
consumer suites are LOCAL gates**, run by whoever remembers to run them, which
14+
root `CLAUDE.md` states plainly (*"the merge gate is the Rust suite plus the
15+
409-check Java run"*). A consumer is one step further out still: even a
16+
session that runs `AllTests` religiously never touches `consumers/`.
17+
18+
This is the **same shape** as the r2il probe step that sat absent from
19+
lance-graph CI until its OGAR dependency reached main — a gate that exists
20+
and is simply never dispatched is indistinguishable from no gate. There the
21+
absence was deliberate and documented; here it is neither.
22+
23+
**Why it is not just "add a job":** a Java job needs JDK 28 with
24+
`--enable-preview` (JEP 401 is preview-gated, per
25+
`ISS-LGJ-TOOLCHAIN-MUST-BE-JDK28-VALHALLA-PANAMA`) and JDK 28 is an EA build
26+
that apt does not carry — the container obtains it from a GitHub release
27+
download path because the distribution hosts are gateway-blocked. A CI runner
28+
has its own network posture, so the acquisition ladder that works here is not
29+
evidence it works there. Scoping that is this issue's first task, not an
30+
assumption to build on.
31+
32+
**Minimum that would have caught this one:** a job that builds
33+
`java/src/{main,test}` plus `consumers/*/src` and runs `AllTests` and the
34+
three consumer mains. No new test, no new assertion — only dispatch.
35+
36+
**Falsifier for any fix:** re-introduce the stale `0` pin on a branch and
37+
confirm the job goes red. A job that builds the consumers but never runs
38+
their mains would pass, and would be the no-gate-with-extra-steps outcome
39+
this entry exists to name.
40+
141
## ISS-LGJ-TOOLCHAIN-MUST-BE-JDK28-VALHALLA-PANAMA — UNBLOCKED; JDK 28 installed and the flip proven (2026-09-19)
242

343
⊘ The entry below says the migration is blocked because no JDK 28 can be

‎.claude/board/LATEST_STATE.md‎

Lines changed: 79 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,76 @@
1+
## 2026-09-22 — D-LGJ-FOLD-5: the consumer stops asking sixteen times, and a red pin on `main` gets root-caused
2+
3+
The consumer half of minor 12. `BricksQuery.sumBy()` was sixteen queries —
4+
one `sumOf` over `where(REGION.eq(v))` per region, each paying plan evaluation
5+
into a selection mask plus the reduction over it — and is now one fused
6+
`sumByGroup`. No selection mask is built at any point.
7+
8+
- **Measured through the consumer, not inherited from the core suite:** 1
9+
crossing at 1,000 and 64,000 rows, and — new — at 1, 16 and 64 groups.
10+
The old path was already invariant in ROWS, which is why its literal was
11+
asserted at both row counts; this one is invariant in GROUPS as well, a
12+
strictly stronger claim, so it gets its own arm instead of riding on the
13+
row-count one.
14+
- **The group-count arm needed a seam, and the seam is the honest kind.** The
15+
public `sumBy()` always asks for exactly `Orders.REGIONS` groups, so nothing
16+
the test could previously reach varied the group count and *"one crossing
17+
whatever the group count"* was a doc comment no input could falsify. A
18+
package-private `sumByGroupCount` serves that arm alone; `getMethods()` does
19+
not see it, so the aggregate-only-egress guard still audits exactly the
20+
public surface the guarantee is about.
21+
- **Disable arm A recovered the old cost formula exactly:** reverting to the
22+
per-group loop measured **2 / 32 / 128 at 1 / 16 / 64 groups**, identical at
23+
both row counts — `2 × groups` — with **parity GREEN throughout**. The two
24+
paths agree on the answer and differ only in cost, which is what makes this a
25+
migration rather than a behaviour change. Note even the degenerate case
26+
discriminates: 1 group costs 2 on the old path, 1 on the new.
27+
- **`Orders.REGIONS` is a mirror, so it is pinned to the fixture and not to a
28+
copied number.** It replaces three literal 16s with one named source for the
29+
native generator's classid cardinality, which the ABI manifest does not
30+
report. A comparison against a second Java literal would drift in lockstep
31+
with whatever edited it and prove nothing; instead every id below REGIONS
32+
must carry rows and id REGIONS itself must carry none. Two-sided for a
33+
reason: arm B (REGIONS = 20) fires the first half only, arm C (REGIONS = 8)
34+
the second only — at 3,960 rows on id 8 — so neither half alone catches
35+
both directions.
36+
- **Return type stays `Map<Integer, Long>`:** sized by the question, one entry
37+
per group, never by the data. Handing back `GroupTotals` would leak a facade
38+
type into a consumer's public surface for nothing.
39+
40+
**And a separate finding, which is the part worth remembering:
41+
`GraphHopTest` was RED on `main`.** 1 FAILED / 65 passed at `bb81d80`,
42+
established by running it from a clean worktree rather than inferred from the
43+
diff's shape. Consumers have **no CI line** — the only workflow gates
44+
`lgj-abi` (fmt, clippy, test) — so nothing reported it, the same
45+
no-CI-line shape that hid the r2il probe step upstream. Filed as
46+
`ISS-LGJ-CONSUMERS-HAVE-NO-CI-LINE`.
47+
48+
Root cause, and it is the design rather than a regression: `Mask.words()` no
49+
longer returns its cached window directly — it re-describes through
50+
`lgj_mask_describe` and compares the returned epoch against its stamp, so a
51+
cached address is never read after the substrate moved under it. Root
52+
`CLAUDE.md` names that as shipped. The pin asserting a second
53+
`materializeRows()` costs **zero** was written against the pure-cache
54+
behaviour that preceded it, so the test moved, not the code; reverting the
55+
number would mean deleting the re-validation. The plan's §3.5 *"describe
56+
cached per Mask; zero crossings steady-state"* is superseded for `Mask`: the
57+
words are still not re-fetched and the population is still never re-scanned,
58+
but the describe is no longer free. Steady-state is **constant per call**.
59+
60+
The re-pin asserts that rather than flipping a literal: a THIRD call is
61+
measured and required to equal the second. A one-off extra describe would
62+
satisfy a bare `== 1` on the second call; only a per-call cost satisfies
63+
second == third. **Disable arm D proves the two arms are independent** —
64+
restoring the pure cache kills the magnitude arm (expected 1, was 0) and
65+
leaves the constancy arm GREEN at 0, because it tests a different property.
66+
67+
- **Gates:** core **612/612**, bricks **70/70** (was 62; the six group-count
68+
arms and two cardinality checks are the +8), graph **68/68** (was 65 + 1
69+
failed; the third-call cost and its content check are the +2), trades
70+
**12/12 + 3/3** — **765 checks**, JDK 28 `--release 28 --enable-preview`
71+
against a freshly built `abi 0.12, ndarray::simd avx512, release`. Four
72+
disable arms, each red-then-green.
73+
174
## 2026-09-22 — minor 12: `lgj_plan_group_sum_i32`, the grouped sum that never builds a selection (fold-distillation wave 4)
275

376
Upstream first (lance-graph #1256 merged `99cdca38`: the tiled executor,
@@ -26,9 +99,12 @@ selection exists at any point.
2699
- **The eighth named materialisation site:** `Engine.groupSumI32`'s
27100
`toArray`, sized by `groups` (the question), pinned in `DoctrineFenceTest`
28101
and listed in root `CLAUDE.md`. `GroupTotals` exposes no array.
29-
- **Still owed:** the `bricks` consumer's `sumBy()` still runs the 32-crossing
30-
path; migrating it to `sumByGroup` is a consumer-wave change, not this
31-
one. `lgj_hop` still holds mask-sized Vecs (pre-existing, unrelated).
102+
- **Still owed:** ⊘ the first clause is DISCHARGED (2026-09-22, D-LGJ-FOLD-5,
103+
see the section above) — struck in place, not deleted: it was true when
104+
written. It read *"the `bricks` consumer's `sumBy()` still runs the
105+
32-crossing path; migrating it to `sumByGroup` is a consumer-wave change,
106+
not this one."* That consumer wave has now run. `lgj_hop` still holds
107+
mask-sized Vecs (pre-existing, unrelated) and remains owed.
32108

33109
## 2026-09-19 — PR #81 merged (`07aa441`): production IS the Valhalla arm; Panama × Valhalla is one membrane
34110

‎.claude/board/STATUS_BOARD.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,5 +131,5 @@ same lowering: lance-graph #1257 (SAP CATS, merged).
131131
| D-id | Deliverable | Status |
132132
|---|---|---|
133133
| D-LGJ-FOLD-4 | ABI minor 12 `lgj_plan_group_sum_i32` + `View.sumByGroup` / `sumByGroupVia` + `GroupTotals`: a `GROUP BY … SUM` as ONE program, one crossing, no selection (`docs/abi.md` §20) | **DONE 2026-09-22** — native 191 tests, Java 612 checks (JDK 28), 1 crossing measured beside the 32-crossing path, six disable arms red-then-green |
134-
| D-LGJ-FOLD-5 | Migrate `consumers/bricks` `sumBy()` from the 32-crossing per-group path onto `sumByGroup` (re-pin BricksAuthTest's crossing arithmetic 32 → 1) | Queued |
134+
| D-LGJ-FOLD-5 | Migrate `consumers/bricks` `sumBy()` from the 32-crossing per-group path onto `sumByGroup` (re-pin BricksAuthTest's crossing arithmetic 32 → 1) | **DONE 2026-09-22** — one fused call; **1 crossing at 1,000 and 64,000 rows AND at 1, 16 and 64 groups**. The group-count arm is new and load-bearing: the public `sumBy()` always asks for `Orders.REGIONS` groups, so nothing previously reachable varied the group count and "one crossing whatever the group count" was unfalsifiable; a package-private `sumByGroupCount` exists for that arm alone (invisible to `getMethods()`, so the aggregate-egress guard still audits exactly the public surface). Disable arm A (revert to the per-group loop) measured the old cost formula exactly — **2 / 32 / 128 at 1 / 16 / 64 groups, identical at both row counts, i.e. `2 × groups`** — with parity GREEN throughout, so the two paths agree on the answer and differ only in cost. `Orders.REGIONS` replaces three literal 16s with one named source, a MIRROR of the native generator's classid cardinality (the manifest does not report it) pinned two-sided against the fixture rather than against a copied literal; arms B (20) and C (8) each fire a DIFFERENT half, so neither half alone catches both directions. BricksAuthTest **70/70** (was 62) |
135135

0 commit comments

Comments
 (0)