Add dispatch trigger for workflow-to-workflow handoff#1
Conversation
Introduces a new `dispatch` trigger type so one running workflow can start another triggered workflow with a payload and a pre-populated `trigger-context/` directory — the data channel that polling triggers already use, now available to programmatic handoff. - `TriggerEvent.inline_context` is materialized into `trigger-context/` in `run_once` (path-traversal guarded). - `TriggerDef::Dispatch` + `triggers/dispatch.rs`: a per-workflow inbox registry; `DispatchTrigger` forwards inbox messages as trigger events. Queueing / one-run-at-a-time is reused from the existing trigger loop. - `DaemonCommand::Dispatch` and the `otter dispatch <wf> [--payload] [--context-file name=path] [--context-dir dir]` CLI. - Tests: dispatch-trigger unit test, run_once inline-context integration test, and a manager-level start->dispatch->run test. Docs in USAGE.md plus an examples/dispatch-handler package. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
whme
left a comment
There was a problem hiding this comment.
This is the data channel polling triggers already use, now available to programmatic handoff between workflows.
I think we should migrate the polling triggers to use the dispatch trigger if possible to showcase the new architecture with existing workflows.
There was a problem hiding this comment.
Pull request overview
Adds a new dispatch trigger to enable workflow-to-workflow (and CLI-to-workflow) handoff by sending a payload plus inline context files that are materialized into trigger-context/ before steps run.
Changes:
- Introduces
TriggerDef::Dispatchplus a newDispatchTrigger/registry for routing inbox messages into trigger events. - Extends
TriggerEventwithinline_contextand updatesEngine::run_once()to write these files intotrigger-context/with path-traversal protection. - Adds
otter dispatchCLI + daemon plumbing, plus documentation and an example workflow.
Reviewed changes
Copilot reviewed 16 out of 16 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| USAGE.md | Documents the new dispatch trigger and CLI usage. |
| examples/dispatch-handler/workflow.toml | Adds a minimal dispatch-triggered example workflow. |
| examples/dispatch-handler/README.md | Adds usage instructions for the dispatch example. |
| crates/otter-tui/src/panels/right_panel.rs | Displays dispatch as a trigger type in the TUI. |
| crates/otter-core/src/workflow_manager.rs | Adds dispatch registry wiring and a dispatch() entrypoint. |
| crates/otter-core/src/workflow_manager_tests.rs | Adds an integration-ish test ensuring dispatch runs write inline context. |
| crates/otter-core/src/types.rs | Adds TriggerDef::Dispatch, DaemonCommand::Dispatch, and TriggerEvent.inline_context. |
| crates/otter-core/src/triggers/polling.rs | Initializes inline_context to None for polling events. |
| crates/otter-core/src/triggers/oneshot.rs | Initializes inline_context to None for oneshot events. |
| crates/otter-core/src/triggers/mod.rs | Registers the new dispatch trigger module and build path. |
| crates/otter-core/src/triggers/manual.rs | Initializes inline_context to None for manual events. |
| crates/otter-core/src/triggers/dispatch.rs | Implements the dispatch inbox trigger + unit test. |
| crates/otter-core/src/engine.rs | Plumbs dispatch registry into trigger construction; materializes inline_context into trigger-context/. |
| crates/otter-core/src/engine_tests.rs | Adds coverage for inline context materialization + traversal skipping. |
| crates/otter-cli/src/main.rs | Adds otter dispatch command and context-file collection logic. |
| crates/otter-cli/src/daemon.rs | Handles DaemonCommand::Dispatch by calling WorkflowManager::dispatch(). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- dispatch.rs: recover from poisoned registry lock instead of panicking the engine task; surface poisoned inbox lock as a TriggerError. - workflow_manager: drop the stale registry entry and return the actionable "not accepting dispatches" error when the inbox is closed. - engine_tests: assert the traversal entry would land in the run dir, not the scratch root, so the guard regression is actually caught. - USAGE.md: document that context files must be valid UTF-8. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
@whme on migrating polling triggers onto the dispatch trigger: agreed it'd be a nice showcase, but I'd keep it out of this PR. The two aren't quite substitutable — polling owns the seen-hash dedup, interval loop, and |
What
Adds a new
dispatchtrigger type so one running workflow can start another triggered workflow with a payload and a pre-populatedtrigger-context/directory. This is the data channel polling triggers already use, now available to programmatic handoff between workflows.This is the otter-core half of the
@otter <command>Gerrit entrypoint rework; the marketplace half (theotter-dispatchworkflow that usesotter dispatchforkind = workflowcommands) lives in a companion PR incheckmk-claude-marketplace.Changes
TriggerEvent.inline_context—(filename, contents)pairs materialized intotrigger-context/inrun_once, beside the existing pollingpending_contextpath. Plain file names only (path-traversal guarded).TriggerDef::Dispatch+ newtriggers/dispatch.rs— a per-workflow inbox registry (DispatchRegistry);DispatchTriggerforwards inbox messages as trigger events. Queueing and one-run-at-a-time are reused from the existingrun_triggeredloop.DaemonCommand::Dispatch+ handler, threaded throughWorkflowManager::dispatch().otter dispatch <workflow> [--payload <str>] [--context-file <name=path>]… [--context-dir <dir>].USAGE.mdand anexamples/dispatch-handlerpackage.Notes
trigger.type = "dispatch". Dispatching to a stopped or non-dispatch workflow returns a clean error.TriggerEventandDaemonCommandadditions use serde defaults; existing trigger constructors setinline_context: None.Testing
triggers::dispatchunit test (inbox → trigger event with inline context).engineintegration test:run_oncewritesinline_contextintotrigger-context/, skips traversal entries.workflow_managerintegration test: start →dispatch→ a run executes with the handed-over context.otter-coresuite green except pre-existing git ssh-signing env failures inworkspace_pool/gittests (unrelated).cargo clippyclean.🤖 Generated with Claude Code