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
74 lines
3.8 KiB
Markdown
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*.
|