fix(relay): retire the kind:30620 event on workflow delete (#2879) - #3392
fix(relay): retire the kind:30620 event on workflow delete (#2879)#3392bonpiedlaroute wants to merge 1 commit into
Conversation
Signed-off-by: bonpiedlaroute <noel.tchidjo@hotmail.com>
|
Flagging an overlap I should have caught before opening this: #2489 (open since #2489 is the broader fix: one transaction under the kind:30620 coordinate This PR is the minimal version: reuse the existing Maintainers should take whichever fits the review budget — I'm happy to close |
|
Closing in favour of #4234. Agreeing with @aweiker's review: the Also worth recording that this root cause now has #2489, #3650, #4318 and #4882 One artefact #4234 doesn't carry, in case it's useful: a relay-level e2e test. Happy to port it onto #4234 if @Wirenut33 wants it — #2489 and #4882 also carry |
wesbillman
left a comment
There was a problem hiding this comment.
Carl, an automated reviewer, commenting via Wes’s GitHub account.
This feature is valid, but this PR is not safe to resurrect or merge for release.
Blockers
-
Workflow deletion remains a non-atomic, non-retryable post-storage side effect. The kind-5 request is committed first; projection deletion and the new kind-30620 soft-delete then run as separate writes. Any transient failure is only logged while ingest still returns success. Replaying the same signed request takes the duplicate fast path before side effects, so the missing lifecycle write is never retried. This can still strand query state and executable state on opposite sides of the deletion. The event tombstone and workflow projection must be committed as one revision-fenced transaction, or the command needs durable retry/reconciliation.
-
The path is incomplete for the already-authorized managed-agent owner case.
validate_standard_deletion_eventpermits a human owner to delete their agent’s kind-30620 coordinate, buthandle_a_tag_deletionpasses the effective human actor todelete_workflow_for_ownerrather than the coordinate owner from theatag. The projection delete therefore fails after the request has already been accepted/stored. Use the validated coordinate owner and cover this case end to end. -
The branch predates timestamp-fenced coordinate deletion. Current
mainrequiresdeletion_created_at_secsinsoft_delete_by_coordinate; this helper calls the old signature. Resolving the merge conflict mechanically would either fail to compile or omit the NIP-09 guarantee that a stale/replayed tombstone cannot erase a newer replacement. Pass the deletion event timestamp through and test old-delete/new-replacement ordering.
The new relay E2E is a useful regression test for the basic UUID happy path, but it does not cover atomic failure/replay recovery, stale deletion ordering, or human-owner deletion of an agent-authored workflow. The safer route is to supersede this closed branch with the atomic, revision-fenced implementation being developed in the newer workflow-deletion work, then retain this E2E scenario there.
wesbillman
left a comment
There was a problem hiding this comment.
Correction: this review was submitted before one delegated review lane returned. It should be treated as procedurally withdrawn pending the consolidated review. GitHub did not reflect the dismissal attempt on this closed PR.
wesbillman
left a comment
There was a problem hiding this comment.
Carl, an automated reviewer, commenting via Wes’s GitHub account.
This feature is valid, but this PR is not safe to resurrect or merge for release.
Blockers
-
Workflow deletion remains a non-atomic, non-retryable post-storage side effect. The kind-5 request is committed first; projection deletion and the new kind-30620 soft-delete then run as separate writes. Any transient failure is only logged while ingest still returns success. Replaying the same signed request takes the duplicate fast path before side effects, so the missing lifecycle write is never retried. This can still strand query state and executable state on opposite sides of the deletion. The event tombstone and workflow projection must be committed as one revision-fenced transaction, or the command needs durable retry/reconciliation.
-
The path is incomplete for the already-authorized managed-agent owner case.
validate_standard_deletion_eventpermits a human owner to delete their agent’s kind-30620 coordinate, buthandle_a_tag_deletionpasses the effective human actor todelete_workflow_for_ownerrather than the coordinate owner from theatag. The projection delete therefore fails after the request has already been accepted/stored. Use the validated coordinate owner and cover this case end to end. -
The branch predates timestamp-fenced coordinate deletion. Current
mainrequiresdeletion_created_at_secsinsoft_delete_by_coordinate; this helper calls the old signature. Resolving the merge conflict mechanically would either fail to compile or omit the NIP-09 guarantee that a stale/replayed tombstone cannot erase a newer replacement. Pass the deletion event timestamp through and test old-delete/new-replacement ordering.
The new relay E2E is a useful regression test for the basic UUID happy path, but it does not cover atomic failure/replay recovery, stale deletion ordering, or human-owner deletion of an agent-authored workflow. The safer route is to supersede this closed branch with the atomic, revision-fenced implementation being developed in the newer workflow-deletion work, then retain this E2E scenario there.
Fixes #2879.
Summary
buzz workflows deleteremoved the workflow row and invalidated the enginecache, but left the kind:30620 definition event live.
workflows listandworkflows getboth read that event, so every inspection surface keptreporting a workflow that
workflows triggerrejected withinvalid: workflow not found. The only way to discover the deletion hadlanded was to trigger it.
Cause
handle_a_tag_deletion'sKIND_WORKFLOW_DEFarm returns afterdelete_workflow_for_owner+invalidate_channel_workflows, so the genericNIP-33 arm below it — the only caller of
soft_delete_by_coordinate— is neverreached for kind:30620. Every other addressable kind, kind:30023 included, is
soft-deleted so REQs stop returning it.
Change
Extract the soft-delete block into
soft_delete_addressable_coordinateandcall it from both arms. No new logic: the behaviour already existed, it was
just unreachable for kind:30620.
The comment above the generic arm stated the current behaviour was deliberate
("doesn't soft-delete the
eventsrow by design — that's a separate concern").It is rewritten, since it would otherwise assert the opposite of what the code
does. My reading is that the concern it deferred is workflow row deletion
semantics, not the visibility of the definition event. Worth noting
soft_delete_by_coordinateis anUPDATE … SET deleted_at = NOW()(
crates/buzz-db/src/event.rs:771) — the row is retained, only its REQvisibility changes, so no definition history is lost.
If the intent really was for the kind:30620 event to outlive the workflow, the
fix belongs in
cmd_list_workflows/cmd_get_workflowinstead, filtering outdefinitions with no live workflow. Happy to switch to that shape.
Authorization
No new surface. Ownership of the a-tag coordinate is already enforced by
validate_standard_deletion_event(
crates/buzz-relay/src/handlers/side_effects.rs:234-253), which runs fromingest.rs:1967before the event is stored and before any side effect —it requires
target_pubkey == actor || is_agent_owner(...), otherwisemust be event author. The check is identical for both arms.Testing
New e2e test
test_workflow_delete_retires_definition_event, mirroringtest_long_form_a_tag_deletionwhich covers this path for kind:30023.Verified it is a real regression guard:
FAILED — workflow deletion should retire the definition event (got 1 events)ok