Skip to content

Stop deleted workflows from coming back after a stale edit#2489

Open
BradGroux wants to merge 1 commit into
block:mainfrom
BradGroux:agent/workflow-coordinate-tombstones
Open

Stop deleted workflows from coming back after a stale edit#2489
BradGroux wants to merge 1 commit into
block:mainfrom
BradGroux:agent/workflow-coordinate-tombstones

Conversation

@BradGroux

@BradGroux BradGroux commented Jul 23, 2026

Copy link
Copy Markdown

Fixes #2390.

What users saw

A deleted workflow could return after someone edited a stale copy. A later
delete removed the workflow row again, but the replacement definition could
remain queryable on the relay.

The failure sequence was:

  1. A client kept stale definition E1.
  2. Editing it published replacement E2 and recreated the workflow row.
  3. A later delete could still reference E1, so event-ID deletion could not reach
    E2.
  4. The workflow-specific coordinate deletion removed the row but left E2 live.
  5. Relay queries returned E2 even though workflow lookup and manual trigger
    rejected the missing row.

Why deletion missed the current definition

Canonical workflow deletion follows this path:

kind:5validate_standard_deletion_event
handle_standard_deletion_eventhandle_a_tag_deletion

For 30620:<owner>:<workflow UUID>, the workflow-specific branch deleted the
workflows control-plane row and returned before the generic coordinate
tombstone path ran. The current kind:30620 definition therefore survived.

What changed

Canonical workflow deletion now uses one database transaction that:

  • acquires the same coordinate-scoped PostgreSQL advisory lock used by
    kind:30620 replacement writes;
  • deletes the workflow row only for the exact community, workflow UUID, and
    owner;
  • tombstones all live kind:30620 definitions for the exact community,
    owner, and d tag;
  • commits the row deletion and event tombstones together.

The shared lock closes the writer/deleter race. A concurrent replacement either
finishes before deletion and is tombstoned, or begins after deletion commits.

The path is idempotent, repairs orphaned definitions when the row is already
gone, invalidates workflow caches after either side changes, reports row and
event deletion counts, and reuses the authoritative event-kind constants.

Safety and scope

The existing validator still proves that the signer owns the coordinate, or is
the owning human for an agent. The database mutation independently scopes both
the row and events by community and owner.

This PR intentionally:

  • handles the canonical UUID d tag used by current workflow writers;
  • does not add a permanent resurrection ban;
  • does not sweep legacy name aliases or migration coordinates;
  • does not change Desktop refetch behavior.

Those remain separate policy, migration, and UX decisions.

Regression proof

The database lifecycle regression covers create/delete, recreation, multiple
simultaneously-live matching definitions, wrong-owner no-op, orphan cleanup,
and repeated idempotent deletion.

The concurrency regression holds an uncommitted replacement under the shared
advisory lock, proves deletion waits, commits the writer, and proves deletion
removes both the row and replacement.

The signed relay end-to-end regression publishes E1, deletes and queries zero
definitions, publishes E2, deletes again, and again queries zero definitions.

Verification

Source tree c44ddf5b16156767169836b1256a59893465c0ba:

  • just test
  • just ci
  • both ignored PostgreSQL regressions above — 1 passed each
  • signed local-relay end-to-end regression — 1 passed
  • independent specification and standards reviews — no findings

One full integration attempt hit the unchanged
steer_folds_into_active_turn_without_cancelling timing test in buzz-agent.
The PR does not touch buzz-agent; the test then passed 10/10 in isolation, and
the subsequent full just test run passed.

GitHub rejected the first push because the repository-configured author email
is protected. The current head
a350356fe9cc0c13e12bba3ac86fd4a17a1fef51 is a metadata-only rewrite to
Brad's GitHub noreply identity. It has the exact tree above and an empty diff
from the fully verified commit. Both focused PostgreSQL regressions were rerun
successfully at the current head, and the required Co-authored-by and
Signed-off-by trailers remain unchanged.

The end-to-end proof used a local relay with PostgreSQL, Redis, and S3-compatible
storage. It validates the signed relay protocol path; it is not production
deployment evidence.

Co-authored-by: Brad Groux <bradgroux@hotmail.com>
Signed-off-by: Brad Groux <bradgroux@hotmail.com>
@BradGroux
BradGroux requested a review from a team as a code owner July 23, 2026 08:52
@BradGroux BradGroux changed the title fix(relay): tombstone workflow definitions atomically Stop deleted workflows from coming back after a stale edit Jul 23, 2026
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.

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

1 participant