docs(perf): audit errata + plan governance/contract amendments (2026-07-24 review)
Adversarial review of the 2026-07-24 runtime audit + modern runtime plan
(43-agent verification at e627d9a4). Audit: append errata section 13 --
the capture ran with ACDREAM_UNCAPPED_RENDER=1 + ACDREAM_DUMP_MOVE_TRUTH=1
(not "normal capped presentation"; the 32 FPS ceiling was the RDP display
path); the 203.6 MiB single-frame figure is frame-thread-only and cannot
include worker-side MeshExtractor decode (it is main-thread publication/
retirement); GiB conversions, committed-peak, 65% LOH fragmentation,
exception-count provenance, GC-counter semantics, and cache-undersizing
corrections. Original sections left untouched per historical-record rules.
Plan: new section 0 governance interlocks (Slices F/G resume the deferred
2026-07-05 MP3/ECS decision -> explicit user gate; side-track session
discipline; freeze-exception scope for H-network/J; evidence-base
corrections; baseline prerequisite blocking Slice C). Slice amendments:
A (per-frame history, process-wide alloc, contention stacks, cache
counters, screenshot-diff pulled forward, reference config, exit criteria),
C (Portal-dat-wins precedence contract, dual-dat + ObjDesc fixtures, cache
identity rule, exception-semantics cleanup), D (unify/retune existing
eviction rather than rebuild; ResidencyManager concurrency contract),
E (gate rescoped to what E owns; release-later != run-later; audio in the
immediate detach set; reveal stall behavior with the AP-115 cue; retail
teardown anchors), F (referee-first digest, journal coalescing bound,
IRenderScene home project, identity enforcement), G (provisional 300 FPS
target; retail dynamics viewcone-cull contract), H (split H-a/H-b/H-c with
the AP-85 lights pin and receive-side allocation scope), I (verbatim-float
bake rule + differential BSP sweep; floats after B), J (teardown protocol,
multi-plan track), K (wire-timing parity per TS-33, hours-scale soak).
Section 11 gate corrections (percentile populations, anti-gaming
process-wide row, provisional budgets, scoped third-visit rule, soak +
jitter rows, scheduled harness); section 12 adjudication protocol;
section 14 order list now includes Slice I and the user decision gate.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
e627d9a403
commit
38e14c6a71
2 changed files with 453 additions and 15 deletions
|
|
@ -19,6 +19,51 @@ visibility ports, and all behavior already accepted by the user.
|
|||
|
||||
---
|
||||
|
||||
## 0. Governance and standing-decision interlocks (2026-07-24 review)
|
||||
|
||||
Added by the 2026-07-24 adversarial review. These interlocks are binding on
|
||||
every executor of this plan; the audit's correction record is
|
||||
[`../research/2026-07-24-runtime-performance-audit.md`](../research/2026-07-24-runtime-performance-audit.md)
|
||||
section 13.
|
||||
|
||||
1. **The ECS deferral stands until the user lifts it.** On 2026-07-05 the
|
||||
user deferred the ECS/MP3 render-world rewrite ("revisit ONLY if the user
|
||||
wants a higher raw FPS number and accepts the rewrite risk" —
|
||||
`claude-memory/project_mp_track_findings.md`), and the follow-up
|
||||
measurement note concluded MP3 was not warranted for town FPS. Slices A–E
|
||||
do not touch that deferral. **Slices F and G resume the deferred MP3
|
||||
work and MUST NOT start without explicit user approval recorded against
|
||||
this section**, taken on the post-Slice-E corrected uncapped-local
|
||||
profile (does partition/submission cost still dominate?). Note the prior
|
||||
"GPU-bound at ~200 FPS" Sawato reading is itself invalidated by the
|
||||
`GpuFrameTimer` boundary artifact this plan's Slice A corrects — the
|
||||
question is genuinely open until re-measured.
|
||||
2. **Session discipline.** This plan executes in dedicated Track MP/LH
|
||||
side-track sessions only. The ACTIVE milestone's critical path wins every
|
||||
conflict; do not begin any slice inside a milestone session.
|
||||
3. **Freeze-exception scope.** The recorded 2026-07-05 Track MP exception
|
||||
covers streaming and WB rendering only. Slice H's network-I/O item and
|
||||
Slice J's gameplay-owner moves touch otherwise-frozen subsystems
|
||||
(network, chat, input) and each requires its own recorded user
|
||||
authorization before starting. (The 2026-07-24 receive-thread robustness
|
||||
fix was separately reviewed and user-approved as a bug fix, not a
|
||||
rework.)
|
||||
4. **Evidence-base corrections.** The §1 exception-traffic row previously
|
||||
cited "4,520 + 1,168" — figures with no derivation in the audit or its
|
||||
committed artifacts; the verifiable figures are the focused-trace 1,587
|
||||
(1,335 invalid DAT probes + 252 receive timeouts). And the audit's
|
||||
204 MiB single-frame allocation is a FRAME-THREAD measurement that cannot
|
||||
include worker-side mesh decode: **Slice E owns the frame-spike symptom;
|
||||
Slices B/C own the process-wide allocation rate and GC pressure.** Win
|
||||
attribution must respect that split.
|
||||
5. **Baseline prerequisite.** No behavior-changing slice (C onward) starts
|
||||
until the Slice A exit criteria in §10 are met: committed uncapped-local
|
||||
AND capped-local nine-stop baselines, a pinned dense-town uncapped
|
||||
baseline, and per-portal allocation/frame timelines, all captured with
|
||||
the corrected tooling and a committed reference-hardware definition.
|
||||
|
||||
---
|
||||
|
||||
## 1. Why the architecture should change
|
||||
|
||||
The complete methodology, attribution, sanitized checkpoint data, and raw-
|
||||
|
|
@ -38,7 +83,7 @@ The 2026-07-24 six-minute, nine-stop connected route established:
|
|||
| Final live GC heap | approximately 194 MiB | The 1–2 GiB process high-water is not an unbounded live-object leak |
|
||||
| Dedicated GPU memory | 633–818 MiB | Texture arrays/caches are bounded but expensive and insufficiently observable |
|
||||
| Revisited locations | entity/emitter/particle counts returned to the same plateaus | Logical lifetime is broadly convergent; optimize rather than replace it |
|
||||
| Exception traffic | 4,520 invalid DAT probes plus 1,168 receive timeouts | Exceptions are being used as normal control flow |
|
||||
| Exception traffic | 1,587 focused-trace exceptions (1,335 invalid DAT probes, 252 receive timeouts); full-route totals pending re-derivation (see §0.4) | Exceptions are being used as normal control flow |
|
||||
|
||||
Allocation attribution names the runtime mesh/texture path
|
||||
(`MeshExtractor.PrepareMeshData` → `PrepareGfxObjMeshData` →
|
||||
|
|
@ -493,6 +538,37 @@ preserving. Each gets a focused implementation plan when it starts.
|
|||
reconcile with active frame time; external GPU engine direction agrees with GL
|
||||
timestamps.
|
||||
|
||||
**Amendment (2026-07-24 review).** Slice A additionally includes, and its
|
||||
gate requires:
|
||||
|
||||
- Per-frame history export (frame index, stage times, frame-thread alloc,
|
||||
gpu time) — landed post-review via `ACDREAM_FRAME_HISTORY`; route-wide
|
||||
p50/p95/p99 populations must come from this export. Until it is the gate
|
||||
population, every percentile gate reads "no 5-second window with p99 > X".
|
||||
- Process-wide allocation rate per scene (dotnet-counters) alongside the
|
||||
frame-thread profiler, plus a per-thread split recipe — the frame-thread
|
||||
gates alone are gameable by moving work to workers (§0.4).
|
||||
- One contention capture WITH STACKS on the route (the 4,614 contentions are
|
||||
unattributed; `_datLock` build-vs-hydration and
|
||||
`ObjectMeshManager._pendingRequests` are the leading candidates, not only
|
||||
the DAT typed-object lock).
|
||||
- Cache hit/miss/evict counters in the checkpoint JSON (landed post-review)
|
||||
so Slice B/C wins are attributable to cold-vs-warm portals.
|
||||
- LOH size + fragmentation (+POH) checkpoint fields (landed post-review; the
|
||||
audit's own JSON showed 65% LOH fragmentation, never analyzed).
|
||||
- Fixed-camera screenshot COMPARISON tooling (pulled forward from Slice G)
|
||||
with a defined match rule (per-channel tolerance + max differing-pixel
|
||||
fraction; pinned `/time`, pinned camera, particles pinned-seed or masked).
|
||||
Slice C's visual gate depends on it existing.
|
||||
- A committed reference-configuration block (CPU, GPU, driver, OS build,
|
||||
display path, resolution, refresh, VSync state, re-baseline rule) — the
|
||||
§11 phrase "reference local hardware" is undefined without it.
|
||||
|
||||
**Exit criterion (blocks Slice C, per §0.5):** committed uncapped-local and
|
||||
capped-local nine-stop baselines, a pinned dense-town uncapped baseline
|
||||
(name the town, pin the camera), and per-portal allocation/frame timelines
|
||||
around each teleport materialization.
|
||||
|
||||
### Slice B — Finish MP1b EnvCell dedup and full bake
|
||||
|
||||
**Purpose:** Make the existing pak physically usable.
|
||||
|
|
@ -531,6 +607,35 @@ code consumes the pak yet.
|
|||
single-frame allocation and p99 materially improve from the 2026-07-24 baseline;
|
||||
connected nine-stop route and screenshots pass.
|
||||
|
||||
**Amendment (2026-07-24 review).** Binding contracts for this slice:
|
||||
|
||||
- **Precedence contract.** The LINQ being replaced is load-bearing content
|
||||
selection, not just allocation:
|
||||
`OrderByDescending(r => r.Database == _dats.Portal)` means
|
||||
**Portal-dat-wins** when an id resolves in more than one database. The
|
||||
typed non-allocating lookup must encode that precedence explicitly, and
|
||||
the equivalence fixtures must include at least one dual-dat id (an id
|
||||
shadowed by `client_highres.dat`) so a precedence regression fails the
|
||||
gate instead of silently changing pixels.
|
||||
- **Seam fixtures.** The DAT-equivalence suite must include ObjDesc-composed
|
||||
fixtures that straddle the bake/runtime split: a drudge, a robed player,
|
||||
and palette-dyed armor (baked base mesh + runtime appearance overlay).
|
||||
- **Cache identity rule.** Pak-backed default-palette pixels and
|
||||
runtime-composed palette-overlaid pixels for the same surface id are
|
||||
distinct cache identities and must never share a key.
|
||||
- **Exception semantics.** When the probe storm is removed, also narrow
|
||||
`ResolveActivation`'s bare `catch { return null; }` (and the sibling
|
||||
`SequencerFactory`, which today has NO catch at all): catch only the
|
||||
specific corrupt-data exception types, rethrow `OperationCanceledException`,
|
||||
log anything unexpected. Otherwise the "no exception storm" gate can pass
|
||||
while real activation failures stay invisible — the pattern CLAUDE.md's
|
||||
no-workarounds rule names explicitly.
|
||||
- **Baseline comparison validity.** "p99 materially improve" compares
|
||||
against the post-Slice-A uncapped-local baseline, not the capped-RDP
|
||||
2026-07-24 capture (whose steady p99 sits at the 31.2 ms display floor).
|
||||
Portal single-frame allocation is FPS-independent and may compare against
|
||||
either.
|
||||
|
||||
### Slice D — Typed asset handles and unified residency
|
||||
|
||||
**Purpose:** Bound and explain CPU/GPU memory.
|
||||
|
|
@ -550,6 +655,31 @@ connected nine-stop route and screenshots pass.
|
|||
**Gate:** same-location third-visit residence plateaus; no stale-generation
|
||||
release; no missing textures; no resource growth after repeated portal loops.
|
||||
|
||||
**Amendment (2026-07-24 review).** Two corrections to this slice's scope:
|
||||
|
||||
- **Do not rebuild what exists.** Fence-gated, budgeted eviction already
|
||||
runs per frame in three of the four systems this slice names:
|
||||
`ObjectMeshManager` (1 GiB / 50-object LRU, reclaimed every frame),
|
||||
`CompositeTextureArrayCache` (128/64 MiB budgets, per-frame throttled GL
|
||||
deletion), and `GlobalMeshBuffer` (capacity shrink with hysteresis). The
|
||||
2026-07-24 route never pushed those ceilings, so its "bounded" residency
|
||||
was bounded-by-route, not policy-exercised. Slice D's job is to UNIFY
|
||||
accounting and observability over the existing physical caches, add a
|
||||
forced-pressure eviction test (deliberately exceed the ceilings), and
|
||||
retune the budgets for multi-session load — plus bring policy to the
|
||||
items that genuinely have none: deferred-alpha scratch arrays,
|
||||
`ObjectMeshManager._boundsCache`, and the audio/animation content caches
|
||||
(audio caches bounded post-review).
|
||||
- **Concurrency contract before code.** The `ResidencyManager` design in
|
||||
§5.3 lists states with no transitions. Before this slice starts, its plan
|
||||
must specify: a transition table including failure/cancellation/corrupt
|
||||
edges; which fields are single-thread-owned vs shared (policy decisions
|
||||
on the update thread, physical release render-thread-only, accounting via
|
||||
atomics or a single-writer journal); the rule that eviction never blocks
|
||||
the render thread and never runs under a lock the render thread takes;
|
||||
and that `ResidencyManager` never touches GL names (only the renderer
|
||||
resource owners row of §4 may).
|
||||
|
||||
### Slice E — Cost-budgeted streaming and retirement
|
||||
|
||||
**Purpose:** Remove update-thread portal transactions.
|
||||
|
|
@ -563,9 +693,44 @@ release; no missing textures; no resource growth after repeated portal loops.
|
|||
- Preserve FIFO within priority/generation and exact retry receipts.
|
||||
- Connect reveal to the single destination-generation readiness state.
|
||||
|
||||
**Gate:** zero `viewport-before-ready`; traversal p99 at or below 16.67 ms target
|
||||
and maximum below 33.3 ms target on the reference local run; no stranded old
|
||||
generation, staged upload, collision, effect, or GPU owner.
|
||||
**Gate (rescoped, 2026-07-24 review):** zero `viewport-before-ready`; no
|
||||
single-frame publication/retirement transaction exceeds the configured
|
||||
time/byte/entity budgets; portal-window frame p99 and single-frame
|
||||
allocation materially improve versus the post-Slice-A baseline; no stranded
|
||||
old generation, staged upload, collision, effect, or GPU owner. The
|
||||
program-level 16.67 ms p99 / 33.3 ms maximum targets are §11 gates verified
|
||||
at Slice H — Slice E cannot own them while the audit's own P2 costs
|
||||
(whole-world partition, unconditional UI/diagnostic work) are assigned to
|
||||
Slices G and H.
|
||||
|
||||
**Amendment (2026-07-24 review).** Binding contracts for this slice:
|
||||
|
||||
- **Release-later never means run-later.** Retired-generation owners are
|
||||
tick-frozen and audio-silenced at the immediate detach edge even when
|
||||
their memory release is budgeted across later frames. Audio is added to
|
||||
the immediate set (or bounded at ≤1 frame): a looping emitter on a
|
||||
retired landblock is user-audible during portal transit, and audio
|
||||
appears in neither of §6.2's lists today. Retail teardown anchors for the
|
||||
slice plan: `CLandBlock::Destroy` 0x0052faa0, `destroy_static_objects`
|
||||
0x0052fa50, `CObjectMaint::DestroyObjects` 0x00508c30 (retail teardown is
|
||||
one synchronous transaction; deferral has retail precedent only via the
|
||||
`AddObjectToBeDestroyed` queue, whose drain is complete-when-run).
|
||||
- **Reveal stall behavior.** A reveal hold longer than retail's 5-second
|
||||
fade ceiling keeps the tunnel animating, surfaces the retail "In Portal
|
||||
Space - Please Wait" cue (AP-115), and emits a diagnostic — never a
|
||||
silent early reveal. The `IsUnhydratable` loud-failure escape stays.
|
||||
Retail anchor: `SmartBox::UseTime` 0x00455410 `blocking_for_cells`; the
|
||||
1/2/5 s fade thresholds are recorded in
|
||||
`claude-memory/project_portal_space.md`.
|
||||
- **All count constants convert.** `MaxCompletionsPerFrame` is not the only
|
||||
count-shaped budget: `MaxDrainIterations` (the ×64 outer drain ceiling)
|
||||
and the priority-ring radius are part of the same redesign; and the
|
||||
uncapped one-frame paths (`BeginFullWindowRetirement`,
|
||||
`TryAdvanceOriginRecenterPreparation` — the ~600-landblock teleport
|
||||
retire) are the primary offenders, not the 9-landblock eager ring.
|
||||
- Deferred loads retained in `_deferredApply` during a burst are a
|
||||
retained-memory high-water source; the staged pipeline must account for
|
||||
them under the CPU-bytes budget.
|
||||
|
||||
### Slice F — Incremental render scene foundation
|
||||
|
||||
|
|
@ -583,6 +748,34 @@ generation, staged upload, collision, effect, or GPU owner.
|
|||
**Gate:** shadow scene matches canonical world/resource checkpoints through the
|
||||
nine-stop route with bounded memory and zero authoritative ownership.
|
||||
|
||||
**Amendment (2026-07-24 review).** Preconditions and contracts:
|
||||
|
||||
- **User approval required before this slice starts** (§0.1 — F/G resume
|
||||
the deferred MP3 work).
|
||||
- **Referee before the match.** The deterministic scene digest is built and
|
||||
validated against the CURRENT pipeline in steady state as its own
|
||||
preceding sub-slice, so the comparison tool has a proven track record
|
||||
before it gates a mirror. During soak/dev runs the digest also runs
|
||||
continuously through portal windows (every N frames), not only at the
|
||||
nine checkpoints — checkpoint-only comparison is blind to drift that
|
||||
self-corrects between stops, which peaks exactly when churn does.
|
||||
- **Journal discipline.** The delta journal is drained to empty every host
|
||||
tick while single-threaded (assert on carryover); deltas coalesce
|
||||
last-writer per (entity, kind) between drains, bounding the journal at
|
||||
O(live entities) instead of O(mutations); on any future thread
|
||||
separation it adopts §6.1's bounded-queue discipline with an explicit
|
||||
no-block rule for the update thread.
|
||||
- **Interface home.** `IRenderScene`, its components, and the delta journal
|
||||
types live in `AcDream.App` for Slices F–I. At Slice J the seam becomes
|
||||
Runtime-emitted ordered entity events (incarnation + transform + flags,
|
||||
no render vocabulary) that an App-side projection adapter maps to
|
||||
`IRenderScene` deltas — write Slice F against that end-state so no render
|
||||
type bakes into a contract Slice J must break.
|
||||
- **Identity enforcement.** The §4 no-unchecked-cast rule gets a concrete
|
||||
mechanism here: wrapper structs with a single `.Value` accessor plus a
|
||||
grep-based architecture test (or analyzer) restricting that accessor to
|
||||
owning assemblies.
|
||||
|
||||
### Slice G — Render snapshot and delta submission cutover
|
||||
|
||||
**Purpose:** Make render cost proportional to visible/changed data.
|
||||
|
|
@ -600,6 +793,20 @@ nine-stop route with bounded memory and zero authoritative ownership.
|
|||
300 FPS or the corrected profile identifies a new dominant stage; steady frame
|
||||
allocation is near zero.
|
||||
|
||||
**Amendment (2026-07-24 review).** The 300 FPS / 3.33 ms figure is
|
||||
provisional until Slice A's corrected pass timing re-measures the dense-town
|
||||
CPU-vs-GPU split on the local display (the prior "GPU-bound ~200 FPS"
|
||||
reading was produced by the boundary-spanning `GpuFrameTimer` this plan
|
||||
corrects, and the 2026-07-24 capture cannot settle it). Bucket queries must
|
||||
preserve the retail dynamics contract: retail drops out-of-flood dynamics
|
||||
via the per-dynamic viewcone CULL, not by set membership
|
||||
(`RetailPViewRenderer.DrawDynamicsLast`) — replacing the whole-world walk
|
||||
with buckets must not change which dynamics get culled. This slice also
|
||||
retires the sibling per-frame rebuilds the audit missed:
|
||||
`EnvCellRenderer.PrepareRenderBatches` (gated post-review) and the
|
||||
nearby-building gather walk, both of which otherwise keep steady render cost
|
||||
proportional to resident content.
|
||||
|
||||
### Slice H — Event-driven UI, diagnostics, lights, and frame cleanup
|
||||
|
||||
**Purpose:** Remove remaining work that scales with uncapped FPS.
|
||||
|
|
@ -617,7 +824,47 @@ allocation is near zero.
|
|||
|
||||
**Gate:** no steady-state Gen-2 collections; steady allocation target
|
||||
≤ 4 KiB/frame initially and zero for the core world/render loop; UI and network
|
||||
behavior tests unchanged.
|
||||
behavior tests unchanged; process-wide steady allocation rate within the §11
|
||||
per-scene target (frame-thread numbers alone are gameable, §0.4); the
|
||||
program-level traversal frame-time targets from §11 are verified here.
|
||||
|
||||
**Amendment (2026-07-24 review).** This slice splits into three separately
|
||||
gated pieces:
|
||||
|
||||
- **H-a — UI/diagnostics/frame cleanup.** Includes: `UiText` shaping cache
|
||||
(appraisal/character-info/effects panels re-run full word-wrap every
|
||||
visible frame; apply the existing `ChatWindowController.GetTranscriptLines`
|
||||
revision+width+font cache pattern — it is already proven in-tree), the
|
||||
cooldown scan's visibility scoping (it is O(all mounted item-list slots)
|
||||
regardless of open windows — the worst-scoped of the audited UI items),
|
||||
diagnostics consumer-gating (reuse the AABB results as the partition's
|
||||
landblock frustum cull rather than deleting them — the same test is
|
||||
computed and discarded one call away from where it is needed),
|
||||
`EnvCellRenderer` prepare gating (landed post-review), and the per-frame
|
||||
scratch items (RetailPViewFrameInput reuse, iterator layers).
|
||||
- **H-b — Lights (pinned; two-failure regression history, register AP-85).**
|
||||
The candidate pool remains the resident registry (`_all`); any narrowing
|
||||
is the existing latency-buffered visible-cell FILTER only; the over-cap
|
||||
ranking stays dynamics-first + nearest-PLAYER; the only change is
|
||||
replacing the >MaxGlobalLights overflow `List.Sort` with a top-k partial
|
||||
select that produces the IDENTICAL selected set. Retail anchors to cite
|
||||
in the slice plan: `CEnvCell::add_dynamic_lights` 0x0052d410,
|
||||
`Render::insert_light` 0x0054d1b0, `Render::add_dynamic_light`
|
||||
0x0054d420, caps 0x0081ec94/98. Gate adds: selected-set equivalence on
|
||||
captured Town Network fixtures (463 lights) + fixed-camera screenshot
|
||||
comparison. Note the audit overstated this item: per-object/per-cell
|
||||
selection is already an allocation-free top-8 insertion; only the
|
||||
overflow path sorts.
|
||||
- **H-c — Network I/O (requires §0.3 authorization).** Scope includes the
|
||||
receive-side per-packet allocation chain the audit missed (fresh
|
||||
`UdpClient.Receive` array + `BodyBytes` copy + per-fragment `ToArray` +
|
||||
per-packet `Packet`/`List`/`Optional` objects), not only the outbound
|
||||
`ToArray`. Invariants pinned: single outstanding receive, kernel arrival
|
||||
order preserved into the inbound queue, ack-per-received-packet at decode
|
||||
acceptance (holtburger pattern), `LinkStatusHolder::OnHeartbeat`
|
||||
0x004113D0 last-heard placement unchanged, `PumpOnce` handshake pacing
|
||||
preserved. (Receive-thread death robustness landed post-review as an
|
||||
approved bug fix.)
|
||||
|
||||
### Slice I — Flat collision assets and residual zero-allocation work
|
||||
|
||||
|
|
@ -633,6 +880,20 @@ behavior tests unchanged.
|
|||
**Gate:** trajectory and retail conformance suites remain bit-equivalent;
|
||||
navigation/collision fixtures match; portal and steady allocation targets pass.
|
||||
|
||||
**Amendment (2026-07-24 review).** Bit-equivalence across the layout change
|
||||
is achievable (RyuJIT SSE scalar IEEE-754; the x87 concerns in project
|
||||
memory are decomp-READING lessons, not runtime determinism) — but only
|
||||
under two conditions the slice plan must state: (a) the bake stores
|
||||
dat-parsed floats VERBATIM, zero arithmetic transformation (no plane
|
||||
renormalization, no quantization, no double round-trips); (b) traversal
|
||||
preserves front/back child visit order and every epsilon comparison. The
|
||||
existing suites are scenario-bounded, so add an old-vs-flat DIFFERENTIAL
|
||||
sweep during the shadow phase: mass BSPQuery/point-in-cell/sweep queries
|
||||
across a large cell sample, outputs compared bit-for-bit between graph and
|
||||
flat implementations while both coexist. Physics transition pooling stays
|
||||
behind its identity/lifetime-test condition. This slice floats — it depends
|
||||
only on Slice B's bake infrastructure, not on F–H.
|
||||
|
||||
### Slice J — Presentation-independent `AcDream.Runtime`
|
||||
|
||||
**Purpose:** Establish the graphical/headless shared client kernel.
|
||||
|
|
@ -649,6 +910,20 @@ navigation/collision fixtures match; portal and steady allocation targets pass.
|
|||
test connects, enters world, moves, receives inventory/chat/world updates,
|
||||
portals, logs out, reconnects, and tears down without loading App/Silk/OpenAL.
|
||||
|
||||
**Amendment (2026-07-24 review).** Requires §0.3 authorization (moves
|
||||
frozen-subsystem owners). Add a **teardown protocol** subsection to §9.2
|
||||
before this slice starts — session reset, logout, and mid-portal disconnect
|
||||
traverse five new owners, and tests without a specified order mean the
|
||||
order gets invented per-slice: (1) cancel scheduler generations; (2) drain
|
||||
or poison journals/queues; (3) unpublish the render scene; (4) release
|
||||
residency, fence-gated; (5) `ContentStore` last (shared, outlives
|
||||
sessions). Every new owner registers with the existing
|
||||
`CompositionAcquisitionScope` lease discipline so disposal order is
|
||||
structural, not remembered. This slice is a multi-plan track (the
|
||||
comparable `GameWindow` decomposition took 8 slices for 14K lines), not a
|
||||
single bisectable slice — plan it as lifetime-group sub-slices with
|
||||
host-parity tests after each move.
|
||||
|
||||
### Slice K — Linux headless and multi-session host
|
||||
|
||||
**Purpose:** Deliver efficient automated bots.
|
||||
|
|
@ -667,6 +942,19 @@ portals, logs out, reconnects, and tears down without loading App/Silk/OpenAL.
|
|||
dependency, bounded incremental memory per session, no busy-loop CPU, clean
|
||||
teardown/reconnect, and parity with graphical runtime command/event behavior.
|
||||
|
||||
**Amendment (2026-07-24 review).** The parity gate covers packet ORDER but
|
||||
not inter-packet TIMING — and AutonomousPosition cadence is already a
|
||||
tracked divergence (register row TS-33,
|
||||
`claude-memory/project_retail_motion_outbound.md`). Add a wire-timing
|
||||
assertion: AutonomousPosition inter-packet intervals and
|
||||
MoveToState-on-change emission points within a stated tolerance band versus
|
||||
the graphical host on the same scenario; the time-wheel scheduler must
|
||||
derive wire cadence from the instance clock, never from tick count. The
|
||||
30-session stress gets an explicit duration measured in HOURS (the
|
||||
characteristic 30-bot failure is slow per-session creep — the #193 class
|
||||
leaked ~1 GiB/min and was found by OOM, not by a 6-minute route), with
|
||||
per-session incremental memory given a numeric ceiling at gate time.
|
||||
|
||||
### Slice L — Linux graphical host and evidence-gated GPU work
|
||||
|
||||
**Purpose:** Finish platform portability and only then pursue remaining GPU
|
||||
|
|
@ -720,6 +1008,41 @@ Absolute process/VRAM targets are fixed after Slice D produces trustworthy
|
|||
category accounting; until then, the binding rule is plateau plus no regression
|
||||
in pixels/range.
|
||||
|
||||
**Amendment (2026-07-24 review).** Gate-table corrections and additions:
|
||||
|
||||
- **Percentile populations.** Until the Slice A per-frame history export is
|
||||
the population, every p99 row reads "no 5-second window with p99 > X" —
|
||||
the windowed profiler cannot produce route-wide percentiles.
|
||||
- **Anti-gaming complement.** New row: process-wide steady allocation rate
|
||||
per scene (dotnet-counters), alongside the frame-thread rows. The
|
||||
frame-thread gates alone reward moving garbage to workers, which is this
|
||||
plan's own remediation shape.
|
||||
- **Provisional budgets.** "≤ 4 MiB portal" and "≤ 4 KiB steady" are
|
||||
round-number initial budgets, not derived values; tighten from post-A/C
|
||||
evidence. "0 B/frame" means alloc p50 = 0 with every nonzero frame
|
||||
attributable to the profiler/report path (the instrument itself allocates
|
||||
its 5-second report on the frame thread).
|
||||
- **Third-visit growth ≤ 5%** is scoped per metric class: same-location
|
||||
tracked CPU/GPU residence measured against the location's own asset set
|
||||
(possible after Slice D accounting); entity/emitter/particle counts stay
|
||||
warnings on a live ACE world; absolute byte deltas reported alongside
|
||||
percentages. The audit's own data shows legitimate +31.7% Sawato
|
||||
tracked-GPU growth from route-wide cache warming — the unscoped gate
|
||||
false-fails on exactly that.
|
||||
- **New row — long-duration soak:** ≥ 60 minutes looping the route; managed
|
||||
heap, LOH size + fragmentation, tracked GPU, handles, and threads all
|
||||
plateau within stated bands. (The canonical route is 359 s; the #193
|
||||
leak class is invisible to it.)
|
||||
- **New row — frame-pacing jitter** (stddev / 99.9th) once the per-frame
|
||||
export exists.
|
||||
- **Scheduled harness.** Slice A or H tasks a scheduled local perf harness:
|
||||
run the committed route against the last committed baseline JSON with the
|
||||
soak script's relative-limit pattern applied cross-commit, failing
|
||||
loudly. Manual-and-unscheduled is not a regression net.
|
||||
- "Reference local hardware" refers to the committed reference-configuration
|
||||
block required by Slice A; gates against an undefined machine are
|
||||
unfalsifiable.
|
||||
|
||||
---
|
||||
|
||||
## 12. Test and review matrix
|
||||
|
|
@ -752,6 +1075,14 @@ For a replacement cutover:
|
|||
- The gate commit removes the old production path.
|
||||
- No test threshold is loosened to accept a regression.
|
||||
|
||||
**Adjudication protocol (2026-07-24 review).** When a cutover comparison
|
||||
disagrees: (1) any confirmed OLD-path bug fixed during the cutover gets its
|
||||
divergence-register row or `docs/ISSUES.md` entry in the same commit;
|
||||
(2) visual differences are adjudicated by the user (visual verification is
|
||||
the user's role per CLAUDE.md), never self-certified; (3) any threshold
|
||||
change requires a written rationale in the gate commit — "the new output is
|
||||
an improvement" carries the same evidence burden as a regression claim.
|
||||
|
||||
---
|
||||
|
||||
## 13. Risks and controls
|
||||
|
|
@ -785,17 +1116,29 @@ has been removed and measured.
|
|||
The intended order is therefore:
|
||||
|
||||
```text
|
||||
honest metrics
|
||||
-> prepared content
|
||||
-> typed residence
|
||||
-> cost-budgeted streaming
|
||||
-> incremental render scene
|
||||
-> delta GPU submission
|
||||
-> residual frame cleanup
|
||||
-> presentation-independent runtime
|
||||
-> Linux/headless/multi-session
|
||||
-> evidence-gated GPU jobs
|
||||
honest metrics + committed baselines (A — exit criteria block C)
|
||||
-> prepared content (B, C)
|
||||
-> typed residence (D — unify/retune, not rebuild)
|
||||
-> cost-budgeted streaming (E)
|
||||
-> [user decision gate: lift the ECS deferral? §0.1]
|
||||
-> incremental render scene (F — referee sub-slice first)
|
||||
-> delta GPU submission (G)
|
||||
-> residual frame cleanup (H-a, H-b, H-c)
|
||||
-> flat collision assets (I — floats; depends only on B)
|
||||
-> presentation-independent runtime (J — §0.3 authorization)
|
||||
-> Linux/headless/multi-session (K)
|
||||
-> evidence-gated GPU jobs (L)
|
||||
```
|
||||
|
||||
This order improves the current graphical client first while laying clean
|
||||
boundaries for Linux and approximately 30 automated headless clients later.
|
||||
|
||||
**Execution discipline (2026-07-24 review):** this plan runs in dedicated
|
||||
Track MP/LH side-track sessions only; the active milestone's critical path
|
||||
wins every conflict (§0.2). Slice I appears in this list explicitly because
|
||||
both earlier order summaries omitted it. The 2026-07-24 review also landed
|
||||
a set of independent fixes ahead of the plan (receive-thread robustness,
|
||||
bounded audio caches, dead ParticleBatcher chain deletion, CellStruct clone
|
||||
guard, solid-color texture cache, EnvCellRenderer prepare gating,
|
||||
measurement tooling) — those are recorded in the relevant slices above as
|
||||
"landed post-review" and must not be re-done.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue