Skip to content

fix(sync): prevent pending board deletions from being resurrected - #206

Merged
johnny4young merged 5 commits into
mainfrom
fix/pending-board-deletion-receive
Oct 8, 2026
Merged

johnny4young merged 5 commits into
mainfrom
fix/pending-board-deletion-receive

Conversation

@johnny4young

@johnny4young johnny4young commented Oct 8, 2026 •

Copy link
Copy Markdown
Owner

Finding

A locally deleted board remains tombstoned until its remote deletion is acknowledged. In that interval, a fetched board record or a clip carrying the old board membership could recreate it. The clip receive path already respected pending deletions; the board receive paths did not.

Correction

  • Check pending board tombstones inside the same GRDB transaction as incoming metadata or membership writes.
  • Skip tombstoned board metadata and report it as skippedAsStale in fetched-page summaries.
  • Exclude only deleted board IDs from a membership rebuild, preserving unrelated known boards, genuinely unknown placeholders and Favorites.
  • Keep the public SyncLocalStore API, schema, ordinary board server-wins behavior and deletion acknowledgement unchanged.

Regression evidence

  • Extracted production SQL plus a portable caller model reproduced both original failures: metadata and membership recreate a deleted board (red).
  • The guarded model rejects both paths, preserves unrelated memberships, clears prior membership on replacement, and permits arrivals after acknowledgement clears the tombstone (green). This is not Swift execution.
  • Four native tests cover metadata, mixed/empty membership rebuilds, a combined fetched page with accurate counts and an arriving clip, and the acknowledgement boundary.
  • git diff --check, release metadata/product-truth and website structural checks pass.
  • make format, make lint, make test and the Swift dependency check were attempted, but this Linux executor has no Swift/Xcode. Native qualification is supplied by the hosted run below; no local native pass is claimed.

Source and integration

Base: main at ccc4403.
Head: d73b54b.
Verified local/uploaded tree: c06b79c82faa51314a7de08a224bcea8c2ea85de.
All open Gancho PR file lists were inspected before editing. No existing PR addresses these board receive paths. #200 touches SyncLocalStore.swift only in separate blob-ownership hunks; this PR remains independently main-based. No combined native integration with other open repairs is claimed.

Limits

This temporary guard lasts only while the existing pending tombstone exists. It does not add permanent deletion history, promise cross-zone ordering after acknowledgement, or retroactively repair boards resurrected before this change.
No live user data, UI flow, schema, dependency, coverage threshold, workflow, credential, permission, merge, release or deployment changes.

CI follow-up

The first run, 37725090341 on 6eb9074, passed strict swift-format and portable/metadata gates, then failed SwiftLint on the new placeholder test's empty-string comparison. Package tests had not run. The forward-only follow-up uses isEmpty with the same assertion semantics; no behavior, gate, test coverage or threshold was weakened. The complete new-head native run below passed.

While this PR was being checked, #194 merged into main at 577036f. A fresh source merge with that main and a separate source merge with #200 at fc98aad are conflict-free. Those source-only checks did not qualify native integration. The completed CI below subsequently tested the merge with main at 577036f; combined native integration with #200 remains untested.

Exact-head native qualification

CI 37726317091 completed successfully for d73b54b. Checkout logs confirm synthetic merge 04ab326, combining this exact head with main at 577036f.

  • Strict swift-format, SwiftLint (606 files, zero violations), dependency locks, metadata/site and build-warning checks pass.
  • All four new BoardTombstoneTests pass natively; the complete package run passes 1,510 tests in 233 suites.
  • Production package-source coverage is 83.9%, above the unchanged 80% floor.
  • All four StoreKit entitlement/purchase tests and both macOS/iOS app builds pass.
  • GitGuardian passes; the non-blocking beta canary is skipped as expected.
  • Read-only peer review found no blocking issue. Current head is unchanged and the PR is mergeable against its target.

Scoped implementation and validation are complete. The PR remains a draft for human review. No separate full UI workflow was dispatched because no UI flow changed; no merge, release or deployment occurred.

johnny4young and others added 5 commits October 7, 2026 22:55
- When a fetched clip names a board with a pending local deletion, re-queue the clip so its next upload removes the stale board reference from the server copy.
- Document that skippedAsStale also counts records beaten by a pending deletion.
- Assert the re-queue in the fetched-page regression test and describe it in the board deletion sync notes.
- Add an Unreleased Fixed changelog entry.
@johnny4young
johnny4young marked this pull request as ready for review October 8, 2026 16:07
@johnny4young
johnny4young merged commit 77e72e8 into main Oct 8, 2026
4 checks passed
@johnny4young
johnny4young deleted the fix/pending-board-deletion-receive branch October 8, 2026 16:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants