The handoff's opening said "Nothing is pushed — the branch does not exist on the remote, and there are 388+ unpushed commits ahead of origin/main." All three clauses were wrong, in the direction that would most alarm a successor into thinking the campaign could be lost. main and github/main are bothd4e956b4, and git branch --contains7b3e2895lists main. The campaign was merged and pushed the same day. The measurement error is recorded because it will recur: this repo has TWO remotes. github is live; origin (git.snakedesert.se) has a cached origin/main ref pointing atf6275f45whose ref file has not been written since 2026-04-27. main is 412 commits ahead of that three-month-stale ref, which is where "388+ unpushed" came from. Measuring push state against origin/* here without checking the ref's age produces a false alarm every time. Also opens Campaign S as the ACTIVE work ahead of the remaining M4 vendor slices, per user direction: "I think its better to fix these kind of things before we add new stuff. Physics and collisions are vital." Clean-room gate ata0690947, all 43 bin/obj directories deleted first per the closeout's own rule 4: 11,231 passed / 4 skipped / 0 failed across all nine projects. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
326 lines
17 KiB
Markdown
326 lines
17 KiB
Markdown
# C5c closeout + successor handoff — the placement cutover's automated half is done (2026-08-06)
|
||
|
||
**Read this before any C5c or post-campaign work.** It is the successor to
|
||
`2026-08-05-c4-closeout-handoff.md` (whose ⚠ BISECT HAZARD block still
|
||
applies and is repeated in §6).
|
||
|
||
Branch `claude/acdream-physics-divergence-5aa784`, 21 commits from `02578441`
|
||
to `7b3e2895`.
|
||
|
||
**CORRECTED 2026-08-06 — the original line here said "Nothing is pushed — the
|
||
branch does not exist on the remote, and there are 388+ unpushed commits ahead
|
||
of `origin/main`". That was FALSE, and false in the direction that would most
|
||
alarm a successor.** The campaign was merged to `main` and pushed the same day:
|
||
`main` and `github/main` are both `d4e956b4`, and `git branch --contains
|
||
7b3e2895` lists `main`.
|
||
|
||
**The measurement error, because it will recur.** This repo has TWO remotes.
|
||
`github` (`git@github.com:eriknihlen/acdream.git`) is the live one. `origin`
|
||
(`https://git.snakedesert.se/erik/acdream.git`) is a second remote whose cached
|
||
ref `origin/main` still points at `f6275f45` and whose ref file has not been
|
||
written since **April 27** — three months stale. `main` is 412 commits ahead of
|
||
that ref, which is where "388+ unpushed" came from. **Never measure push state
|
||
against `origin/*` in this repo without checking the ref's age first**; compare
|
||
against `github/main`, or check `git branch --contains <sha>`.
|
||
|
||
---
|
||
|
||
## 1. One-paragraph state
|
||
|
||
Every implementation item in the placement cutover campaign is now landed and
|
||
dual-reviewed. **C5b** (classify-before-merge), **#280** (portal destination
|
||
prefetch), **#276's remainder**, **AP-22** and **AD-10** all shipped with both
|
||
review lenses PASS. **#309** was accepted as a standing divergence by user
|
||
decision rather than fixed. What remains for C5c is **entirely connected/visual
|
||
work plus the ledger close** — none of it can be done without the user at the
|
||
client, and the probe strip cannot be done before it.
|
||
|
||
---
|
||
|
||
## 2. What landed, by slice
|
||
|
||
| Slice | Commits | Outcome |
|
||
|---|---|---|
|
||
| **C5b** — classify before merge (#275) | `735f0a72`, `ed806997`, `23aa62f2`, `ff100cf3`, `9ee9c1a1` | Retired **AP-131** and **AD-60**'s legacy half. Seven production lines; five commits, because the review found a headless regression the change itself introduced. |
|
||
| **#280** — portal destination prefetch | `3aab05b0`, `73cdb95c`, `bcb66ccd` | Reveal window now derives from the live streaming radii. **D-1**, an unrecoverable portal hang, was found by review and fixed. |
|
||
| **#276 remainder** | `408c8e8f`, `fafc0b65` | Settle now adopts the transition's resolved cell across an indoor seam. |
|
||
| **#316**, **#317** | `429775d4`, `1d2d4bb8` | Report-only investigations. #316 **cosmetic**; #317 **no retail basis**. |
|
||
| **#309** | `43cfdc4a` | **Accepted as a standing divergence** (user decision), not a planned fix. AP-136 is its permanent record. |
|
||
| **AP-22** | `bc4679cd`, `619de97a`, `ef976c6d` | Invented collision cylinder deleted in **all three** copies. Row retired. |
|
||
| **AD-10** | `fe6ee877`, `886333a2`, `fb454b74`, `2223ed17`, `7b3e2895` | **Retired by deletion** — its stated justification was false at HEAD. |
|
||
|
||
---
|
||
|
||
## 2.5 C5c's automated gate — PASS
|
||
|
||
Run at `7b3e2895` on the final binary, **after deleting all 44 `bin`/`obj`
|
||
directories** (see §7 rule 4 — this session had three stale-artifact
|
||
incidents, so an incremental result would not have been evidence):
|
||
|
||
```
|
||
dotnet test AcDream.slnx -c Release -m:1 # ACDREAM_PAK_PATH set
|
||
```
|
||
|
||
**11,196 passed / 4 skipped / 0 failed**, all nine projects:
|
||
|
||
| Project | Passed | Skipped |
|
||
|---|---|---|
|
||
| AcDream.App.Tests | 4,172 | 3 |
|
||
| AcDream.Core.Tests | 4,261 | 1 |
|
||
| AcDream.Runtime.Tests | 1,220 | 0 |
|
||
| AcDream.Core.Net.Tests | 764 | 0 |
|
||
| AcDream.UI.Abstractions.Tests | 546 | 0 |
|
||
| AcDream.Content.Tests | 125 | 0 |
|
||
| AcDream.Headless.Tests | 89 | 0 |
|
||
| AcDream.Bake.Tests | 15 | 0 |
|
||
| AcDream.Cli.Tests | 4 | 0 |
|
||
|
||
The 4 skips are the pre-existing set; no new skip was added anywhere in the
|
||
campaign. **None of the three known load-sensitive flakes (#302, #308, #321)
|
||
fired** in this run — but they are separately filed and must never be
|
||
conflated if one does.
|
||
|
||
Net movement across the campaign: 11,106 at `02578441` → **11,196**, +90.
|
||
|
||
---
|
||
|
||
## 2.6 Connected gate — #280 USER-PASSED 2026-08-06; the rest NOT RUN
|
||
|
||
**#280's reveal gate: PASS, user-accepted.** User's words: *"now portal space
|
||
takes longer but terrain is complete when I exit."* Both halves are the
|
||
criteria the gate specified — a measurably longer hold (the fix doing its job)
|
||
and a complete destination on reveal (the acceptance).
|
||
|
||
Probe evidence, `ACDREAM_PROBE_PARK=1`, retail UI, Release, `ACDREAM_STREAM_RADIUS`
|
||
unset (`c5c-gate-after2.log`):
|
||
|
||
- **Three Portal reveals plus a Login reveal, every one at `radius=12`.**
|
||
Pre-#280 this was a hardcoded `1`.
|
||
- Each portal hold raised the wait cue at ~5.0 s (`elapsedMs=5031 / 5000 /
|
||
5010`) before `materialized → world-visible → complete`.
|
||
- Generation 4 revealed cell `0x3032001C` — the same cell as generation 1's
|
||
login, i.e. a repeat visit to an already-seen landblock.
|
||
- The park path was exercised (95 `[park...]` lines).
|
||
|
||
**An accidental but genuine A/B.** An earlier run in the same session set
|
||
`ACDREAM_PROBE_REVEAL_RADIUS=1`, which is a radius VALUE, not an on/off flag —
|
||
so it forced the pre-fix window. The user observed the original defect
|
||
(landscape building in the background) with `radius=1` on every probe line, and
|
||
did not observe it with `radius=12`. That is the before/after pair the gate
|
||
asked for, obtained by accident. **Successors: this probe overrides the radius;
|
||
it does not merely enable logging.**
|
||
|
||
### NOT RUN — do not read this section as gate coverage
|
||
|
||
| Owed gate | Status |
|
||
|---|---|
|
||
| D-1's two reachability scenarios (double-recall to the same landblock with a walk between; mid-hold quality-preset drop) | **NOT RUN.** Generation 4's repeat visit is suggestive but is not the demote path D-1 needs. |
|
||
| AP-136's six-step park check | **NOT RUN as the six-step protocol.** The probe fired 95 times incidentally; that is not the check. |
|
||
| Route-7 thickening | **NOT RUN** — `ACDREAM_PROBE_REMOTE_TELEPORT` recorded **0** lines; no remote teleport occurred. |
|
||
| Two-client observation | **NOT RUN** |
|
||
| Canonical nine-stop soak | **NOT RUN** |
|
||
| Lifecycle/reconnect route on the final binary | **NOT RUN** |
|
||
| AD-65 / AD-66 local-player visual gate | **NOT RUN** |
|
||
|
||
The campaign ledger closes with these outstanding **by user direction**, not
|
||
because they were discharged. Anyone citing "C5c passed" must cite §2.6's table
|
||
alongside it.
|
||
|
||
---
|
||
|
||
## 3. WHAT C5C STILL OWES — all of it needs the user
|
||
|
||
Nothing below can be discharged without a live client. The automated half is
|
||
complete.
|
||
|
||
### 3.1 Connected gates, batched into one sitting
|
||
|
||
1. **#280's reveal gate.** The user's original repro was a **recall**, so the
|
||
route needs a **lifestone leg**, not only `/teleloc`, plus a first-login
|
||
stop. A/B with `ACDREAM_PROBE_REVEAL_RADIUS`, `ACDREAM_STREAM_RADIUS`
|
||
unset. The pre-fix run is *expected to show the defect*; the post-fix hold
|
||
must be measurably **longer**. If it isn't, the gate widened nothing.
|
||
2. **#280 D-1's two reachability scenarios, never reproduced live**: two
|
||
consecutive recalls to the **same** landblock with walking in between, and
|
||
a mid-hold quality-preset drop. Both are fixed and unit-covered; neither has
|
||
been seen on a running client.
|
||
3. **AP-136's six-step park check** — `ACDREAM_PROBE_PARK=1`. **This survives
|
||
#309's deferral**: it validates the SHIPPED rollback path
|
||
(`restorableOnCancel` in `SubmitPreparedPlacementCore`, the shared core
|
||
behind every production placement), not the deferred fix.
|
||
4. **Route-7 thickening** — its gate passed on one `cause=propagate` probe line;
|
||
the evidence is THIN.
|
||
5. **AD-65 / AD-66** need a local-player visual gate before any fix (see §5).
|
||
6. **C5c proper**: two-client observation, canonical nine-stop soak,
|
||
lifecycle/reconnect route on the final binary, and the user's visual matrix.
|
||
7. **AP-156 — the BSP flood-sphere placement fix (`b52967de`).**
|
||
**The criterion is CONTAINMENT, not size, and a null result on tall props is
|
||
EXPECTED.** Two things must be said to the user before this one is run, or
|
||
its outcome will be misread in both directions:
|
||
- *"A prop stopped blocking"* is **not** by itself a bug report. AP-156
|
||
shrinks the flood for 143 of the 172 AP-152 Setups on purpose, because
|
||
retail's BSP branch (`calc_cross_cells` @0x00515230, `0x0051528f jne`)
|
||
contributes **no primitive at all** — the old flood was larger only
|
||
because it included a sphere retail never reads. The question to ask of
|
||
any such observation is whether the object's own BSP geometry still
|
||
reaches the cell it stopped blocking from. If it does, that is a bug; if
|
||
it does not, that is retail.
|
||
- *"A tall prop STILL does not block"* is the expected symptom of
|
||
**AP-158 / #333**, not of this fix. AP-156 puts the geometry in the right
|
||
cell; acdream's own `maxReach` broadphase filter — which retail does not
|
||
have at all — then discards it one layer down, for 118 of the 477 unique
|
||
installed physics-BSP GfxObjs. **A null result on tall props is not
|
||
evidence against AP-156.** Run this gate after #333, or run it on
|
||
short/wide off-centre props where the offset is inside the ~2.5 m budget.
|
||
|
||
Population, for the user: the change touches **525 of the 530** BSP-bearing
|
||
Setups (not the 172 the commit body names — that is AP-152's dispatch
|
||
population). See the AP-156 register row.
|
||
|
||
### 3.2 Then, and only then
|
||
|
||
**The probe strip — DEFERRED 2026-08-06, deliberately, and the ledger closes
|
||
without it.**
|
||
|
||
Six flags — `ACDREAM_PROBE_REMOTE_LANDING`, `ACDREAM_PROBE_REMOTE_SLIDE`,
|
||
`ACDREAM_PROBE_PARK`, `ACDREAM_PROBE_REMOTE_TELEPORT`,
|
||
`ACDREAM_PROBE_CHILD_CELL`, `ACDREAM_PROBE_LOCAL_TELEPORT` — plus
|
||
`ACDREAM_PROBE_REVEAL_RADIUS`.
|
||
|
||
**Not stripped, because §2.6's table says their gates were never run.** Only
|
||
#280's reveal gate was discharged. Route-7 thickening recorded zero
|
||
remote-teleport lines; AP-136's six-step park protocol was not performed; the
|
||
two-client observation, nine-stop soak and lifecycle/reconnect route did not
|
||
happen. Stripping the family now would delete exactly the instrumentation
|
||
those still-owed gates need, which is the failure this section was written to
|
||
prevent — so honouring it means *not* stripping, even though the campaign is
|
||
closing.
|
||
|
||
`ACDREAM_PROBE_REVEAL_RADIUS` is kept too, despite #280 being closed: **AP-149**
|
||
(outer ring accepts terrain-only) and **#326** (the missing Viewing Distance
|
||
option) are both open and both would want the same A/B harness. Retiring it now
|
||
would mean rebuilding it.
|
||
|
||
**Successor:** strip the family when the §2.6 gates are actually run, not on a
|
||
calendar. Note again that `ACDREAM_PROBE_REVEAL_RADIUS` is a radius **value**,
|
||
not a boolean — setting it to `1` silently reproduces the pre-#280 defect.
|
||
|
||
### 3.3 Then close the ledger
|
||
|
||
Register / roadmap / milestones / memory, and the campaign ledger close.
|
||
|
||
---
|
||
|
||
## 4. Issues filed this session
|
||
|
||
| # | Subject |
|
||
|---|---|
|
||
| **#321** | `DatSoundCacheTests` concurrent-decode dedup, full-suite load (third load-sensitive flake) |
|
||
| **#322** | Two callers compute the same pre-placement flags from the same inputs |
|
||
| **#323** | A far-snap store can silently stale a pending initial-create receipt |
|
||
| **#324** | Graphical and no-window hosts run parallel, non-shared inbound routes |
|
||
| **#325** | **Gate A's teleport test is narrower than retail's** — `==` where retail is "not older" |
|
||
| **#326** | acdream has no Viewing Distance option (retail's `Render.LandscapeDrawDistance`) |
|
||
| **#327** | No analogue of retail's DDD prefetch progress readout |
|
||
| **#328** | Camera far plane hardcoded 5000 f; retail's `zfar` is byte-verified 4000 |
|
||
| **#329** | Portal wait cue arms 5 s late; retail emits per tunnel rotation segment |
|
||
| **#330** | **Headless registers no live-entity collision at all** — a bot walks through every NPC |
|
||
| **#331** | **`ResolveWithTransition` refuses ALL uphill motion with a body supplied** |
|
||
| **#332** | Headless bots appear to have no remote dead-reckoning |
|
||
|
||
**#331 is the one to look at first.** Severity was raised from UNKNOWN once the
|
||
discriminator was found: it is the **`body:` parameter**, not the fixture. With
|
||
`body: null` the uphill sweep climbs; with a body it returns `ok=False` and zero
|
||
movement — under a call profile identical to the local player's
|
||
(`IsPlayer | EdgeSlide`, human two-sphere Setup), on ramps as shallow as **1.1°**.
|
||
A diagonal request keeps cross-slope X and zeroes only up-slope Y. Nothing in
|
||
the suite asserts uphill progress on a walkable slope, which is why it was
|
||
invisible — the test that found it passed **vacuously**.
|
||
|
||
---
|
||
|
||
## 5. Register movement
|
||
|
||
**Retired:** AP-1, AD-1 (C5a) · AP-145 (C5a) · **AP-131**, **AD-60**'s legacy
|
||
half (C5b) · **AP-22** · **AD-10**.
|
||
|
||
**Filed:** AP-147 (delta-stream cardinality) · **AP-148** (#325's Gate A
|
||
narrowing) · AP-149 (outer ring accepts terrain-only) · **AP-150** (wait-cue
|
||
delay is not retail's trigger) · AP-151 (gate stricter than retail on the
|
||
GPU-upload axis) · **AP-152** (live path emits primitives *and* BSP additively
|
||
where retail is exclusive; 172/5,935 Setups incl. BSP doors) · AD-64 (the
|
||
duplicated residency decision) · **AD-65**, **AD-66**.
|
||
|
||
**AD-65 deserves attention.** Its magnitude was filed at half the truth: the row
|
||
states `cos²θ` but quantified `1−cosθ`. Corrected to **25% short at 30°, 50% at
|
||
45°**, confirmed by measurement (#331's probe: 0.0735 m for a 0.1 m request at
|
||
30.96° = `cos²(30.96)`). **It is a live lead for #269's slope-slide residual** —
|
||
and note that project memory's #269 do-not-retry covers *friction and jump
|
||
chains*, which are byte-exonerated; `AdjustOffset` is a different function and
|
||
is **not** covered by it.
|
||
|
||
---
|
||
|
||
## 6. ⚠ BISECT HAZARD — carried forward
|
||
|
||
Commits **`735f0a72..23aa62f2`** contain a live headless defect: every remote
|
||
entity's `FullCellId` is frozen at its placement value for the whole session in
|
||
`AcDream.Headless`, and the local player loses one of AP-146's three
|
||
cell-refresh edges. Introduced by `735f0a72`, fixed at `ff100cf3`. Nothing
|
||
throws; no test in the range catches it.
|
||
|
||
---
|
||
|
||
## 7. Process findings — stated as rules, each paid for this session
|
||
|
||
1. **A blast-radius enumeration only reaches as far as the call graph its author
|
||
walked.** C5b's was performed over the graphical `OnPosition` path and missed
|
||
`AcDream.Headless` entirely — 11,000 green tests, one frozen host. Ask *which*
|
||
traversal, and what it structurally could not reach. Both hosts, every time.
|
||
2. **Two independent adversarial reviewers converging is near-proof; a lone
|
||
finding is a lead.** It happened three times this session (the headless hole,
|
||
the missing payload gate, D-1) and all three were real.
|
||
3. **Assume a test does not discriminate until sabotage proves it.** SEVEN green
|
||
tests covering nothing were found or avoided: C5b's conservation test, #276's
|
||
three settler tests, #280's tautological integration fixture, the atlas-tier
|
||
seam 4,170 tests missed, AD-10's only existing test (a dead method with a
|
||
hard-coded formula), and AD-10's contract-specified T1 sabotage which came
|
||
back green and was rejected rather than shipped.
|
||
4. **`bin`/`obj` can serve deleted code even under `--no-incremental` and
|
||
`-t:Rebuild`.** Three incidents. Delete all 44 directories before any
|
||
verdict-deciding result. A stale artifact does not look like an error — it
|
||
produces a plausible failure, or a plausible pass.
|
||
5. **Every contract in this campaign has been wrong at least once, and the
|
||
implementation is what catches it.** C5b's §3-D2 (a "dead" ternary that was
|
||
live), #280's §7 (three false items), AP-22's §7.1 (a literal that was a
|
||
three-way condition), AD-10's §7.1 (a non-discriminating sabotage). Brief
|
||
implementers to rebut, and treat a reasoned rebuttal as more valuable than a
|
||
compliant edit.
|
||
6. **Verify a cited address is the construct you claim.** AP-150 mis-cited
|
||
`0x004D7064` — a `PStringBase` constructor — as the `SendNotice` call,
|
||
*despite* being filed with a byte-level disassembly. Precision of method does
|
||
not prevent an error of line.
|
||
7. **Binary Ninja drops flag tests.** `if (-((eax_7 - eax_7)) == 0)` renders an
|
||
always-true where a real wrap-safe compare lives. Confirmed at Gate A
|
||
(0x00454054), `DoVectorUpdate` (0x004521F5), `HandlePlayerTeleport`
|
||
(0x00452186), and `FindObjCollisions`. Disassemble the PDB-paired binary
|
||
wherever a comparison or constant is load-bearing.
|
||
8. **Do not fan out subagents.** Two blowups: six agents spawning their own
|
||
children exhausted a session usage limit and killed four tasks mid-flight;
|
||
a later three spawned five more. Every brief must say **"do not spawn
|
||
subagents"** explicitly — none of the early ones did. One code-writer at a
|
||
time; the shared worktree tolerates no more.
|
||
9. **A shell failure inside a compound command can leave a commit claiming work
|
||
it did not do.** `ef976c6d` was needed because a heredoc invoked `python`
|
||
(absent here; `py` is the binary) while the commit still reported success.
|
||
|
||
---
|
||
|
||
## 8. Where to start
|
||
|
||
- **Post-campaign:** #331 first (§4), then AP-152 and #330 — both are real
|
||
collision divergences with user-visible consequences.
|
||
- **Domain entry points remain** `claude-memory/project_physics_collision_digest.md`
|
||
and `claude-memory/project_render_pipeline_digest.md`.
|
||
- **The campaign plan** (`docs/plans/2026-08-02-placement-cutover.md`) item 5 now
|
||
records AP-22 and AD-10 as retired; item 3 records #280 as done.
|