pcbjam/docs/features/ysync-review/02-bug-footprint-blob-zeroes-pad-nets.md
Gergő Törcsvári ae5d41a3d7
fix(ysync): review bugs 01–07 — listener registration, pad nets, child removals, dirty-set emit, targeted rebaseline, seed arbitration, stale hook (doc 17)
All seven ysync-review bugs fixed and verified; every repro's expected-fail
marker removed (they now run as regression tests). Also fixes two bugs found
while verifying (doc 17 F5/F6): file-seeded Y bodies are re-upserted in the
editor's serialization (doc-16 F4 was an artifact of bug 01), and the
0008-era "asyncify-fragile envelope parse" was really wrapInBoardEnvelope
emitting display layer names — canonical LSET::Name() fixes track/via/zone
v2 applies; makeFromBlob now logs parse errors instead of swallowing them.

Verified: shared 98, standalone collab 37, ysync e2e 20/20 (chromium),
collab regression set 21 passed / 3 pre-existing skips (firefox).

Bumps: kicad (board_commit child-removal listener notification),
web/pcbjam-shared (slot prune + arbitrated seed).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JgThWXtdvrYLK47EDFoGdq
2026-07-06 08:57:57 +02:00

74 lines
3.8 KiB
Markdown

# Bug 02 — Footprint blobs zero pad net codes; net data loss propagates to peers, the Y.Doc, and materialized files
**Severity:** high (silent, converging data corruption on boards with nets)
**Status:** FIXED 2026-07-03 — see [17](17-fixes-bugs-01-07.md) (batch 2)
with `(net 1 "SIG")` stripped (repro below)
## Where
- `wasm/bindings/pcbnew_embind.cpp:275-322``blobForItem`, specifically the footprint
branch's `pad->SetNetCode( 0 )` loop (`:293-295`)
- Consumers: `flushDiff`'s `liftBlob` (`:593-618`), `pcbCollabSnapshotItems` (`:996-1019`),
`doApplyItems` (`:822-894`)
## What happens
`blobForItem` copies clipboard `SaveSelection`'s "make safe to transfer" steps for
footprints, including zeroing every pad's net code before `Format()`. That is correct
for pasting into a *foreign* board (net codes are per-board indices), but the collab
wire connects peers editing the *same* board with identity-by-uuid — nets should
survive.
Concrete flow for a plain footprint **move** on the sender:
1. `flushDiff` sees the footprint's scalar json changed → `liftBlob` → footprint blob
with **netless pads** goes out on the v2 wire (`wChanged`).
2. TS `itemsWireToDelta` re-flattens the blob; the pad items' bodies differ from the
Y.Doc's (which still has `(net N "NAME")` from the file seed) → pads are `updated`
**the Y.Doc's pad bodies lose their nets**.
3. Peers receive the change, render the footprint from the (now netless) Y view, and
`doApplyItems` does remove+add — **the peer's board now has net-0 pads** on that
footprint. Ratsnest lines to it disappear; DRC connectivity changes.
4. The sender's own editor still has nets → the sender now permanently drifts from the
Y.Doc (drift-detect will report it forever).
5. Once the *peer* touches the same footprint, its (already netless) blob flows back
and the session converges on netless pads everywhere.
6. In `docSource: "ydoc"` mode the next open materializes the board from the doc
(`docToFile`) — **the author's file itself loses pad nets across a reload**.
Also note: `pcbCollabSnapshotItems` (editor-snapshot seeding and adopt comparisons)
uses the same blob, so even the seed path bakes in the loss when the room is seeded
from the editor snapshot instead of the file. The file-seed path (`fileToDoc`)
preserves nets — until the first footprint edit destroys them.
## Why the "safety" step doesn't apply here
`MapNets` remapping only runs for the `(kicad_pcb …)` envelope parse
(`makeFromBlob`, `:346-368`); bare footprint blobs never get net remapping — they get
nothing, because the nets were already stripped at the source. Peers in a collab
session share the same net table lineage, so the paste-into-foreign-board rationale
doesn't hold.
## Fix direction
1. In `blobForItem`'s footprint branch, **drop the `SetNetCode(0)` loop** (keep the
mandatory-field uuid restore and `SetLocked(false)`).
2. Verify on the receiving side that pad `(net N "NAME")` tokens parse correctly
against the live board (`io.SetBoard(aBoard)` should resolve them). If net *codes*
can diverge between peers after local edits, remap by **name** on apply (the same
thing `MapNets` does for envelope boards) rather than trusting the code.
3. Add a regression test: seed a board with netted pads, move a footprint on tab A,
assert tab B's pad nets AND the Y.Doc pad bodies still carry the nets.
## Repro
`tests/kicad/ysync-repros-pcbnew.spec.ts` "footprint blob preserves pad nets"
(`test.fail`), with the green "footprint blob embeds its pad children" precondition
pinning that only the nets — not the pads — are missing. See
[16](16-repro-suite-results-and-empirical-findings.md).
## Related
- [04-bug-lossy-change-detection.md](04-bug-lossy-change-detection.md) — pad property
edits are separately invisible to the differ; this file is about the emit *payload*,
that one about the emit *trigger*.