Two things land together: the plan and contract draft P6 needs before it starts, and the
working notes that until now existed on exactly one machine.
1. P6's plan and contract draft (neither started; not one line of code)
docs/Disaggregated/P6-SPAWN-PLAN.md the package plan
docs/Disaggregated/P6-CONTRACT-DRAFT.md the contract draft (moves to
MG_Remote/CONTRACT-P6.md when c6 lands)
The three findings that mattered, all written into the documents:
a. P6 is SMALLER than its roadmap cell suggests. The transport primitives are already in
the tree and already tested: SocketDoorbell including hangup detection and the two
lost-wakeup fences (Doorbell.cpp:211-360, four cases in FdPassingTest), SCM_RIGHTS fd
passing, ShmSegment::Adopt, and the SurfaceOp / WindowKind schema already defined in
protocol.fbs. inproc uses a real second shm mapping rather than an alias, and
SessionRings.h:22-28 says that was done so today is not the first time it runs. P6 is
assembly plus a control plane plus a process, not "write a socket transport".
b. The evidence for "P6 is only a transport swap" has EXPIRED. It was bought by P5c, whose
audit found 59 direct memory accesses in a tree that already looked clean. P5e changed
the tree again. So the first package, a6, is a read-only audit that writes no code, and
every contract row that depends on it is marked PENDING a6 rather than decided in
advance.
c. P6 does not get a game running on the phone. ANativeWindow* is meaningless in the child,
and protocol.fbs:186-196's own comment says Android transfers the window out of band -
that band is P12. P6 lands pbuffer / surfaceless / offscreen, which is what HeadlessGL,
the split-equivalent lane and trace replay need, and which is exactly what P6's own exit
gate measures. A real window reaching the server is a named refusal. This gets its own
section because "P6 is done" would otherwise read as "the game runs now".
The contract adds rule G: a role may not name a process-local handle. It extends P5c's
rule E (a role may not name the other role's memory) from memory to identity - EGLDisplay /
EGLSurface / EGLContext / ANativeWindow* / fd number / pid / mapped address either cross as
a token this contract defines or they do not cross, and a descriptor crosses only through
SCM_RIGHTS, where the kernel renumbers it.
What P5e left for P6 gets its own section. The latch semantics are already designed
(ARCHITECTURE.md:504) and are copied unchanged. What is new is that after run-ahead the
client no longer waits while the peer process can DIE: the client holds records that were
published and will never be applied, and Android's freezer suspends the child without
closing anything. So slow and dead must be distinguishable WITHOUT a timeout threshold.
That is P6's only new design; everything else is carriage. It shares a predicate with E1's
outstanding re-specification (ID-122), and the two should be designed together.
Two gates carried over from P5e's lessons: G2 requires integration-spawn's test-name set to
match integration-split's name for name, because a differing set means the lane is quietly
running less; and the arm-proof gate requires every entry to record the child pid and
transport=spawn in its OWN private log - ID-124 established that this kind of false green
really happens, and a spawn lane that actually ran monolith would pass every other gate.
S2's shape must be settled before it is written, and the document says why: E1 is the
cautionary case.
2. The working notes move into the repository (docs/Disaggregated/notes/, 415 .md files)
ROADMAP / CURRENT_STAGE_PROGRESS / MEASUREMENTS cite ruling numbers, audit line numbers and
the provenance of gate figures from ~/w7/notes/, a directory that exists on one machine and
is under no version control. A record cited hundreds of times that lives only locally is not
a record, so it moves in whole, with its directory structure intact.
Only .md moves. Scripts, run logs, census and gate result JSON, patches, APKs and nm dumps
stay where they are: they are evidence DATA rather than documents - large, regenerated per
run, and mostly carrying absolute paths and device serials that mean something only on the
machine that produced them. The mapping and this split are written in
docs/Disaggregated/notes/README.md.
Two exceptions are recorded there too: P6's BRIEF has been promoted to a first-class
document and keeps no copy under notes/, so the two cannot drift apart; and recovered/ holds
material salvaged from failed workflow runs, which may duplicate the like-named phase
directories and is kept because a few of those were the only complete version at the time.
CI's citation lint takes docs/Disaggregated/*.md only (non-recursive), and notes/ is
deliberately outside it: those notes' file:line citations were written against their own
code heads, and resolving them against today's HEAD would fail wholesale - which is not an
error, it is their correct state as a historical record. To check one, use the revision it
declares (--rev).
3. Inconsistencies fixed in passing
- README.md's status line still said P5e was in progress while the stage table already said
closed.
- CURRENT_STAGE_PROGRESS's next-step item 1 still listed the exit gate as pending, though
(a) and (b) were run in the previous commit; rewritten to closed plus the one item that
actually remains (E1).
- ROADMAP's P6 row still had the pre-phase plan in three of its cells; it now carries the
package order, the scope boundary and the two new gates.
Checked with check_doc_citations.py: 125 citations across docs/Disaggregated/*.md, 0 problems.
13 KiB
P6 class-C dynamic census
Date: 2026-09-16. Tree: /home/swung/w7/p5-joint, p5/joint at
e61d00123c41d74f98cd06b63768be12d9b6348a. The checkout was read only.
build-split/CTestTestfile.cmake was confirmed before execution. Every process ran with
GLIBC_TUNABLES=glibc.malloc.tcache_count=0 and MOBILEGL_TRANSPORT=inproc.
Method and evidence
ctest -L integration-gpu --show-only=json-v1 supplied each entry's exact command, arguments,
environment, working directory and timeout. The wrapper then drove the 1,149 commands with eight
workers and a unique MOBILEGL_LOG_FILE_PATH. Exact per-entry status, return code, command, working
directory and first Fatal{...} text are in
/home/swung/w7/p6-census-logs/results.json; the corresponding <entry>.log and <entry>.out
files are beside it. Runner source is /home/swung/w7/notes/tools/p6_census_lane.{sh,py}.
“Distinct scenario” below means the final GTestSuite.Case pair, deliberately collapsing backend,
Split, SmallRing and feature-arm registry variants. Counts are registry entries. Thus the two
columns answer different questions.
| status | entries |
|---|---|
| passed | 426 |
| skipped | 185 |
| subprocess-aborted | 511 |
| ordinary failed | 27 |
| total | 1,149 |
This exactly reproduces P5's 426/185/511/27. All 511 aborts have a real FATAL-severity first line:
505 UnmigratedVerb, six StageSnapshotTooNarrow. None of the 27 ordinary failures has a Fatal.
First unmigrated slot by integration entry
| slot | entries | distinct scenarios | ROADMAP subsystem | approximate next first blocker if this slot is migrated |
|---|---|---|---|---|
BindImageTexture |
138 | 71 | P3b/P4b image sweep / texture binding | Usually DispatchCompute for image-store cases; otherwise a draw, then MemoryBarrier or readback. |
BeginTransformFeedback |
95 | 44 | P9 XFB reverse/scatter (with P3b/P4b client scatter debt) | Usually the draw is already class B; next class C is approximately EndTransformFeedback or an XFB query end. |
DrawElements |
56 | 27 | P8 index host mirror / draw protocol breadth | Most integration cases appear to proceed to class-B ReadPixels; Minecraft's next distinct class-C slot cannot be known until this one is live. |
DispatchCompute |
49 | 25 | P7/P8 compute command and resource state | Commonly MemoryBarrier, then readback/draw. |
PatchParameteri |
43 | 19 | P7/P8 tessellation patch state / draw breadth | Commonly BeginTransformFeedback; otherwise the patch draw itself. |
CopyImageSubData |
30 | 14 | P8 CopyImage mirror | GetTexImage/GetTextureImage, BindImageTexture, or a draw, depending on the case. |
MemoryBarrier |
18 | 9 | P7/P8 compute ordering | The GuiBatch sources next issue indexed/multi-draw work, approximately MultiDrawElementsBaseVertex or DrawElements. |
MultiDrawElementsBaseVertex |
18 | 9 | P8 draw-vbo multi-draw breadth | Usually class-B readback; no later class-C call is visible in the scenario body. |
ClearBufferiv |
12 | 6 | P3b/P4b framebuffer depth; P7 Magma twin | Usually class-B draw/readback; no later class-C call is visible. |
ClearBufferuiv |
8 | 4 | P3b/P4b framebuffer depth; P7 Magma twin | Usually class-B draw/readback; no later class-C call is visible. |
ClearBufferfv |
6 | 3 | P3b/P4b framebuffer depth; P7 Magma twin | — |
DrawArraysInstancedBaseInstance |
4 | 2 | P8 draw protocol breadth | — |
DrawElementsBaseVertex |
4 | 2 | P8 index/draw protocol breadth | — |
ShaderStorageBlockBinding |
4 | 2 | P7/P8 compute/buffer binding | — |
CopyTexImage2D |
3 | 1 | P3b/P4b texture copy; P9 readback | — |
MultiDrawArraysIndirectCount |
2 | 1 | P8 indirect-count resolution | — |
MultiDrawArrays |
2 | 1 | P8 draw protocol breadth | — |
MultiDrawElementsIndirect |
2 | 1 | P8 indirect/index mirror | — |
BindTransformFeedback |
2 | 1 | P9 XFB namespace/scatter | — |
MultiDrawElements |
2 | 1 | P8 draw protocol breadth | — |
ClearNamedFramebufferfv |
2 | 1 | P3b/P4b named FBO state; P7 twin | — |
GenerateMipmap |
2 | 2 | P8 mip plan / CPU fallback | — |
DrawArraysInstanced |
2 | 1 | P8 draw protocol breadth | — |
ClearBufferfi |
1 | 1 | P3b/P4b framebuffer depth-stencil; P7 twin | — |
DrawElementsInstancedBaseVertex |
0 | 0 | P8 index/draw protocol breadth | Trace-only first blocker: two backend runs, one fixture. |
The “next” column is intentionally an approximation from the scenario sources and the calls before the Fatal. A first-blocker run cannot observe calls after its abort, and repeated calls to the same slot are treated as solved by migrating that slot.
Fatal-family census
| first diagnostic family | entries | distinct scenarios | interpretation |
|---|---|---|---|
UnmigratedVerb |
505 | 248 | Actual FATAL-severity aborts. |
StageSnapshotTooNarrow, "respecify_whole_store" |
6 | 4 | Actual FATAL-severity aborts; P8/P11 staging/adoption debt, not class C. |
| no Fatal | 27 | 13 | Ordinary assertion failures; the P5 family attribution remains applicable. |
One passing refusal-control log contains the words Fatal{ProtocolCorruption} inside an ERROR-level
explanation of what the applier would do. It is not a Fatal event and is excluded from the abort
table; this distinction prevents a textual grep from inventing a seventh non-verb abort.
Trace fixtures under inproc
All 40 cases in trace_cases.json are hydrated locally: each required archive and primary golden
exists in the joint fixture directory and is not an LFS pointer. No fetch was performed. The
reference registry has 80 backend entries; 79 are CI-eligible because iterationrp excludes
DirectGLES. Each eligible entry was run alone with a private log by
p6_census_traces.{sh,py}. Consolidated evidence is
/home/swung/w7/retrace-out/p6-census/summary.{json,txt}; only those two retrace-output files remain.
| case(s) | backend result / first Fatal |
|---|---|
OpenRA |
DirectGLES PASS SSIM 1.0; DirectVulkan PASS SSIM 1.0; no Fatal. |
improved-transparency-minecraft-26.3 |
Both backends: Fatal{UnmigratedVerb, "DrawElementsInstancedBaseVertex"}. |
minecraft-1.21.4-fabric-iris-iterationrp-in-world |
DirectVulkan only: Fatal{UnmigratedVerb, "DrawElements"}. |
minecraft-1.21.4-startup; minecraft-1.21.4-main-menu; minecraft-1.21.11-main-menu; minecraft-1.17-main-menu-854; minecraft-1.21.4-in-world; minecraft-1.21.4-rd12-odinlite-in-world; minecraft-1.21.4-fabric-sodium-in-world; minecraft-1.21.4-fabric-common-mods-in-world; minecraft-1.21.4-fabric-common-mods-inventory; minecraft-1.21.4-fabric-rei-inventory; minecraft-1.21.4-fabric-xaero-minimap-in-world; minecraft-1.21.4-fabric-xaero-world-map-in-world; minecraft-1.21.4-fabric-journeymap-in-world; minecraft-1.21.4-fabric-modernui-inventory; minecraft-1.21.4-fabric-rei-inventory-normal-world; minecraft-1.21.4-fabric-xaero-minimap-in-world-normal-world; minecraft-1.21.4-fabric-xaero-world-map-in-world-normal-world; minecraft-1.21.4-fabric-journeymap-in-world-normal-world; minecraft-1.21.4-fabric-modernui-inventory-normal-world; minecraft-1.21.4-fabric-iris-bsl-in-world; minecraft-1.21.4-fabric-iris-makeup-ultrafast-in-world; minecraft-1.21.4-fabric-iris-super-duper-vanilla-in-world; minecraft-1.21.4-fabric-iris-sundial-lite-in-world; minecraft-1.21.4-fabric-iris-complementary-reimagined-in-world; minecraft-1.21.4-fabric-iris-complementary-unbound-in-world; minecraft-1.21.4-fabric-iris-mellow-in-world; minecraft-1.21.4-fabric-iris-nostalgia-in-world; minecraft-1.21.4-fabric-iris-bliss-in-world; minecraft-1.21.4-fabric-iris-chocapic-v6-lite-in-world; minecraft-1.21.4-fabric-iris-iterationt-in-world; minecraft-1.21.4-fabric-iris-iterationt-nodsa-in-world; minecraft-1.21.4-fabric-iris-photon-v1.1-in-world; minecraft-1.21.4-fabric-iris-photon-v1.3b-in-world; minecraft-1.21.4-fabric-iris-derivative-main-d24.4.14-in-world; minecraft-1.21.1-neoforge-create-indirect-in-world; minecraft-1.21.1-neoforge-create-instancing-in-world; minecraft-1.21.4-fabric-iris-bsl-esc-menu-854 |
Both backends: Fatal{UnmigratedVerb, "DrawElements"}. |
Trace totals: 2/79 pass; 75 backend entries (38 fixture cases) first-stop at DrawElements; two
backend entries (one case) first-stop at DrawElementsInstancedBaseVertex.
Static cross: class-C slots not hit by anything measured
These 39 of the static census's 64 class-C slots appear in neither the integration lane nor the hydrated traces and are last in the migration order:
BeginOcclusionQuery, BeginTimeElapsedQuery, BeginXfbPrimitivesQuery,
BlitNamedFramebuffer, ClearNamedFramebufferfi, ClearNamedFramebufferiv,
ClearNamedFramebufferuiv, ClientWaitSync, CopyTexSubImage2D, DeleteBackendQuery,
DeleteSync, DeleteTransformFeedback, DispatchComputeIndirect, DrawArraysIndirect,
DrawElementsIndirect, DrawElementsInstanced, DrawElementsInstancedBaseInstance,
DrawElementsInstancedBaseVertexBaseInstance, DrawRangeElements,
DrawRangeElementsBaseVertex, EndOcclusionQuery, EndTimeElapsedQuery,
EndTransformFeedback, EndXfbPrimitivesQuery, FenceSync, GetGpuTimestampNs,
GetQueryResult64, GetSyncStatus, GetTexImage, GetTextureImage,
IsQueryResultAvailable, MemoryBarrierByRegion, MultiDrawArraysIndirect,
MultiDrawElementsIndirectCount, PauseTransformFeedback, QueryCounterTimestamp,
ResumeTransformFeedback, SetSwapInterval, WaitSync.
Proposed migration order
Wave 1 — current first blockers
Wave 1 moves the top ten integration blockers plus the trace-only instanced draw blocker. It advances 544 currently aborting backend-entry executions, representing 267 distinct scenario/fixture names, to their next blocker. Counts below are current first blockers, not promises that the scenario then passes.
| package owner / slots | scenarios advanced | required applier and client-row seam |
|---|---|---|
P3b/P4b + P7/P8 image/compute: BindImageTexture, DispatchCompute, CopyImageSubData, MemoryBarrier |
119 (235 lane entries) | Reuse MGPipeApplySetShaderImages and MGPipeApplySetDispatchProgram; add/finish MGPipeApplyLaunchGrid, MGPipeApplyResourceCopyRegion, MGPipeApplyMemoryBarrier. Replace the MGR_ASSIGN_UNMIGRATED rows for table.GL.BindImageTexture, CopyImageSubData, MemoryBarrier, and the hand-written table.GL.DispatchCompute = &DispatchCompute_Unmigrated with their emit/client-resolved rows. |
P9 XFB/tess: BeginTransformFeedback, PatchParameteri |
63 (138 lane entries) | Reuse MGPipeApplySetPatchState; add/finish MGPipeApplyBeginStreamOutput. Replace the table.GL.BeginTransformFeedback and table.GL.PatchParameteri unmigrated rows. |
P8 indexed/multi-draw: DrawElements, MultiDrawElementsBaseVertex, DrawElementsInstancedBaseVertex |
75 (74 lane + 77 trace backend entries) | Reuse MGPipeApplySetVertexBuffers and MGPipeApplySetIndexBuffer; add/finish the command-side MGPipeApplyDrawVbo (today DrawVbo goes directly to IVerbSink::OnDrawVbo, and PipeApply.h has no command applier). Replace all three table.GL.* unmigrated rows with EmitDrawVbo-family rows. |
P3b/P4b + P7 framebuffer clear: ClearBufferiv, ClearBufferuiv |
10 (20 lane entries) | Add/finish MGPipeApplyClear (today Clear also goes directly to IVerbSink::OnClear). Replace the two table.GL.ClearBuffer* unmigrated rows with EmitClear adapters. |
The missing command applier names above are deliberately called out: PipeWireCodec.cpp says the
five P5 class-B verbs have no MGPipeApply*, and its off-reduced-path command cases return false.
A package must either add these named PipeApply seams or explicitly extend the IVerbSink pattern;
claiming an existing PipeApply.h entry point would be false.
Wave 2 — measured tail
Migrate the remaining measured slots, still packaged by subsystem: P8 draw variants
(DrawArraysInstancedBaseInstance, DrawElementsBaseVertex, MultiDrawArraysIndirectCount,
MultiDrawArrays, MultiDrawElementsIndirect, MultiDrawElements, DrawArraysInstanced);
P3b/P4b/P7 framebuffer (ClearBufferfv, ClearBufferfi, ClearNamedFramebufferfv);
P7/P8 compute (ShaderStorageBlockBinding); P8 texture/copy (CopyTexImage2D,
GenerateMipmap); and P9 XFB (BindTransformFeedback). This advances the remaining 38 current
verb-abort entries after Wave 1's measured set.
Wave 3 — unmeasured static tail
Migrate the 39 “not hit” slots above, grouping query/sync/present under P10, reverse readback under P9, and residual draw/copy breadth under P8. Their order should not displace any Wave 1 or Wave 2 package unless a newly added real workload produces a first-blocker measurement.