Files
MobileGL/docs/Disaggregated/notes/p5e/PREAMBLE-ADDENDUM-WAVE3.md
T
swung0x48 aba329cc53 [Docs] (Disaggregated): P6's plan and contract draft; the working notes move from a local directory into the repository
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.
2026-09-18 23:31:39 -04:00

5.3 KiB
Raw Blame History

Addendum to PACKAGE-PREAMBLE-P5E.md for wave 3 (pa / mv / gl), 2026-09-18

The preamble is still correct except where this file overrides it. Read both.

1 The contract is landed, the draft is gone

CONTRACT-P5E-draft.md was landed by c0e as MobileGL/MG_Remote/CONTRACT-P5E.md in your tree. Read the landed file, not the draft. INTEGRATOR-DECISIONS-P5E.md now runs to ID-110 and still overrides both where they differ.

2 The gate has a sixth step, and it is not optional

bash ~/w7/notes/tools/p5e_gate.sh <slug> gpu        # ctest -L integration-gpu, ~10 min at -j 4

integration-split entries ALL export MOBILEGL_TRANSPORT=inproc. The push build has a second server arm (Transport == Monolith) selected at runtime, and until 2026-09-18 it had no gated entry at all. A P5e package landed a null dereference on it under 2250 green unit entries and 113 green split entries: the game SIGSEGV'd at startup on the phone, and ctest -L integration-gpu on the same head was 1157/1294 — 137 SEGFAULTs across 38 scenarios. See rulings ID-107 / ID-109 / ID-110, fix commit 66621767, and docs/Disaggregated/CURRENT_STAGE_PROGRESS.md §2.5.

Run gpu before you report. 1294/1294 or explain.

3 The arm-selection rule, stated as a checklist

Ruling ID-81: handle arms are selected by MG_Config::Transport != Monolith; the push-monolith build keeps its frontend arms token for token. Apply this to your own diff:

  • "A handle was noted" is NOT the statement "the record arm is selected." They are different claims and conflating them is exactly what cost the phase 137 scenarios. A seat that is safe only because every writer of a note happens to be transport-gated is safe by a chain, not by its own statement. State the transport test where you rely on it. MGB_TEXTURE_RECORD_ARM_SELECTED() (MG_Backend/DirectGLES/Managers.cpp, ID-110) is the worked example.
  • Never infer the arm from a pointer being null. Decide with MG_Config::Transport (or your family's selector, which must itself reduce to it) and pass the pointer as glue.
  • A null/missing record on the handle arm is a NAMED refusal, never a quiet fall-back to the frontend. RefuseNullFrontendTextureOffTheHandleArm (Managers.cpp) is the shape: it aborts with Fatal{RoleViolation, "<name>"} and folds to a constant in the pull build so no call is added there.
  • If you pass a null object into a body that can still read one, you have written the fix1 bug. Either the body cannot reach a frontend read on that arm, or the refusal fires first.

4 Reading the strict lane properly

ctest does NOT carry MGLOG_F into its captured stdout, and the lane's entries write no log file, so p5e_gate.sh <slug> strict can only give you a pass count. The pass count is not your deliverable. Produce the per-entry marker census:

bash ~/w7/notes/p5e/strict_census2.sh ~/w7/p5e-<slug>

It re-runs each red entry alone against the test binary with MOBILEGL_LOG_FILE_PATH set and writes ~/w7/p5e-logs/strict-census/census2.tsv (marker TAB entry). Copy it to a per-slug path first if you do not want to clobber the shared one. Report the table before and after your change.

One known blind spot in that script, which you must not repeat as a finding: it derives the gtest filter from the last two dot-components of the ctest entry name, so entries with a third segment (Ct., F1., SmallRing., PersistentMapArm., NamedBlit.) are re-run WITHOUT the extra ENVIRONMENT their lane block sets. DirectGLES.Split.PersistentMapArm.PersistentCoherentMapScenario.TheMapLandsInTheArmItsLaneDeclares is the one entry whose marker the census failed to capture for this reason. If your family touches such an entry, take its marker under its own lane environment (ctest -V -N -R '^<entry>$' prints the block's environment).

5 The baseline you are changing, measured

Head 7c6f6886. integration-split 116/116 green; under MOBILEGL_IPC_STRICT_ERRORS=1, 7 pass and 109 abort with:

marker entries owner
GetProgramForDraw@DrawArrays 61 pa
GetFramebufferBindingSlot@ReadPixels 21 allowlist (readback)
GetBoundVertexArray@DrawArrays 9 mv
GetProgramForDispatch@DispatchCompute 7 pa
GetTextureObject@CopyImageSubData 6 allowlist (retires P7)
ValidateProgramName@ShaderStorageBlockBinding 2 allowlist (retires P9)
GetTextureUnitObject@CopyTexImage2D 2 allowlist (CopyTex)
(census artifact, §4) 1 —

Touch only your own rows. If your change removes or adds a row that is not yours, that is a finding for the report, not something to quietly absorb.

6 Device numbers, for context only — you do not run the device

Redmi, Minecraft 26.3-rc-3, ~849 draws/frame, GPU pinned, fan on, 30 s windows, interleaved:

arm fps p50 client GL thread ms/frame apply thread ms/frame
monolith ×3 267.9 / 195.1 / 254.2 3.898 / 4.781 / 4.694 —
inproc ×2 166.4 / 143.5 6.098 / 6.958 5.305 / 5.653

The split arm's client thread costs about 1.5× the monolith arm's single thread, and the device stats line shows the client entering the doorbell wait about 916 times per frame against 849 draws. That is the lockstep your package is clearing the way to remove. It is context, not a target: your deliverable is the marker table and the gate.