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>
This commit is contained in:
parent
2edfc70467
commit
ce0bfce1cf
1 changed files with 72 additions and 0 deletions
72
docs/research/2026-08-07-s2-static-sphere-contract.md
Normal file
72
docs/research/2026-08-07-s2-static-sphere-contract.md
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
# 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` (~1018–1036)
|
||||||
|
- `src/AcDream.Content/LandblockPhysicsContentBuilder.cs` (~672–690)
|
||||||
|
|
||||||
|
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.
|
||||||
Loading…
Add table
Add a link
Reference in a new issue