Repository navigation
ADR 0034: Centralized shim routing via dispatch.yml #730
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 3 commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
c4be98f
docs: add ADR 0033 for centralized shim routing via dispatch.yml
waynesun09 68a8675
docs: add per-repo installation consequence to ADR 0033
waynesun09 73922c8
docs: renumber ADR 0033 to 0034 (centralized shim routing)
waynesun09 96567a1
docs: update ADR 0027 → 0029 cross-reference in ADR 0034
waynesun09 7bff81a
docs: address review feedback on ADR 0034
waynesun09 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
156 changes: 156 additions & 0 deletions
156
docs/ADRs/0034-centralized-shim-routing-via-dispatch.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,156 @@ | ||
| --- | ||
| title: "34. Centralized shim routing via dispatch.yml" | ||
| status: Proposed | ||
| relates_to: | ||
| - agent-infrastructure | ||
| topics: | ||
| - shim-workflow | ||
| - dispatch | ||
| - routing | ||
| - workflow-call | ||
| --- | ||
|
|
||
| # 34. Centralized shim routing via dispatch.yml | ||
|
|
||
| Date: 2026-05-07 | ||
|
|
||
| ## Status | ||
|
|
||
| Proposed | ||
|
|
||
| ## Context | ||
|
|
||
| The target-repo shim workflow (`fullsend.yaml`) routes GitHub events to agent | ||
| stages by mapping events to `dispatch.yml` calls. Each stage requires its own | ||
| job in the shim: an `if:` filter, a `jq` payload builder, and a `gh workflow | ||
| run` dispatch step. The shim currently has 8 jobs and ~380 lines. | ||
|
|
||
| Every time a new stage is added, every enrolled repo's shim must be updated. | ||
| Adding the retro stage required 2 new jobs (~80 lines) in every target repo. | ||
| Command matching uses a 3-line pattern (exact match, space-delimited args, | ||
| newline-delimited body) repeated 6 times across the shim. | ||
|
|
||
| The routing logic — "which events map to which stages" — is conceptually | ||
| part of the centralized fullsend system, not target-repo configuration. Yet | ||
| it is duplicated in every enrolled repo and drifts when shims are not | ||
| updated after scaffold changes. | ||
|
|
||
| The token mint work | ||
| ([ADR 0027 PR #655](https://github.com/fullsend-ai/fullsend/pull/655)) | ||
| migrates the shim from `workflow_dispatch` + `gh workflow run` to native | ||
| `workflow_call`. This removes the dispatch credential (`FULLSEND_DISPATCH_TOKEN`) | ||
| and the imperative dispatch boilerplate, creating the natural opportunity to | ||
|
waynesun09 marked this conversation as resolved.
Resolved
|
||
| also move routing into `dispatch.yml`. | ||
|
|
||
| [ADR 0030](0030-reusable-workflows-for-action-installed-distribution.md) | ||
| addresses `.fullsend` agent workflow drift via reusable workflows but | ||
| explicitly leaves the shim unchanged. This ADR addresses the shim side. | ||
|
|
||
| ## Options | ||
|
|
||
| ### Option A: Per-stage jobs in the shim (status quo pattern) | ||
|
|
||
| Each stage gets its own job in the shim with an `if:` filter, payload builder, | ||
| and dispatch call. With `workflow_call` (token mint), the dispatch mechanism | ||
| changes but the structure stays the same: 8 jobs, ~250 lines, routing in the | ||
| shim. | ||
|
|
||
| New stages require updating every enrolled repo's shim. | ||
|
|
||
| ### Option B: Merge paired jobs | ||
|
|
||
| Combine jobs that dispatch the same stage (`dispatch-fix-bot` + | ||
| `dispatch-fix-human` → `dispatch-fix`, `dispatch-retro` + | ||
| `dispatch-retro-command` → `dispatch-retro`). Reduces to 6 jobs, ~300 lines. | ||
| Quick win, no contract changes. New stages still require shim updates. | ||
|
|
||
| ### Option C: Centralized routing in dispatch.yml | ||
|
|
||
| The shim becomes a single `dispatch` job that forwards the event context to | ||
| `dispatch.yml` via `workflow_call` without specifying a stage. `dispatch.yml` | ||
| examines `event_type` and the event payload to determine which stage to | ||
| trigger, then fans out to the matching agent workflow. | ||
|
|
||
| The shim shrinks to ~50 lines (one `dispatch` job + one `stop-fix` job). | ||
| `dispatch.yml` gains ~50 lines of routing logic. New stages require zero | ||
| changes to enrolled repos. | ||
|
|
||
| ## Decision | ||
|
|
||
| Use Option C. Move event-to-stage routing into `dispatch.yml`. The shim | ||
| forwards event context via `workflow_call` without determining the stage. | ||
|
|
||
| The shim has two jobs: | ||
|
|
||
| 1. **`dispatch`** — builds a universal minimal payload from safe GitHub context | ||
| fields and calls `.fullsend/dispatch.yml` via `workflow_call` with | ||
| `event_type`, `event_action`, `source_repo`, and `event_payload` inputs. | ||
|
waynesun09 marked this conversation as resolved.
Resolved
|
||
| Filters out bot comments via `if:` to avoid unnecessary invocations. | ||
|
|
||
|
waynesun09 marked this conversation as resolved.
Resolved
|
||
| 2. **`stop-fix`** — adds the `fullsend-no-fix` label and posts a comment. | ||
| This job acts directly on the target repo and does not dispatch to | ||
| `.fullsend`. It stays in the shim (~25 lines). | ||
|
|
||
| `dispatch.yml` gains a routing step that maps `event_type` + payload fields to | ||
| a stage name: | ||
|
|
||
| - `issue_comment` with `/triage`, `/code`, `/review`, `/fix`, `/retro` | ||
| commands → corresponding stage | ||
| - `issue_comment` on `needs-info` issue from non-bot → `triage` | ||
| - `issues` + `labeled` with `ready-to-code` or `ready-for-review` → `code` | ||
| or `review` | ||
| - `pull_request_target` opened/synchronize/ready\_for\_review → `review` | ||
|
waynesun09 marked this conversation as resolved.
Resolved
|
||
| - `pull_request_target` closed → `retro` | ||
| - `pull_request_review` with bot `changes_requested` → `fix` | ||
|
|
||
| If no stage matches, `dispatch.yml` exits early with no fan-out. The existing | ||
| kill switch, role enablement, and `# fullsend-stage:` marker scanning | ||
| ([ADR 0026](0026-stage-based-dispatch-for-agent-workflow-decoupling.md)) run | ||
| after stage determination, unchanged. | ||
|
|
||
| The `stage` input to `dispatch.yml` becomes optional. When provided | ||
| (backwards compatibility with old shims), it is used directly. When omitted, | ||
| `dispatch.yml` determines the stage from the event context. | ||
|
|
||
| ### Security properties preserved | ||
|
|
||
| - **`pull_request_target`** runs the base branch version of the shim, | ||
| preventing PRs from modifying it | ||
| ([ADR 0009](0009-pull-request-target-in-shim-workflows.md)). | ||
| - **Payload built from individual context fields** — not inline shell — to | ||
| prevent script injection from attacker-controlled fields. | ||
| - **Fork-PR blocking** for the fix stage moves to `fix.yml`, where it already | ||
| exists (redundant with the shim's current check). | ||
| - **Author association checks** for `/fix`, `/retro`, `/stop-fix` move to | ||
| `dispatch.yml`'s routing step. | ||
| - **`workflow_call` inputs stay under 10.** The current 5 inputs minus `stage` | ||
| plus `event_action` = 5 inputs. | ||
|
|
||
|
waynesun09 marked this conversation as resolved.
Resolved
|
||
| ## Consequences | ||
|
|
||
| - Adding a new stage (command or event trigger) requires only a `case` branch | ||
|
waynesun09 marked this conversation as resolved.
Resolved
|
||
| in `dispatch.yml` and a new agent workflow file. No enrolled repo changes. | ||
| - Enrolled repos gain a single concurrency group | ||
| (`fullsend-${{ github.event.pull_request.number || github.event.issue.number }}`). | ||
| In practice, only one agent should run per issue/PR at a time. | ||
| - Events that don't match any stage still trigger a `workflow_call` to | ||
| `dispatch.yml`, which exits early. Cost: one runner spin-up (~20s). The | ||
| `if:` filter on the dispatch job eliminates bot comments, the | ||
| highest-volume no-op. | ||
| - Old shims (per-stage jobs with explicit `stage` input) continue to work — | ||
| `dispatch.yml` supports both explicit and auto-determined stages. | ||
| - The `stop-fix` job remains in the shim because it acts on the target repo | ||
| directly (label + comment), not via `.fullsend` dispatch. | ||
| - This decision is sequenced after the token mint migration. The token mint | ||
| provides `workflow_call`; this ADR uses it to simplify routing. | ||
| - **Per-repo installation | ||
| ([ADR 0031](0031-per-repo-installation-mode.md))** needs the same routing | ||
| logic but published upstream as `reusable-fullsend.yml` in | ||
| `fullsend-ai/fullsend`. Per-repo repos have no `.fullsend/dispatch.yml` — | ||
| their thin `fullsend.yml` calls `reusable-fullsend.yml` directly, which | ||
| routes events to stages and fans out to per-stage reusable workflows. The | ||
| routing implementation should be shared: either `dispatch.yml` calls | ||
| `reusable-fullsend.yml` upstream (unifying both models), or both embed | ||
| the same routing script. Per-org shims could also adopt | ||
| `reusable-fullsend.yml` directly, eliminating `dispatch.yml` as a | ||
| routing layer entirely — see ADR 0031 Open Questions. | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.