oficina / slides Public

fix: codex 3, 4, 7, 10 — validate the document not the projection, stable block order, no cycles, real phone reading order

Codex 3: the write gate parsed the JSON view, which drops a formatting
attribute it does not know, while y-prosemirror 1.3.7 deletes the
XmlText that carries one. New core ABI call slides_core_check (ABI 2)
walks every text fragment for anything outside §2.6; appendUpdate
refuses such an update 422 before it is stored; fixtures/check.mjs is
the same walk and the stage strips unknown attributes before binding.

Codex 4: canvas-to-flow (and move_block) cloned every block map,
cleared the array and re-inserted copies, so a concurrent text edit
addressed a deleted map and was lost on merge. Reading order is now an
`order` key on each block map, sorted by the view and never exposed;
reorders write the key and the maps stay put. Vectors regenerated (three
.yjs moved, no view moved).

Codex 7: columns children may name a block once, in one list per
slide, and never a block the columns block sits under — refused in both
implementations with one message. The renderer and the layout engine
append whatever nothing reaches at the end, marked, rather than dropping
it (WarnUnreached; data-unreached).

Codex 10: rendering for the phone viewport emits the blocks in the
phone layer's reading order (regions, then a stable sort by order with
unset as 0, columns children included), so assistive technology and the
Tab key follow what a sighted reader sees; CSS order is no longer
load-bearing. Desktop rendering unchanged; no golden moved.

Spec §2.2, §2.3, §2.4, §2.5, §2.6, §3.3 and rulings 18–20 were amended
in the tree and were carried by f1c235d.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YYaKcMy5XrMcsfizRXcRSY
claude agent, operated by paul@keymail.dev pushed by paul@keymail.dev 986722dd4415d49b6b6373ffcb63677a7df82f52 parent f1c235d
38 files changed, +2197 −129
  • core/src/abi.rs +37 −1
  • core/src/check.rs +294 −0
  • core/src/layout.rs +110 −18
  • core/src/lib.rs +17 −12
  • core/src/ops.rs +146 −16
  • core/src/schema.rs +60 −2
  • core/src/view.rs +8 −1
  • core/tests/ops.rs +160 −0
  • fixtures/check.mjs +146 −0
  • fixtures/check.test.mjs +110 −0
  • fixtures/generate.mjs +6 −0
  • fixtures/layout.mjs +15 −11
  • fixtures/ops-apply.mjs +48 −7
  • fixtures/ops-model.mjs +37 −6
  • fixtures/ops.test.mjs +98 −0
  • fixtures/poison.mjs +16 −0
  • fixtures/stage.mjs +13 −4
  • fixtures/vectors/canvas.yjs binary
  • fixtures/vectors/columns.yjs binary
  • fixtures/vectors/every-block.yjs binary
  • fixtures/view.mjs +7 −3
  • fixtures/ydoc.mjs +30 −0
  • internal/slides/core/check_test.go +90 −0
  • internal/slides/core/core.go +41 −2
  • internal/slides/core/core_test.go +2 −2
  • internal/slides/core/testdata/slides_core.wasm binary
  • internal/slides/deck/deck.go +6 −0
  • internal/slides/layout/layout.go +65 −6
  • internal/slides/layout/paint.go +6 −0
  • internal/slides/layout/unreached_test.go +90 −0
  • internal/slides/live.go +23 −5
  • internal/slides/render/order_test.go +140 −0
  • internal/slides/render/render.go +134 −18
  • internal/slides/render/unreached_test.go +93 −0
  • internal/slides/static/{editor.c5e9b4881d5c8a54.js => editor.b15cb47d0e317eee.js} +15 −15
  • internal/slides/static/slides.css +14 −0
  • internal/slidestest/integrity_test.go +120 −0
  • internal/slidestest/testdata/poison/unknownmark.yjs binary

Intent

Human

<task-notification> <task-id>ac2281ca669d9e1c6</task-id> <tool-use-id>toolu_013TmDDhbeExQX5puph7a4xH</tool-use-id> <output-file>/home/paulca/.cache/tmp/claude-1001/-home-paulca-amadan-net-oficina/c8f62d65-b2d1-4a65-b79e-6a8e82e1e5c6/tasks/ac2281ca669d9e1c6.output</output-file> <status>completed</status> <summary>Agent "Codex wave B: capabilities" finished</summary> <note>A task-notification fires each time this agent stops with no live background children of its own. The user can send it another message and resume it, so the same task-id may notify more than once.</note> <result>Status: done, one commit, not pushed. **f1c235d** — `fix: codex 1, 2, 5, 6, 13, 15 — staged uploads, upload receipts, follow behind its own capability, offboarding, remote ordering and attribution` Per finding (all six citations held; none declined): - **1 fixed** — uploads go to an app-minted staging key (hash of deck+actor+nonce), registration reads and re-hashes (the promised rehash now real) and promotes to an immutable final object; no PUT is ever signed for a verified address; dev `blobs.Dir` path kept. New `asset_uploads` table + migration 0008. - **2 fixed** — registration accepts only a receipt matching this deck *and* actor, unspent, &lt;2h. A ref alone is 404. Cross-deck copy path deliberately not built (spec says why). - **5 fixed** — new `GET /s/{token}/stream`; followers stay on `/s/{token}` for page, fragment, stream and assets; sqid never rendered on a follow page; stream re-resolves the link per event. Permission mismatch settled in spec §3.4 + ruling 15. - **6 fixed** — admission rechecked in `currentUser` (sessions revoked on all devices, cookie cleared, live talks ended, `/signin?err=removed`) and in the bearer gate (401). Offboarding table in spec §6.1; share links deliberately survive. - **13 fixed** — `present.js` tracks requested vs displayed with monotonic request stamps. - **15 fixed** — `remote:&lt;session&gt;` with issuer logged; spec §6.2 + ruling 17. Tests: `make ci` green. New `internal/slidestest/capabilities_test.go` (7 tests) plus updated `assets_test.go`/`present_test.go` — all pass; each new test fails against the old behaviour. Browser drive `go test -tags browser ./internal/slidestest/` **ok (63.9s) only with the Bash sandbox disabled** — sandboxed, Chromium dies on `socket() failed: Operation not permitted` (controller may want `/sandbox`). Full gate deliberately not run (wave A mid-edit on `core/`, `fixtures/`, wasm). Concerns: (1) no `node --test` for `present.js` — no JS harness exists outside `fixtures/`, which was off-limits; finding 13 rests on the browser drive plus attribute assertions. (2) `fixtures/uploads.mjs` has one now-false comment line ("computing it here is what makes the presigned URL possible") — comment only, left untouched as wave A's path. (3) Registration now re-writes ≤20 MiB per picture; documented as the accepted cost. (4) I took spec rulings 14–17; wave A's are 18–19, no collision. Its spec/api edits present in the tree were kept and its spec text rode into this commit (shared file, staged by name).</result> <usage><subagent_tokens>321711</subagent_tokens><tool_uses>171</tool_uses><duration_ms>1897871</duration_ms></usage> </task-notification>

core/src/abi.rs

Loading diff…

core/src/check.rs

Loading diff…

core/src/layout.rs

Loading diff…

core/src/lib.rs

Loading diff…

core/src/ops.rs

Loading diff…

core/src/schema.rs

Loading diff…

core/src/view.rs

Loading diff…

core/tests/ops.rs

Loading diff…

fixtures/check.mjs

Loading diff…

fixtures/check.test.mjs

Loading diff…

fixtures/generate.mjs

Loading diff…

fixtures/layout.mjs

Loading diff…

fixtures/ops-apply.mjs

Loading diff…

fixtures/ops-model.mjs

Loading diff…

fixtures/ops.test.mjs

Loading diff…

fixtures/poison.mjs

Loading diff…

fixtures/stage.mjs

Loading diff…

fixtures/vectors/canvas.yjs

Loading diff…

fixtures/vectors/columns.yjs

Loading diff…

fixtures/vectors/every-block.yjs

Loading diff…

fixtures/view.mjs

Loading diff…

fixtures/ydoc.mjs

Loading diff…

internal/slides/core/check_test.go

Loading diff…

internal/slides/core/core.go

Loading diff…

internal/slides/core/core_test.go

Loading diff…

internal/slides/core/testdata/slides_core.wasm

Loading diff…

internal/slides/deck/deck.go

Loading diff…

internal/slides/layout/layout.go

Loading diff…

internal/slides/layout/paint.go

Loading diff…

internal/slides/layout/unreached_test.go

Loading diff…

internal/slides/live.go

Loading diff…

internal/slides/render/order_test.go

Loading diff…

internal/slides/render/render.go

Loading diff…

internal/slides/render/unreached_test.go

Loading diff…

internal/slides/static/{editor.c5e9b4881d5c8a54.js => editor.b15cb47d0e317eee.js}

Loading diff…

internal/slides/static/slides.css

Loading diff…

internal/slidestest/integrity_test.go

Loading diff…

internal/slidestest/testdata/poison/unknownmark.yjs

Loading diff…