Files
buzz/crates
npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67d 4dc65b6ee5 fix(workflow): wire community-scoped durable claim into scheduler
The scheduled-workflow fire path never called the durable claim
primitives (`claim_scheduled_workflow_fire` had zero callers outside the
DB wrappers and tests), so with N relay pods every pod that saw a due
cron/interval row created a run and executed the side effect — a
multi-pod duplicate-run bug. And the claim primitive itself was still
keyed by bare `workflow_id` (`WHERE w.id = $1`) despite the schema's
`(community_id, id)` workflow identity: with duplicate workflow UUIDs
across communities (which the schema explicitly allows and the Issue-4
confinement tests pin), a single `INSERT ... SELECT` matched every
community's row and fanned one claim across all of them.

Scope the claim to its community and wire the claim->run boundary into
the scheduler:

- `claim_scheduled_workflow_fire` takes `CommunityId` and binds
  `WHERE w.community_id = $1 AND w.id = $2`. The community is server
  provenance — the `workflow.community_id` from the global scan, never
  client input. This reverses the earlier S1 "resolve-from-id-alone"
  lock, which was written against a globally-unique-`workflow_id`
  assumption the final `(community_id, id)` schema does not hold (and
  which is unimplementable on that key). The surviving invariant is
  "the claim community is server provenance, not client-controlled."

- `WorkflowEngine::run` now claims before `create_workflow_run`; the
  loser skips before any run creation or side effect. The claim anchor
  `scheduled_for` is deterministic across pods: the cron's own scheduled
  instant (`cron_fire_instant`, not `now`) or the interval bucket
  boundary (`interval_fire_instant`, floor to the interval). The
  interval anchor is seeded from `latest_scheduled_workflow_fire` on the
  first tick after restart so a process bounce can't double-fire within
  an interval. `attach_scheduled_workflow_run` links the won claim to
  its run for ops/audit forensics.

Tests:
- Rewrite the stale S1 comment block and `claim_for_workflow_in_other_
  community_no_ops` (which encoded the now-false globally-unique
  assumption) into `claim_confined_to_its_community`: a dup workflow
  UUID in A and B claims independently (claiming A/id leaves B/id
  claimable). Proven RED on the bare-`id` regression.
- `concurrent_same_window_claims_exactly_one_wins` and the prune-anchor
  test updated to the scoped signature.
- New `cron_fire_instant` / `interval_fire_instant` unit tests pin the
  deterministic, drift-stable claim anchors.

Full `cargo test -p buzz-db -p buzz-workflow -p buzz-relay` green vs
live Postgres (serial); clippy clean on the trio.

Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
2026-06-27 14:42:30 -04:00
..