Skip to content

feat(acp): isolated workflow-delivery verifier (node E) - #6824

Draft
loganj wants to merge 17 commits into
darryl/delivery-protocol-vocabularyfrom
wakko/workflow-acp-verifier
Draft

feat(acp): isolated workflow-delivery verifier (node E)#6824
loganj wants to merge 17 commits into
darryl/delivery-protocol-vocabularyfrom
wakko/workflow-acp-verifier

Conversation

@loganj

@loganj loganj commented Aug 25, 2026

Copy link
Copy Markdown
Collaborator

🤖

Summary

Details

Why. The preserved #2737 verifier flattened every failure to None, so a transient fetch failure was indistinguishable from a forged binding, and the recorded run cause was never re-verified. This node fixes both while preserving the fail-closed behavior of every existing check.

Typed outcomes. verify_workflow_delivery returns VerifyError::Mismatch(MismatchKind) (permanent — binding disagreement or forgery; never retried) vs VerifyError::Unavailable(UnavailableKind) (transient — required signed authority not supplied; retriable). Neither variant may become dispatch. The verifier performs no I/O: callers fetch the signed definition/message/cause events and pass them in (FetchedAuthority), so absence is typed as Unavailable, never conflated with mismatch.

Canonical vocabulary (from B).

  • Wake authentication uses the identifier-only WorkflowDeliveryWake strict parse (kind 24620, relay-authored, signature-verified, exact target). Wakes are hints: they name a delivery and nothing else; every binding field is verified against signed authority, not wake tags.
  • Target admission uses message_v1_targets: only the canonical four-field message-v1 marker p-tag admits a durable delivery recipient. Ordinary mentions never do; malformed marker tags fail closed.
  • The recorded WorkflowDeliveryCause is re-verified. An Event cause requires the exact signed event (id + signature + channel binding → Mismatch on disagreement, Unavailable when not supplied). Schedule/Webhook identities are opaque typed server-side authority and cannot be conflated with an absent signed cause.

Preserved behavior. Definition identity (kind 30620, d workflow UUID, h channel), message identity (kind 9, channel, relay authorship, workflow-definition/run/step tags), step lookup + send_message action + channel-field consistency, trigger-context revision binding, and exact template re-render equality all match the preserved #2737 source; so do workflow_delivery_principal / is_workflow_delivery_candidate fail-closed admission.

Tests. 10 pure tests including mutation vectors: every binding field (workflow, run, step, channel, target, definition event, message event, cause, rendered content) is flipped one at a time and must produce a permanent Mismatch; every dropped input must produce a transient Unavailable; a control fixture verifies. Cause verification requires independent authority for every cause class — the exact signed event for Event, the durable scheduled_workflow_fires claim binding (community, workflow, slot, linked run) for Schedule, and the durable invocation record binding for Webhook (the record itself is owned by a later node; modeled as the required typed authority input). The claimed delivery row is never its own proof: absent authority is Unavailable(Cause); disagreeing, unbound, or wrong-class authority is Mismatch(Cause), covered by one-field row mutations (different community, same-slot different workflow, different slot/invocation, different linked run, unattached run) for both durable classes. Validated with cargo fmt --check, cargo clippy -p buzz-acp --all-targets -- -D warnings, and cargo test -p buzz-acp (811 unit + 9 integration passing) at this exact head.

Buzz channel: buzz-workflow-owner-trigger-a-foundation (11ae68c1-38e7-4591-958e-e4da94d758f0)

loganj and others added 16 commits August 24, 2026 19:16
Co-authored-by: Larry <8cf5a83f590ec0955b11647d1c88f796a98e088c30a492c58e0e46c3026ae7a4@buzz.block.builderlab.xyz>
Signed-off-by: loganj <loganj@squareup.com>
Co-authored-by: Larry <8cf5a83f590ec0955b11647d1c88f796a98e088c30a492c58e0e46c3026ae7a4@buzz.block.builderlab.xyz>
Signed-off-by: Logan Johnson <loganj@squareup.com>
Retain exact-revision manual triggering and its transaction boundary while deferring automatic trigger and approval lifecycle propagation.

Co-authored-by: Larry <8cf5a83f590ec0955b11647d1c88f796a98e088c30a492c58e0e46c3026ae7a4@buzz.block.builderlab.xyz>
Signed-off-by: Logan Johnson <loganj@squareup.com>
Checkpoint run-level revision persistence before wiring every execution and resume path.

Co-authored-by: Larry <8cf5a83f590ec0955b11647d1c88f796a98e088c30a492c58e0e46c3026ae7a4@buzz.block.builderlab.xyz>

Signed-off-by: loganj <loganj@squareup.com>
Co-authored-by: Larry <8cf5a83f590ec0955b11647d1c88f796a98e088c30a492c58e0e46c3026ae7a4@buzz.block.builderlab.xyz>
Signed-off-by: Logan Johnson <loganj@squareup.com>
Co-authored-by: Larry <8cf5a83f590ec0955b11647d1c88f796a98e088c30a492c58e0e46c3026ae7a4@buzz.block.builderlab.xyz>
Signed-off-by: Logan Johnson <loganj@squareup.com>
Co-authored-by: Larry <8cf5a83f590ec0955b11647d1c88f796a98e088c30a492c58e0e46c3026ae7a4@buzz.block.builderlab.xyz>
Signed-off-by: Logan Johnson <loganj@squareup.com>
The SEC-006 event-path tests seeded rows through the legacy
create_workflow helper, which leaves definition_event_id NULL. Run
creation now fails closed on NULL-revision rows, so the positive arms
(owner-authorized workflows must fire) broke. Seed through
upsert_workflow — the production insert path — so fixtures carry a
bound signed revision like every real workflow row.

Co-authored-by: Wakko <0999168a3b1a4dabdf398cf43ba2893bb29b74dc06d0b31c2d3b51dbebea1612@buzz.block.builderlab.xyz>
Signed-off-by: Logan Johnson <loganj@squareup.com>
WorkflowRunRecord grew a definition_event_id field for revision-bound
execution; the five pure field-shape unit tests in buzz-db still built
the struct without it, breaking every test target in the crate
(cargo check --workspace passes because it skips test cfg). Add the
field so --all-targets compiles again.

Co-authored-by: Wakko <0999168a3b1a4dabdf398cf43ba2893bb29b74dc06d0b31c2d3b51dbebea1612@buzz.block.builderlab.xyz>
Signed-off-by: Logan Johnson <loganj@squareup.com>
Historical runs execute the exact signed revision persisted at run
creation; NIP-33 replacement soft-deletes the superseded kind-30620
event, so load_run_definition must read through soft deletion. Pin
that invariant: the test fails against get_event_by_id and passes
against get_event_by_id_including_deleted (mutation-verified on the
validation workstation).

Co-authored-by: Wakko <0999168a3b1a4dabdf398cf43ba2893bb29b74dc06d0b31c2d3b51dbebea1612@buzz.block.builderlab.xyz>
Signed-off-by: Logan Johnson <loganj@squareup.com>
…endpoint

The current_workflow_revision helper in desktop trigger_workflow was
referenced only from #[cfg(test)] workflows_tests.rs after the exact
signed-revision binding moved server-side behind the authenticated
/workflows/{id}/revision endpoint, leaving a dead-code lint risk in
non-test desktop builds. Remove the helper and its unit test.

Co-authored-by: Wakko <0999168a3b1a4dabdf398cf43ba2893bb29b74dc06d0b31c2d3b51dbebea1612@buzz.block.builderlab.xyz>
Signed-off-by: Logan Johnson <loganj@squareup.com>
…trigger-foundation

* origin/main:
  Extract community persistence (#6668)
  Fix mobile Huddle agent voice turn states (#6611)
  Add inline profile camera capture (#6680)
  Hide Huddles in mobile agent DMs (#6676)
  fix(desktop): polish inline chip states (#6718)
  Centralize replaceable event persistence (#6660)
  feat(workflows): discover trigger filter values (#6712)
  feat(desktop): simplify the message action rail (#6529)

Signed-off-by: Logan Johnson <loganj@squareup.com>

# Conflicts:
#	crates/buzz-relay/src/handlers/command_executor.rs
Keep revision revalidation on the command transaction and carry the immutable authorized workflow owner across the transaction boundary. Add one-connection and concurrent PostgreSQL regressions.

Signed-off-by: Logan Johnson <loganj@squareup.com>

Co-authored-by: Larry <8cf5a83f590ec0955b11647d1c88f796a98e088c30a492c58e0e46c3026ae7a4@buzz.block.builderlab.xyz>
…trigger-foundation

* origin/main:
  Add database pressure observability (#6700)
  revert fixed mention highlight (#6716)
  highlight search terms in results and messages (#6702)
  fix(desktop): make lightbox zoom controls interactive (#6710)
  Support community deletion in versioned media buckets (#6738)
  Fix TipTap editor mount race (#6779)
  feat(buzz-agent): gate LLM tool calls on session/request_permission (#5712)
  Add staging dev relay image workflow (#6709)

Signed-off-by: Logan Johnson <loganj@squareup.com>
Co-authored-by: Brother Darryl <146fb160a3266e6165bfa385f6048c975eda9e21cf65da097a0b5ea7952532a5@buzz.block.builderlab.xyz>
Signed-off-by: Logan Johnson <loganj@squareup.com>
Co-authored-by: Brother Darryl <146fb160a3266e6165bfa385f6048c975eda9e21cf65da097a0b5ea7952532a5@buzz.block.builderlab.xyz>

Signed-off-by: Logan Johnson <loganj@squareup.com>
@loganj
loganj force-pushed the wakko/workflow-acp-verifier branch from 27ff389 to d4f5cda Compare August 25, 2026 20:54
Extract the pure workflow-delivery verification behavior preserved in
PR #2737 into a dedicated, isolated module (node E of the workflow
replacement tree) and centralize exact definition/message/owner/
channel/step/cause verification behind a typed result, built on the
canonical delivery protocol vocabulary (node B,
buzz_core::workflow_delivery).

- crates/buzz-acp/src/verifier.rs: pure module, no I/O and no
  production caller. Callers supply already-fetched signed events.
- Wake authentication adopts the canonical identifier-only
  WorkflowDeliveryWake (strict kind-24620 parse, relay authorship,
  signature, exact target). Wakes are hints: they name a delivery,
  never carry binding authority; every binding field is verified
  against signed authority instead of wake tags.
- Target admission uses canonical message_v1_targets: only the
  four-field marker p-tag admits a durable delivery recipient;
  ordinary mentions never do, and malformed marker tags fail closed.
- verify_workflow_delivery types failures: VerifyError::Mismatch
  (permanent - binding disagreement or forgery) vs
  VerifyError::Unavailable (transient - authority not supplied).
  Neither becomes dispatch.
- The recorded canonical WorkflowDeliveryCause is re-verified against
  independent caller-fetched authority (CauseAuthority) for every
  cause class, and durable authority must carry the source row's full
  binding identity, never just the scalar the delivery re-presents:
  an Event cause requires the exact signed event (id, signature,
  channel binding); a Schedule cause requires the durable
  scheduled_workflow_fires claim binding (community, workflow, slot,
  linked run - two workflows can fire the same second); a Webhook
  cause requires the durable invocation record binding (community,
  workflow, invocation id, linked run; the durable record itself is
  owned by a later node and modeled here as the required authority
  input). The delivery snapshot carries the server-resolved
  CommunityId for these comparisons. Absent authority is Unavailable;
  disagreeing, unbound, or wrong-class authority is Mismatch. The
  claimed delivery row is never its own proof.
- Mutation tests flip every binding field (workflow, run, step,
  channel, target, definition event, message event, cause, rendered
  content) one at a time and assert permanent mismatch. Durable-cause
  coverage includes the exact matching row control plus one-field row
  mutations (different community, same-slot different workflow,
  different slot/invocation, different linked run, unattached run)
  for both Schedule and Webhook; dropped authority for every cause
  class asserts transient unavailability; wrong-class authority in
  all directions asserts mismatch.

Dormant by contract: no polling, claims, dispatch, producer, or
production reachability. PRs #2737 and #6697 are unchanged.

Signed-off-by: Logan Johnson <loganj@squareup.com>
Co-authored-by: Wakko <0999168a3b1a4dabdf398cf43ba2893bb29b74dc06d0b31c2d3b51dbebea1612@buzz.block.builderlab.xyz>
@loganj
loganj force-pushed the wakko/workflow-acp-verifier branch from d4f5cda to c8b89d3 Compare August 25, 2026 21:23
@loganj
loganj force-pushed the darryl/delivery-protocol-vocabulary branch from 46aae72 to 092f9fb Compare August 26, 2026 02:49
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.

1 participant