docs(render): S4-c2 contract (packet §8) — two FIFO alpha lists, the DrawMesh router, four flush sites, AM/FL transcript gate

Drafted from the five alphadepth captures (10,556 AM lines, every clip=0;
FL by return address 13,705 / 309 / 18 / 23) and the OH1 alpha-list
contract. Dispatch waits for S4-c1 to land.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-09-03 21:46:01 +02:00
parent 060c392ecc
commit d5fbcc6d5b
2 changed files with 112 additions and 1 deletions

View file

@ -327,3 +327,114 @@ the oracle for that pose).
`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.