relay: workflow a-tag deletion leaves current kind:30620 event live — ghost workflows in Desktop list, silent resurrection on edit
- Dominant language
- Rust
- Stars
- 32.7k
- Forks
- 4.3k
- Avg merge
- 1d 13h
- Merged PRs (30d)
- 253
Description
## Summary
Deleting a workflow (kind:5 with `a`-tag address) removes the `workflows` control-plane row (log: "Workflow deleted via NIP-09 a-tag (UUID)") but can leave the **current kind:30620 definition event live** in the event log. The Desktop workflows list is built from `/query kinds:[30620]`, so it keeps showing "ghost" workflows that no longer exist.
Worse, the ghosts are interactive: editing/toggling a ghost entry submits a kind:30620 update for the deleted id, and `handle_workflow_def` treats an unknown id as a create — silently **resurrecting the deleted workflow**. Manual-trigger (46020) on a ghost returns "workflow not found", which is how users notice.
## Observed sequence (single-node relay, `ghcr.io/block/buzz:main`)
1. Create workflow W (definition event E1). Delete W → row deleted, **E1 tombstoned** ✅
2. Ghost entry still visible in a stale Desktop list → user edits it → kind:30620 update with W's d-tag → workflow re-created with **new definition event E2** ✅ (by design, but surprising from a ghost)
3. Delete W again → relay logs "Workflow deleted via NIP-09 a-tag (UUID)", row deleted, **but E2 stays live** (`events.deleted_at IS NULL`) ❌
4. `/query kinds:[30620]` returns E2 forever → Desktop lists a workflow that `get_workflow`/trigger reject as not found.
Step 1 vs step 3 suggests event tombstoning keys on specific event ids (`e` tags from the client's possibly-stale view) rather than the a-tag address: the second delete referenced the original event id, not E2.
## Suggested fix
On NIP-09 a-tag workflow deletion, tombstone **all** live kind:30620 events whose `d` tag matches the deleted workflow id (address-based, same transaction as the row delete). Optionally: reject kind:30620 updates for ids with a deletion tombstone instead of silently re-creating.
Related UX: the workflows list should refetch after delete mutations resolve; a stale list is what makes the resurrection path reachable.
Contributor guide
Assessment
This issue has not been assessed yet.