Skip to content

workflows list shows deleted workflows indefinitely (a-tag delete doesn't soft-delete the kind:30620 events row) #5077

Description

@larreala

Summary

workflows delete (NIP-09 a-tag deletion targeting a kind:30620 workflow definition) genuinely deletes the workflow's row from the operational workflows table the scheduler reads, but workflows list queries the raw kind:30620 Nostr events directly with no deletion-state filtering — so a successfully deleted workflow keeps appearing in list forever ("zombie" listing). No scheduling risk (the scheduler can't see a row that's gone), but it's a confusing/incorrect list result after a confirmed-accepted delete.

Root cause

  • buzz workflows delete publishes a NIP-09 kind:5 deletion event with an a-tag coordinate 30620:<pubkey>:<workflow_id> (crates/buzz-cli/src/commands/workflows.rs:139-144buzz_sdk::build_workflow_deletebuild_delete_addressable, crates/buzz-sdk/src/builders.rs:1503).
  • The relay ingests it in handle_a_tag_deletion (crates/buzz-relay/src/handlers/side_effects.rs:2104-2117), which special-cases KIND_WORKFLOW_DEF (30620) to call delete_workflow_for_owner — a hard DELETE FROM workflows WHERE ... (crates/buzz-db/src/workflow.rs:768-788) against the operational workflows table.
  • buzz workflows list (crates/buzz-cli/src/commands/workflows.rs:13-19, cmd_list_workflows) does not query that table. It sends a raw REQ for kinds:[30620], #h:[channel_id] directly against the event store, with no deletion-state filtering.
  • The relay's own comment at side_effects.rs:2151-2158 explains the split is deliberate: workflow a-tag deletion is called out as not soft-deleting the underlying events row "by design", unlike every other addressable/parameterized-replaceable kind, which does soft-delete the live (kind, pubkey, d_tag) row specifically "so REQs stop returning it."

Impact

  • No scheduling risk: list_enabled_channel_workflows / list_all_enabled_workflows (crates/buzz-db/src/workflow.rs:425-457, the latter doc-commented as used by the cron scheduler) filter against the same workflows table the delete removes the row from, so a deleted workflow cannot fire.
  • User-facing confusion: workflows delete reports accepted: true (correctly — the deletion event is valid and its effect on the scheduler is real), but workflows list shows the definition indefinitely afterward, with no way to distinguish "actually still active" from "deleted but zombie-listed" via the CLI.

Suggested fix

Bring workflow a-tag deletion in line with the general NIP-33 pattern: also soft-delete the kind:30620 events row (matching (kind, pubkey, d_tag)) when delete_workflow_for_owner succeeds, the same way every other addressable kind's deletion path already does. Alternatively, have cmd_list_workflows cross-reference the workflows table (or an equivalent still-active check) before including an entry.

Repro

  1. Create a workflow, confirm it in buzz workflows list.
  2. buzz workflows delete <id> — returns accepted: true.
  3. buzz workflows list — the deleted workflow is still present.

Happy to provide more detail if useful; found this while investigating a "zombie workflow" report in our own deployment (larreala/AgenticOS#67).

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