Skip to content

relay: workflow a-tag deletion leaves current kind:30620 event live — ghost workflows in Desktop list, silent resurrection on edit #2390

Description

@leesalminen

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions