acdream/docs/research/2026-09-01-overhaul/s4-depth-alpha-packet.md
Erik 01674bcc78 fix(render): repair particle CYpt preparation
Retain the GfxObj-authored SortCenter independently from the rendered AABB center and use its scaled, oriented world point for billboard/mode-2-5 CYpt. Keep the visual center unchanged.

Reserve prepared source payload only at Append, roll back the exact tail once on false or exception, cap retained preparation at 3000 CLIP plus 3000 ALPHA, preserve immediate duplicates, account the bounded scratch, and correct the static-record ordering comment. The lead-authorized plan edit records candidate 44e2bc227b's retail FAIL and this section 19 result.

Gates: Release solution build 0 warnings/0 errors; focused particle/driver/queue/order/bounds 95/95; register boundary/count 1/1; real allocations 2/2 at 0 B; shader/manifest 32/32; one-shot hermetic 16751/16751; InstalledDat 385 pass/10 documented fail/1 skip; diff-check pass. No graphical client.

Mutations: rendered pos first failed distance expected 40 actual 116; AABB center expected 40 actual 116; dropped size expected 40 actual 65; dropped orientation expected 40 actual 160; eager reservation first failed Assert.Empty with one payload; omitted false rollback first failed Assert.Single with two payloads; omitted exception rollback first failed Assert.Empty with one payload; removed caps first failed prepared count expected 6000 actual 6002.

Evidence note: the first ordinary InstalledDat filter produced 385 pass/2 expected fail/1 skip but excluded the eight required KnownFailure rows; the separate inclusive no-retry artifact produced the exact documented 10-failure set. Initial test fixture compilation/analyzer setup failures are recorded in packet section 19.5.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-09-04 14:37:23 +02:00

1631 lines
105 KiB
Markdown
Raw 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.

# S4 packet — depth and alpha: what S3 already delivered, what remains (lead draft, 2026-09-03 evening)
**Status:** DRAFT written while the S3 post-hoc lenses ran; NOT dispatchable
until the S3 review closes (plan §9 order: S3 review → G3 → S4). Every owner
named below is re-located at chunk time; names, not line numbers.
Specs: `oh1-depth-lifecycle.md`, `oh1-alpha-list-contract.md`; the plan's §9 S4
section; the §7 alpha/depth captures under `oh-capture/` (`*.alphadepth.log`,
line formats in `tools/walk-oracle/oh/oh-capture-alphadepth.cdb.template`).
## 1. What S3 already delivered of S4's chunk 1
S3 chunk 2 (§8 of the S3 packet, landed `be9b4c1f` + `434a7df2`) implemented
the S4 chunk-1 scope almost whole, because the walk could not be exact without
it:
| S4 chunk-1 item (plan §9) | State after S3 | Where |
|---|---|---|
| one frame-persistent latch owner for `portalsDrawnCount` | DONE — `WalkFrameDriver.PortalsDrawnCount`, retained across frames, never cleared by Begin/Abort/EndFrame | S3 §8.2 B2 |
| landscape → flush → stamp → clear → seal block gated exactly as retail (`outside_view.view_count > 0`) | DONE — `OnInteriorFloodDrawTurn(cells, outsideViewCount)`; `LandscapeFlush` leaf; stamp advance; clear ONLY IF count ≠ 0 (read-then-zero); seals | S3 §8.2 B1/B3 |
| `WalkFrameDriver`'s unconditional interior clear deleted | DONE | S3 §8.2 B1 |
| mid-frame stamp placed exactly (0x005a4886) | DONE (`AdvanceWalkPartPassStamp` at the flush leaf) | S3 §8.2 B1 |
| exit seals from the exact current views and cell order, counted by the seal leaf's return value | DONE — `DrawExitSeals` returns the submitted count; punch fans never count | S3 §8.2 B2 |
| look-ins cannot touch the root latch | DONE (look-in `DrawCells` never increments) | S3 §8.2 B2 |
| truth-table tests over root kind / draw_landscape / outside-view count / previous-current portal count | PARTIAL — the chunk-2 tests flipped the two wrong contracts (§8.3); the full truth table and the two-consecutive-frames latch test are NOT yet one theory | S4-c1 below |
The depth-lifecycle spec's "Current acdream correspondence" table (its rows
marked **missing** / **wrong** for the counter, the gate and the test
contracts) is therefore stale for those rows; the rows that still stand are
the three below.
## 2. What remains for S4 chunk 1 (S4-c1, small)
- **Far-punch depth constant.** Retail writes the far-Z punch at the exact
bits `0x3F7FFFEF`; `portal_depth.vert` uses the decimal `0.99999988`. The
chunk decides by TEST, not by eye: a pin that reinterprets `0.99999988f` and
asserts `BitConverter.SingleToInt32Bits == 0x3F7FFFEF`; if they differ, the
shader takes the hex literal (`uintBitsToFloat(0x3F7FFFEFu)`) and the
manifest hash is re-pinned. (1 2⁻²³ is the expected identity; prove it.)
- **The ±12 local-input reject.** Ghidra arbitration (S3 packet §2, the
"±12 test is an equality degenerate guard") says the check rejects a
whole polygon whose local x/y equals ±12 exactly — a degenerate-input
guard, not a clip rule. Port it at the punch-event producer as an
equality test on the LOCAL polygon before transform, with a test that
feeds one exact ±12 vertex polygon and one at ±11.999 (only the first is
rejected). If the arbitration table shows the branch can never fire for a
real DAT polygon, record that as the reason to skip and cite the table.
- **Seal/punch color state.** Retail's portal-depth draws keep color writes
ENABLED with `SRCALPHA/INVSRCALPHA` and source alpha 0 (a no-op blend);
acdream sets `ColorWrite=false`. Pixel-identical by construction; the
chunk adds the register row (AD-nnn: "portal-depth color path: write-mask
off instead of a zero-alpha blend — identical output, different state") OR
ports the exact state if the Vulkan pipeline already carries the blend
attachment; the reviewer decides which is cheaper to keep honest.
- **Truth table + two-frame latch test** (plan §9 S4 tests): one theory in
`WalkFrameDriverTests` over (root kind, draw_landscape, outside-view count,
previous count) → the exact event subsequence (`LFLUSH`, stamp, `CLEAR`?,
`SEALS`?), plus the consecutive-frames case proving the latch crosses
frames, plus "multiple look-ins isolated from the root".
## 3. S4 chunk 2 — the alpha lists (the slice's body)
**Current state (verified 2026-09-03 at `7df0b94c9`):** `RetailAlphaQueue` is
ONE scope-global list sorted by viewer distance at flush
(`SortRetailOrder` in `Flush` / `FlushFartherThan`); submitters:
`WbDrawDispatcher` (two `queue.Submit` sites: the packed path and the walk
classify path with `viewerDistance`) and `ParticleRenderer` (one site).
Barriers: `IWalkFrameLeafRenderer.AlphaBarrier``FlushLandscapeAlpha`
`_alpha.Flush()` at the building turn (retail `DrawBuilding`'s
`FlushAlphaList(0f)`), the `LandscapeFlush` leaf (retail `PView::DrawCells`'s
`FlushAlphaList(0f)` @0x005a4872), and two `RetailPViewRenderer` end-of-pass
calls (the top-level `SmartBox::RenderNormalMode` flush). `FlushFartherThan`
(the pre-punch "drain the farther content" approximation) is the AP-34 row.
**Retail (spec §5§7, verified against the decomp at chunk time):** two
independent FIFO lists (CLIP and ALPHA) of 3000 `AlphaListEntry` each;
`AddMeshToAlphaList` appends, never sorts; each `DrawMesh` invocation owns a
first-for-this-list flag that captures material + object matrix for the
list's replay; `FlushAlphaList(threshold)` is a no-op only when BOTH counts
are below `threshold × 3000`, otherwise drains CLIP then ALPHA in append
order, resets both, restores the object matrix, does NOT restore the
material; capacity overflow = append returns false and the delayed subset is
NOT drawn; `DrawBlock` calls `FlushAlphaList(0.75)` per land cell (the
pressure valve — inert below 2250 entries).
**Behavior change (chunk contract to be written after the S3 review):**
1. Replace the sorted single queue with two FIFO records mirroring the
retail entry (source token, first-for-list material/matrix capture,
`overrideClipmap`, list id) — no distance field, no sort.
2. Route every submitter to the exact list by the retail `DrawMesh` branch
table (spec §4): CLIP for the multipass clip-map delayed duplicate, ALPHA
for delayed subsets; sky/detail immediate gates; material `has_alpha`
fallback.
3. Flush sites exactly: `DrawBuilding` 0f (the existing `AlphaBarrier`),
`DrawBlock` 0.75 per land cell (NEW — a threshold flush at the `SC` turn;
inert at today's counts, pinned by a capacity test), `PView::DrawCells`
0f (the existing `LandscapeFlush`), top-level 0f (the existing pass-end
flush). Delete `FlushFartherThan` and retire AP-34.
4. Capacity 3000 per list with the exact overflow behavior (append false,
subset dropped) — pinned, not "expanded or recovered".
5. Transparent EnvCell subsets routed to their retail list (spec §6: the
environment detail surface makes eligible subsets immediate).
**Tests (plan §9 S4):** multiple translucent objects in one cell with
reversed distance vs insertion order (FIFO wins); equal-key stable tie; two
cells where a global sort disagrees with traversal; particle/object/
transparent-cell overlap; `DrawBuilding 0f` flush; pre-clear and final
flush; the 0.75 partial flush at exactly 2250; alternating blend modes across
Vulkan batches; capacity 3000 overflow drops the subset.
**Gate:** the alpha/depth transcripts exact against the §7 captures
(`*.alphadepth.log``AM` add-mesh, `FL` flush, `PM` portal mask lines), the
walk/depth/alpha suites, Release build; then the owner's G3 (S4's owner gate
per the plan) — the lead's S3-state G3 pre-run of 2026-09-03 (20/20 frames)
is the baseline to diff against.
## 4. S4 chunk 3 — deletions
Delete the obsolete stencil/bias/skip paths and the CYpt sort once chunk 2's
transcript is exact: `RetailAlphaOrdering.ComputeViewerDistance` and every
`viewerDistance` argument on the submit path; any remaining
`FlushFartherThan` consumer; the `#117`/`#129` bias remnants the S1 hygiene
already removed from `portal_depth.vert` (verify nothing else carries them).
Register: AP-34 retired; any row describing the distance sort retired; the
seal/punch color-state row from S4-c1 kept or retired per its decision.
## 6. S4-c1 contract (lead draft, 2026-09-03 evening — dispatch after the S3 review closes; re-locate every owner)
**Retail facts (verified in the decomp / the Ghidra arbitration table of
`oh1-depth-lifecycle.md`):**
- R1 far-Z punch: `DrawPortalPolyInternal` @0x0059bc90's tail writes the
punch depth from the constant with bits `0x3F7FFFEF`.
- R2 the ±12 reject: `0x59BCD60x59BD28` then `0x59BD400x59BD66` compare
each SOURCE vertex's LOCAL `x`/`y` against ±12 BEFORE `xformStart`. The
QUANTIFIER (S4-c1 round-1 retail lens, 2026-09-03 night — the lead's first
wording here and in T2 was inverted): retail rejects the polygon only when
ALL of its local vertices lie on ONE of the four planes x=+12, x=12,
y=+12, y=12 — a polygon degenerate onto a bounding plane; a polygon with
merely one vertex on such a plane is drawn and counted. Four accumulating
predicates, one per plane, each true only if every vertex satisfied it;
return if any predicate survives (degenerate-input guard, S3 packet §2).
Then `0x59BD700x59BD74`: only false/true-depth SEALS increment the
counter (modulo 65,536); far punches never do (already ported, S3 §8.2 B2).
`0x59BDBC0x59BDBF`: fewer than 3 CLIPPED vertices submit no fan (after
the possible increment).
- R3 portal-depth state: depth test ALWAYS, depth write on, no cull; color
writes ENABLED with `SRCALPHA/INVSRCALPHA` and source alpha 0 (a no-op
blend); no stencil.
**Current acdream (at `d0c981212`):**
- `portal_depth.vert:53` `clipPos.z = clipPos.w * 0.99999988;` — the decimal
literal is 1 2⁻²³ = bits `0x3F7FFFFE`; retail's constant `0x3F7FFFEF` is
0.99999898672… — FIFTEEN ULPs nearer the camera (lead's bit check,
2026-09-03: `SingleToInt32Bits(0.99999988f) == 0x3F7FFFFE`). The depth
spec's "**wrong constant**" row is right. C0 below fixes it; T1 pins it.
- No ±12 reject anywhere on the punch/seal producers
(`WalkFrameDriver.OnPunchGeometry` @ the walk's punch event;
`RetailPViewRenderer.DrawWalkExitPortalMasks`); the clipped-count <3 gate
exists as the `vertices.Length < 3` early return in `DrawWalkPunchFan`.
- `PortalDepthMaskRenderer.Rhi.cs`: `GpuCompareOp.Always`, depth write on,
`Blend = GpuBlendMode.None`, `ColorWrite = false` pixel-identical to R3
(a zero-alpha blend changes no color), state-different.
**Behavior change (small):**
- C0 the far-punch constant: `portal_depth.vert` takes retail's exact bits
(`uintBitsToFloat(0x3F7FFFEFu)`, comment citing `DrawPortalPolyInternal`
@0x0059bc90's tail), the SPIR-V is recompiled and `VulkanShaderManifestTests`
re-pinned. Visible effect: the far punch lands 15 ULPs nearer than
before at 24-bit depth that is the difference between "at the far plane"
and "one step in front of it", which is exactly the retail relationship the
seals (true depth) and the interior repaint rely on; any capture pose whose
`PM` lines show `mode=1` punches is the regression check.
- C1 the ±12 local-input reject ported at the producer that owns the LOCAL
polygon (the walk's punch-event producer for punch fans; the exit-seal
enumeration for seals both read the dat portal polygon in cell-local
space before the world transform), as retail's four per-plane predicates:
the polygon is dropped only when EVERY source vertex lies on the same one
of x=+12, x=12, y=+12, y=12 (exact equality), BEFORE any counter
increment (order: reject transform clip count). A polygon with one
vertex on a bound is NOT rejected. If the table shows the bound can never
be hit by an authored portal polygon, the port still lands (it is retail's
code) with the test proving the reject fires on a synthetic all-on-one-
plane polygon and NOT on a one-vertex-on-the-plane polygon.
- C2 no state change for R3: add register row "portal-depth color path:
ColorWrite=false instead of retail's zero-alpha SRCALPHA/INVSRCALPHA blend
identical output, different pipeline state" (an AD row, with the
citation), unless the reviewer prefers the exact blend attachment; the
lead's default is the row (zero-risk, honest).
- C3 the truth-table theory and the two-frame latch test 2 above).
**Tests:** T1 reads `portal_depth.vert`'s punch line and asserts the literal
it carries has bits `0x3F7FFFEF` (a source pin it FAILS at `d0c981212`,
where the line reads `0.99999988` = `0x3F7FFFFE`; MUTATION: any other
literal); T2 ±12 reject (corrected 2026-09-03 night the lead's first
wording inverted retail's quantifier): a synthetic local polygon with EVERY
vertex at x = 12 exactly (degenerate onto the plane) no punch event and no
seal, counter unchanged; the same polygon with ONE vertex at x = 12 and the
rest inside punched/sealed and counted; every vertex at x = 11.999
punched/sealed; T3 the truth table
(root kind × draw_landscape × outside-view count × previous count) the
exact event subsequence; T4 two consecutive frames prove the latch crosses
frames (frame 1 seals N>0 → frame 2 clears; frame 1 seals 0 → frame 2 does
not clear); T5 multiple look-ins never touch the root latch. Mutation checks
for every new pin, texts inline in the commit body.
**Gates:** build 0 warnings; hermetic green; InstalledDat the four known;
depth-event transcript unchanged at the four capture poses (the `PM`/`PC`
lines of the alpha-depth captures — S4-c1 changes no event unless a ±12
polygon exists in a captured pose, in which case the transcript is the
proof); the lead's four-pose self-gate with validation on (zero errors).
## 7. S4-c1 fix round 1 (lead, 2026-09-03 night — after the three-lens review of `c7ab5b6d8`; round 1 of the two the plan allows)
**Verdicts.** Retail lens FAIL, production lens FAIL, build lens FAIL (the
same blocking quantifier; plus: the commit body's "register tests 52/52"
gate is vacuous — no test in the repository reads the divergence register,
so a register row is checked only by a human read; §7's gates say so and
drop that filter from the claimed evidence). The BLOCKING finding is one
defect seen by all three: C1's
quantifier is inverted — the port rejects a polygon when ANY vertex lies on
ANY ±12 plane; retail (PDB-paired bytes at VA 0x59BCD60x59BCE5:
`fld`/`fcomp 12.0`/`fnstsw`/`test ah,0x44`/`jnp` over `xor bl,bl` — four
accumulating per-plane flags) rejects only when EVERY vertex lies on the
SAME plane. The lead's §6 paraphrase carried the same inversion and is
already corrected (§6 R2/C1/T2). Consequences the lenses proved: the T2
pins lock the wrong rule in; the guard's doc claims authored polygons
"essentially never" hit the bound, but the production lens's DAT scan found
2,889 portal polygons with a ±12 vertex and 2,163 EXIT polygons lying
ENTIRELY on a ±12 plane; and the §6 depth-event transcript gate (PM/PC
lines) was neither run nor automated — the one check that would have shown
the effect on real data. Minor: the manifest re-pin comment has the ULP
direction inverted and points at the wrong test class.
**Why this matters beyond the round.** If 2,163 exit polygons lie entirely
on a bounding plane, retail never punches or seals THOSE portals — which is
the "never-drawn portal polygon = panel" family the PV campaign named
(#456) and very likely the mechanism behind retail's own cathedral seam
leak (#465's "hole"). Porting the guard correctly therefore changes acdream's
seal/punch set at many poses toward retail's; the captured `PM`/`PC` lines
are the only proof that the change lands exactly, so F3 builds that
comparison as this slice's own gate (S4's automated gate already requires
"depth-event transcripts exact against the §7 captures").
**F1 (BLOCKING) — port the quantifier retail has.** Replace
`IsRejectedByPortalPolygonBoundaryGuard`'s any-vertex test with four
per-plane predicates (x=+12, x=12, y=+12, y=12), each initially true and
cleared by the first vertex NOT on that plane; reject iff any predicate
survives all vertices (exact float equality, LOCAL coordinates, before any
transform), at both producers (`WalkFrameDriver.OnPunchGeometry`'s punch
path and `RetailPViewPassExecutor`'s seal enumeration). Rewrite the doc
comment with the DAT counts (recompute them with a scratch scan over the
installed DAT and cite the numbers in the commit body — the "essentially
never" sentence is deleted). T2 becomes: every vertex at x=12 → rejected;
one vertex at x=12, the rest inside → kept, punched/sealed, counted; every
vertex at x=11.999 → kept; every vertex at y=12 → rejected; vertices split
across x=12 and y=12 (none on one common plane) → kept. MUTATION: restore
the any-vertex form → the "one vertex" and "split planes" cases fail;
delete the guard → the all-on-plane cases fail. Rewrite the existing T2
tests and `WalkFrameDriverTests.OnPunchGeometry_RejectsWholePolygonOn…` to
these semantics (the names say what they pin).
**F2 — count order at the seal path.** Retail: guard → transform → clip →
count; the fan is skipped when the CLIPPED count is under 3, AFTER the
counter increment (`0x59BDBC0x59BDBF`). `RetailPViewPassExecutor`'s seal
loop drops sub-3-vertex LOCAL polygons before the guard and before
counting. Port retail's order (authored polygons always have ≥3 vertices,
so this changes no real frame — pin it with a synthetic 2-vertex polygon:
counted, not drawn) and note the register row AD-119's neighbour if a
comment there describes the old order.
**F3 — the depth-event transcript gate, automated.** Parse the four
`oh-capture/*.alphadepth.log` fixtures' `PM poly=<ptr> mode=<0|1>
counterBefore=<hex>` and `PC ov=<n> counter=<hex> fc=<0|1>` lines per frame
(the `oh-capture-alpha-depth.cdb.template` documents them) into per-frame
sequences of (mode, counterBefore) and (ov, counter, fc); replay each
pose's frame 2 through `RetailFrameWalk`/`WalkFrameDriver` with a recording
leaf (the existing `WalkTraceReplayContext` + DAT builder; punch fans =
mode 1 events, exit seals = mode 0 events; the driver's `PortalsDrawnCount`
before each event = counterBefore; the interior turn's ov/count/forceClear
= PC) and compare the sequences exactly. Pointers are not compared. Expect:
with F1 correct, the four poses match; if a pose does not, the test prints
both sequences and is tagged `Status=KnownFailure` with the divergence
written into this section — never weakened. The oracle-side parser lives
beside `WalkOracleTrace` (a `WalkAlphaDepthTrace`), the comparison beside
`WalkTraceConformanceTests`. This is the slice's own gate, not extra
infrastructure.
**F4 — comment truth.** `VulkanShaderManifestTests.cs:80-81`: `0x3F7FFFFE`
is FARTHER from the camera (larger z/w), and T1 is
`PortalDepthVert_FarPunchConstant_MatchesRetailExactBits` in that same
file, not in `WalkVisibilityMathTests`.
**F5 — commit message:** every mutation text verbatim (F1's four, F2's
one, F3's "swap one PM mode" mutation), the DAT scan numbers and the scan
method, the per-pose PM/PC match/mismatch table, `Co-Authored-By` last.
**Gates:** build 0 warnings; hermetic fully green; InstalledDat exactly the
four known plus, if F3 finds a divergence, that row tagged KnownFailure and
written up here; the shader and register test classes green; the lead's
four-pose validation self-gate afterwards (zero validation errors; the
cathedral floating-stairs frame diffed against the S3-closed gate — a
changed seal set is EXPECTED to change the exterior-slab region if retail's
panel family is what the guard reproduces; the owner's retail screenshot is
the oracle for that pose).
## 5. Open questions to settle before dispatch
- SETTLED (2026-09-03): the walk's `SC` turn is `IWalkEventSink.OnSortCellTurn`
in `WalkFrameDriver` (today a transcript print at `DrawSortCell` ENTRY,
fired from `RetailFrameWalk.DrawLandscape`); the 0.75 valve is retail's
`DrawBlock` call AFTER `DrawSortCell` returns, so chunk 2 adds one leaf
method at the sort-cell EXIT (a `WalkFrameEventKind` of its own, ordered
after the cell's object turn) — not a print-site change.
- PARTLY SETTLED (2026-09-03): `ParticleRenderer` submits every deferred
particle draw (billboard and mesh kinds alike) to the single queue with a
viewer distance (`queue.Submit(_alphaSource, token, sqrt(DistanceSq))`).
In retail a particle is a `DrawMesh` like any other subset, so its list is
decided by the §4 branch table on the emitter's subset mask and material
(rows 3/4 → ALPHA under the default `0x0E` delay mask; row 1 immediate
while `m_currentlyDrawingSky` is set — the weather emitters). Chunk 2
routes particles through the same branch table, not a particle-specific
rule; the parts capture (`*.parts.log`, `PD`/`DM` lines) is the oracle for
which emitters ever appear as immediate draws.
- SETTLED (2026-09-03): the `alphadepth` captures' `AM` lines carry the list
selector (`AM mesh=… surf=… csurf=… new=… clip=… listSel=…``listSel`
is the list, `new` the first-for-list flag, `clip` the clip-map duplicate,
`surf`/`csurf` the raw surface type and material), and `FL` lines mark
every flush (terrace-edge: 3,515 `FL` lines over its frames), so the
chunk-2 transcript compares per list and per flush site, not merged.
## 8. S4-c2 contract (lead draft, 2026-09-03 night — dispatch after S4-c1 lands on the campaign branch; re-locate every owner at dispatch)
**Scope (ONE production behavior change):** replace the single distance-sorted
`RetailAlphaQueue` with retail's two append-order lists, route every delayed
subset by the exact `DrawMesh` branch table, and flush at retail's four
normal-world sites under retail's threshold rule. No evidence infrastructure
beyond the transcript gate this chunk consumes (G-c2 below).
**Capture profile the contract rests on (the five `*.alphadepth.log` files,
counted 2026-09-03):** 10,556 `AM` lines, every one `clip=0` (so retail's
`MultiPassAlpha` row 2 never fired live — default false, spec §3); `listSel`
0 = 6,330 lines, 1 = 4,183 (two lists, both live); `FL` lines by return
address: `005a1a0c` (DrawBlock 0.75) 13,705, `0059f310` (DrawBuilding 0f)
309, `005a4877` (PView::DrawCells 0f) 18, `00453b90` (RenderNormalMode 0f)
23; `new=1` 9,685 / `new=0` 871 (the first-for-list flag is mostly set —
most DrawMesh invocations append one subset per list).
**Retail facts the implementer verifies at the decomp BEFORE writing (quote
the predicate, never paraphrase — `feedback_quote_decomp_predicates`):**
1. `D3DPolyRender::AddMeshToAlphaList` @0x0059C230 — append only, two
static arrays of 3000 entries and two uint16 counts, returns false at
capacity (spec §5). Identify which `listSel` value is CLIP and which is
ALPHA from the bytes (the capture's `listSel` is the raw byte at
`@esp+0x18`), and record it in the code comment.
2. `D3DPolyRender::FlushAlphaList(threshold)` @0x0059D2E0 — the early
return fires only when BOTH counts are under `threshold × 3000`; quote
the comparison (strict `<` or `<=`) from the bytes and pin the boundary
count the bytes give (spec §5 says equality at 2250 drains); drain CLIP
fully, then ALPHA fully, in append order; reset both counts; restore the
object matrix, not the material.
3. `DrawMesh` branch table = spec §4 rows 15 verbatim; delay mask
`s_AlphaDelayMask` @0x00820D88 default `0x0E`; `MultiPassAlpha`
@0x0081EF96 default false; the constructed subset mask = spec §2
(surface type bits `0x00010300``0x02`, `0x04``0x08`, `0x10`
`0x04`, else `0x00`; OR `0x01` when the polygon's signed stippling byte
is > 0).
4. The four normal-world flush sites (spec §7) — confirm each capture
return address above lies inside the named function in
`docs/research/named-retail/symbols.json`.
**C1 — two lists.** `RetailAlphaQueue` becomes two FIFO records, CLIP and
ALPHA, capacity 3000 each. Entry = (source, token, list, overrideClipmap,
first-for-list). NO distance field; `Submit` takes the list and the
override flag, never a viewer distance. Append at capacity returns false
and the subset is DROPPED (no immediate fallback) — pinned. Keep the
`IRetailAlphaDrawSource` protocol (`PrepareAlphaDraws` once per drain with
the tokens in list order, CLIP then ALPHA; only adjacent same-source
entries form a batch — the "never group across another entry" invariant
stays, it is what keeps compositing exact).
**C2 — flush semantics and sites.** `Flush(threshold)` implements fact 2.
Sites: `AlphaBarrier` (DrawBuilding) → `0f`; NEW leaf event at the
sort-cell EXIT — its own `WalkFrameEventKind`, recorded after
`OnLandscapeCellTurn` (the object turn) and before the next cell's land
turn (packet §5 SETTLED) → `0.75f`; `LandscapeFlush` (PView::DrawCells) →
`0f`; pass end (RenderNormalMode) → `0f`. `FlushFartherThan` deleted;
AP-34 retired in the same commit; every `viewerDistance` argument on the
submit path and `RetailAlphaOrdering.ComputeViewerDistance` (with its
test) deleted — chunk 3's deletion list shrinks accordingly.
**C3 — routing.** Both `WbDrawDispatcher` submit sites
(`SubmitWalkAlphaInstance`, `DeferTransparentGroups`) and
`ParticleRenderer`'s site route through ONE pure router implementing spec
§4 with these inputs: currently-drawing-sky (true only inside the `Sky`
leaf — weather emitters draw immediately there; `*.parts.log` is the
oracle for which emitters ever appear immediate), the delay mask (one
owner, default `0x0E`, no environment read), detail-surface presence (the
active detail owner from VisualMaster), `MultiPassAlpha` (one owner,
false), the spec §2 constructed mask from the DAT surface type and the
polygon's stippling, and the material's `has_alpha`. Row 1 draws never
touch a list. Truth-table test over every (mask `0x00``0x09`) × delay
`0x0E` × sky × detail × multipass × has_alpha cell in spec §4, each row
with a mutation text.
**C4 — transparent EnvCell subsets** go through the same router (spec §6:
the environment detail surface makes eligible subsets immediate).
**C5 — register.** AP-34 deleted; any row describing the distance sort
deleted; retail's non-restored material after a flush is a no-op under
Vulkan's per-batch material binding — a code comment at the drain, not a
row, unless the implementer can argue a visible effect.
**Must NOT:** keep any distance anywhere on the alpha path; "recover" a
capacity overflow; touch S4-c1's punch/seal machinery; add flush sites
beyond the four; read an environment variable for the delay mask.
**Tests (each with a mutation text showing the pin fails without the
change — `feedback_every_new_pin_must_be_shown_to_fail`):** FIFO beats
reversed distance in one cell; equal-key stable tie; two cells where a
global sort disagrees with traversal; particle/object/transparent-cell
overlap; DrawBuilding `0f` flush; pre-clear and final flush; the `0.75`
valve exactly at the boundary count from fact 2 (and one below it: no-op);
alternating blend modes across Vulkan batches; capacity 3000 overflow
drops the subset; the router truth table; the new event kind's ordering.
**Gate G-c2 (the chunk's real proof):** extend S4-c1's automated alphadepth
transcript gate from PM/PC to AM/FL — per capture frame, the sequence of
(flush site, threshold) and, per flush, the drained entry count PER LIST
must match the capture's AM/FL stream; a diverging pose is tagged
KnownFailure with a written explanation, never weakened. Then the walk/
depth/alpha suites, Release build, the validation self-gate at the S3-closed
poses diffed against the S3-closed run, and a stationary-phase soak A/B
against the S4-c1 binary (removing the sort must not regress; #463's
turn-phase rule).
**Lenses:** one Sonnet implementer; two sequential review lenses on the
session model (retail-faithful — every predicate quoted; production —
allocation, the scratch budget, the gate's honesty); each lens returns
PASS/FAIL with file:line findings; two fix rounds maximum, then the ledger.
## 9. S4-c1 fix round 2 (lead, 2026-09-03 23:05 — the LAST round; a third stops the chunk and writes it up)
**Round 1 (`2bd353234`) verdicts:** retail-faithful lens PASS, production lens PASS, gate-honesty lens FAIL. All findings converge on the transcript gate's terrace-edge row plus one pin gap. Fix these, nothing else:
**R2-1 (blocking, gate honesty).** The terrace-edge KnownFailure write-up (test doc comment and commit body) claims "the PC sequence matches exactly"; it does not — PC diverges in the same counter field (expected `[(0,2,0),(0,2,0)]`, actual `[(0,0,0),(0,0,0)]`), and the PM assert throws before the PC assert runs, so the test never printed both sequences as §7 F3 required. Resolution: **R2-2** below removes the divergence; whatever remains must be asserted so that BOTH sequences are always compared and printed together (collect both diffs, one assertion message), and every written claim about a sequence must be one the test actually evaluated.
**R2-2 (major, both PASS lenses).** The terrace-edge divergence is a harness initial-condition choice: retail's `portalsDrawnCount` (`wo(008719b4)`) is a persistent session value that the capture's FIRST `PC`/`PM` line already carries (cathedral 0, foundry 1, holtburg 2, terrace 2, cathedral-stair-arch 8). Seed `WalkFrameDriver.PortalsDrawnCount` from the fixture's first observed counter before the ONE replay pass (the field is settable from the test assembly), delete the two-pass "priming" replay, and re-run all poses. Expected: terrace-edge turns green with NO KnownFailure tag; every other pose stays exact by the same arithmetic the passing poses already prove. If any pose still diverges after seeding, it keeps a KnownFailure tag AND its divergence is written into THIS section (not only into a doc comment) with both sequences quoted.
**R2-3 (major, retail lens).** T2's five cases do not discriminate "every vertex lies on SOME plane" (per-vertex OR, AND across vertices) from retail's "every vertex lies on the SAME plane", and real DAT content exists where they disagree. Add the discriminating case to T2 and to the punch pin: a polygon whose every vertex is on a ±12 plane but not all on the same one, e.g. `[(12,0,3), (0,12,3), (12,5,3)]` — ADMITTED by retail's four per-plane accumulators (no accumulator survives: x+12 is cleared by vertex 2, y+12 by vertex 1). Mutation text: implementing the per-vertex-OR form rejects it (assert flips).
**R2-4 (sixth pose).** The campaign branch now carries `docs/research/2026-09-01-overhaul/oh-capture/cathedral-stair-arch.walk.log` + `.alphadepth.log` (commit `4f12cd06b` on `claude/campaign-w-retail-frame-walk`; also `.pmverts.log` at `3a6aa7eef`) — the #464 artifact pose, root `0xF4180114`, seven outside views, sixteen building punches (twelve rejected by the guard on local y=12.000) and eight seals with `counterBefore` 0..7, steady-state counter 8. Cherry-pick those two capture commits into the impl worktree (`git cherry-pick 4f12cd06b 3a6aa7eef`; if either conflicts on README.md, keep both rows) and add `AlphaDepthTranscript_CathedralStairArch_MatchesRetailFrame2` as a sixth Fact. The lead already ran it against `2bd353234` with the capture copied in: PASS (the seed value 8 comes from the fixture under R2-2).
**R2-5 (comment truth, minor ×4).** (a) `RetailPViewPassExecutor.DrawPortalDepthWrite`'s summary still describes the retired ">=3 vertices" enumeration and `DrawExitPortalMask`'s summary still promises the counter "always matches what reached the GPU" — both false after F2; fix the text. (b) The F2 pin's doc comment names a mutation that would NOT fail (moving `submitted++` after `DrawDepthFan` is unobservable — void return, no throw); say so, keep only the mutation that fails (restoring the `<3` pre-filter). (c) `WalkVisibilityMath`'s doc comment states DAT-scan figures as exact facts that an independent scan does not fully reproduce; state each figure WITH its counting rule (what is counted, over which id range, how unresolvable polygons are treated) or keep only the figures both scans agree on (2,889 any-vertex / 2,163 all-on-one-plane / 100 % exits / 0 interior). (d) The commit body must not cite "register tests 52/52" as evidence — no test reads the register; register truth is the human read (no row changes; AD-119 unchanged).
**Not in scope:** anything under `src/` beyond R2-5(a)'s comments; the seal path's test-side reproduction (accepted as-is under F3's "recording leaf" wording); the lead's punch-geometry scratch dump (stays in `s4-c1-gate`, never committed).
**Gates (all reproduced by the lenses, none claimed vacuously):** Release build 0 warnings; hermetic lane green; InstalledDat lane with ONLY the pre-existing #383 ×2, TowerAscent, #458 KnownFailure failures (terrace-edge must no longer be among them); all six transcript Facts green; the shader classes green. One commit on top of `2bd353234` in `s4-c1-impl`.
**Lenses (sequential, session model, narrowed to the round-2 diff):** retail-faithful (R2-3's predicate and R2-2's seeding argued from the bytes/decomp — the counter's persistence across frames at `PView::DrawCells` and the reset site), gate-honesty (every sentence in the commit body and every test doc comment is something the tests evaluate; both sequences compared together), production (no allocation/behavior change outside the test project except R2-5(a) comments).
## 10. S4-c2 landing (Sonnet implementer, 2026-09-04) — gate G-c2 result and open scope
**C1/C2/C3/C5 landed as specified.** `RetailAlphaQueue` is retail's own two FIFO
lists (`RetailAlphaEntry`, capacity 3000 each, `TryAppend` returns `false` and
drops the subset at capacity, `Flush(site, threshold)` implements the exact
Ghidra-verified no-op predicate — `if (clipCount < threshold*3000 && alphaCount
< threshold*3000) return;` — and drains CLIP fully then ALPHA fully, only
adjacent same-source runs batched). `RetailAlphaMeshRouter` ports `DrawMesh`'s
five-row branch table and `ConstructMesh`'s mask formula as pure functions,
independently verified against a fresh 2026-09-04 Ghidra decompile of both
functions plus `AddMeshToAlphaList` and `FlushAlphaList` (not just the
packet's own researched spec — see the commit body for the exact quoted
predicates). `WbDrawDispatcher`'s two submit sites and `ParticleRenderer`'s
one route through the router with a shared `SubmitToAlphaQueue`/inline
equivalent; **S4-c2 fix round 1 correction (M1, blocking):** the claim that
"rows 1/2/4/5 are provably unreachable there" was WRONG for the particle
site, which the round's own retail-faithful review lens caught as a
production crash. `WbDrawDispatcher`'s sites genuinely never reach a mask
other than 0x02 (`IsOpaque` pre-filters both Opaque and ClipMap upstream, so
row 3 always fires there — this part of the claim holds). But
`ParticleRenderer.TryAppendMeshDraws` applies NO such pre-filter: a
mesh-particle batch's `TranslucencyKind.Opaque` classification (mask 0x00) is
a real, reachable input, hitting row 5 (Immediate) — and the throw-on-non-
Append guard fired in the render loop. The fix round deleted both throws,
implemented rows 1/5's immediate draw at the particle site (reusing the
non-deferring `DrawOrdered` code, no new renderer), and derived
`materialHasAlpha` for row 4 from `CMaterial::CheckAlphaValues`
@0x005396a0/`SetTranslucencySimple` @0x005396f0's retail rule — a particle's
current interpolated alpha (already carried in `ColorArgb`'s top byte) stands
in for retail's live per-instance material alpha. `FlushFartherThan`, `RetailAlphaOrdering.ComputeViewerDistance`,
and every `viewerDistance` argument on the submit path are deleted. A NEW
`WalkFrameEventKind.SortCellExit` / `IWalkEventSink.OnSortCellExit` /
`IWalkFrameLeafRenderer.FlushSortCellExit` fires once per admitted land-block
cell, immediately after that cell's `OnLandscapeCellTurn` and before the next
cell's `OnLandCellTurn` — pinned by a dedicated two-block far/near ordering
test in `RetailFrameWalkTests.cs`, mutation-checked (reordering the two calls
in `RetailFrameWalk.cs` makes the pin fail with "SCX must immediately follow
its own cell's SC").
**C4 (EnvCell) — SUPERSEDED, S4-c2 fix round 1 (M6, 2026-09-04).** This
landing's original claim (EnvCell "deliberately NOT done," needing "a
genuinely new deferred-replay abstraction") was WRONG in the same round's own
two-lens review: `EnvCellRenderer.RenderTransparentOrdered` already replayed a
cell's transparent batch on demand, so no new abstraction was needed. The fix
round wired it: `RetailPViewPassExecutor.SubmitOrDrawTransparentCellShell`
routes every real transparent subset through the SAME
`RetailAlphaMeshRouter` table every other alpha submitter uses. Fix round 2
retains `TextureBatchData.RetailSurfaceMask` on each `ObjectRenderBatch`:
canonical `0x08` reaches CLIP, `0x02` reaches ALPHA, and a mixed cell appends
at most one filtered replay token per `(cell,list)`; detail-active eligible
subsets remain immediate at the cell turn. AP-34's residuals are now filed as **AP-238**
(EnvCell — narrowed to the per-cell-vs-per-subset token GRANULARITY only,
not "never through the shared queue") and **AP-239** (the mask-reconstruction
gap, formerly filed as the colliding id "AP-237" — see A2/M8c below); a THIRD
residual, **AP-240**, was filed this round for M2's separate finding: ordinary
clip-mapped GfxObj/scenery/building content never reaches the CLIP list at
all (`WbDrawDispatcher.IsOpaque` filters it out upstream). CLIP nevertheless
has two genuine production feeder families: clip-mapped mesh particles and
exact-mask EnvCell subsets. AP-239's own cited example (cloud
GfxObj `0x01004C35`) was DISPROVEN this round (M7) — its surface's alpha-family
bits win in both retail's real priority and acdream's reconstruction, so it
never actually diverges; a 2026-09-04 DAT scan found 27 real instances instead
(e.g. surface `0x08000015` on GfxObj `0x010001EC`). These residuals do not
change the affected subset's blend mode, but overlap-dependent compositing
order can be visibly different; AP-238/AP-240 state the concrete overlap.
**Gate G-c2 result, all six poses, real InstalledDat run
(`RunAlphaFlushTranscriptGate`, `WalkTraceConformanceTests.AlphaDepthTranscript.cs`):**
the flush **SITE+THRESHOLD sequence matches the capture EXACTLY for all six
poses** — same count and order of `DrawBuilding`(0f)/`LandscapeFlush`(0f)/
`RenderNormalMode`(0f) calls, and (confirming the packet's own capture-profile
prediction) **zero `SortCellExit`(0.75f) drains in any of the six captures'
frame 2** — every real per-land-cell valve call stayed under the 2250-entry
no-op threshold at these scene complexities, exactly matching this gate's own
zero-content replay. The **drained-count-per-list dimension diverges on every
0f flush for every pose** — this hermetic replay (`EmptyAlphaDepthWorldData`,
the same zero-content design the PM/PC gate it extends already uses) submits
no real GfxObj/particle content, so every observed count is `(0,0)` where
retail's capture shows real volume. Full quoted sequences (site, threshold,
clip, alpha), expected vs. actual, per pose:
- **cathedral-arrival:** expected `[(DrawBuilding,0,108,40), (DrawBuilding,0,3,0), (DrawBuilding,0,28,7), (DrawBuilding,0,7,5), (DrawBuilding,0,70,24), (DrawBuilding,0,13,0), (DrawBuilding,0,0,0), (DrawBuilding,0,0,4), (DrawBuilding,0,0,0), (DrawBuilding,0,0,0), (DrawBuilding,0,0,0), (DrawBuilding,0,34,7), (DrawBuilding,0,0,0), (DrawBuilding,0,0,0), (LandscapeFlush,0,0,0), (RenderNormalMode,0,0,0)]`; actual: same 16-entry SITE sequence, every count `(0,0)`.
- **cathedral-leak:** expected `[(DrawBuilding,0,108,40), (DrawBuilding,0,3,0), (DrawBuilding,0,28,7), (DrawBuilding,0,7,5), (DrawBuilding,0,70,24), (DrawBuilding,0,13,0), (DrawBuilding,0,0,0), (DrawBuilding,0,0,4), (DrawBuilding,0,0,0), (DrawBuilding,0,0,0), (DrawBuilding,0,0,0), (DrawBuilding,0,34,9), (DrawBuilding,0,0,0), (DrawBuilding,0,0,0), (LandscapeFlush,0,0,0), (RenderNormalMode,0,0,0)]`; actual: same 16-entry SITE sequence, every count `(0,0)`.
- **cathedral-stair-arch:** expected `[(DrawBuilding,0,0,0), (DrawBuilding,0,0,0), (DrawBuilding,0,47,30), (DrawBuilding,0,21,0), (DrawBuilding,0,2,0), (DrawBuilding,0,34,27), (DrawBuilding,0,43,1), (DrawBuilding,0,0,0), (DrawBuilding,0,0,0), (DrawBuilding,0,0,0), (DrawBuilding,0,2,4), (LandscapeFlush,0,0,0), (RenderNormalMode,0,32,6)]`; actual: same 13-entry SITE sequence, every count `(0,0)`.
- **foundry-deep:** expected `[(DrawBuilding,0,1,0), (DrawBuilding,0,0,0)×11, (LandscapeFlush,0,0,0), (RenderNormalMode,0,18,7)]` (12 DrawBuilding entries total — **S4-c2 fix round 1 correction, A5: the landing note originally said "×10", undercounting by one against its own stated 12-entry total; 1+11=12**); actual: same 14-entry SITE sequence, every count `(0,0)`.
- **holtburg-doorway-still:** expected `[(DrawBuilding,0,12,0), (DrawBuilding,0,23,6), (DrawBuilding,0,0,0), (DrawBuilding,0,15,0), (DrawBuilding,0,0,0), (DrawBuilding,0,0,0), (DrawBuilding,0,0,1), (DrawBuilding,0,0,0)×6, (LandscapeFlush,0,0,0), (RenderNormalMode,0,33,0)]` (13 DrawBuilding entries total — **S4-c2 fix round 1 correction, A5: the landing note originally said "×5", undercounting by one against its own stated 13-entry total; 7+6=13**); actual: same 15-entry SITE sequence, every count `(0,0)`.
- **terrace-edge:** expected `[(DrawBuilding,0,158,452), (DrawBuilding,0,22,204), (DrawBuilding,0,0,0)×3, (DrawBuilding,0,22,523), (DrawBuilding,0,13,7), (DrawBuilding,0,14,5), (DrawBuilding,0,0,0)×3, (DrawBuilding,0,11,0), (DrawBuilding,0,0,0), (DrawBuilding,0,13,12), (RenderNormalMode,0,56,0)]` (14 DrawBuilding entries, no LandscapeFlush — this pose never runs a qualifying interior turn, matching its own PM/PC KnownFailure history); actual: same 15-entry SITE sequence, every count `(0,0)`.
All six `AlphaFlushCounts_*` Facts are tagged `[Trait("Status",
"KnownFailure")]`; the paired `AlphaFlushSites_*` Facts are live and pass.
The count Facts remain KnownFailure for exactly this reason, never weakened (the real
comparison runs and genuinely fails on the count dimension; the site
dimension is asserted unconditionally in the same method and genuinely
passes). Root cause is a HARNESS scope limit shared with the pre-existing
PM/PC gate, not a routing defect: closing it for real would mean feeding
`WalkFrameDriver` genuine per-pose GfxObj/particle content from the installed
DAT (the production `WalkProductionWorldData` path, not
`EmptyAlphaDepthWorldData`) — a materially larger apparatus than this chunk's
bounded scope, flagged here for a future chunk rather than attempted.
**§11 result (fix round 1, Sonnet implementer, 2026-09-04):** all items
M1M8/A1A8 from the campaign branch's §11 landed in one commit on top of
this landing. M1 (blocking) deleted both unreachable-branch throws and
implemented the particle site's real rows 1/4/5; M3 wired building-detail
Row 1 into `SubmitToAlphaQueue` with a new single-instance immediate-draw
path (`DrawImmediateAlphaInstance`/`Rhi`), removing the drain's own detail
branch (M3c) since detail-eligible content no longer reaches it; M6 wired
EnvCell's whole transparent shell into the SAME router/queue as one ALPHA
token per cell when detail was off, disproving this section's own "needs a
new deferred-replay abstraction" claim above; that round's false
homogeneous-`0x02` premise and whole-cell replay were then corrected by the
exact-mask/list-filtered §12 implementation; A1 deleted a mislabelled
outdoor-root `LandscapeFlush` call retail never makes there (its content now
joins the pass-end `RenderNormalMode` drain like retail's own outdoor root);
M5 split the six former `AlphaFlushTranscript_*` Facts into live `AlphaFlushSites_*`
(unconditional pass) and `AlphaFlushCounts_*` (KnownFailure, three honest
reasons instead of one) and added a live boundary pin through
`WalkFrameDriver.Replay`'s own `SortCellExit` arm; M4 deleted the unread
`IsFirstForList` field (retail's per-DrawMesh, per-list value genuinely
varies: 9,685 `new=1`, 871 `new=0`; acdream has no one-to-one retained
producer or Vulkan consumer); M2/A2/M8c retired `AP-34` (restored as
`~~AP-34~~`, not left deleted), renumbered the colliding `AP-236`/`AP-237` to
`AP-238`/`AP-239`, and filed `AP-240` for the CLIP-list-for-ordinary-content
gap M2 found; M7 disproved AP-239's cloud citation and replaced it with a
real scanned instance; A4 fixed `ApplyScratchRetention`'s source-count
argument; A7 renamed/redocumented the mesh-router truth-table oracle
honestly. See the fix-round commit body for every mutation check's actual
failing-assertion text and the real gate output lines.
**§12 result (fix round 2 plus the owner-authorized post-stop repair,
2026-09-04):** the owner-authorized repair supersedes the earlier §12 result's
EnvCell exactness, scratch-bound, and allocation claims. The repaired real
leaf path now excludes deferred non-additive transparent subsets from the
opaque pass, classifies retained ClipMap presence with `(mask & 0x08) != 0`
(including legal positive-stipple mask `0x09`), and keeps canonical row 3 at
`OverrideClipmap=false`. Its CLIP base pipeline is the paired-retail
`ONE/INVSRCALPHA`, alpha test `GREATER_EQUAL`, reference `100/255` for a
paletted texture or `200/255` for DDS/non-paletted, with depth test/write on
and alpha-to-coverage off. Both fragment shaders recognize only those two
named references (within 1e-6 for GLSL constant folding); arbitrary `0.5`
falls back to the ordinary `0.05` empty-fragment cutoff. Exact `uParamB=1`
remains `mesh_detail.vert`'s pre-existing building-category sentinel and also
uses `0.05`, so the new reference does not erase building detail. Detail-
active ClipMap replay retains the selected cutoff and a depth-writing clip
base before the detail contribution. ALPHA remains straight-alpha with depth
write off and the two subsets do not cross-feed.
Rejected EnvCell reservations now roll back the tail cell id while keeping
the source registered for exactly one flush/end/abort reset. Storm coverage
uses two sources, 3,000 accepted tokens, and 9,000 rejected attempts; rejected
pending count returns to zero, retained capacity stays at or below 4,096 (the
bounded geometric backing for the 3,000-list limit), and rejected payload is
never prepared or drawn. The allocation claims now execute the real whole-
leaf route scan, `DrawCellShell`, list-filtered replay and
`EnvCellRenderer.SubmitRhi`, and the real private particle
`DrawImmediateParticleSubmissionRhi`, against a preallocated RHI with call
recording disabled. Both report 0 managed bytes per warmed iteration. This
replaces the prior static/delegate-only evidence.
The exact state is backed by `D3DPolyRender::SetSurface` @0x0059c4d0 and the
paired executable: @0x0059c72a selects blend ids 2/6
(`ONE/INVSRCALPHA`), globals 0x00820d8c and 0x00820d90 contain 0x64 and 0xc8,
@0x0059c821 calls `SetAlphaTestEnable`, @0x0059c838 calls
`SetAlphaTestFunction` with function 7 (`GREATER_EQUAL`), and @0x0059c866
passes depth-write byte 1. No Content model, serializer, or package-format
file changed. AP-238 and AP-240 now state only the repaired production truth;
the remaining AP-238 divergence is token granularity, while ordinary Wb
ClipMap A2C remains AP-240 and is unchanged.
Real clean-state return matrix:
- Release solution build: `Build succeeded`, `0 Warning(s)`, `0 Error(s)`.
- Hermetic lane: every project green, `16,735 passed / 0 skipped / 0 failed`
across 14 assemblies; App `6,882/6,882`.
- InstalledDat: exactly `Passed: 255`, `Failed: 10`, `Skipped: 1`,
`Total: 266`. The failures are only
`TowerAscent_StaircaseStaysConeVisible_EveryStep`,
`MainGameUiAndChatInput_MediaBearingChildrenNowBuildAsRealWidgets`
(#383), `EveryAuthoredInvisibleWidget_StartsHiddenAcrossAllLayouts`
(#383), `Oh_doorway_still_first_frame_diff` (#458), and the six
`AlphaFlushCounts_*`; every `AlphaFlushSites_*` passes.
- Checked-in SPIR-V was rebuilt with `tools/compile-shaders.ps1`: 24/24;
shader descriptor/manifest/pack validator filter: `32/32`.
- Focused queue/router/walk/driver/particle/Wb/EnvCell/PView production
filter: `239/239`. The explicit real production allocation filter is
`2/2`, and both pins assert 0 B.
- Register read: AP-238, AP-239, AP-240, AD-120, and `~~AP-34~~` each have
exactly one physical row; active physical rows remain AP=159 and AD=92.
- `git diff --check`: PASS.
Evidence-history disclosure: the first hermetic invocation was stopped after
App exposed two stale test oracles introduced by this repair: the old
`mesh_modern.frag.spv` SHA (expected
`b702b644862aca31ce1fb0677adc5872b39c4ea87f595a89363b44d10f2cc50e`, actual
`e094a966f7653a79a57d1c208f36d3100a8dcb20891584e6c74b4697e95c1950`) and the old
five-blend cardinality (expected 5, actual 6). Both assertions were corrected
to the exact new shader/state contract, then the complete lane above ran once
without an internal retry. The same evidence directory was mistakenly reused,
so script startup replaced that first invocation's TRX/log; the exact failures
remain recorded here rather than being presented as a clean first attempt.
The observed Core.Net PIDs belonged to those two separate script invocations,
not a project retry: Core.Net passed 1,077 in the stopped invocation and again
in the clean invocation. The first InstalledDat command also omitted
`ACDREAM_RUN_INSTALLED_DAT_TESTS=1` and was invalid evidence (161 pass / 8 fail
/ 97 skip); the documented environment produced the 255/10/1 result above.
A narrower exploratory focused filter hit the pre-existing global
`Console.Out` capture race once (`218 passed / 1 failed`,
`OutdoorRoot_TranscriptFlagOn_PrintsLandscapeThenLandCellThenSortCellThenBuilding_InOrder`,
"LS must precede LC"); that Fact passed isolated 1/1 and in the broader
239/239 focused run above. A direct unfiltered `WalkTraceConformanceTests`
probe also produced its seven documented InstalledDat failures and is not the
focused clean lane.
Actual owner-repair mutation failures (each production mutation was applied,
run to the named first failure, and exactly reversed before the final gates):
1. Restoring the opaque predicate to `!IsAdditive` fails
`WholeLeaf_MixedCellDrawsOpaqueAtTurnThenClipAndAlphaAtDrain`
first at draw count: expected 1, actual 3.
2. Selecting `_alphaPipeline` for CLIP fails
`WholeLeaf_ClipDrainBindsExactStateAndTextureClassReference` first at the
bind sequence: expected `[envcell-clip]`, actual `[envcell-alpha]`.
3. Disabling CLIP depth write fails that same production Theory first at
`Assert.True(clipPipeline.Depth.Write)`: expected true, actual false.
4. Swapping the palette/DDS references fails the DDS row first: expected
`0.784313738`, actual `0.392156869` (and the palette row reports the
inverse).
5. Mutating `mesh_modern.frag` from `<` to `<=` (strict `GREATER`) fails
`ClipShaders_UseGreaterEqualForThePerRangeReference` first at the preceding
`Assert.Contains("if (color.a < alphaCutoff) discard;")`: the required `<`
source spelling is absent after the mutation. The later
`Assert.DoesNotContain` is not the first failure.
6. Restoring row-3 `OverrideClipmap=true` fails the real-interface clip Theory
first at `Assert.False`: expected false, actual true.
7. Deleting failed-append rollback fails all flush/end/abort rejection-storm
rows at the first bounded pending-count assertion: expected 0, actual
9,000.
8. Selecting `_transparentDetailPipeline` for a ClipMap detail contribution
fails the leaf detail pin's bind sequence: expected second bind
`envcell-retail-detail-clip`, actual `envcell-retail-detail-alpha`.
9. Resetting detail `ParamB` to zero fails the same detail pin first at the
second pushed reference: expected `0.784313738`, actual 0.
10. Classifying CLIP with exact mask equality excludes legal mask `0x09` and
fails `WholeLeaf_PositiveStippleClipMaskUsesClipPipelineAndDdsReference`: expected
`envcell-clip`, actual `envcell-alpha`.
11. Mapping the new blend to `SRC_ALPHA/INVSRCALPHA` fails
`AllRetailBlendModesAreRepresentable`: expected
`(One, OneMinusSrcAlpha)`, actual `(SrcAlpha, OneMinusSrcAlpha)`.
12. Restoring `mesh_detail.vert`'s former `uParamB > 0.5` category predicate
fails `ClipShaders_UseGreaterEqualForThePerRangeReference` first because
`vDetailCategory = uParamB == 1.0` is absent; the DDS reference must remain
EnvCell rather than masquerading as the building sentinel.
13. Treating every positive detail `uParamB` as a cutoff fails the same source
pin first because `isRetailClipReference(uParamB) ? uParamB : 0.05` is
absent; this is the negative proof that exact building sentinel 1 is not a
cutoff.
14. Adding arbitrary `0.5` as an accepted reference to either
`mesh_modern.frag` or `mesh_detail.frag` fails that source pin first at
`Assert.DoesNotContain("value - 0.5")`; both shader mutations were run and
reversed independently.
No graphical client was launched. G3/G4 remain unpassed pending the campaign
branch's fresh build, reviews, and owner-visible graphical/soak gates.
## 13. S4-c2 final-round result — STOPPED UNLANDED (2026-09-04)
**Stop rule fired.** Final-fix implementation `c4cbc1d0d` (directly on
`cc8e5677a`, worktree `s4-c2-impl`) passed its implementer matrix, but BOTH
sequential §12 lenses found code/test/register changes that would require a
third fix round. Section 12 explicitly bars that round. None of
`048d5b12f`, `cc8e5677a`, or `c4cbc1d0d` was cherry-picked to the campaign
branch; the branch's latest renderer code remains S4-c1 `766f9e749`.
### 13.1 Retail-faithful lens — FAIL
Paired executable/PDB was re-confirmed (`{9e847e2f-777c-4bd9-886c-22256bb87f32}`,
age 1). The particle row-5 fix and exact EnvCell `ConstructMesh` mask/list
selection are correct. The following are not:
1. **BLOCKING — a deferred EnvCell subset already draws at the cell turn.**
`WalkProductionLeafRenderer.DrawCellShell` calls `DrawOpaqueCellShells`
before `SubmitOrDrawTransparentCellShell`. `EnvCellRenderer`'s opaque pass
does not exclude a non-additive `IsTransparent` batch, so canonical pure
ClipMap `F4180104` / surface `08000BFF` is drawn immediately before its
CLIP token is appended, then drawn again at the drain. Retail row 3 only
appends. The new production fixture starts at dispatch and never executes
this preceding opaque-shell call.
2. **MAJOR — CLIP/detail-immediate surface state is wrong.** All non-additive
EnvCell transparent ranges bind `_alphaPipeline` (StraightAlpha,
depth-write off). Retail's pure ClipMap replay uses ONE/INVSRCALPHA,
alpha test, and depth-write on (`SetSurface` 0x0059c6e40x0059c806;
`SetDepthBufferMode` call 0x0059c866). Detail-active row 1 uses that same
retail base state before its detail combine. The port also hardcodes
`overrideClipmap:true` although the router's ordinary mask-`0x08` row has
false; replay ignores the flag. Tests assert draw counts, not bound
pipeline/blend/depth/override state, and construct mask-`0x08` as
`AlphaBlend`, so the wrong state stays green.
3. **MAJOR — register/result claims are false.** AP-238's “token granularity
only” statement omits premature/double draw and surface-state divergence;
AP-240's EnvCell “CLIP feeder” is only queue bookkeeping while the visible
subset has already drawn. The implementation's §12 result repeats those
claims.
4. **MINOR truth:** the mesh-particle pipeline summary still says it shares
the billboard depth bracket despite the opaque depth-write exception; an
EnvCell test comment cites a nonexistent `Assert.Empty(log)`.
The lens reproduced 46/46 relevant queue/particle/EnvCell/PView tests and
42/42 particle/EnvCell renderer tests. Green tests do not contradict the
findings because they omit the preceding opaque pass and pipeline state.
### 13.2 Production/gate-honesty lens — FAIL
The second lens independently confirmed both rendering blockers and added:
1. **MAJOR — source scratch is not queue-bounded.** Each fixed-list
`EnvCellAlphaDrawSource` retains pending/prepared/draw `List<uint>`
buffers. Payload is reserved before queue admission; after 3,000 entries
`TryAppend` rejects, but the source continues growing and `Reset` only
clears capacity. The queue's scratch budget/capacity therefore does not
bound retained source memory under rejection pressure.
2. **MAJOR — allocation evidence is vacuous for the claimed paths.** The two
0-B tests feed route flags directly into static dispatch with no-op sinks;
they do not run the production EnvCell scan/filter/RHI path, and the
particle test does not run `DrawImmediateParticleSubmissionRhi`.
3. R2-3's rejected-first-use cleanup itself is correct for flush/end/abort;
M1, M3, and A1 have material production pins; allowed scope and
`git diff --check` pass.
This lens reproduced Release 0 warnings/errors, 81/81 focused production
tests, and 32/32 shader tests. Its official hermetic no-retry run was
16,727 pass / 1 pre-existing `Console.Out` capture-race failure; isolated App
rerun passed 6,875/6,875. InstalledDat was unavailable in that lens because
the opt-in DAT environment was not configured. The implementer's clean-state
evidence remains: hermetic 16,728/16,728; InstalledDat 255 pass / 10 expected
fail / 1 skip; shaders 32/32; corrected focused 211/211. Those counts prove
the tested behavior only; they do not repair the missing discriminators.
### 13.3 Disposition
- `s4-c2-impl` remains clean, detached at unlanded `c4cbc1d0d` for evidence.
- No code fix, cherry-pick, graphical client, validation self-gate, or
stationary A/B was performed after the stop. The user-required graphical
gate is deliberately inapplicable to unlanded code.
- A technically complete next attempt would need to fix pass membership,
carry exact clip/override state into an EnvCell clip/depth-writing pipeline,
bound source-owned payload under rejection, add end-to-end walk-leaf plus
pipeline-state/allocation discriminators, and rewrite AP-238/AP-240/result
truth. That is a THIRD fix round and is NOT authorized by this campaign
chunk. Resume only by an explicit owner decision that changes the process;
otherwise S4-c2 is stopped and G3/G4 remain unpassed.
## 14. S4-c2 owner-authorized repair after the stop (2026-09-04)
**Authority and base.** The owner replied “Ok do fix it” after receiving the
§13 stop explanation. That explicitly authorizes the otherwise-forbidden third
fix round for this chunk only. It does not weaken plan §5's general two-round
rule. Work from clean evidence commit `c4cbc1d0d` in `s4-c2-impl`; the campaign
branch remains at stop record `dc971ea2b` with S4-c1 `766f9e749` as its latest
renderer until both review lenses pass. This exception is one bounded repair:
any new code/test/register finding after its reviews stops again and returns to
the owner; there is no implicit fourth round.
The lead re-read `docs/architecture/worldbuilder-inventory.md` before writing
this contract and re-verified the surface state in the named retail decomp and
the paired executable/PDB. `D3DPolyRender::SetSurface` @0x0059c4d0 does the
following for a pure `Base1ClipMap` surface when `overrideClipmap == 0`:
- @0x0059c724..0x0059c743 selects `BLEND_ONE/BLEND_INVSRCALPHA`, enables
alpha test, and leaves alpha blending enabled;
- @0x0059c73c..0x0059c75d selects alpha-test reference 100 for a paletted
texture and 200 for a DDS/non-paletted texture; @0x0059c821..0x0059c838
uses `GREATER_EQUAL`;
- @0x0059c7b7..0x0059c806 and the `SetDepthBufferMode` call @0x0059c866 keep
depth test and depth write enabled for this state.
The existing prepared payload already preserves everything needed without a
Content/package change: `RetailSurfaceMask` identifies the CLIP subset and
`TextureKey.PaletteId` distinguishes the 100/255 versus 200/255 reference.
`RetailAlphaMeshRouter.Route` is also decisive here: this EnvCell call site
hardcodes `multiPassAlpha:false`, so ordinary mask `0x08` is row 3, CLIP,
`OverrideClipmap=false`. The current hardcoded `true` is not an approximation.
### 14.1 F3-1 — a deferred EnvCell subset is absent from the opaque turn
Correct both pass-filter loops in `EnvCellRenderer.RenderModernMDIInternal`.
`WbRenderPass.Opaque` admits only non-additive, non-transparent batches;
`WbRenderPass.Transparent` admits the transparent/additive batches selected by
the exact route filter. Do not change `SinglePass` behavior. Consequently,
`WalkProductionLeafRenderer.DrawCellShell` may call `DrawOpaqueCellShells`
before transparent dispatch, but canonical `F4180104` / surface `08000BFF`
must produce no draw there, append one CLIP token, and draw exactly once at the
next real queue drain.
The required production pin calls the real leaf's `DrawCellShell`, not the
static dispatcher in isolation. It records the complete sequence through the
preceding opaque call, submission, and queue drain. Mutating either pass-filter
loop back to the current predicate must first fail the “no pre-drain ClipMap
draw” assertion. Keep a mixed opaque + `0x08` + `0x02` cell in the fixture so
the opaque subset still draws once at the turn while CLIP and ALPHA do not.
### 14.2 F3-2 — exact CLIP state and exact row-3 override
Add the backend blend spelling for `ONE/ONE_MINUS_SRC_ALPHA` (normally named
premultiplied/source-over) and map it explicitly in the Vulkan blend table.
Add an EnvCell CLIP pipeline using the existing `mesh_modern` shader and world
mesh layout: that blend, depth test ON, depth write ON, retail world compare,
clockwise front face with the existing per-range cull override, alpha-to-
coverage OFF. Do not add a shader.
Extend the existing shader/pass-state input just enough to express retail's
CLIP test: `GREATER_EQUAL 100/255` when `TextureKey.PaletteId != 0`, otherwise
`GREATER_EQUAL 200/255`. Preserve ordinary opaque/alpha/additive behavior.
Because one MDI run cannot carry two alpha references or two base states,
range grouping must include the CLIP-vs-ALPHA/additive state and the applicable
reference; it must not infer state from `IsTransparent` alone. Detail-active
row-1 replay uses the same CLIP base state before the already-established
detail combine and applies the same alpha reference. Do not route a pure
ClipMap subset through `_alphaPipeline` or `_transparentDetailPipeline`'s
no-depth base state.
Queue submission for the ordinary mask-`0x08` EnvCell token carries
`overrideClipmap:false`, exactly as the router decision says. Either retain the
decision/flag in the EnvCell token or make the row-3-only invariant explicit
and pinned; do not hardcode `true`, and do not add a generic flag that replay
ignores.
Production RHI pins inspect actual `GpuRecordedPipelineBind` calls and the
created pipeline descriptions, not only MDI counts. They prove:
- CLIP drain: the CLIP pipeline immediately precedes its draw; blend factors
are ONE/ONE_MINUS_SRC_ALPHA; depth test/write are true; A2C is false; the
paletted and DDS thresholds are 100/255 and 200/255;
- ALPHA drain remains StraightAlpha with depth write off; mixed CLIP/ALPHA
replay does not cross-feed subsets;
- detail ON draws at the leaf turn, queues nothing, and a pure ClipMap base
draw still uses CLIP/depth-writing state before its detail contribution;
- the recorded queue entry for canonical row 3 has `OverrideClipmap=false`.
Mutations selecting `_alphaPipeline` for CLIP, disabling CLIP depth write,
swapping either alpha reference, using strict `GREATER` instead of
`GREATER_EQUAL`, or restoring `overrideClipmap:true` must each fail a named
assertion. Recompile the checked-in SPIR-V and run the shader contract/manifest
lane if shader source changes.
### 14.3 F3-3 — rejected payload cannot grow source scratch
Keep the queue rule from §12.4: a rejected first-use source is registered and
reset exactly once, and overflow drops rather than drawing immediately. Change
the EnvCell reservation API so a failed `TryAppend` rolls back the just-
reserved cell id (or equivalently never retains it after rejection). Accepted
token indices remain stable. Pending, prepared, and draw scratch may retain
warmed capacity, but each is bounded by the 3,000-entry list limit; repeated
rejection cannot grow count or retained capacity beyond the one bounded
geometric allocation needed for that limit.
Pins use two sources and more than one full list's worth of rejected attempts.
They assert the rejected source participates in flush/end/abort cleanup, never
prepares/draws, its pending count returns to zero, and its retained capacity is
bounded after the rejection storm. Restoring reserve-without-rollback must
fail on the first bounded-count/capacity assertion.
### 14.4 F3-4 — production-path allocation evidence, not delegate exercises
Delete or relabel the two vacuous 0-B claims. The replacement EnvCell warmed
measurement must execute the real route scan, the real leaf submission, the
real list-filtered replay, and `EnvCellRenderer.SubmitRhi`. The particle
measurement must execute `DrawImmediateParticleSubmissionRhi`. Instrumentation
objects from `RecordingGpuDevice` are not production allocations; use a
preallocated/no-recording test RHI if necessary, but do not replace the
production method with a no-op delegate. After warm-up, the changed hot paths
must report 0 managed bytes per iteration. If production itself allocates,
fix the production path rather than weakening or rewording the assertion.
### 14.5 F3-5 — truth, scope, gates, and return
Correct the stale particle depth-bracket summary and the nonexistent
`Assert.Empty(log)` comment. Rewrite AP-238, AP-240, and the §12 result so they
state only what the repaired production path and tests prove. Any remaining
modern translation (including an alpha-reference or detail-combine mismatch)
gets a divergence-register row in the same implementation commit; do not bury
it in prose. No new row is needed for an exact port.
**Allowed files:** `src/AcDream.App/**`, `tests/AcDream.App.Tests/**`,
`docs/architecture/retail-divergence-register.md`, and this packet. The
prepared Content model/serializer and recipe-8 package format do not change;
the existing `TextureKey.PaletteId` is the required source fact. One lead-made
implementation commit on top of `c4cbc1d0d`; the implementer returns a clean
diff and evidence but does not commit.
**Must not:** reintroduce viewer distance or another flush site; change the
two-list FIFO/strict-less valve; change ordinary Wb ClipMap A2C (AP-240); add
particle A2C; add a shader; recover overflow with an immediate draw; run a
graphical client from the implementation or review worktree; touch S4-c1's
punch/seal behavior.
**Automated return matrix:** Release solution build with zero warnings/errors;
hermetic lane wholly green; InstalledDat exactly the expected known-failure
set from §12 unless a named, evidenced count changes; shader classes and SPIR-V
manifest green; focused queue/router/walk/particle/Wb/EnvCell/PView suite; the
new whole-leaf, pipeline-state, rejection-storm, and real production allocation
pins; register identity/count checks; `git diff --check`. The commit body names
every mutation and its actual first failing assertion.
**Sequential reviews:** first retail-faithful against
`ConstructMesh`/`DrawMesh`/`SetSurface` and the paired executable, then
production/gate-honesty against pass membership, range grouping, bounds,
allocation, mutations, scope, register, and gate output. The lead verifies
every finding. Only dual PASS authorizes landing the complete S4-c2 stack,
followed by a fresh campaign Release build and the graphical/soak gates. G3/G4
remain unpassed until those later gates; nothing merges to main before G4.
## 15. Owner-authorized repair review stop (2026-09-04)
The owner-authorized implementation is clean and unlanded at
`a094bf2b77c3061288185f069fa0b5e494ee8540` in `s4-c2-impl`, on top of
`c4cbc1d0d` / `cc8e5677a` / `048d5b12f`. The lead independently rebuilt the
Release solution with zero warnings/errors, reproduced the two real 0-B
allocation pins, recompiled 24/24 shader pairs, checked the three final SPIR-V
hashes, inspected the 14-assembly 16,735/16,735 hermetic evidence, and
re-verified `SetSurface` against both the named pseudo-C and paired executable.
No graphical client was launched.
The first sequential retail lens found the repaired behavior retail-faithful:
whole-leaf opaque exclusion, `0x08`/`0x09` CLIP grouping, row-3
`overrideClipmap=false`, blend ids 2/6 (`ONE/INVSRCALPHA`), references
100/200, inclusive `GREATER_EQUAL`, depth write, exact shader-reference versus
building-sentinel separation, detail CLIP state, ALPHA isolation,
rollback/reset, particle row 5, AP-238, and AP-240 all passed. Its focused
checks passed 60/60 and 88/88. It nevertheless returned **FAIL** because §14
requires every evidence/prose claim to be exact and permits no repair after a
review finding:
1. **MAJOR — commit evidence:** `a094bf2b7` summarizes “14 negative mutations
restored” instead of naming every mutation and actual first failing
assertion as §14.5 requires. The packet has the list, but the binding
requirement says the commit body must carry it.
2. **MAJOR — mutation truth:** §12 mutation 5 says the `<``<=` mutation
first fails `Assert.DoesNotContain`; the test actually executes and fails
the preceding `Assert.Contains("if (color.a < alphaCutoff) discard;")`
first.
3. **MINOR — address prose:** §12 attributes function 7 / `GREATER_EQUAL` to
`0x0059c821`; that call is `SetAlphaTestEnable`. The function call is at
`0x0059c838`, as the implementation commit body correctly records.
4. **MINOR — stale source prose:** `mesh_detail.vert` still says EnvCell always
sets and ignores `uParamB=0`; CLIP now passes and consumes 100/255 or
200/255. `EnvCellRenderer.Rhi.cs` still says the filtered opaque detail
replay includes ClipMap, which the repaired pass filter excludes.
`ParticleRenderer.Rhi.cs` still says five pipelines although it creates six.
5. **MINOR — stale test prose:** `RetailAlphaMeshRouterTests.cs` says ClipMap
never reaches `MaskFromTranslucencyKind` from production; mesh particles do
at `ParticleRenderer.cs`, consistent with AP-240.
Because the retail lens failed, the production/gate lens was not started.
Correcting the commit metadata, packet, source comments, and test comment
would be a post-review fourth repair. §14 explicitly grants no implicit fourth
round, so the implementation remains unlanded, the campaign renderer remains
S4-c1 `766f9e749`, and G3/G4 remain unpassed. Resume only by another explicit
owner process decision.
## 16. Owner-authorized evidence/prose repair budget (2026-09-04)
The owner replied: “Ok but fix those and finish these. You can try 5 more
times.” This explicitly authorizes up to five additional bounded
implementation-plus-sequential-review attempts for S4-c2. It does not weaken
the retail/binary verification, dual-review, landing, graphical-gate, ledger,
or no-merge-before-G4 rules. Attempt 1/5 starts from clean unlanded
`a094bf2b77c3061288185f069fa0b5e494ee8540`; the campaign branch remains on
S4-c1 until a complete attempt receives retail PASS followed by production/
gate PASS.
### 16.1 Attempt 1/5 contract — evidence and comment truth only
Make exactly the retail lens's §15 corrections; no renderer behavior, shader
instruction, SPIR-V, test assertion, register row, package model, or gate is
weakened or changed:
1. In the §12 result, attribute `SetAlphaTestEnable` to `0x0059c821` and
`SetAlphaTestFunction(GREATER_EQUAL)` to `0x0059c838`.
2. Correct mutation 5's actual first failure: after `<` becomes `<=`, the
preceding `Assert.Contains("if (color.a < alphaCutoff) discard;")` fails
because the required source spelling is absent. Do not claim the later
`Assert.DoesNotContain` is first.
3. Update `mesh_detail.vert`: exact `uParamB=1` selects the building/object
category; EnvCell zero and CLIP 100/255 or 200/255 select the bound
environment category, and the fragment shader consumes the CLIP values.
4. Update `EnvCellRenderer.Rhi.cs`: the opaque environment-detail replay is
for the already-filtered opaque shell commands; transparent ClipMap is
excluded and takes its immediate/delayed CLIP path with its own detail
contribution.
5. Update `ParticleRenderer.Rhi.cs` from “five pipelines” to the exact six.
6. Update `RetailAlphaMeshRouterTests.cs`: ordinary Wb reconstruction still
owns AP-239, while mesh particles genuinely call
`MaskFromTranslucencyKind`; do not claim production never calls it.
7. The lead amends the unlanded implementation commit so its body names all
fourteen owner-repair mutations and each actual first failing assertion,
using the corrected mutation-5 ordering above. The implementation agent
returns an uncommitted diff and does not amend or commit.
Allowed implementation files are only the four source/test files named above
plus this packet (five files total). `git diff --check`, Release build, the
exact focused source/comment
and behavioral tests, shader/manifest tests, and the two real 0-B allocation
pins must remain green. The unchanged 16,735/16,735 hermetic and 255/10/1
InstalledDat evidence may be reused only if the diff remains prose-only;
otherwise rerun the affected complete lane. No graphical client runs from an
implementation/review worktree.
Review attempt 1 sequentially: retail/evidence lens first, then production/
gate lens only on PASS. A finding consumes attempt 1 and returns to the lead,
who writes the next bounded contract against that exact finding; up to four
further attempts remain. Dual PASS authorizes landing the full S4-c2 stack,
fresh campaign Release/hermetic/InstalledDat gates, then the graphical and
stationary-soak gates. G3/G4 remain unpassed until those steps finish.
### 16.2 Attempt 1/5 result; attempt 2/5 contract
Attempt 1's five-file prose diff is correct and remains uncommitted on clean
base `a094bf2b7`. Release passed with zero warnings/errors, the affected tests
passed 37/37, both real allocation pins passed 2/2, and `git diff --check`
passed. The shader lane correctly failed 31/32:
`CommittedSpirvIsNotStaleAgainstItsGlslSource` reports that
`mesh_detail.vert`'s source hash changed after its required comment correction.
The attempt-1 contract allowed the GLSL comment but forbade the mechanically
required manifest/SPIR-V refresh. The implementer stopped without weakening
the freshness test or touching either artifact. This contract conflict
consumes attempt 1; four attempts remain.
Attempt 2/5 retains the exact five-file diff from §16.1 and permits only the
mechanical shader-artifact closure:
1. Run `tools/compile-shaders.ps1` once from `s4-c2-impl`.
2. Permit changes only to
`src/AcDream.App/Rendering/Shaders/spv/shaders.manifest.json` and, if the
deterministic compiler rewrites it, the paired
`mesh_detail.vert.spv`. No other shader source or SPIR-V may change.
3. Verify the vertex shader's executable instructions are unchanged: if the
SPIR-V bytes change, compare/disassemble the before/after modules and prove
the difference is non-semantic metadata only; otherwise stop instead of
accepting a shader behavior change.
4. Run Release build, the complete shader/manifest lane (32/32), the 37
affected tests, both real allocation pins, `git diff --check`, and a strict
changed-file audit. The unchanged hermetic/InstalledDat evidence remains
reusable because the diff is prose plus deterministic shader metadata.
The implementation agent still returns an uncommitted diff. The lead verifies
the artifact closure and amends the unlanded implementation commit with all
fourteen mutation names/first failures. Then review attempt 2 sequentially,
retail/evidence first and production/gate second. A finding consumes attempt 2;
three further attempts remain.
## 17. Attempt 2 landing result (2026-09-04)
Attempt 2 retained the five evidence/comment fixes from §16.1 and refreshed
only `src/AcDream.App/Rendering/Shaders/spv/shaders.manifest.json` after the
one permitted compiler run. `mesh_detail.vert.spv` remained byte-identical:
SHA-256 `5346247ab7d606046943e19b28888c814e08dc6cb27cd9750096ac055457eb57`,
Git blob `5ae4f2379167f739f297d2f00189901e29d5e412`. The lead amended the
implementation evidence to name all fourteen mutations and their actual first
assertions; final reviewed implementation commit: `daaeab0ba`.
The sequential retail/evidence lens PASSED first. It independently confirmed
the paired PDB/executable GUID `{9e847e2f-777c-4bd9-886c-22256bb87f32}` age 1
and the required bytes: blend IDs 2/6 at `0x0059C72A`/`0x0059C72F`, alpha-test
enable at `0x0059C821`, `GREATER_EQUAL` at `0x0059C838`, and depth-write at
`0x0059C866`. Whole-leaf exclusion, `0x08`/`0x09` CLIP, row-3 override false,
references/sentinel/detail/ALPHA/rollback/particles/register all passed. The
production/gate-honesty lens then PASSED: real production reachability,
pipeline state, bounded 3000/4096 scratch, production 0-B pins, register truth,
and all fourteen mutations passed. Its fresh gates were Release 0W/0E,
shader 32/32, affected 37/37, allocation 2/2, production focused 239/239.
The stack landed on the campaign branch in order:
`89f1e2676` (`048d5b12f`), `9ccb61a8e` (`cc8e5677a`), `0aa166aa0`
(`c4cbc1d0d`), and `252886e84` (`daaeab0ba`). All 43 non-packet files at the
campaign tip are byte-identical to the reviewed implementation stack.
Fresh campaign gates:
- Release build: PASS, zero warnings/errors.
- Shader/manifest 32/32; attempt-2 affected 37/37; real allocation 2/2 at
0 B; focused production 239/239.
- Official no-retry 14-assembly hermetic artifact
`%TEMP%\acdream-s4-c2-campaign-252886e84`: 16,734 pass / 1 fail / 0 skip.
The sole failure is the unchanged global `Console.Out` capture race in
`WalkFrameDriverTests.Collect_WeatherTurnFiredMatchesThePrintedOcLineExactly`;
the exact theory passed 2/2 isolated. The isolated pass does not rewrite the
original lane verdict.
- InstalledDat with explicit installed DAT path: 255 pass / 10 documented
fail / 1 skip. The failures are TowerAscent, two #383 sweeps, #458, and six
`AlphaFlushCounts_*`; every `AlphaFlushSites_*` row passed.
Validation-on graphical evidence is provisional until owner review:
- `logs/selfgate-20260904-115818-s4c2-landed-route`: `00-login.png`,
`01-cathedral-stairs.png`, `02-facility-stairs.png`,
`03-holtburg-house.png`, `04-holtburg-terrain-leak.png` — PASS. Facility's
left-panel crop matches the S4-c1 control; only ordinary time/weather drift.
- `logs/selfgate-20260904-120025-s4c2-landed-g3b`:
`13-capture-terrace-edge.png`, `14-capture-cathedral-arrival.png`,
`15-capture-holtburg-doorway.png`, `16-capture-foundry-deep.png` — PASS.
Pose 15 is the intentional #468 outdoor replacement and the camera is close
to the translucent self body; owner review remains explicit.
The first tip soak
`logs/connected-r6-soak-20260904-120607.report.json` failed before its first
sample on known #462 and then reproduced known #461 during abort/logout; keep
it as a failed attempt. The retry
`logs/connected-r6-soak-20260904-120756.report.json` passed 9/9 with exit 0
and graceful teardown. The clean S4-c1 control at `9ba8f013e`, report
`s4-c1-impl/logs/connected-r6-soak-20260904-121735.report.json`, also passed
9/9 in the same session. Stationary tip-minus-control averages: CPU p95
0.01 ms, GPU p95 +0.16 ms, allocation p50 +1.2 KiB, update p95 0.12 ms,
working set 24.6 MiB, private memory 17.7 MiB. Per-destination CPU p95 was
within ±0.1 ms, GPU p95 within 0.0+0.4 ms; only Caul/Sawato allocation p50
rose materially (+5.6/+5.8 KiB).
S4-c2 is LANDED and closed. Attempt 1/5 was consumed by the contract conflict;
attempt 2/5 passed; three attempts remain unused. G3/G4 remain unpassed. A
fresh landed-source audit then found that the upstream per-cell CYpt order was
still missing. Execute §18's bounded S4-c3a repair first; only afterward write
S4-c3b's deletion contract from the repaired code and re-check the
AP-238/AP-240/`AlphaFlushCounts_*` residuals there.
## 18. S4-c3a contract — restore retail's per-cell pre-append order (lead, 2026-09-04)
### 18.1 Why §4's deletion guess is not dispatchable yet
The landed S4-c2 queue is an exact two-list FIFO, but the fresh source audit
found that its upstream production order is not yet retail's. This is a missed
item from the plan's S4 chunk-2 phrase “alpha list records + insertion sort,”
not new scope:
- named retail `RenderDeviceD3D::DrawObjCellForDummies` @`0x005A0760` calls
`UpdateObjCell`, then calls `CShadowPart::insertion_sort` at `0x005A0786`
when `num_shadow_parts > 1`, before `DrawObjCell`;
- outdoor `RenderDeviceD3D::DrawBlock` @`0x005A17C0` performs the same update
and sort at `0x005A18A3` for every visible land cell before its draw loop;
- `UpdateObjCell` @`0x005A0690` updates each owning physics object's viewer
distance, and `CShadowPart::insertion_sort` @`0x006B5130` is stable and
orders descending `CPhysicsPart::CYpt` (far to near); equal CYpt retains the
pre-sort shadow-list order;
- `CPhysicsObj::add_particle_shadow_to_cell` @`0x00514A70` calls
`CPartArray::AddPartsShadow`, so scene-particle parts participate in that
same per-cell list rather than forming an unconditional cell-tail block.
Production currently reads `ShadowObjectRegistry.GetRetailPartEntriesInCell`
in registration order, collapses adjacent part rows to one entity record in
`WalkProductionWorldData.ResolveCellView`, classifies static then dynamic
records separately, and emits `CellParticles` after both. The only surviving
sort in `DeferTransparentGroups` is by retained submission ordinal; it
reconstructs an already-grouped append sequence and never computes CYpt.
Therefore §4 must not delete `LocalSortCenter` yet. First make the upstream
cell order real; c3b then deletes facts and parameters proven dead.
### 18.2 C1 — one bounded per-cell stable CYpt order
For each normal-world outdoor or interior/look-in object-list cell turn:
1. compute every eligible object part's CYpt key from its authored GfxObj
`SortCenter` transformed by that part's current model and the current world
viewpoint; squared distance is acceptable as the nonnegative monotonic key,
but do not replace the authored sort center with the entity origin;
2. stable-sort far to near within that one cell turn only; all surface subsets
of one part retain authored subset order;
3. combine static and dynamic object records before the sort—the retail cell
has one `shadow_part_list`, not two independently sorted lists;
4. never sort across cells, building turns, landscape/clear/seal barriers, or
frame scopes. The two `RetailAlphaQueue` lists remain unsorted FIFO lists.
Opaque/cutout commands may still be emitted through the ordered draw stream
and delayed subsets through the alpha submission list, but both projections
must derive from the same stable part order. A cell with no particles still
submits its sorted object alpha range. A building shell is not an ordinary
cell shadow part and keeps its existing `DrawBuilding` alpha mark/detail rule.
### 18.3 C2 — merge scene-particle alpha at the same cell turn
The existing cell particle event must prepare (not prematurely submit) that
cell's scene-particle draw records, expose each record's already-computed
authored CYpt/distance key, and merge them far-to-near with the cell's delayed
object subset records before either source appends to `RetailAlphaQueue`.
Particle subsets sharing one part key remain in authored/submission order.
The merge is stable and allocation-free after warm-up. It must preserve every
S4-c2 router outcome: billboard ALPHA, mesh CLIP/ALPHA, row-5 immediate opaque
state, capacity drop, and source-owned rollback. An earlier building barrier
must still be unable to see a later cell's staged content.
Retail's one physical shadow-list insertion ordinal across object and particle
registrations is not represented by one acdream owner today. For an exactly
equal cross-source CYpt tie, keep a deterministic stable source order and file
**AP-242** in this implementation commit; do not invent an “exact” common
ordinal. Also file **AP-241** for the narrower remaining full-list difference:
row-5 immediate particle draws and ordinary opaque/cutout object draws still
use separate Vulkan submission channels, so their relative coplanar depth
winner is not a literal replay of one interleaved `CShadowPart::draw` loop.
These rows disclose only the unported consequence after C1/C2, not the delayed
alpha order C1/C2 now make exact. Increment the register header count and add
identity/count pins in the same commit.
### 18.4 C3 — deletion/truth boundary for c3a
- Keep `ObjectRenderData.SortCenter`, `CachedBatch.LocalSortCenter`,
`WalkClassifiedBatch.LocalSortCenter`, and the per-instance/part equivalent
needed by C1. c3b deletes only fields proven unused after the exact order is
live; do not erase the evidence first.
- Remove a `cameraWorldPosition` parameter only when the replacement CYpt key
is retained on the staged record and a signature/source pin proves no alpha
submit API silently recomputes or ignores it. No `_ = cameraWorldPosition`
tombstone survives c3b.
- Keep `AlphaSubmissionOrderComparer`: it restores original submission
ordinal after material grouping and is not the deleted scope-global distance
sort. Keep building-degrade `ViewerDistanceTo`, portal-BFS distance,
opaque/private-viewport `SortDistance`, and the immediate off-screen
`ParticleSubmissionOrdering.Sort`; none is the world-alpha queue sort.
- Update `WbDrawDispatcher`, `ParticleRenderer`, `GroupKey`,
`WalkStaticStreamPopulator`, `worldbuilder-inventory.md`, and
`acdream-architecture.md` prose so world alpha is described as per-cell
CYpt order feeding two FIFO lists—not one scope-global far-to-near queue.
`AlphaFlushCounts_*` remains KnownFailure in c3a. Correct its three reasons:
(1) the replay world is content-empty; (2) AP-238 coalesces EnvCell to one
`(cell,list)` token; (3) AP-239/AP-240 alter CLIP-vs-ALPHA membership for the
named ordinary-Wb cases. Delete the six rows only in a later chunk that first
supplies a real content-bearing production replay. The broad stale claim
“acdream appends one entry per instance while retail appends per subset” is
false for the ordinary Wb/particle paths and must be removed. AP-238/AP-239/
AP-240 stay active and truthful.
### 18.5 Required tests and mutations
Use real production seams, not a surrogate list sorter:
1. one cell with near-first/far-second translucent object parts appends far
first; reversing the comparator fails;
2. equal-CYpt object parts retain input order; replacing the stable algorithm
with an unstable/tie-reversing one fails;
3. static and dynamic parts interleave by CYpt instead of forming two blocks;
4. a multipart entity preserves per-part order and authored subset order;
5. particle/object delayed records with reversed distances merge into the
same far-to-near pre-append sequence; restoring the old particle-tail event
fails;
6. two cells whose distances disagree globally remain cell-major; a global
sort mutation fails;
7. the real queue still drains CLIP then ALPHA FIFO, and the four flush sites,
0.75 valve, building barrier, EnvCell CLIP state, row-5 immediate particle
state, rollback, and 3000-entry drop tests remain green;
8. the production combined-order hot path reports 0 managed bytes per warmed
iteration; the pin must call the actual driver/particle/dispatcher merge;
9. source/signature pins distinguish retained CYpt data from deleted dead
camera threading, and documentation/register pins fail on the old
“scope-global distance-sorted queue” wording or missing AP-241/AP-242.
The commit body names every mutation and its actual first failing assertion.
No new pin is accepted without its observed failing text.
### 18.6 Scope, gates, and review
**Allowed production files:** the existing normal-world alpha participants
under `src/AcDream.App/Rendering/{Walk,Wb}/`, `ParticleRenderer.cs` and
`.Rhi.cs`, plus the minimum shared alpha record needed for the merge. Tests may
change only under `tests/AcDream.App.Tests/Rendering/` and the directly
affected Core dispatcher tests. Documentation changes are limited to this
packet, `worldbuilder-inventory.md`, `acdream-architecture.md`, and
`retail-divergence-register.md`.
**Must not:** sort inside `RetailAlphaQueue`; merge CLIP and ALPHA; change any
S4-c2 mask/pipeline/reference/depth decision; move a flush/barrier; sort across
cells; change building degrade, private/sealed viewport ordering, portal BFS,
prepared package formats, render-scene ownership, or shadow/collision
registration; add allocations to the warmed path; run a graphical client from
the implementation/review worktree.
**Automated return:** `git diff --check`; Release solution build 0W/0E;
focused per-cell/order/queue/particle/Wb/walk/EnvCell/PView tests; real 0-B
pin; shader/manifest lane; full hermetic lane; InstalledDat with exactly the
documented global set plus the six retained `AlphaFlushCounts_*`, no new
failure. The implementer commits one bounded change on a clean scratch
worktree and returns the commit, changed-file list, gate output, and mutation
ledger.
**Sequential reviews:** (1) retail lens checks the named decomp and paired
binary at `0x005A0690`, `0x005A0786`, `0x005A18A3`, and
`0x006B5130..0x006B519D`, per-cell scope, comparator direction/stability,
part/subset grouping, particle reachability, and every retained deviation;
(2) production/gate-honesty lens checks real event reachability, barrier
isolation, two-list FIFO preservation, allocation/bounds/rollback, mutations,
register count, allowed files, and every gate claim. A failed lens gets one
bounded fix contract; a third fix round stops and is written up. Dual PASS
lands c3a; the lead then writes c3b's deletion-only contract from that code.
### 18.7 Implementation result (scratch implementation, 2026-09-04)
The bounded c3a implementation now builds one combined registry view for each
ordinary outdoor/interior object-cell turn, transforms each GfxObj's authored
`SortCenter`, applies a stable descending per-cell insertion sort, and derives
both opaque/cutout commands and delayed object records from that same order.
The real particle leaf prepares but does not expose delayed records; the cell
event merges their retained keys with the object's range before either source
appends to the unchanged CLIP/ALPHA FIFO lists. Row-5 immediate routing,
capacity-drop/source rollback, barriers, and building-shell ownership are
unchanged. Exact equal object/particle keys use the disclosed object-first tie.
The paired-binary correction is recorded as AP-243: retail's
`UpdateObjCell` greater-than-50 m branch at `0x005A06B7..0x005A0720` gives
all parts one cell CYpt/heading through `CPhysicsObj::UpdateViewerDistance`
and therefore retains insertion order, while this bounded port always uses
the more exact per-part authored center. AP-241/AP-242 record the two other
narrow residuals. The active AP header and physical table are pinned at 162
rows, with exactly one row for each AP-241/AP-242/AP-243.
Lead-approved process-scope clarification: the real-path integration also
changes exactly `src/AcDream.App/Rendering/RetailPViewPassExecutor.WalkLeaf.cs`
and `src/AcDream.App/Rendering/RetailPViewPassExecutor.cs`. The literal
`{Walk,Wb}/` production-file list omitted the existing leaf adapter and the
thin `PrepareCellParticleAlpha` forwarder required to reach the expressly
allowed `ParticleRenderer`; relocating that bridge would create an artificial
seam. These two files contain only the interface adaptation and forwarder—no
router, queue, mask, pipeline, reference, depth, or flush behavior. The lead
approved this minimum real-path reachability delta before commit. It is a
contract-scope clarification, not a retail behavior deviation, so it files no
additional AP row.
Clean-state return matrix (no graphical client was launched):
- Initial solution `--no-restore` build was invalid setup evidence: 42
projects lacked `project.assets.json` (`NETSDK1004`). One solution restore
followed; the official Release solution build then succeeded with 0
warnings and 0 errors.
- Shader descriptor/manifest/SPIR-V validation: 32/32. No shader source,
checked-in SPIR-V, manifest, Content model, serializer, or package format
changed.
- Focused queue/router/particle/Wb/walk/EnvCell/PView production filter:
210/210. The three real warmed routes (EnvCell whole-leaf SubmitRhi,
immediate mesh particle RHI, and the new driver/particle/dispatcher merge)
pass 3/3 and each asserts 0 managed bytes.
- Official one-shot 14-assembly hermetic gate: 16,743 passed / 0 skipped /
0 failed. Evidence:
`%TEMP%/acdream-s4-c3a-impl-20260904-2015`.
- InstalledDat with explicit `C:\Turbine\Asheron's Call`: 385 passed / 10
failed / 1 skipped. The failures are exactly TowerAscent, the two #383
layout sweeps, #458 `Oh_doorway_still_first_frame_diff`, and the six
retained `AlphaFlushCounts_*`; all six paired `AlphaFlushSites_*` pass.
Evidence: `%TEMP%/acdream-s4-c3a-installed-20260904-2017`.
- `git diff --check`: pass.
Observed mutation first failures (every mutation was reversed with a patch
before the return matrix):
1. Reverse the CYpt comparator: `PopulateCellObjects_UsesAuthoredSortCenterAndStableFarToNearOrder`
expected `[202,101]`, actual `[101,202]` at the first sequence assertion.
2. Move equal keys left (`>=`): `PopulateCellObjects_EqualCyptRetainsEntityPartAndSubsetOrder`
expected `[11,12,21,22]`, actual `[22,21,12,11]`.
3. Restore static/dynamic blocks: `PopulateCellObjects_InterleavesStaticAndDynamicOpaquePartsByCypt`
expected `[2,3,1]`, actual `[3,1,2]`.
4. Replace authored SortCenter with entity translation: the authored-center
test first expected `[202,101]`, actual `[101,202]`.
5. Restore the old object-then-particle tail: `CellTurn_RealParticlePreparationMergesWithObjectAlphaByCypt`
expected source order `[Wb,Particle,Wb,Particle]`, actual
`[Wb,Wb,Particle,Particle]` at position 1.
6. Sort all staged objects scope-globally before replay:
`SeparateCellTurnsRemainCellMajorWhenLaterCellIsFarther` first expected
model X `5`, actual `50`.
7. Restore a dead optional camera parameter on `SubmitWalkAlphaInstance`:
`WorldAlphaCyptContract_RetainsTheKeyWithoutDeadCameraSubmitThreading`
first expected parameter count 2, actual 3.
8. Reinsert “distance-sorts one shared queue” into the inventory:
`WorldAlphaCyptDocumentationAndRegister_PinPerCellTruthAndThreeResiduals`
first failed `Assert.DoesNotContain` on that exact phrase.
9. Rename AP-241 away: that same truth pin first failed `Assert.Single`
because no AP-241 row matched.
10. Allocate one object in the real cell-merge path:
`ProductionCellObjectParticleMerge_WarmedPathAllocatesZeroBytes` first
expected 0, actual 3,072 managed bytes.
## 19. S4-c3a fix round 1 — authored particle CYpt and bounded preparation (lead, 2026-09-04)
The first retail-faithfulness lens returned **FAIL** on candidate
`44e2bc227ba74d10074cfe985eee094e10b7f438`. Ordinary object parts are
correct, but the billboard/mode-25 particle path orders by its rendered quad
center. That center is the vertex-AABB-derived `ParticleGfxInfo.CenterOffset`
after viewer-facing adjustment, not the GfxObj's authored `sort_center`.
Retail does not use the rendered center for CYpt:
`CPhysicsPart::UpdateViewerDistance @ 0x0050E030` reads the current GfxObj's
`sort_center` at offsets `+0x7C/+0x80/+0x84`, scales it, passes it with the
part `Position` to `Position::get_offset`, computes the length, and writes
CYpt. The paired bytes at `0x0050E036..0x0050E0AE` confirm that named-decomp
sequence. The same lens also found the stale statement that static records
are never re-sorted downstream.
The lead's pre-production audit found two coupled safety holes in the new
prepared-particle seam. `_preparedCellAlphaScratch` is a new retained list but
is absent from the alpha scratch accounting/retention bound. More importantly,
`PrepareForCellAlpha` reserves `_deferredAlpha` payload tokens before the
driver reaches the queue visibility edge. An exception from a later immediate
duplicate can strand unregistered source payload; ordinary capacity rejection
retains payload for entries that can never draw. These contradict §18.3's
source-owned rollback requirement. They are included in this same bounded
round rather than deferred to a predictable production-lens failure.
### 19.1 F1 — one authored particle sort point, separate from its draw center
Retain `GfxObj.SortCenter` explicitly in `ParticleGfxInfo`. Populate it
directly in `AuthoredParticleGfxInfo`; synthetic texture-only/default
billboards, which have no GfxObj, use zero. Do not derive it from vertex bounds
and do not replace or alter the visual `CenterOffset`.
For every billboard/mode-25 particle, compute the CYpt point before any
viewer-facing draw-frame adjustment:
`particle.Position + Transform(authoredSortCenter * particle.Size, ParticleOrientation)`.
Use the squared viewer distance from that point for `ParticleInstance` and
`ParticleSubmission`. The rendered quad position/axes remain byte-for-byte
the existing `CenterOffset`/facing result. The full-mesh path already applies
the equivalent `ObjectRenderData.SortCenter` through its model; keep the two
paths semantically identical and do not add a second sort.
Add a real GfxObj-backed billboard test whose authored SortCenter is distinct
from both its particle origin and vertex AABB center, with non-unit size and a
non-identity orientation. Drive the actual cell particle preparation and
driver merge against ordinary object alpha. It must prove that the authored
point determines the cross-source order while the captured billboard draw
center remains the existing visual center. Required mutations: using rendered
`pos`, using the AABB center, dropping size, or dropping orientation must each
fail a named first assertion.
### 19.2 F2 — reserve only at the queue visibility edge and roll back rejection
A prepared particle record retains only the information needed to reconstruct
its source payload when `Append` is called; preparation must not mutate
`_deferredAlpha`. At `Append`, reserve one tail token, call
`RetailAlphaQueue.TryAppend`, and roll back that exact tail token when the
append returns false or throws. A rejected first-use source must remain
registered by the queue and receive one flush/end/abort reset, but it owns no
rejected payload and never prepares or draws it. Accepted token indices remain
stable. An exception from a later row-5 or clip-immediate draw during
preparation must leave the source payload count unchanged.
Bound `_preparedCellAlphaScratch` by the physical two-list limit: retain no
more than the first 3,000 delayed candidates for CLIP and the first 3,000 for
ALPHA. Later candidates for a list cannot be admitted even in an otherwise
empty frame, so dropping only their prepared records is behavior-equivalent;
their immediate duplicate, if any, still executes. Include this list in an
explicit retained-byte/capacity diagnostic or an equivalent dedicated bound.
After a rejection storm its count is zero at reset/next preparation and its
retained capacity is no greater than the bounded geometric backing required
for 6,000 records (8,192 with `List<T>` growth). Do not change either queue's
3,000-entry capacity, FIFO append order, source registration, CLIP-then-ALPHA
drain, router decisions, or row-5 immediate state.
Pins cover: more than 3,000 candidates for each list; accepted/rejected token
counts; no rejected prepare/draw; flush, end, and abort cleanup; an injected
immediate-draw exception after at least one delayed candidate; bounded count,
capacity, and retained bytes; and the real warmed driver/particle/dispatcher
merge at 0 managed bytes. Mutations restoring eager reservation, omitting
false/exception rollback, or removing the per-list preparation cap must fail
at their first named count/capacity assertion.
### 19.3 F3 — prose and register truth
Correct `WalkFrameDriver.cs`'s `WalkFrameStaticRecords` comment: records retain
authored traversal/registration order until the ordinary-cell populator builds
the combined static/dynamic part list and performs the retail per-cell CYpt
sort. They are not final stream order. After F1, AP-241's positive authored-
part-order claim becomes true; AP-241/AP-242/AP-243 otherwise remain unchanged,
unique, inside the AP table, and the active physical count remains 162. No new
deviation is introduced by this exact repair.
### 19.4 Scope, return, and review order
Production edits are limited to `ParticleRenderer.cs`,
`ParticleSubmissionOrdering.cs`, and the comment-only correction in
`Walk/WalkFrameDriver.cs`. Tests may change only
`ParticleRendererRouteTests.cs` and `Walk/WalkFrameDriverTests.cs`; this packet
records the result. If the implementer proves one additional existing test
helper is the minimum real-path fixture seam, the lead must approve and record
it before commit. No router, queue, pipeline, shader, manifest, Content model,
serializer, package format, building-shell, EnvCell, or PView behavior changes.
Return one fix commit on top of `44e2bc227b`: `git diff --check`; Release 0W/0E;
the focused particle/driver/queue/order/bounds suite; the real 0-B pin; shader
contract lane; and a mutation ledger naming each actual first failure. No
graphical launch. The retail lens re-reviews F1, the paired bytes, F3, and the
unchanged S4-c2 behavior first. Only a retail PASS unlocks the sequential
production/gate-honesty lens over the entire c3a stack, with special attention
to F2. A failed re-review gets the one remaining bounded fix round; a further
finding after that would require a third round and stops the chunk.
### 19.5 Fix-round-1 implementation result (Sonnet, 2026-09-04)
**IMPLEMENTED — RETAIL RE-REVIEW REQUIRED.** This fix commit retains the
GfxObj-authored `SortCenter` separately from the rendered AABB-derived
`CenterOffset`. Billboard/mode-25 CYpt now uses the authored point after the
particle's scale and orientation, while the captured draw center remains the
existing visual center. The prepared-cell seam now reserves source payload
only from `Append`, rolls back the exact tail once on either a false return or
an exception, retains at most the first 3,000 CLIP and 3,000 ALPHA candidates,
and accounts for the bounded scratch list in its retained-byte diagnostic.
The row-2 immediate duplicate remains independent of delayed retention. The
static-record comment now describes the later ordinary-cell combined CYpt
sort.
The lead separately authorized one plan-ledger edit so the S4-c3a row records
candidate `44e2bc227b`'s retail FAIL and this §19 fix result. That file is
ledger currency only; it does not widen production scope. AP-241/AP-242/AP-243,
the active physical count of 162, both retail queues and their capacities,
router decisions, FIFO order, CLIP-then-ALPHA drain, row-5 immediate state,
pipelines, shaders, manifest, Content model, serializer, package format,
building shells, EnvCells, and PView behavior remain unchanged.
Automated return:
- `dotnet build AcDream.slnx -c Release --no-restore --nologo` — PASS, 0
warnings / 0 errors.
- focused `ParticleRendererRouteTests|WalkFrameDriverTests|RetailAlphaQueueTests|RetailParticleGeometryClassifierTests`
lane — PASS, 95/95, 0 skipped.
- `WorldAlphaCyptDocumentationAndRegister_PinPerCellTruthAndThreeResiduals`
— PASS, 1/1; all three residual rows remain unique inside the AP section and
the physical count remains 162.
- real warmed `ProductionCellObjectParticleMerge_WarmedPathAllocatesZeroBytes|ProductionImmediateMesh_WarmedDrawImmediateParticleSubmissionRhiDoesNotAllocate`
lane — PASS, 2/2; both production-path pins observed 0 managed bytes.
- `VulkanShaderDescriptorContractTests|VulkanShaderManifestTests|RenderPackSpirvValidatorTests`
— PASS, 32/32, 0 skipped.
- official one-shot 14-assembly hermetic gate — PASS, 16,751 passed / 0
skipped / 0 failed. Evidence:
`%TEMP%/acdream-s4-c3a-fix1-20260904-2215`.
- InstalledDat with explicit `C:\Turbine\Asheron's Call` and the documented
ordinary set plus TowerAscent, #458 `Oh_doorway_still_first_frame_diff`, and
the six retained `AlphaFlushCounts_*` — 385 passed / 10 expected failed / 1
skipped. The failures are exactly TowerAscent, the two #383 layout sweeps,
#458, and the six count rows; no new failure appeared. Evidence:
`%TEMP%/acdream-s4-c3a-fix1-installed-inclusive-20260904-2222`.
- `git diff --check` — PASS (only the repository's LF-to-CRLF working-copy
notices).
Mutation ledger (each mutation was applied alone, its named test was run once,
and the production source was restored before the next mutation):
1. Use rendered `pos` for billboard CYpt: `CellTurn_GfxObjBillboardUsesScaledOrientedAuthoredSortCenterButKeepsVisualCenter`
first failed its distance assertion, expected `40`, actual `116`.
2. Use the AABB-derived `CenterOffset`: the same test first failed its distance
assertion, expected `40`, actual `116`.
3. Drop particle size from authored SortCenter: the same test first failed its
distance assertion, expected `40`, actual `65`.
4. Drop particle orientation from authored SortCenter: the same test first
failed its distance assertion, expected `40`, actual `160`.
5. Restore eager source-payload reservation during preparation:
`PreparedCellAlpha_ImmediateExceptionAfterDelayedCandidateDoesNotReservePayload`
first failed `Assert.Empty`; the collection contained one
`DeferredParticleDraw`.
6. Omit rollback after `TryAppend` returns false:
`PreparedCellAlpha_AcceptsStableTokenAndRollsBackRejectedTailOnAbort` first
failed `Assert.Single`; the collection contained two payloads.
7. Omit rollback after `TryAppend` throws:
`PreparedCellAlpha_AppendExceptionRollsBackExactTailToken` first failed
`Assert.Empty`; the collection contained one payload.
8. Remove the two per-list preparation caps:
`PreparedCellAlpha_CapsBothListsAndRejectStormRetainsNoPayloadOrUnboundedScratch`
first failed the prepared-count assertion, expected `6000`, actual `6002`.
Before the return matrix, fixture construction exposed and corrected two
test-only setup issues: missing `DatReaderWriter.Lib`/`Types` imports plus a
nonexistent vector comparer caused the first compile attempt to fail, and
xUnit2013 rejected two count-shaped assertions. Neither failure reached a
production positive assertion. The first InstalledDat invocation used the
release guide's ordinary `Status!=KnownFailure` filter and honestly returned
385 passed / 2 expected failed / 1 skipped; that was incomplete §18.6 evidence
because it excluded TowerAscent, #458, and the six count rows. It was not
retried or rewritten: the separate inclusive artifact above is the required
documented-set gate. No graphical client was launched.