@@ -230,9 +230,24 @@ build):
230230 not row count — verified fixed-size on both the Rust and Java sides);
231231 ` Abi.java ` 's ` readCarvings ` (bounded by ` CARVING_SLOTS ` , a manifest
232232 constant, not n_rows); ` Engine.facetSumResolved ` 's fixed ` long[2] `
233- result pair. None of the five is a hidden proportional-to-n_rows
234- population copy — keep this list exhaustive when a sixth site is added,
235- rather than letting the enumeration silently go stale again.
233+ result pair; ` View.where() ` 's ` List.copyOf ` of the PREDICATE chain; and
234+ ` NativePattern.plan() ` 's ` predicates.stream()…toList() ` . None of the
235+ seven is a hidden proportional-to-n_rows population copy — keep this list
236+ exhaustive when an eighth site is added, rather than letting the
237+ enumeration silently go stale again.
238+
239+ > ** ⊘ RE-AUDITED 2026-09-16 and the list WAS stale — it claimed five and
240+ > the tree had seven.** ` View.where() ` and ` NativePattern.plan() ` were
241+ > never listed. Both are legitimate, and * why* is the sharper statement of
242+ > the rule: they are bounded by the number of predicates the developer
243+ > CHAINED, i.e. by the size of the query they typed — never by the data.
244+ > So the invariant is not "no allocation on the Java side"; it is
245+ > ** nothing proportional to ROWS** , and a query-shaped allocation is the
246+ > boring front's own size, not the substrate's. The audit that found this
247+ > is one grep (` long[] ` , ` toArray ` , ` copyOf ` , ` .stream() ` across
248+ > ` java/src/main ` ) and it is the check to re-run before citing the list —
249+ > the enumeration had already gone stale once under a sentence telling the
250+ > next session not to let it.
236251 Temporary kernel scratch (SIMD scratch masks, decode buffers) is
237252 allowed and is NOT the same claim as a second canonical copy.
238253- ** Layout parity is independently derived, not self-compared.**
@@ -328,6 +343,150 @@ simd_{amx,avx512,avx2, Rust: lgj-abi kernels → ndarray::simd
328343> holds at the TOP row (facade ↔ facade) and the BOTTOM row (backends ↔ the
329344> Rust floor). It BREAKS in the middle. Do not reason from the middle row.
330345
346+ ## THE JAVA SURFACE IS ` sql() ` , NOT THE MASK ALGEBRA (operator-ruled, 2026-09-16)
347+
348+ ** Verbatim, in four parts:**
349+
350+ > * "Java doesnt use masking ops. ` Mask.minus() ` , ` RowStore.hop() ` . Lance-graph
351+ > does. Java just sees boring ` sql() ` handed to duckdb (Example)."*
352+ >
353+ > * "nobody should ever start trying to optimize Java (except making it boring
354+ > front)."*
355+ >
356+ > * "the boringness is then handed to lancegraph as zero copy."*
357+ >
358+ > * "and handled akin to ndarray polyfill — java doesnt know why there is
359+ > ` sql() ` polyfill, we just make sure there is."*
360+
361+ ### What this corrects, by name
362+
363+ ⊘ The sentence three paragraphs up — * "offers the menu to the table in a
364+ pleasing way, ** using masking ops** "* — is ** struck on those three words** .
365+ The menu framing survives; the mechanism named in it does not. lance-graph
366+ uses masking ops. Java does not, must not, and is never told they exist.
367+
368+ ⊘ The isomorphism table's top row previously read `Java (View / Mask /
369+ RowStore / consumers)` as the facade. ** ` Mask` and ` RowStore` are NOT the
370+ facade** — they are the substrate's algebra standing on the Java side of the
371+ wall. ` Mask.minus() ` and ` RowStore.hop() ` are the operator's own two examples
372+ of the wrong shape. The facade is the boring call a Java developer already
373+ knows how to write.
374+
375+ ### The polyfill relation is EXACT, and it is the whole design
376+
377+ This is the part that makes the ruling operational rather than stylistic.
378+ ` ndarray::simd ` is a facade with ** 37 functions and zero shipping
379+ instructions** — a consumer crate calls ` U8x64::cmpeq_mask ` and has no idea
380+ whether AVX-512, NEON, wasm or scalar answered it. The consumer does not
381+ know why the polyfill exists. It only knows it is there.
382+
383+ ` sql() ` is that, one tier up:
384+
385+ | tier | the caller writes | the caller does not know |
386+ | ---| ---| ---|
387+ | consumer crate → ndarray | ` U8x64::cmpeq_mask(..) ` | which of six backends ran |
388+ | Java → lance-graph | ` sql("select …") ` | that masks, ternlog, hops or popcounts exist at all |
389+
390+ ** "We just make sure there is one"** is the standing obligation, and it falls
391+ on the Rust side, never on Java. A missing ` sql() ` capability is a
392+ lance-graph/ABI gap to close — exactly as a missing SIMD primitive is an
393+ ` ndarray::simd ` gap to close, never a licence for a consumer to write
394+ intrinsics. This is the MISSING-CAPABILITY STOP RULE already in this file,
395+ now with its Java-side spelling.
396+
397+ ### The three operational consequences
398+
399+ 1 . ** Nobody optimizes Java. Ever.** The only sanctioned work on the Java side
400+ is making it ** more boring** — more ordinary, more familiar, closer to what
401+ a Java developer would have written without us. A PR whose stated goal is a
402+ faster Java path is rejected on its goal, before its diff is read. "Boring"
403+ is the performance strategy: the speed is elsewhere.
404+ 2 . ** The boringness is handed down as zero copy.** The ordinary-looking call
405+ is not translated, re-encoded, or marshalled. It crosses as a NAME and the
406+ bytes stay put — which is what makes the boring surface honest rather than
407+ a disguise. Zero-copy is not an optimization applied to the glove; it is
408+ the condition under which a boring glove is allowed to exist.
409+ 3 . ** A mask concept on a public Java signature is a defect** , regardless of
410+ how well it works. ` java-surface-warden ` already blocks byte positions and
411+ arithmetic; this ruling adds the population algebra by name — ` Mask ` ,
412+ ` minus ` , ` hop ` , ` ternlog ` , ` popcount ` , lane ids, opcodes. Those are
413+ T1/T2 words (see ` kernel-membrane-warden ` ), and T3 does not speak them.
414+
415+ 4 . ** Java never does materialization — and the row count is what proves it**
416+ (operator, same day): * "Java never does materialization. A billion rows op
417+ is handed behind the front and handled as a masking hattrick nobody knew
418+ what's coming."* This is the boring front's load-bearing property, not a
419+ performance note. A billion-row operation crosses as a NAME and returns a
420+ scalar; the billion never exists on the Java side, in any form, at any
421+ moment. That is why the surface can afford to be boring: ` sql() ` looks
422+ identical at 10 rows and at 10^9, because Java's work is the same at both —
423+ none.
424+
425+ ** The checkable form of "never":** every public Java call must be
426+ ** O(1) in rows** , and the ONLY exit is a method whose name begins with
427+ ` materialize ` (today: exactly one, ` Mask.materializeRows() ` , O(n), stated
428+ in its javadoc). The naming rule is not a loophole in "never" — it is the
429+ mechanism that makes "never" auditable, because anything proportional to
430+ row count that is NOT named that way is a defect by inspection, with no
431+ judgement call required. Keep the exhaustive materialization-site list in
432+ the zero-copy section current for exactly this reason: an unnamed sixth
433+ site is the failure this rule exists to catch.
434+
435+ ** The corollary that bites hardest in review:** a Java-side loop is not
436+ merely slow, it is * proof that something was materialized to loop over* .
437+ E1 forbids the loop; this forbids the thing that made a loop expressible.
438+ Two statements of one rule from opposite ends.
439+
440+ ### THE ENDGAME — why "boring" is strategy, not taste (operator, same day)
441+
442+ > * "endgame is make Java low code ** 'Bring your own software'** to beat
443+ > palantir foundry at its own game."*
444+
445+ Foundry's proposition is * bring your data to our platform* — and then learn
446+ its ontology tooling, its pipeline builder, its workshop, its idioms. The
447+ learning curve is not a cost of the product; ** it IS the product** , because
448+ every hour a customer spends learning it is an hour of lock-in that a
449+ competitor must refund before they can switch.
450+
451+ BYOS inverts exactly that: ** bring your own software.** The customer keeps
452+ their Java, their SQL, their JDBC-shaped habits, their existing build — and
453+ the substrate is underneath without them learning a new vocabulary to reach
454+ it. Nothing to port, nothing to adopt, nothing to unlearn if they leave.
455+
456+ ** This is what makes each of the three consequences above non-negotiable
457+ rather than stylistic:**
458+
459+ - ** Novel API is the enemy, not slow API.** ` Mask.minus() ` is something a
460+ customer must LEARN; ` sql() ` is something they already know. Every unit of
461+ novelty on the Java surface is a unit of Foundry-shaped lock-in-by-curve —
462+ built by us, against our own pitch. That is why optimizing Java loses the
463+ game even when it succeeds: optimization on that side produces novel API,
464+ and novel API is precisely the thing BYOS promises not to require.
465+ - ** "Boring" is therefore the competitive moat, measured as absence.** The
466+ win condition is that a Java developer writes what they would have written
467+ anyway and the result arrives at substrate speed. There is no demo of this
468+ that looks impressive — it looks like nothing happened, which is the point.
469+ - ** The advantage must live where the customer does not have to look.** Speed
470+ comes from lance-graph and ndarray; the customer gets it by writing ordinary
471+ code. A surface that has to be studied to be fast has already conceded the
472+ argument, however fast it then is.
473+
474+ ** Practical test for any proposed Java-side addition:** * would a customer who
475+ has never read our documentation write this line by accident, from habit
476+ alone?* If yes, it is a candidate. If it needs a paragraph of explanation, it
477+ is Foundry's business model with our name on it — reject it and close the gap
478+ on the Rust side instead.
479+
480+ ### Scope — what this does NOT do
481+
482+ It does not delete ` Mask ` or ` RowStore ` today. They exist, they are tested,
483+ and the four leak-on-throw fixes that landed with this ruling make existing
484+ code correct rather than expanding it. What the ruling settles is the
485+ DIRECTION: the boring ` sql() ` -shaped surface is the target, the mask algebra
486+ is not to be grown on the Java side, and no future session may cite the
487+ struck sentence above as licence to add another ` Mask.* ` verb.
488+
489+
331490Measured grounding (2026-08-27, in-tree): ` simd.rs ` is 37 functions and
332491ZERO shipping instructions — every raw intrinsic in it sits inside
333492` #[cfg(test)] ` as the wrapper's oracle; ` simd_avx512.rs ` alone carries 488.
0 commit comments