acdream/docs/research/2026-08-07-s2-static-sphere-contract.md
Erik ce0bfce1cf docs: S2 contract — AP-155's static sphere-as-cylinder emission, both sites pinned
Both static publication paths emit an authored Setup Sphere as a
base-anchored Cylinder (r, 2r) while the live path emits a Sphere for
the same data — different narrow-phase dispatch and a route-dependent
collision difference for the same object. The fix mirrors FromSetup's
step-3 emission at both sites; the contract's first test is the
route-independence property asserted shape-for-shape, and the dispatch
test picks geometry where cap-hit and curve-hit verdicts differ so the
sphere verdict is observable, not inferred.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 01:00:11 +02:00

72 lines
3.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# S2 contract (Campaign S) — AP-155: static publication must emit authored Spheres as Spheres
**Date:** 2026-08-07 (overnight). **Scoped by:** the session lead.
**Implementer:** one Sonnet agent. **Review:** dual Opus.
## The divergence (AP-155, narrowed to its surviving half)
Both static publication paths emit an authored Setup `Sphere` as a
height-capped **Cylinder**:
- `src/AcDream.App/Streaming/LandblockPhysicsPublisher.cs` (~10181036)
- `src/AcDream.Content/LandblockPhysicsContentBuilder.cs` (~672690)
Identical code in both: `ShadowShape.Cylinder(radius = r*scale,
cylHeight = 2r, base = origin*scale - r*ẑ rotated)`. The LIVE path
(`ShadowShapeBuilder.FromSetup`, step 3) emits `ShadowShape.Sphere` for the
same authored data. Consequences: (a) narrow-phase dispatch differs
(CCylSphere tests vs CSphere tests — a mover meets a flat cap where retail
meets a curved surface), and (b) the SAME object collides differently
depending on whether it arrived as a landblock static or a live spawn —
the route-dependence Campaign S's plan calls out. Retail's
`CPhysicsObj::FindObjCollisions` dispatches Spheres to
`CSphere::intersects_sphere` — there is no cylinder substitution anywhere
in retail for this branch.
## The fix
Replace both emissions with `ShadowShape.Sphere`, mirroring
`FromSetup` step 3's emission EXACTLY — same origin/scale composition, same
guards (`Radius <= 0` skip stays). Read FromSetup first and copy its
semantics rather than inventing; if FromSetup's sphere emission differs from
what a plain mirror would produce here (e.g. BoundsCenter handling), STOP
and report the difference instead of choosing.
Then check the ripple: `BuildFloodSpheres`' sphere branch (uncapped) now
sees these as Spheres — flood behaviour matches the live path, which is the
point. `RetailSphereCap` applies to the Cylinder branch only (AP-156's
retirement of the 10-cap on non-cyl branches) — verify with a test, not by
reading alone.
## Tests
1. Parity: for a representative Setup with authored Spheres, the static
publication's registered shapes are shape-for-shape identical (type,
local position, radius, scale) to `FromSetup`'s output for the same
inputs. This is the route-independence property, asserted directly.
2. Dispatch: a mover resolve against a statically-published sphere object
takes the Sphere narrow phase (assert via outcome on a diagonal approach
that distinguishes cap-hit from curve-hit: pick geometry where cylinder
and sphere verdicts differ, assert the sphere verdict).
3. Sabotage: restore the Cylinder emission at ONE site; tests 1 and 2 both
redden; restore.
4. Existing static publication and content-builder suites stay green.
## Measurement (report, not gate)
Count, over installed landblocks already swept by existing content tests
(reuse their enumeration), how many static entities carry sphere-only
Setups — the affected population. Three example object ids.
## Scope — OUT
Register/ISSUES edits (session lead). `ShadowShapeBuilder`,
`ShadowObjectRegistry` internals. The live path. Anything about
SortingSphere (AP-157 is measured and deferred).
## Acceptance
Full suite `dotnet test AcDream.slnx -c Release -m:1` green, totals
reported; sabotage reported verbatim; nothing committed. Only
`C:\Users\erikn\source\repos\acdream`, absolute paths, no subagents;
contradictions → STOP and report.