Adds `docs/reports/2026-07-29-overnight-report.md` and brings the Campaign V report page's status section in line with what is actually true. The report leads with what needs the user, because most of it is genuinely blocked rather than merely unfinished: the #259 reboot and the five V11 runtime gates behind it, the enum campaign's three decisions (adopt `WeenieError` wholesale? adopt retail's `SoundId` table or record our subset as a divergence? re-clone the reference repos), the wire audit's one instrumented session, and the walked-portal follow-up for #256/#257. Then what landed, with numbers, then Campaign V's final state. Two notes worth surfacing rather than burying. `references/` is empty **in the main checkout**, not just in a worktree — which is why 456 of the enum campaign's 864 property members are single-sourced, and why CLAUDE.md's "cross-reference at least two of them" is currently unexecutable as written. And two `MEMORY.md` entries index research documents that exist nowhere: not in the tree, not under any ref, and the memory directory has no `research/` folder at all. Two separate sessions hit that wall tonight and both regenerated from scratch. User-level memory is out of my reach, so it is recorded as a needs-user item with the two documents that supersede them. The Campaign V page said V8 was running, V9 pending and V10/V11 ahead. All four have landed. Its status section now records the deletion (204 files, +1,870 / -27,607), the three findings worth keeping — Chorizite survives on the pak format rather than on `IUniformBuffer`, `Studio/SampleData.cs` was production code, and `WbMeshAdapter.Dispose()`'s GPU-work wait had been silently dead since V6a — and, plainly, that the runtime gates did not run because this machine cannot create a Win32 Vulkan surface for any process. A gate that could not run is not a gate that passed. The #248 and #250 entries are updated to DONE with their outstanding halves named. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
21 KiB
Overnight consolidation — 2026-07-29
Branch: claude/git-sync-status-5fb1d2
Started at: d312bd2f (Campaign V slice V11 landed; OpenGL deleted)
Sessions merged: the enum verification campaign and the wire-stack audit
Machine constraint: #259 — Win32 Vulkan surface creation is
broken process-wide, so no client was launched. Everything below is build,
test and documentation.
1. What needs you
Open questions first, because several of them gate work that is otherwise ready to start. Nothing here is blocked on more analysis — each one needs a decision or a machine that can make a window.
1.1 Reboot the machine, then run five gates
vulkaninfo --summary — a Khronos tool containing no acdream code — fails at
vkGetPhysicalDeviceSurfaceCapabilitiesKHR with ERROR_UNKNOWN. Vulkan itself
is healthy (both adapters enumerate and report 1.4); Win32 surface creation is
what is broken, and it is broken for every process on the box. It was bisected
to a pre-V11 commit whose offline capture had succeeded three hours earlier, so
it is transient driver/compositor state of the class a reboot clears.
V11 is committed and statically green. Its runtime evidence is outstanding, and this is the exact outstanding list from campaign §5.5.24:
| Gate | Command |
|---|---|
| Offline pixel gate (VK self-differential) | tools/run-offline-pixel-gate.ps1 -Out artifacts/v11-post -Baseline artifacts/v11-pre -SkipBuild |
| Repeat connected gate | tools/run-repeat-connected-gate.ps1 -Runs 3 |
| Connected world-lifecycle route | tools/run-connected-world-lifecycle-gate.ps1 |
| Validation proven-loaded, zero errors | any connected launch with ACDREAM_DEVTOOLS=1 |
| Working-set re-measure | the offline scene, once it renders |
The pre-deletion baseline for the self-differential is already on disk at
artifacts/v11-pre, captured before the deletion. The left-hand side of the
comparison survived the fault; only the right-hand side is missing.
Also now runtime-outstanding: #248's near-plane fix landed tonight with a unit test, but its other acceptance criterion — unchanged culling in the offline pixel gate and the connected route — needs the same window. The change can only tighten culling toward the true frustum, which the issue had already established is the safe direction, but it has not been seen running.
1.2 Three decisions the enum campaign could not make
The campaign explicitly refused to guess on these and recorded them rather than inventing an answer. All three are yours.
WeenieError— adopt 362 server-side status codes wholesale? acdream has 16 members; the catalog'sStatusMessagehas 372, with 10 shared and zero conflicts. Retail'scharError(26 members) is a different enum — character-creation errors only — so retail cannot arbitrate this one. Register row AP-15 already tracks that our translation table covers only ~30 common codes. Ideally paired with retail'sstring_table.binfor the real sentences.SoundId— adopt retail's table, or record the subset as a divergence? Ours is a curated 23-member local subset of retail'sSoundType(206), with acdream-local names (FootstepGrass,BuffApplied) that do not correspond 1:1 to retail members. Either adopt wholesale, or give it a register row. Neither was done.- Re-clone the reference repos. Five of six are empty — and this is true
of the main checkout, not just a worktree. It has two concrete costs. The
enum campaign left 456 of 864 property members single-sourced (transcribed
from the catalog, no independent attestation) that ACE and Chorizite would
promote to two-oracle confirmed. And CLAUDE.md's "cross-reference at least two
of them" is currently unexecutable as written, so every session re-solves it
privately — four duplicate ACE copies under
%TEMP%are the evidence. Working copies exist atC:\Users\erikn\source\repos\client\ACEandC:\Users\erikn\source\repos\holtburger; Chorizite.ACProtocol was not found anywhere on disk and needs a genuine re-clone.
A fourth, cheaper lead the campaign flagged but could not pursue: the client
DAT's own EnumMapper file type is a genuinely retail oracle for property
names, and acclientlib already has a reader for it. That is the most promising
unexplored route to attesting the single-sourced members without any re-clone.
1.3 One instrumented connected session
The wire audit's highest-leverage next action, and it needs a connected client:
ACDREAM_DUMP_OPCODES=1
on an otherwise normal Coldeve session. The audit catalogued all 349 opcodes but could not rank the 128 MISSING rows, because nothing tells it which of them the server actually sends during play. One environment variable converts "128 missing parsers, unknown priority" into a real work order. This is the cheapest high-value item on the board.
1.4 Walked-portal follow-up for #256 and #257
Both were observed in the same long live Coldeve session with repeated town-portal-network transits, and both are HIGH:
- #256 — server-spawned signs and portals go visually missing after repeated transits while remaining interactive, so the entity is alive in the object table and only its render projection is gone.
- #257 — working set balloons to ~1.5 GB against the ~930 MiB neighborhood the campaign baselines measured, suggesting per-transit accumulation.
Neither has been reproduced since, and neither can be worked without a client. Both are marked "backend attribution pending" — whether they are Vulkan-era or pre-existing is unknown, and a walked portal-network session is the only thing that settles it. Worth doing in the same sitting as §1.1, since the machine will already be up.
1.5 Two MEMORY.md entries point at documents that do not exist
Out of my reach to fix — user-level memory lives outside this repo — so flagging it here instead.
claude-memory/MEMORY.md indexes research/2026-06-04-property-enum-divergence.md
(the "929 values across 7 enums" ledger) and
research/2026-06-04-magic-number-audit.md. Neither exists — not in the
working tree, not under any ref (git log --all --diff-filter=A finds no add),
and the memory directory has no research/ subfolder at all. The same pattern
holds for the 2026-06-04 combat-math and wire-catalog drops.
This has a real cost, already paid twice tonight. The enum campaign could not reproduce or audit the 929 figure and regenerated from scratch, arriving at 864 property members; the wire audit hit the identical wall. A link that silently resolves to nothing is worse than no link, because a future session plans around a document that does not exist. Suggest either restoring them or repointing those index entries at the two documents that landed tonight, which supersede them:
docs/research/2026-07-29-enum-verification-campaign.mddocs/research/2026-07-29-wire-stack-audit.md
2. What landed overnight
Six commits on claude/git-sync-status-5fb1d2, d312bd2f → HEAD.
2.1 The enum verification campaign (merged)
github/overnight/enums (c19680fd, 5 commits) merged with no conflicts.
It was cut at b70b9832, before the deletion, and its subject turned out to be
disjoint from V11's: the campaign lives entirely in AcDream.Core and its tests,
while V11 emptied AcDream.App. Not one file was touched by both.
- 17 files, +3,767 / −27 lines
- +597 tests, verified exactly — the
Propertiesnamespace alone runs 597 - Names for AC's seven property tables, verified against two oracles
- Corrections to
DamageType's rotated bits andItemType's shifted craft ladder - The retail members the equipment and physics enums were missing
- Names for
AmmoType,CombatUseandItemUseable
2.2 The wire-stack audit (merged)
github/overnight/wire-audit (41f74fcd, 6 commits), same base, one conflict
— see §2.3.
- 14 files, +2,068 / −38 lines
- +59 tests;
AcDream.Core.Net.Testswent 600 → 659 - Three real parser fixes: ranged speech carries a range float the parser was
eating; a chat type that is never sent was silently dropping every transient
string on it;
xpSpentis a dword on the wire where we were writing eight bytes - The transport flag word pinned against ACE across all 23 bits
- Golden fixtures generated from ACE's own writer rather than hand-typed hex
- The audit document, covering all 349 opcodes
2.3 The one merge conflict: #255, reconciled
Both branches reopened #255 — the RetailDatLoader concurrency tests that
measure the thread pool rather than the loader — on the same day, from different
trees, without knowing about each other.
Neither is a duplicate. The V11 closeout saw 2 failures in 5 complete-solution
Release runs on the post-deletion tree; the wire-audit session saw 2 in 4 on
the pre-deletion tree; both saw 124/124 in isolation every time. They
independently reached the same conclusion — TaskCreationOptions.LongRunning is
a hint, not a guarantee — and independently proposed the same fix, a rendezvous
inside the read stub.
Resolved by merging the two notes into one issue with both evidence sets kept as labelled subsections, rather than one overwriting the other. Four failures across nine runs on two trees is a materially stronger case than either half, and the agreement between two blind observations is the part worth preserving. No assertion was weakened; the fix itself remains open.
My own 20-run baseline tonight independently reproduced it a third time — 4 failures in 20 complete-solution runs — which is consistent with both.
2.4 #248 — the near plane is col3, not col4 + col3
FrustumPlanes.FromViewProjection extracted the near plane with the
Gribb-Hartmann form written for OpenGL's [-1,1] clip-space z range, against
projections from Matrix4x4.CreatePerspectiveFieldOfView, whose range is [0,1].
Under [-1,1] the near plane is clip.z = -clip.w, which is col4 + col3;
under [0,1] it is clip.z = 0, which is col3 alone.
The error put the effective near threshold at -n·f/(2f-n) — about 0.5 m where
the retail chase camera asks for 1.0 m — which only ever kept geometry the true
frustum would have dropped. Harmless, and still wrong; it is the same mistake
that was visible in PortalProjection, where it culled the cell behind a
doorway the camera stood close to.
Pinned by a theory over four near/far pairs asserting the extracted plane is unit length, faces down -Z, and stands off the eye by exactly the camera's near value. The test was run against the old formula before commit and fails all four cases, so it measures the fix rather than merely accompanying it. The far plane is unchanged under both conventions and is pinned alongside so a later edit cannot drift it.
Status: DONE for the unit-test half of its acceptance criterion; the gate half is in §1.1.
2.5 #250 — fixed at the measurement, and the family was larger than four
The four named members shared one root: the measured window was never the
warmed path. This had been dismissed as inherent noise in
GC.GetAllocatedBytesForCurrentThread three separate times. It is not noise.
| Test | Warmup | Measured window |
|---|---|---|
UiDatFontTests |
1 call | a 10,000-iteration loop written inline |
RenderFrameProductTests |
8 calls | a 1,000-iteration loop written inline |
CurrentRenderSceneOracleTests |
1 call | a 10,000-iteration loop written inline |
ArchRenderSceneTests |
Apply(registrations) |
Apply(**updates**) — a different switch arm |
Two mechanisms follow. A test method is JIT-compiled at tier 0 like anything
else, and a long-running loop in tier-0 code is replaced mid-flight by on-stack
replacement, which compiles on the thread running the loop — so its bookkeeping
is charged to the window being measured. That is the loop-shaped ones. And
ArchRenderSceneTests warmed one arm of a switch and measured the other, so the
measured call was the first ever into ApplyUpdate and paid that arm's tier-0
JIT, type loads and static initialisation inside the window;
RenderFrameProductTests warmed 8 times, below the tier-0 call-counting
threshold of 30, so promotion was still pending when measurement began.
That also explains the signature nobody could account for — clean in isolation, failing about one run in three. Alone, the process is quiet and the runtime has finished before the assertion arrives. Alongside eight other test assemblies, tier-0 compilation never stops, the call-counting delay is re-armed continually, and the work slides into the window.
The fix is tests/AcDream.App.Tests/ZeroAllocationProbe.cs. It invokes the
step many times before measuring anything, then measures windows that run the
same already-warmed loop over the same already-taken path. Each window is a
batch of 32 invocations and it reports the minimum across 4 of them. Both
halves are load-bearing: the minimum excludes a one-time cost, and the batch is
what keeps the assertion as strong as the loops it replaces — minimising over
single invocations would report zero for a path that allocates every tenth
call. I had written it that way first and the apparatus test caught it.
The bound is untouched: exactly zero, no tolerance, no retry, no assertion
relaxed. ZeroAllocationProbeTests proves the apparatus can still fail — a
step allocating every call reads above zero and does throw, a first-invocation
cost reads as zero, a cost every tenth call is caught, and the one stated limit
(the batch must cover the period) is pinned as a test rather than left as prose.
The family was larger than four. The first pass converted only the named four.
A 20-run baseline disproved "none of the others has been observed failing" within
minutes — LiveEntityRuntimeTests failed in run 2, StaticRenderProjection
JournalTests in runs 14 and 18, both the same shape, neither previously
recorded. Run 19 was sharper still: the issue named
SurfaceOverrideFingerprint_DictionaryHotPathAllocatesNothing, and the first
pass had converted a different test in that same file. Matching by file was not
matching by test.
All ten strict-zero sites are now on the probe. Two came out stricter rather than merely steadier:
StaticRenderProjectionJournalTestswas measuring a synchronise whose journal does not coalesce. Repeating it grew the journal by 1,000 entries per call — 192,000 by the end of a probe run — so the steady state it claimed to test did not exist, and the single-call window had been hiding that. Its step is now the whole frame cycle, synchronise and drain, puttingDrainToinside the measured window for the first time.RetailInboundEventDispatcherTestsasserted a hard-coded 1,001 callbacks; it now counts its own dispatches and pins the callback count against them.
Four sites asserting a tolerance rather than zero were deliberately left alone
(CellViewDedupTests ×2, PortalProjectionTests ×2) — their ceilings already
absorb this noise and none has flaked. PortalProjectionTests is worth
revisiting: its ceiling exists explicitly to tolerate "a tiered-JIT/ArrayPool
bookkeeping transition ... to the first measured batch", which is exactly what
the probe removes, so it could probably be tightened to zero now.
Acceptance: the issue asks for 20 consecutive clean Release runs of the App suite. See §4 for the count actually achieved and how to read it.
2.6 Worktree sweep
Removed 9 finished worktrees — the two temp ones
(%TEMP%\claude\prerevert, %TEMP%\claude\v4a-verify-base) and seven
agent-a* paths including the two whose branches were merged tonight. Pruned
afterward. codex/*, .worktrees/* and the named claude/* worktrees were not
touched.
Two were deliberately left in place, because "finished" is load-bearing and neither is:
agent-a1589ee2f5360c7a2— has an unresolved merge conflict (UU src/AcDream.Core/Physics/TransitionTypes.cs), three other modified physics files, and an untracked newEdgeSlideTests.cs. This is in-progress L.2.3 step-up/step-down work belonging to some other session, not tonight's.agent-af83539515c87d43e— has an uncommitted 1,073-line edit toWbDrawDispatcher.cs(−961/+112), an in-progress GL-removal pass. V11 has since landed the equivalent, so this is probably superseded scratch — but it is a thousand lines of uncommitted work and deleting it is not reversible.
Both are yours to keep or discard.
3. Campaign V — final state
The campaign is complete and the tree is statically green. Its runtime evidence is outstanding, and that is a machine fault rather than a campaign result.
V11 removed the GL backend across 5 commits (844cf092 → c265b52d): 204 files,
+1,870 / −27,607 lines. Gone are Gpu/Gl, ManagedGL*, GLHelpers,
GLStateScope, both render-state caches, BindlessSupport,
GraphicalGlFunctionProbe, the ImGui project, the Studio tree, the ui-studio
verb, RenderBackendKind and the ACDREAM_RENDER_BACKEND escape hatch.
Silk.NET.OpenGL and .Extensions.ARB are dropped — final reference count
zero. 9/9 shader pairs recompile.
Three findings from the deletion are worth keeping:
- Chorizite could not be dropped, and not for the reason the risk register
predicted. It was assumed to survive only via
IUniformBuffer. In factTextureFormatsits in theIWorldTextureArray.CreateClampedArraysignature that the Vulkan path implements, andBoundingBoxis a serialized type in the pak format. Dropping it touches the on-disk format and is its own slice. Studio/SampleData.cswas production code, not devtools —InteractionRetainedUiCompositionuses it as the character sheet's fallback. Moved, not deleted.- One real bug fell out.
WbMeshAdapter.Dispose()was still pattern-matching the deletedGpuFrameFlightControllerto decide whether to wait for submitted GPU work.VulkanFrameFlightControllerreplaced that type at V6a and the site was never updated, so the wait had been silently dead on every Vulkan run since V6a. Deleting the GL type is what turned a no-op into a compile error.
Nothing was relaxed to manufacture a pass and nothing was declared green on a prediction. A gate that could not run is not a gate that passed — the outstanding list is §1.1.
4. Numbers
Complete solution suite
| Passed | Skipped | Total | |
|---|---|---|---|
V11 baseline (d312bd2f) |
8,999 | 5 | 9,004 |
Tonight (HEAD) |
9,666 | 5 | 9,671 |
The +667 decomposes exactly, with nothing unaccounted for:
| Source | Tests |
|---|---|
| Enum verification campaign | +597 |
| Wire-stack audit | +59 |
| #248 near-plane theory (4 near/far pairs) + far-plane pin | +5 |
#250 ZeroAllocationProbe apparatus guards |
+6 |
| Total | +667 |
Per project at HEAD: App 3,941/3 · Core 3,898/2 · Core.Net 659 · UI.Abstractions
543 · Runtime 415 · Content 124 · Headless 67 · Bake 15 · Cli 4. Zero failures.
Release build: 0 errors. 21 warnings, all pre-existing — one xUnit1025
(duplicate InlineData in MotionInterpreterTests) and a family of CS8767
nullability mismatches in the App test composition fakes. None cites a file
touched tonight; the count is unchanged from the V11 baseline.
The #250 acceptance gate
The issue asks for 20 consecutive clean Release runs of the App suite, and
that is the right scope: it isolates #250 from #255, which lives in
AcDream.Content.Tests and is still open.
Result: 20 / 20 clean, 3,941 passed / 3 skipped every run.
For contrast, the pre-fix 20-run complete-solution baseline taken earlier the same night, on the same machine:
| Failure | Runs |
|---|---|
| #250 family (3 distinct tests) | 2, 14, 18, 19 |
#255 RetailDatLoaderTests |
7, 14, 17, 18 |
So the #250 family went from 4 failing runs in 20 to 0 in 20, and the three tests involved were each converted. #255 is untouched and still flakes — it is a different issue with an open fix, and it will keep appearing in complete-solution runs until the rendezvous lands.
One caveat, stated plainly: 20 consecutive App-suite runs is what the acceptance criterion asks for and what was delivered, but the App suite alone applies less parallel pressure than a complete-solution run. The strongest evidence would be 20 clean complete-solution runs, and that is not currently obtainable while #255 flakes independently. Re-running this gate after #255 is fixed would close that gap.
5. Where to pick up
- Reboot, then §1.1's five gates and §1.4's walked portal session — one sitting.
- Answer §1.2's three enum decisions; they unblock adoption commits that are otherwise ready.
- One
ACDREAM_DUMP_OPCODES=1session (§1.3) turns the wire audit's 128 MISSING rows into a ranked work order. - #255's rendezvous fix — the diagnosis is settled from three independent observations and the fix direction is agreed; it is only unwritten.