Skip to content

Commit cee012c

Browse files
committed
graph: re-pin materializeRows() steady-state cost 0 -> 1, the re-validation's own number
`GraphHopTest` was RED on `origin/main` before this branch existed -- 1 FAILED / 65 passed, verified by running it from a clean worktree at bb81d80, not inferred. Consumers have no CI line (the only workflow gates `lgj-abi`: fmt, clippy, test), so nothing would have reported it. The assertion said a second `materializeRows()` on the same Mask costs zero further crossings, citing the plan's §3.5 "describe cached per Mask; zero crossings steady-state". It measured 1. Root cause, and it is deliberate: `Mask.words()` no longer returns the cached window directly. It re-describes through `lgj_mask_describe` and compares the returned epoch against the stamp it holds, so a cached address is never read after the substrate has moved under it. Root CLAUDE.md names that as the shipped property -- `Mask` "re-validates the cached window against the generation-checked registry at each top-level facade call". The stale pin was written against the pure-cache behaviour that preceded it. Reverting the number would mean deleting the re-validation, so the test moves, not the code. The describe is still cached in the sense that matters: the words are not re-fetched and the population is never re-scanned -- `lgj_mask_describe` fills a descriptor and does no work over the rows. It is simply no longer free. Steady-state is "constant per call", not "zero". So the re-pin asserts that, rather than just flipping 0 to 1: a THIRD call is measured and required to equal the second. One extra describe happening once would satisfy a bare `== 1` on the second call; only a per-call cost satisfies second == third, which is what a per-call re-validation predicts. The content of the third call is checked against the first as well -- the re-validation returns the same selection, it does not re-answer the question. `PREDICTED_MATERIALIZE_STEADY_CROSSINGS` replaces a bare literal, so the number now carries its provenance next to its sibling first-call constant. Gates: GraphHopTest 68/68 (was 65 + 1 failed; the third-call cost and its content check are the +2), core 612/612, bricks 70/70, trades 12/12 + 3/3, JDK 28 `--release 28 --enable-preview` against abi 0.12. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GXUahz73MZxtxWcfpHp9dG
1 parent 1aa159d commit cee012c

1 file changed

Lines changed: 40 additions & 6 deletions

File tree

‎consumers/graph/src/test/java/com/adaworldapi/graph/GraphHopTest.java‎

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,26 @@ public static void main(String[] args) {
149149
private static final long PREDICTED_COUNT_CROSSINGS = 1; // MEASURED-AND-PINNED
150150
private static final long PREDICTED_MINUS_CROSSINGS = 2; // MEASURED-AND-PINNED
151151
private static final long PREDICTED_MATERIALIZE_FIRST_CROSSINGS = 1; // MEASURED-AND-PINNED
152+
153+
/**
154+
* What a materializeRows() call costs AFTER the first, re-pinned 1 (was 0) on 2026-09-22.
155+
*
156+
* <p>It was 0 while {@code Mask.words()} was a pure cache. It is 1 because that cache now
157+
* re-validates: every top-level facade call re-describes the mask through {@code
158+
* lgj_mask_describe} and compares the returned epoch against the stamp it holds, so a cached
159+
* address can never be read after the substrate has moved under it. That is the shipped
160+
* safety property (root {@code CLAUDE.md}, zero-copy section: {@code Mask} "re-validates the
161+
* cached window against the generation-checked registry at each top-level facade call"), so
162+
* the number is the design's, not a regression — reverting it would mean deleting the
163+
* re-validation.
164+
*
165+
* <p>The pin this replaces cited the plan's §3.5 "describe cached per Mask; zero crossings
166+
* steady-state". That reading is superseded for {@code Mask}: the describe is still cached in
167+
* the sense that the WORDS are not re-fetched and the population is never re-scanned —
168+
* {@code lgj_mask_describe} fills a descriptor and does no work over the rows — but it is no
169+
* longer free. Steady-state is now "constant per call", which is what the arms below assert.
170+
*/
171+
private static final long PREDICTED_MATERIALIZE_STEADY_CROSSINGS = 1; // MEASURED-AND-PINNED
152172
private static final long PREDICTED_IMPORT_CROSSINGS = 2; // MEASURED-AND-PINNED
153173

154174
private static long[] seedRows() {
@@ -449,9 +469,11 @@ private static void checkCrossingsProportionalToHops(Checks c) {
449469
c.eq("minus(Mask) costs exactly " + PREDICTED_MINUS_CROSSINGS + " crossing(s)",
450470
PREDICTED_MINUS_CROSSINGS, minusCost);
451471

452-
// materializeRows(): "describe cached per Mask; zero crossings steady-state" (spec
453-
// §3.5) -- so the FIRST call on a given Mask should cost something, the SECOND call
454-
// on the very same Mask should cost nothing further.
472+
// materializeRows(): constant cost per call, not zero after the first -- see
473+
// PREDICTED_MATERIALIZE_STEADY_CROSSINGS for why the spec's "zero crossings
474+
// steady-state" no longer holds and why that is the design rather than a regression.
475+
// A THIRD call is measured too: it is what distinguishes "constant per call" from
476+
// "one extra, once", and only the former is consistent with a per-call re-validation.
455477
long beforeFirstMaterialize = Diagnostics.crossings();
456478
long[] materializedOnce = afterMinus.materializeRows();
457479
long firstMaterializeCost = Diagnostics.crossings() - beforeFirstMaterialize;
@@ -462,9 +484,21 @@ private static void checkCrossingsProportionalToHops(Checks c) {
462484
c.eq("materializeRows() first call costs " + PREDICTED_MATERIALIZE_FIRST_CROSSINGS
463485
+ " crossing (the one-time describe of this Mask's word lane)",
464486
PREDICTED_MATERIALIZE_FIRST_CROSSINGS, firstMaterializeCost);
465-
c.eq("materializeRows() SECOND call on the SAME Mask costs zero further crossings"
466-
+ " -- steady-state, per spec §3.5",
467-
0, secondMaterializeCost);
487+
long beforeThirdMaterialize = Diagnostics.crossings();
488+
long[] materializedThrice = afterMinus.materializeRows();
489+
long thirdMaterializeCost = Diagnostics.crossings() - beforeThirdMaterialize;
490+
491+
c.eq("materializeRows() SECOND call on the SAME Mask costs "
492+
+ PREDICTED_MATERIALIZE_STEADY_CROSSINGS
493+
+ " further crossing -- the cached window is re-validated against the"
494+
+ " registry on every facade call, never read on trust",
495+
PREDICTED_MATERIALIZE_STEADY_CROSSINGS, secondMaterializeCost);
496+
c.eq("materializeRows() THIRD call costs the same as the second -- constant per call,"
497+
+ " which a one-off extra describe would not produce",
498+
secondMaterializeCost, thirdMaterializeCost);
499+
c.that("the third materializeRows() agrees with the first on content too -- the"
500+
+ " re-validation returns the same selection, it does not re-answer it",
501+
sameRowSet(materializedOnce, materializedThrice));
468502
c.that("both materializeRows() calls agree on content (the describe caching did not"
469503
+ " change the answer)",
470504
sameRowSet(materializedOnce, materializedAgain));

0 commit comments

Comments
 (0)