# Packet B receipt — shared reader and pixels Helios, 12 September 2026. Actor: Cursor on Icarus (`centaur-macmini-1`). Packet 0 remains a source map. Packet A remains an in-process host receipt. This packet does not close Packet A live-client, browser, or restart-through-Cursor-Settings. It does not close movie-with-sound. Three identities stay separate: this Packet B tip, inspected baseline `f9beb591` (tag `korgi/railway-studio`), unidentified Railway production build `9298f5e8-2c43-444e-adee-4c5c86617cef`. ## Implementation / test commit and environment | Item | Value | |---|---| | Branch | `cursor/packet-b-0101` | | Parent | `ef58c6ac58d9a0b9fa1e8f3ac0f6361afc7c6d11` (Packet A) | | Implementation/test commit | `45b7d1cb329441cbc8c01f8edc72350428b754c1` | | Host | Icarus, Darwin 25.6.0 arm64 | | Node | v22.23.1 | | Typecheck | `npx tsc --noEmit` — exit 0 | Commands: ```bash node --experimental-strip-types --test \ src/lib/korgi/evaluate-frame.test.ts \ src/lib/korgi/packet-b.test.ts \ src/lib/korgi/mcp/packet-a.test.ts \ src/lib/korgi/mcp/mcp.test.ts \ src/lib/stage-render.test.ts \ src/lib/production-desk.test.ts \ src/lib/project-io.test.ts \ src/lib/korgi/satsuei-desk.test.ts ``` 63/63 passing in that slice (implementation commit). Boundary verification added eight tests; the same command is **71/71** on the verification commit. ## Changed reader call sites | Site | Before | After | |---|---|---| | `src/lib/korgi/evaluate-frame.ts` | `drawingForEvaluatedFrame` | plus `evaluatedColumnsAtFrame` (every timed production column) | | `src/lib/stage-render.ts` `renderFrame` | `drawingAt(project.takes, frame)` | production: composite each column via `drawingForEvaluatedFrame`; blank column skipped; no `paint[frame]` fallback. Dump-only: previous `takes` overlay. | | `src/lib/korgi/satsuei-desk.ts` `productionDrawingAtFrame` | `drawingAtCel` | `drawingForEvaluatedFrame`; dump route stays clear glasses | | `src/lib/production-desk.ts` `deskPoseIdAtFrame` | nuki fell through to other columns / production `poseId` / `takes` | production nuki is `null` | | `src/lib/store.ts` `liveDrawing` | returned dump fallback | keeps selected `poseId` when the evaluated cell is blank; does not read `takes` | | `korgi.film.setClock` | stretched dump columns only | independently hold-last/truncates `celColumns` when `filmLen` changes | | `korgi.stage.patch` | camera keys not in the payload schema | `cameraKeys` / `columnKeys` / `tableKeys` accepted | | `normalizeJournal` | dropped `kind: "draw"` commands that had no DrawOp (`setCel`) | keeps inverse-only draw patches so pack checksum matches saved cels | Not dual-write. Dump `takes` on the Packet B fixture stay `06` on every frame. ## 240@24 route (bounded action-path) Supported sequence: ```text New Project (24@12, valid production sheet, route=production) → korgi.film.setClock { filmLen: 240, fps: 24 } (studio, confirmed) → clock 240@24 → celColumns each sum to 240 (hold-last of the production sheet itself) → evaluationRouteOf = production → drawingForEvaluatedFrame(239) = A1 → dump takes[0] remains the dump drawing (01), not A1 ``` `store.setClock` now dispatches that action with `confirmed: true` after a Studio confirm. Empty `celColumns` stay empty (dump-only). No silent `migrateLegacyLoop`. No copy from dump into production. ## Native fixture Authored only through Studio actions (`mintDrawing`, `editCelHold`, `setCel`, `setExposure`, `stage.patch`). Mint/hold remain STUDIO_ONLY. Sheet (cel-A), 24@12, pattern then blanks: ```text A1×2 A2×2 A1×2 A3×2 blank×2 (+ blank to 24) ``` Second level: cel-B holds B1 for the cut. Dump Column 1 is all `06` with a bottom-row lure cel so a dump reader cannot pass. Deterministic artwork is 32×18 indexed paint blocks (no generation). Stored bytes are `project.cels[:color]`. | Asset | sha256 of encoded `z` (gzip mtime 0) | |---|---| | A1 | `bf1cd892e3346f5d31b0676241bf5eee2ea075ca718586432ccfb78043a2a5e2` | | A2 (fixture block, before ingest) | `f664d23d607f18226d3e9e652165fa0d04598ffbe3b5afa8ff3d3d7826057901` | | A3 | `d8af84591b9d11d6e52ec1283b2b69fc9f62c7fb479ef84e2e1d0fa888b5be61` | | B1 | `7a4b5b8d7b40fe957d53e2a19f5ec97bb034faa270ef7d71d916b8c08d3eed3a` | | dump 06 lure | `5c97f4b1f51bad99ba78490880bca3e066243ab35549a6d3c3f1f907f2ba88e5` | | A4 (unique second A1 run) | `beda36f7775b534eeda682dbe14d6cdbb21b772b3abfc53ac4f68a3f26979aa9` | Encoded-`z` strings from implementation commit `45b7d1c` used unpinned gzip timestamps and are not comparable to this table. After unique: first A1 run remains A1 with the original hash. Second run is A4. Undo/redo of the unique `setCel` + `editCelHold` restores those distinctions. Evaluated frames (after unique): | Frame | cel-A | cel-B | dump `takes` | |---|---|---|---| | 0–1 | A1 | B1 | 06 | | 2–3 | A2 | B1 | 06 | | 4–5 | A4 | B1 | 06 | | 6–7 | A3 | B1 | 06 | | 8–9 | `null` | B1 | 06 | Blank cel-A does not erase B1. Photography at frame 8: glass A is `clear`; glass B samples B1. Dump-only (`celColumns: []`) still renders the 06 lure. An all-blank valid production sheet stays on the production route. ## Pixel / render evidence PNG sequence (pre-unique fixture, 32×18, live camera keys) at `docs/oss/packet-b/frames/frame-00.png` … `frame-09.png`. SHA-256 list: `docs/oss/packet-b/frame-hashes.json`. Camera interpolation can change PNG bytes inside a hold. Hold identity is the evaluated drawing id. With `cameraKeys` cleared, adjacent hold frames hash equal (A1×2, blank×2) and A1 ≠ A2 ≠ blank. PNG sequence evidence does **not** close movie-with-sound. ## Clean reopen and injected ingest failure `packKorgi` / `unpackKorgi` restores A1/A4/B1 hashes, cel-A blanks, cel-B, two camera keys, and dump `06`. Opened without the original tab or object URLs. `applyIngestScan` stores cel bytes in a local `EncodedCel` then commits `drawingArts` + `cels`. Inject `failBeforeByteStorage` or `failBeforeProjectCommit`: thrown `IngestCommitError`; `documentFingerprint` and A2 planes unchanged. Successful ingest changes the fingerprint and writes `A2:color`. `normalizeJournal` now keeps `setCel` field patches (`kind: "draw"` without a DrawOp) so a packed journal of those actions checksum-matches. ## Status rows Astra (12 September 2026) accepted Packet B as a **reported unit/host implementation receipt**. The full ingest/save/reopen gate stayed open. This section records the bounded B-boundary verification that followed. No authorization to land `main` or redeploy. Neither `45b7d1c` nor `3e29306` identifies the Railway build. | Gate | Label | |---|---| | **A live-client completion** (actual Cursor connection to KORGI; user-approved binding to the local Studio project; rename + follow-peg both visible; permitted Studio edit; MCP `tools/list` + `tools/call` sees the updated revision; save → terminate → reopen; reject the old binding) | **unproven** / **not implemented**. Packet A in-process host tests remain passing. Observed initialize is the 2026-09-04 pixellab/`mcp-remote` trace — client-environment evidence, not a Cursor → KORGI stdio handshake. Custom-method harness (`korgi/bind`) remains separate evidence. Protocol version is not reopened. | | **B unit/host** (production holds select the intended pixels on playback, photography, and `renderFrame`; pack/unpack restores that result; unique vs reuse; ingest interrupt; 240@24 stays production) | **passing** as reported at `45b7d1cb329441cbc8c01f8edc72350428b754c1`. | | **B boundary** (real PNG through `korgi.draw.ingestScan` / DrawPad `ingestScanFile`; visible samples; `.korgi` save → terminate → reopen; post-reopen undo/redo; failed `writeKorgiHandle` cannot say Saved) | **passing** on the local Studio writer and host reopen in this verification commit. Not a live GUI click-through. Not Railway. Not movie-with-sound. | ## Policy (unchanged) Dual-write dump↔production: **prohibited**. Silent `migrateLegacyLoop`: **prohibited**. Broadening mint/hold to MCP: **not done**. PixelLab: **not used**. Trusted approval cannot be replaced by submitting an `origin` string. ## B-boundary verification Helios, 12 September 2026. Bounded verification of the existing fixture — the sheet was not rebuilt. Ingest onto A2 uses `korgi.draw.ingestScan` with PNG `bytes`. A `cel` payload present at the same time is ignored when bytes exist. | Item | Value | |---|---| | Branch | `cursor/packet-b-0101` | | Implementation commit | `45b7d1cb329441cbc8c01f8edc72350428b754c1` | | Docs tip at assignment | `3e29306` | | Boundary verification commit | `008124ef83da791076cba8417ce0362b79ebe841` (ingest/save/reopen proof) | | Identity pin (gzip mtime 0 + decoded-plane hashes) | `ff99352afd1e26400cc0f72ec658c6cb7d520861` | | Local build | `npx tsc --noEmit` exit 0; Packet B slice **71/71**; assigned command **132/132** including cel-paint | | Host | Icarus, Darwin 25.6.0 arm64 | | Node | v22.23.1 | | Native `.korgi` | `docs/oss/packet-b/packet-b-boundary.korgi` (zip timestamps are not a content identity; unpack restores planes and `scan-A2` PNG) | | Railway / `main` | not identified; not landed; not redeployed | ### Source raster vs prepared cel Tiny 32×18 PNG, shirt `#c45c4a` at (2,8) and fill `#d8c4a8` at (16,4), remainder transparent. No generator. | Identity | Value | |---|---| | File | `docs/oss/packet-b/source-ingest.png` | | Asset id | `scan-A2` (`kind: scan`, path `assets/data/scan-A2.png`) | | Original PNG sha256 | `be7da9c5521907f06499f33a7f78985832dfda1967caddb67125696634440483` | | Prepared cel planes sha256 | `a0c2d6669f5ef262edb48131f6c464b09b1a42c4742d34c82e82592b4a2e9d9f` (decoded ink\|paint\|shadow) | | Prepared cel encoded `z` sha256 | `4136c13ce569869e265b6b8dc1366ea69a34042cc99e0e2810ede45c30487609` (gzip mtime 0) | Those hashes are different on purpose. Encoded-`z` is cel evidence after gzip, not the input file. Decoded-plane sha256 is the pixel identity. Known samples after ingest (not “fingerprint changed”): - Shirt index 3 at (2,8); fill index 2 at (16,4). - Old A2 block at (8,8) is gone (paint 0). - Frame 2 `renderFrame` occupies shirt and fill; (8,8) empty; B1 at (28,1) still occupied. - Frame 0 A1 shirt at (2,8) unchanged; ingest fill is absent there. - Photography at frame 2 succeeded. DrawPad reads `file.arrayBuffer()` and passes `bytes` into `ingestScanFile` → `korgi.draw.ingestScan`. The store stashes original PNG under `assetBytes["scan-A2"]`. ### Save / terminate / reopen `writeKorgiHandle` (File System Access, durable after `close()`, abort on failure) wrote `docs/oss/packet-b/packet-b-boundary.korgi`. A new `createProjectHost().openPacked` plus `unpackKorgi` of those bytes, without the original store object: - First A1 use remains A1; second use remains A4. - Blank cel-A at frame 8; B1 still visible. - Two camera keys intact; dump `takes` remain `06`. - Ingested A2 samples and original PNG bytes (`scan-A2`) remain. - Undo of unique `setCel` + `editCelHold` after reopen returns frame 4 to A1; A2 ingest pixels stay. Redo restores A4. Pack/unpack remains. This adds the writer/reopen boundary. ### Persistence failure (actual save writer) Existing ingest interrupts kept: - `failBeforeByteStorage` / `failBeforeProjectCommit` on the **payload.cel** path (`interrupted ingest cannot masquerade as a saved addition`). - The same two injects on the **PNG bytes** path (`interrupted PNG ingest cannot register scan bytes or replace A2`). Added: `writeExplicitKorgi` / `saveProject({ failBeforeClose: true })`. | Role | Result | |---|---| | Last successfully saved revision | fingerprint of the ingested project on disk; file sha256 unchanged after the failed save | | Current in-memory revision | dirty project after an `editCelHold`; fingerprint differs | | Failed save | notice `Save failed: …`; **not** `Saved`; `dirty` stays true; `explicitSaveGeneration` not bumped | | Failed ingest | `IngestCommitError`; fingerprint and A2 planes unchanged; no `scan-A2` record | A refused ingest leaves the previously saved project unchanged. A failed save of a changed project does not acknowledge that changed revision as durable. ### Camera-off frame evidence `docs/oss/packet-b/frame-hashes.json` is the **pre-unique, camera-on** sequence. Frames 02 and 03 may differ as PNG bytes because camera interpolates; that is not a broken hold. Hold identity is the evaluated drawing id. Camera disabled, other composition fixed (`docs/oss/packet-b/stills/`, PNG-file sha256): | Assertion | Result | |---|---| | Before unique: first A1 (f00) and second A1 (f04) | same `f90e0d0a…cb6c` | | After unique: first A1 unchanged | still `f90e0d0a…cb6c` | | After unique: second run is A4 | `0ec4d6de…9ce9` | | Blank cel-A f08: B1 visible, dump 06 (y=17) absent | `7f4d7c84…f012` | PNG-file hashes stay distinct from encoded-`z` cel hashes. Manifest: `docs/oss/packet-b/MANIFEST.json`. Native fixture: `docs/oss/packet-b/packet-b-boundary.korgi`. ### Clock mutation and blank vs selection `korgi.film.setClock` registry: `confirm: "prompt"`, `affected` includes `celColumns`, studio permission. Inverse patches include `celColumns` when `filmLen` changes. - FPS-only `{ fps: 24 }`: production exposure runs unchanged; command `affected` omits `celColumns`. - Extending 24 → 30: blank tail at cel-A stays `null` through frame 29; dump `takes[29]` stays `06` independently (not copied into production). - Truncation 24 → 8 is undoable after pack/unpack. - Unconfirmed execute refuses; `window.confirm` false leaves the Studio document unchanged. Blank playback: `poseId` may remain `A1` while cel-A at frame 8 is `null`. `deskPoseIdAtFrame` is `null`. `renderFrame` drawing is empty; A1 pixels omitted; B1 remains; dump lure omitted. Photography glass A is `clear`. The editor does not have to forget the selected bank cel. ### Not done on this branch - Packet A live-client (separate work branch if staffed). - Packet C one-provider generation-to-cel. - `main` fast-forward, Railway redeploy, PixelLab spend.