# Miss 08 + opts 12/13 implemented; opt 14 deliberately deferred (2026-07-03)

**Status:** miss [08](08-miss-layout-state-never-syncs.md) (both halves), opt
[12](12-opt-hot-path-full-model-work.md)'s TS items, and opt
[13](13-opt-parked-dirty-full-sheet-replace.md) implemented and verified. All
TS/shared-side — the C++ already did its part (eeschema's `itemBlob` emits the
`lib_symbols` context; `doApplyItems`' `findLib` was designed to prefer a
blob-carried definition), so no WASM rebuild was needed.

## 08A — lib_symbols channel

- New `kdoc_libsymbols` Y.Map (lib id → `(symbol …)` definition text): concurrent
  placements of DIFFERENT symbols merge per definition instead of LWW-clobbering
  one layout slot.
- `docToY` extracts the layout's `(lib_symbols …)` definitions into the map and
  keeps the layout slot EMPTY (the injection point); `yToDoc` re-injects the
  map's definitions sorted by lib id — the order KiCad's own writer emits, so
  materialization matches editor saves.
- Emit direction: `wireLibSymbols(wire)` recovers definitions from the
  multi-form clipboard blob (`unwrapWireItem` still strips them from the ITEM
  payload); the binding stores them in the same transaction as the item delta.
- Apply direction: `deltaToItemsWire(delta, view, libDefs)` prefixes a root
  item carrying `(lib_id …)` with its definition — exactly the multi-form shape
  eeschema's paste path parses, so `findLib`'s first branch resolves it and a
  peer that has never seen the symbol renders it correctly.
- e2e: `tests/kicad/ysync-libsymbols.spec.ts` — a joiner whose cold copy lacks
  the symbol adopts it WITH its definition (chromium two-tab; F2 applies).

## 08B — coarse layout save-sync

- New shared `syncLayoutToY(fileDoc, ydoc, origin)`: reconciles non-item layout
  slots per HEAD KEYWORD group (title block, paper, setup, settings…) from a
  just-saved file — a changed group is replaced wholesale (LWW at head
  granularity; different heads merge). `{item}` slots are never touched.
- Deliberate freezes: `net` (pcbnew's root net table — net-creating edits are
  not possible in the standalone; repeated positional heads can't be reconciled
  by name) stays seed-frozen; `lib_symbols` routes to the defs map ADDITIVELY
  (a save can't know about a peer's not-yet-applied placement).
- Wiring: `registerSaveHook` gained `onSavedText` (decoded saved text);
  WasmTool routes it — eeschema per sheet via the manager's new
  `syncLayoutFromSave` (parked rooms sync too; the dirty mark it causes costs
  only the real delta now, see opt 13), pcbnew/pl_editor via the single-room
  doc.
- Known limitation (unchanged from the doc's "coarse but converging"): a peer's
  EDITOR does not live-update non-item state — the room doc converges, so
  materialization/reopen and drift-detect see it; live propagation into an open
  editor's title block would need a C++ apply for non-item state.

## Opt 12 — TS hot-path (items 2 + 4)

- `yToItemUnchecked` (no zod) feeds the binding's `itemsView()` — both hot
  directions (every local emit, every remote batch). zod stays at the trust
  boundaries: wire parse, seed, materialize.
- `itemsWireToDelta` builds the parent→children index ONCE per conversion
  (was: full-scan `descendants()` per wire item and per removed id).
- NOT taken from doc 12: retiring the C++ scalar scan + legacy emit (the scan
  still feeds the legacy wire and covers eeschema's unreported post-Push
  connectivity cleanup — retire it together with the legacy-wire removal,
  miss 11 follow-up), and the lazy `deltaToItemsWire` view (item 3) — killing
  zod removed the dominant cost; revisit if profiling says otherwise.

## Opt 13 — diff-on-rebind adopt (option 2)

`seed()`'s adopt no longer renders and re-applies EVERY doc root. It diffs the
editor snapshot against the doc view (`itemsWireToDelta` + `wireItemUuids`) and
applies only the doc-authoritative difference:

- doc-only ROOTS → added (their sexprs embed descendants; a doc-only child makes
  its shared parent differ and rides the parent's re-apply);
- differing items → the DOC's version re-applied, lifted to their root (the C++
  upsert replaces roots; a bare child apply would mis-parent);
- editor-only ROOTS → removed (editor-only children vanish with their parent's
  re-apply).

An empty diff degrades to baseline-only — the parked-dirty rebind, the clean
revisit, and the cold divergent adopt now share one code path, and the adopt's
single commit (and undo entry — the miss-09 "adopt undo bomb") shrinks to the
real changed set.

## Opt 14 — deferred, on purpose

The slot-level CRDT refinement is a meaningful rewrite of `kicad-y`'s write path
plus the whole conversion layer, with real interleaving-anomaly risk. Doc 14's
own revisit triggers (measured bandwidth/storage cost; concurrent same-item UX
complaints) are unmet, and the flatten already gives per-child granularity.
Nothing in this batch forecloses it — `body` stayed one JSON value.

## Verification

- pcbjam-shared: 107 (9 new in `test/layout-libsymbols-sync.test.ts`)
- standalone: 79 + 2 known pre-existing wasm-assets fixture failures
  (collab suite 42, incl. 3 new diff-adopt + 2 lib_symbols binding tests;
  save-flow +2)
- e2e (chromium): full ysync set + `ysync-libsymbols.spec.ts` green
- No C++ changes; no WASM rebuild.

## Remaining from the review

Misses [09](09-miss-undo-not-collab-aware.md) (collab-aware undo — the adopt
shrink here removes its worst case) and [10](10-miss-no-repair-path.md) (drift
repair), opt 12's C++ retirement half (with the legacy-wire removal), opt 14
(triggers above), and doc 17's carried-over follow-ups.
