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
This commit is contained in:
Gergő Törcsvári 2026-07-03 18:08:37 +02:00
commit ae5d41a3d7
No known key found for this signature in database
GPG key ID: 8E75F2CDE64E5322
18 changed files with 527 additions and 109 deletions

View file

@ -192,15 +192,13 @@ test.describe("pcbnew ysync repros (v2 items wire, single tab)", () => {
});
test("footprint blob preserves pad nets", async ({ page, testLogger }) => {
test.fail(); // bug 02 — blobForItem's pad->SetNetCode(0) loop strips them
await bootOpen(page);
const blob = await fp1Blob(page);
// CORRECT: peers share the same board/net lineage — identity-by-uuid, not
// a foreign-board paste — so `(net 1 "SIG")` must survive the wire
// (02-bug-footprint-blob-zeroes-pad-nets.md). TODAY: pads go out net-0 and
// the loss propagates to the Y.Doc, peers, and materialized files.
expect(blob).toContain(`(net 1 "SIG")`);
// Peers share the same board/net lineage — identity-by-uuid, not a
// foreign-board paste — so the SIG net must survive the wire
// (02-bug-footprint-blob-zeroes-pad-nets.md). KiCad 10 formats pad nets by
// NAME only, `(net "SIG")`; accept the legacy code+name form too.
expect(blob).toMatch(/\(net (1 )?"SIG"\)/);
expect(hasAbort(testLogger), "no WASM abort").toBe(false);
});
@ -226,8 +224,6 @@ test.describe("pcbnew ysync repros (v2 items wire, single tab)", () => {
});
test("applyItems removes a footprint CHILD by uuid", async ({ page, testLogger }) => {
test.fail(); // bug 03 (receiving half) — the parent-footprint guard skips it
await bootOpen(page);
expect(await saveRead(page)).toContain(FP1_TXT);
// The wire a peer sends after deleting the fp_text: a bare child removal
@ -278,8 +274,6 @@ test.describe("pcbnew ysync repros (v2 items wire, single tab)", () => {
page,
testLogger,
}) => {
test.fail(); // bug 05 — the post-apply GLOBAL rebaseline swallows it
await bootOpen(page);
await page.evaluate(() => window.Module.kicadCollabSnapshotItems());
await captureEmits(page);
@ -349,8 +343,6 @@ test.describe("pcbnew ysync repros (v2 items wire, single tab)", () => {
}
test("an anchor-centred footprint rotation reaches the wire", async ({ page, testLogger }) => {
test.fail(); // bug 04 — no orientation in the footprint's scalar json
const ok = await armed(page, "kicadCollabTestRotateItem");
test.skip(!ok, "wasm build predates the ysync repro hooks");
@ -381,8 +373,6 @@ test.describe("pcbnew ysync repros (v2 items wire, single tab)", () => {
});
test("a pad size edit reaches the wire", async ({ page, testLogger }) => {
test.fail(); // bug 04 — pads are not visited by forEachTopItem at all
const ok = await armed(page, "kicadCollabTestSetPadSize");
test.skip(!ok, "wasm build predates the ysync repro hooks");
@ -412,8 +402,6 @@ test.describe("pcbnew ysync repros (v2 items wire, single tab)", () => {
});
test("a graphic-shape endpoint drag reaches the wire", async ({ page, testLogger }) => {
test.fail(); // bug 04 — Drawings' json is position-only; GetPosition() is the start
const ok = await armed(page, "kicadCollabTestMoveEndpoint");
test.skip(!ok, "wasm build predates the ysync repro hooks");
@ -471,8 +459,6 @@ test.describe("pcbnew ysync repros (v2 items wire, single tab)", () => {
});
test("a child deletion goes out as the parent's re-blob", async ({ page, testLogger }) => {
test.fail(); // bug 03 (sending half) — flushDiff has no liftBlob for removals
const ok = await armed(page, "kicadCollabTestRemoveItem");
test.skip(!ok, "wasm build predates the ysync repro hooks");