Skip to content

fix(desktop): stop hiding repos re-announced after a deletion - #3782

Open
daxdax89 wants to merge 1 commit into
block:mainfrom
daxdax89:fix/projects-honor-deletion-created-at
Open

fix(desktop): stop hiding repos re-announced after a deletion#3782
daxdax89 wants to merge 1 commit into
block:mainfrom
daxdax89:fix/projects-honor-deletion-created-at

Conversation

@daxdax89

Copy link
Copy Markdown

Fixes #3760.

Problem

isDeletedByA hid a project card whenever any same-author kind:5 tombstone carried its 30617:<owner>:<dtag> coordinate, with no timestamp comparison:

event.pubkey.toLowerCase() === project.owner.toLowerCase() &&
event.tags.some((tag) => tag[0] === "a" && tag[1] === coordinate)

Repo announcements are addressable, so the coordinate outlives any single event at it. Deleting a repo and announcing the same dtag again is a legitimate recovery path, and NIP-09 scopes an a-tag deletion to versions "up to the created_at timestamp of the deletion request event" — a tombstone must not hide an announcement published after it.

The relay already gets this right, which is what made the failure confusing to diagnose: the newer announcement is stored, served over REQ, and clone/push works, while Desktop's Repositories list never renders the card again for anyone. Once an owner had ever deleted a project (including via Desktop's own Delete project button), no re-announcement at that coordinate could come back.

Fix

Bound the match to event.created_at >= project.createdAt, keeping the existing author check. The comparison is inclusive, matching NIP-09's "up to the created_at timestamp".

The predicate and projectCoordinate move from hooks.ts into lib/projectDeletions.ts. Two reasons: the predicate was unexported and therefore untestable, and hooks.ts was at 997 of the 1000 lines desktop/scripts/check-file-sizes.mjs allows, so the fix could not have been added in place. hooks.ts is now 986 lines and both call sites (fetchProjects, fetchProject) are unchanged apart from the import.

I kept the isDeletedByA name so it still matches the issue.

Tests

lib/projectDeletions.test.mjs — 8 cases covering the regression (tombstone older than the announcement no longer hides it), a newer tombstone still hiding it, and the inclusive same-timestamp boundary. The author check, case-insensitive pubkey match, and coordinate matching already worked and are pinned so a later refactor cannot quietly drop them.

Verified the suite catches the bug: with the created_at comparison removed, exactly one test fails — "a tombstone older than the announcement does not hide it".

Checks

just desktop-check, just desktop-typecheck, just desktop-test (3827 pass), and just desktop-build all pass locally.

Out of scope

The issue's second half is deliberately not addressed here, since it needs the managed-agent ownership relationship on the client and is a larger change:

  • isDeletedByA only honors deletions whose pubkey equals the announcement author, so a relay-accepted owner-of-agent deletion (is_agent_owner in validate_standard_deletion_event) still leaves the card rendered.
  • canDelete in ProjectsView.tsx gates on strict key equality via isProjectOwnedByCurrentUser, so a user cannot delete their own managed agent's repos from Desktop even though the relay would authorize it.

Happy to follow up on those separately if useful.

Related PRs

Searched open PRs and issues for duplicates; found none touching isDeletedByA or repo-deletion filtering.

`isDeletedByA` matched any same-author kind:5 tombstone carrying the
project's `30617:<owner>:<dtag>` coordinate, with no timestamp comparison.
Repo announcements are addressable, so the coordinate outlives any single
event at it: deleting a repo and announcing the same dtag again is a
legitimate recovery path. NIP-09 scopes an `a`-tag deletion to versions
"up to the created_at timestamp of the deletion request event", so a
tombstone must not hide an announcement published after it.

The relay already gets this right, which made the failure confusing: the
newer announcement was stored, served over REQ, and clone/push worked,
while Desktop's Repositories list never rendered the card again for anyone.

Bound the match to `event.created_at >= project.createdAt` and move the
predicate (with `projectCoordinate`) into `lib/projectDeletions.ts` so it
is unit-testable, which also keeps hooks.ts under the 1000-line guard.

Fixes block#3760

Signed-off-by: DaX <daxdax89@gmail.com>
@daxdax89
daxdax89 requested a review from a team as a code owner July 30, 2026 18:07
@Chessing234

Copy link
Copy Markdown
Contributor

good catch on re-announced repos. can you add a test where created_at equals the deletion time so the boundary is pinned?

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.

Desktop: Repositories list hides re-announced repos forever — deletion filter ignores created_at

2 participants