Skip to content

Phase 1 / Forge / Queries: shapecast, overlap, point query, closest point - #64

Merged
guysenpai merged 14 commits into
mainfrom
phase-1/forge/queries-shapecast-overlap
Jul 27, 2026
Merged

Phase 1 / Forge / Queries: shapecast, overlap, point query, closest point#64
guysenpai merged 14 commits into
mainfrom
phase-1/forge/queries-shapecast-overlap

Conversation

@guysenpai

Copy link
Copy Markdown
Contributor

M1.1.10 — eleventh core sub-milestone of the M1.1 rigid arc, and the second and last that interrogates the world instead of advancing it. Replaces the five @panic bodies M1.1.9 left behind.

Brief: briefs/M1.1.10-queries-shapecast-overlap.md — gate-by-gate execution log, three Recorded deviations, closing notes.

What it settles beyond the five bodies

  • A shape cast is not expressible over the ray kernels. A sphere cast against a box is a ray against a rounded box, which rayShape rejects — asserted in the same test that shows the cast answering that geometry, so the design claim is proven rather than stated. The cast gets its own kernel, and because a support map covers every bounded convex it has no shape to reject and no error channel at all.
  • The solver-side family moves to the solver scalar, []BodyId outputs. The public f32 surface of engine-tier-interfaces.md §1 and api/types.zig does not move — verified by an empty git diff over api/.
  • BodyId cannot order a query result, measured before it was fixed: two unit spheres at (20, ±0.5, 0) return the bit-identical distance 19.133974075 at f32, and exchanging their creation order exchanged the answer. The key is (distance, entity, BodyId) across the family.

Gates

Gate Content
E1 ordering key, RED-first on main, BodyManager.entity
E2 Aabb(T).inflate + the swept traversal, queryRay re-expressed at zero extent
E3 the GJK cast kernel, closed-form oracles, the named ceiling
E4 the four BodyManager adapters
E5 the query/ package (move alone, then the five bodies)
E6 the acceptance suites
E7 bench, inherited envelopes, audits, closure

Numbers, reported and not gated

Casts land at 1.24–1.34 µs/query over 10 000 static bodies, shape overlap at 232 ns. The cast kernel costs 1.59× the analytic ray kernels on identical rays through an identical traversal. The eleven inherited M1.1.5–M1.1.8 envelopes moved by zero, digit for digit, at both precisions.

Recorded deviations

  • RD-1 — §1.11.11's bound row applied strictly. Resolved in the spec (1588 lines).
  • RD-2 — the FROZEN import set excludes where Simplex(T) lives; resolved on the epa.zig precedent.
  • RD-3 — the query/ package does not keep its files under 500 lines; ruled kept.

Open at merge

CLAUDE.md is unpatched: its Current state table has been stale since M1.1.9 merged and its L68 tag row carries the superseded tie-break formulation. Both patches were announced with the E7 GO and not carried, so nothing was written rather than invented; the exact staleness is tabulated in the brief against the repo.

Verification

303/303 forge_3d tests in all four legs — f32 and -Dphysics_f64=true × debug and ReleaseSafe. Full zig build test exit 0. zig build clean, zig build lint green, zig fmt --check green.

🤖 Generated with Claude Code

guysenpai and others added 14 commits July 26, 2026 21:25
Two tests for `engine-physics-forge.md` §1.11.14, committed ALONE against
the M1.1.9 code so the failing state is a commit anyone can check out and
re-run. The fix follows in the next commit.

`an exact distance tie is broken by entity, not by creation order` is RED
here, at both precisions: two unit spheres at (20, +/-0.5, 0) against a ray
from the origin along +X return the bit-identical distance 19.133974075 at
f32, and with the entity indices crossed against creation order the
`BodyId`-only tie-break answers a different entity in each of the two build
orders. Its three preconditions pass in the same run, which is what makes
the failure mean something: the two BodyIds are ordered by creation, the
two kernel distances are bit-identical, and both equal the closed form
asserted with no tolerance.

`two bodies on the same entity fall back on BodyId` is GREEN here, and
correctly so: where the entities are equal the two keys coincide. It pins
the residual §1.11.14 names, including the non-invariance itself.
`engine-physics-forge.md` §1.11.14. `BodyId` is a slot index, so it encodes
creation order; leading the ordering key with it makes a query's answer a
function of the order the scene was built in, which is the inverse of the
invariance §1.11.6 claims. The key becomes `(distance, entity, BodyId)`,
with `BodyId` only as the final tie-break for the residual §1.11.14 names
(two bodies of one entity at an exactly equal distance are not invariant,
and nothing forces one body per entity).

- `BodyManager.entity(id) ?EntityId`: the column has existed since M1.1.0
  and was never exposed, because nothing needed it. The ORDER does.
- `hitLess` gains the entity term over an `entityKey` helper: `index`
  major, `generation` minor, deliberately not `@bitCast` (`EntityId` is a
  packed struct whose `index` sits in the LOW 32 bits, so a bitcast would
  order by generation first — a layout accident, not a decision).
- `ClosestCollector.add` now CALLS `hitLess` instead of re-deriving the
  comparison inline, so `closest` and an `all` truncated to one slot cannot
  disagree structurally rather than by two hand-written comparisons
  happening to agree.
- The solver-side `RayHit` carries the entity. The alternative was passing
  the store into the comparator, which would turn each of `AllCollector`'s
  replace-worst comparisons into a lookup; the public `RaycastHit` carries
  it too, so the mirror is not widened.

Verified in both senses: the previous commit is RED, and reverting the key
to `BodyId`-only with everything else in place fails BOTH ordering tests
(258/260) while the same-entity residual test stays green. The delivered
M1.1.9 test `equal distance is broken by the smaller BodyId` had its
entities aligned with creation order, so it passed under either rule and
discriminated nothing; its entities are now attached to the POSITIONS,
making its radius-1.5 scene a second independent witness, and it is renamed
`equal distance is broken by the owning entity`.

260/260 at f32 and -Dphysics_f64=true, debug and ReleaseSafe.
`engine-physics-forge.md` §1.11.10. `Aabb(T).inflate(half_extents)` in
`foundation/math` under the first-consumer rule, and
`Bvh(T).queryCast(ray, extent, collector)` plus its `Broadphase(T)`
aggregate. Additive in the strict sense: same collector contract, same
near-first descent, same visited accounting, and `Aabb.rayInterval`
untouched — the only change is that `castInterval` hands the slab test
`node.aabb.inflate(extent)`.

`queryRay` is now one line on both types, `queryCast` at a zero extent.
The argument for why that changes nothing is written at the site: `min - 0`
is exact and sign-preserving, `max + 0` maps a `-0.0` bound to `+0.0`, and
no comparison downstream distinguishes the two zeros — the parallel-slab
test uses `<`/`>`, `@min`/`@max` treat them as equal, `enter > exit` and
`accepts` are sign-of-zero-blind, the near-first order compares equal
values, and the `0 * inf` NaN repair is unconditional.

Inflating the node is EXACT, not conservative-with-respect-to-the-boxes:
the Minkowski sum of two boxes is a box, so `node.inflate(extent)` is
exactly the set of centres at which the swept box overlaps the node. An
inline test pins that equivalence over a 13^3 lattice of centres, both
verdicts counted. The `extent`/`c0` contract lives on `queryCast`: the ray
starts at the CENTRE of the cast shape's initial world AABB, not its
position, with the line saying why the two coincide for the three shapes
the store builds today and why that is a property of those shapes.

The refactor's proof asserts visited counts MEASURED on the pre-refactor
traversal (cloud 91/181/115/1/91 all, 77/125/89/1/91 closest; line 399 all,
17 closest — the last two re-read off the running code, not copied from the
M1.1.9 comment that records them). Counts and sets alone proved
insufficient: delegating at extent 0.001 left both unchanged, so the pin
also carries an ulp-sensitive case — one box entered at exactly t == 10,
driven through both entries at a bound of 10 and at 10 - 1 ulp.

Three counter-factuals run, each isolating its target: dropping the
inflation fails the three inflation tests and leaves the zero-extent pin
green; delegating at 0.001 fails the pin and the M1.1.9 closed-bound test;
neutralising both bound reads fails five tests including the mandatory
discrimination guard on `queryCast prunes with the collector bound`.
Breaking `inflate` fails both new foundation tests, so their wiring is
observed too.

266/266 at f32 and -Dphysics_f64=true, debug and ReleaseSafe.
`engine-physics-forge.md` §1.11.11. A cast is a raycast from the
configuration-space origin along `-d` against `C = core_A - core_B`,
inflated by `r_a + r_b` — so a sphere cast against a box is a ray against a
ROUNDED box, exactly what `raycast.zig` rejects, which is why the cast has
its own kernel. Covering every bounded convex through its support map, it
has no shape to reject and therefore no error channel at all.

`gjk.zig` is imported for `Simplex(T)` alone and `gjk()` is never called —
the dependency `epa.zig` already declares, for the same reason: a simplex
solver is not a GJK descent. The FROZEN scope mandates reusing `Simplex(T)`
while listing an import set excluding where it lives; the enumerated item
forbids the LOOP, and the repo precedent settles the prose.

`CastExit` is the termination table, one variant per row, so no row is
unobservable. The zero-direction exit is a distinct eighth variant labelled
as belonging to the DOMAIN table, so the seven termination variants stay a
faithful mirror. `castShapeBounded` exposes the ceiling because on the three
cores the store builds 32 is unreachable — the hardest scene converges in 4
— and a guard never seen to fire is a comment with extra syntax.

The "no longer approaching" guard is at TRUE ZERO with the argument written
at the branch: the division is never reached there, a denormal denominator
overflows the step to infinity hence the parameter past `max_distance` hence
a miss by the bound test, and the numerator is strictly positive so no NaN
is reachable. The reference's `-1.0e-18f` is not reproduced.

DEFECT found by the oracles: implemented literally, "l'unique restart
autorisé" as one restart per CALL truncated a sphere/sphere cast whose
closed form is 7 at 6.952526 — for a POINT core the Minkowski difference is
a single point, so every sample repeats and the march is healthy anyway. The
budget is refreshed at each ADVANCE; the scene now converges 6.214835 ->
6.952526 -> 6.999764 -> 7.000001.

RD-1: the bound test is STRICT, against §1.11.11's own "atteint ou dépasse"
row and in favour of the closed interval the same section declares — the
parameter is a lower bound, so reaching the bound proves nothing while
exceeding it does, and the advance can land exactly on the answer. KB patch
pending.

Seven counter-factuals run, all bit: the restart placement (found the real
defect), inflation dropped (270/275), inclusive bound (274/275), ceiling
returning a miss (274/275), normal left as A's outward (269/275), witness
offset dropped (273/275), zero-direction guard removed (271/275 + 1 crash).

275/275 at f32 and -Dphysics_f64=true, debug and ReleaseSafe.
`castShapeBody`, `overlapShapeBody`, `containsPointBody`, `closestPointBody`
— each resolves a body's pose and support shape through `store` and calls
the exact kernel, mirroring `raycastBody` / `gjkPair` / `collidePair`. No new
geometry: this is the wiring.

`*const BodyManager` on all four, so a query cannot wake anything
structurally rather than by convention. Each validates the HANDLE and then
the SHAPE and answers null for either — a live handle whose shape has been
destroyed is a different failure from a removed body, and checking only the
first would read a freed shape.

The core + inflation-radius convention is honoured at the wiring point,
which is where a second one could slip in unseen: the body's `Shape` becomes
a `SupportShape` through the one shared `supportShape`, and nothing on this
path adds a margin. `overlapShapeBody` introduces no threshold at all — the
predicate is "the GJK regime is not separated", whose margin is GJK's own.

The cast and closest-point adapters return WORLD space through two dedicated
types, `BodyCastHit` and `BodyClosestPoint`, rather than reusing the kernel's
`CastHit` whose doc says "A's frame". Unlike `raycastBody`, whose local frame
is the BODY's and differs per candidate, the cast kernel's frame is the
QUERY's and is a traversal constant, so returning it would hand the caller a
contract in a frame unrelated to the body it asked about.

`closestPointBody` follows §1.11.13 in its order: solidity first by the same
membership predicate the point query uses, upstream of any classification;
outside, GJK against a degenerate point core, then
`max(0, core - r_b)` and the surface projection. A hard core reaches no
normalisation at all; where one is reached it is guarded at true zero.

Four counter-factuals. Breaking the KERNEL fails all three adapter tests
(270/280): the adapters do not survive it. Perturbing the ADAPTER by one ulp
fails ONLY the bit-for-bit differential (279/280): it detects a
reimplementation at the finest grain, and is deliberately blind to a
uniformly wrong kernel, which is E3's oracles' job. Skipping the stale-shape
check and waking the body each fail exactly their own test.

280/280 at f32 and -Dphysics_f64=true, debug and ReleaseSafe.
Step one of E5, alone in its own commit so it reads at `--stat` and `-M` as a
move rather than a change. `git mv query.zig query/root.zig`, then the three
ray collectors and the per-candidate `evaluate` they share extracted into
`query/ray.zig`.

The extracted block is BYTE-IDENTICAL to the original, verified by
reconstructing it from `HEAD:query.zig` and comparing: the only alteration is
the three `pub` markers the split requires so `root.zig` can name the
collectors it constructs. Nothing was reworded, renamed or reflowed.

`root.zig` keeps every construction site textually identical by aliasing the
three collectors back (`const ClosestCollector = ray_mod.ClosestCollector;`),
so the entry functions are unchanged line for line. Its own edits are the
mechanical consequences of the move and nothing else: the header path, four
`../` import prefixes, the dropped `narrowphase` import (it left with
`evaluate`), and `hitLess` becoming `pub` because the split puts it and its
caller in different files.

`forge_3d/root.zig` re-exports the package at the same public path, so
`forge_3d.query.raycast` is unchanged for every caller and no test changed
other than its import line.

280/280 at f32, -Dphysics_f64=true and ReleaseSafe.
Step two of E5, after the move landed alone. `query/cast.zig` and
`query/overlap.zig` join the package; the four solver-side mirror types
(`CastQuery`, `CastHit`, `OverlapRequest`, `ClosestPointHit`) and the five
bodies replace the five `@panic` stubs.

The five take the SOLVER scalar and return `[]BodyId`, per §1.11.8's
structural corollary: an entry typed `f32` inside the solver forces the
conversion into the solver, so under -Dphysics_f64 a time of impact would be
narrowed to f32 before leaving the kernel and widened again at the interface
tier. `api/types.zig` and `engine-tier-interfaces.md` §1 are untouched.

The three spec sections meeting a compiler for the first time all hold:
`bodyAabb` is already the TIGHT world AABB and already takes `store`, which
is why `overlapAabb` needs it; `overlapShape`'s predicate is
`gjk(...).status != .separated` with no constant of its own; and
`closestPoint` decides solidity upstream of any classification, `.shallow`
not being an interior.

One ordering key, four consumers: `keyLess` is the identity half — the WHOLE
key for the three overlaps, which have no distance — and `hitLess`,
`castLess`, `closestLess` are it behind an exact distance comparison.
Retention on overflow is replace-worst under that key. No deduplication:
the solver returns bodies, and projecting onto entities is owed by §13.
`unitDirection` is likewise factored so the cast cannot acquire a different
degenerate-direction rule from the ray.

`worldAabb` becomes pub: a cast needs the initial world AABB of a shape that
is not a body, and the swept ray starts at that AABB's CENTRE.

The signature pin now carries two distinct claims: the eight entries at
`Real`, and the public types pinned FIELD BY FIELD. The second half is the
point — those types used to be pinned incidentally by the stubs' signatures,
and now that no entry mentions them a rename there would surface only at the
freeze. A detector that stops detecting when its incidental references go is
not one.

One minimal scene per entry, answers written from the geometry. The
`overlapAabb` guard is observed ON THE TRAVERSAL: a counting collector proves
the broadphase handed the body over through its fat box while the tight box
does not overlap, so the rejection is the exact kernel's.

285/285 at f32 and -Dphysics_f64=true, debug and ReleaseSafe.
E6 covers § Acceptance criteria item by item. Nothing was inexpressible as
written, so nothing was reinterpreted into something adjacent and easier;
the coverage audit is in the brief, entry by entry.

New here: the normal at the time of impact (the same pair cast from four
directions including one on no axis, the sign pinned by the closed form
recomputed in the test rather than transcribed); the initial contact; the
face-inclusive AABB with one ulp past the face as the discrimination;
pointQuery over all three shapes at inside / boundary / just-outside; the
four closest-point behaviours; and, for all five entries, the mask at both
ends of its domain, the exclusion list, a sleeping body, buffer overflow
under the ordering key, invariance under creation-order permutation and
bit-identity between runs.

`initial contact` is built so the distinction is real: the cast shape is a bar
spanning x in [-5, 5] centred at the origin, the hit body a unit box at 4.5
spanning [3.5, 5.5]. The witness IS a point of the hit body, the cast ORIGIN
demonstrably is not, and the two are therefore different — without the middle
assertion the test cannot tell §1.11.11's rule from the one it refutes.

The M1.1.9 invariance test asserted the geometry and not the identity. It now
asserts the ENTITY answered, and that `raycastAll` returns the four entities
in the same order under every permutation. The `BodyId` is deliberately not
asserted: it follows creation order by construction.

The shallow-band test places its probe inside a band that is a few ULPs wide
by construction — `.shallow` holds exactly on `r < dist <= r + margin` with
`margin = conv_k * floatEps * coordScale` — so the probe is placed in those
units and not in metres, above the noise_k deep floor and below the 16-ULP
contact margin. The outside-the-solid precondition and the regime are both
asserted before the claim.

Six counter-factuals run, all bit: `overlapAabb` returning the candidate set
(2 fail), `.shallow` read as an interior (1), the shallow guard itself with
the probe moved out of the band (1), `keyLess` reverted to BodyId alone (4),
the object mask ignored (4 — one per family), and the ceiling returning a
miss (E3). The iteration ceiling is referenced, not doubled.

Two E5 tests removed rather than left beside their successors: a sphere-only
point-query test subsumed by the three-shape one that carries the brief's
item name, and a closest-point test whose three claims each acquired a
dedicated test.

303/303 at f32 and -Dphysics_f64=true, debug and ReleaseSafe.
E7. `bench/forge_3d_shapecast.zig` over the raycast bench's own 10 000-body
static grid, so the two tables are comparable. MEASURED and REPORTED, no
envelope pre-registered: sphere 1300.8 ns/query, box 1344.7, capsule 1244.0,
shape overlap 231.5. The last two rows are a pair that answers a question
rather than reporting a number — a radius-0 sphere cast sweeps a point, so its
traversal is `queryRay` exactly, and the whole difference against the raycast
entry is the GJK march replacing the analytic ray kernels: 1.59x on identical
rays with an identical 0.89 hit rate, which also cross-checks that the two
paths agree on what they hit.

The leak check binds, measured both ways. A deliberate 4 KiB
alloc-without-free with `safety` forced true reports the address and the
bench's own LEAK DETECTED line; the SAME leak with the DEFAULT safety prints
"no leaks". The second run is the one that matters: the default is not a
weaker check, it reports success unconditionally.

The eleven inherited M1.1.5-M1.1.8 envelopes re-measured with one throwaway
probe appended identically to both legs — this branch and `main` at dd7fa1f
through a worktree — at both precisions: ZERO movement, digit for digit.
Probe and worktree removed. One honesty note recorded: resting-box speed at
f32 reads 0.000000000 where M1.1.9's table recorded 0.000000001, and it reads
the same on `main`, so it is a difference between two probes' ninth decimal
and not a movement.

Local §3.6.1 audits clean. Language audit: ZERO French prose, TWELVE counted
verbatim spec quotations, each tabulated with its site. Recorded first because
it nearly produced a false pass — `grep -E "[eee]"` with an accented bracket
class silently matches nothing on UTF-8 here, so the first sweep reported a
clean tree over files that demonstrably contain accented text. Redone in
Python.

Closing grep on the ordering key: 0 occurrences of `(distance, BodyId)` as a
query key anywhere; 1 live mention of "the smaller BodyId", which is the
§1.11.14 residual itself and true as written; the M1.1.9 brief's three lines
correct as history.

`CLAUDE.md` NOT patched — the content was announced with the GO and not
carried, so nothing was written rather than invented. The exact staleness is
tabulated in the brief against the repo rather than recalled: four rows of
`Current state`, internally inconsistent as they stand, plus the L68 tag row
carrying the superseded tie-break formulation.

303/303 at f32 and -Dphysics_f64=true, debug and ReleaseSafe.
E8, three review findings, no deferred debt.

P1 — `closestPointBody` returned `{ distance = 0, position = point }` in
`.deep`, an INTERIOR answer for a point the membership test had just placed
outside, and the comment above it claimed the regime was unreachable. Both
wrong: `.deep` fires whenever the core distance falls inside
`conv_k * floatEps * coordScale`, so a point a few ULPs outside a HARD core
lands there while the membership test — the authority on solidity — says
outside.

The fix keeps to what `.deep` specifies. `closest_a` needed no regime split
at all: A's core is a single point, so it IS the queried point everywhere,
and only `closest_b` was ever regime-dependent. In `.deep` that field is the
ZERO VECTOR and `distance` is 0 — using them would have answered the world
origin. What `.deep` does carry is the terminal simplex, whose vertices hold
`support_b`, so `deepCoreWitness` re-solves the closest-origin problem over
the `w`s for the barycentric weights and combines the `support_b`s with them
— the reconstruction `shapecast.zig` already performs from the same data. The
simplex is in the probe's frame, so world is A-frame + `point`. The three
regimes now share one projection, and the `scale == 0` guard's comment is
corrected: it is REACHABLE in the band.

The band MEASURED against coordScale, which for this probe is RELATIVE
geometry (`|body_pos - point| + coreExtent`), so the band does NOT grow with
the world coordinate: 5.211e-6 at f32 at 1 m as at 10 km. What grows is
ulp(coordinate), and that bounds its REACH — 5 representable points inside it
at 1 m, none at 100 m or 10 km, because the nearest float outside the face is
already past it. An absence of reachability, not of defect; and it explains a
probe that steps by a fraction of the coordinate seeing nothing at scale.

RED-first and TWO-SIDED: 16 eps (in the band) and 64 eps (out of it) through
the same assertions, each with two guards — the membership test really says
outside, and the regime really is the one that side exercises. Plus a pin that
`max_distance == 0` answers only for an interior point. Both RED at 303/305
before the fix. Three counter-factuals bite: the old early return (2 fail),
the unspecified `closest_b` (1), the dropped frame mapping (1).

P2 — the domain assertion was one row of five, on five entries. Measured, not
assumed: `shapeCast` and `closestPoint` asserted only the bound, the other
three asserted nothing, and `store.get(...) orelse return` sat ahead of
everything. All five now assert §1.11.11's domain BEFORE the handle
resolution, which is the substance: a stale handle would otherwise
short-circuit the check and let a NaN pose reach the kernel on the next call.
`assertUnitRotation` is not cosmetic — these rotations are inverses BY
CONJUGATION, and M1.1.9 burned on an f32-unit quaternion widened to f64.
`overlapAabb` deliberately does not assert `min <= max`: §1.11.12 states no
such constraint. Verified the guard fires in the right place — a NaN with a
STALE handle aborts inside `assertFiniteVec` and never reaches the
short-circuit.

P3 — the trailing blank line removed; the FROZEN SECTION is byte-identical to
the first commit, compared programmatically. No RD.

`CLAUDE.md` stays unpatched: the content is suspended, and the Tags line as
drafted describes `closestPoint` without this defect or its correction.

305/305 at f32 and -Dphysics_f64=true, debug and ReleaseSafe.
E9, one review finding, no deferred debt. `engine-physics-forge.md` §1.11.12
(1590 lines) carries the rule.

Reproduced, all three measurements exactly. Against a body whose tight world
AABB is [-2, 2]^3: `min=(1,1,1) max=(-1,-1,-1)` returned ONE body,
`min=(9,9,9) max=(-9,-9,-9)` returned ZERO, an inversion on two axes only
returned ONE. The answer was not "sometimes non-empty" — it was ARBITRARY,
following the amplitude and the axes of the malformation, because
`Aabb.overlaps` is written for well-formed boxes and on an inverted one
reduces to "does the body enclose both bounds".

Fixed by an EXPLICIT REJECTION at the entry, before the traversal, so nothing
is walked. Not an assertion: it holds in debug only and would leave the answer
arbitrary where the engine runs, on an entry returning a `u32` with no error
channel. Not the predicate's job: `Aabb.overlaps` serves the broadphase's hot
path. Not defensive padding: it is the semantics of the region the caller
described, and it belongs where the caller's bounds cross the boundary. The
test is strict `>`, so a DEGENERATE box stays a legal region.

Two-sided: the three malformed forms, each behind a guard asserting the
malformation is real and annotated with what it returned before; then the
well-formed equivalent of each, which must still answer the body; then two
degenerate boxes. Counter-factuals bite both ways — removing the rejection
fails the E9 test (305/306), and making it non-strict fails the degenerate
cases AND `overlapAabb is face-inclusive`, an E6 test written for another
purpose (304/306).

Which other entry takes caller bounds: MEASURED over every box construction in
`query/`. `overlapAabb` is the only one. The other four build theirs from
`worldAabb` (extents are sums of non-negative terms), from `pointAabb`
(degenerate by construction), or from `fromMinMax(p,p).inflate(max_distance)`
— that last one well-formed ONLY because `max_distance >= 0` is asserted, an
implicit dependency now written at `closestPointCandidates`.

The E8 entry's arbitrage is marked WRONG in place rather than edited away: it
read that an inverted box is a well-defined query with an empty answer, which
was reasoning where a measurement was available. The false claim stays visible
with its correction beside it, because what it records is the failure mode.

CLAUDE.md remains unapplied; the corrected paragraph is recorded verbatim in
the brief so the superseded one cannot be used by mistake.

306/306 at f32 and -Dphysics_f64=true, debug and ReleaseSafe.
Current state moves to M1.1.10 code-complete with M1.1.9 closed at
dd7fa1f / v0.11.9-queries-raycast, and the Tags table gains the
v0.11.10-queries-shapecast-overlap row.

The two raw pipes inside `|pos_b - pos_a|` are escaped as `\|` so the
markdown row keeps its four cells, the convention the M1.1.9 row
already follows for `|y|` and `|w|`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@guysenpai
guysenpai merged commit 712e4b5 into main Jul 27, 2026
10 checks passed
@guysenpai
guysenpai deleted the phase-1/forge/queries-shapecast-overlap branch July 27, 2026 16:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant