Skip to content

Commit fc27575

Browse files
docs: align skills and architecture with ADR 41 dispatch
Update architecture, installation guides, and agent skills for synchronous dispatch and fullsend_ai_ref version pinning. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent e9ea61f commit fc27575

7 files changed

Lines changed: 123 additions & 91 deletions

File tree

docs/ADRs/0035-layered-content-resolution.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,10 @@ In both cases the main dirs (`agents/`, `skills/`, etc.) are not committed —
5858
they are populated at runtime from upstream.
5959

6060
**B. Runtime layering via reusable workflows.** Each reusable workflow adds a
61-
"Prepare workspace" step that sparse-checkouts upstream defaults from
62-
`fullsend-ai/fullsend@v0`, copies them into the main dirs (`agents/`, `skills/`,
61+
"Prepare workspace" step that checks out upstream defaults from
62+
`fullsend-ai/fullsend` at the ref supplied via `fullsend_ai_ref` (PR #1278
63+
replaced the earlier checkout at `@v0` with a checkout at a
64+
caller-controlled ref), copies them into the main dirs (`agents/`, `skills/`,
6365
etc.), then copies customizations on top so override files replace upstream
6466
defaults. The workflow inspects `install_mode` to resolve the correct
6567
customization base:
@@ -89,7 +91,7 @@ File categories after this change:
8991
scaffold creates the structure, orgs add real files.
9092
- **Upstream defaults** (~60 files): agents, skills, schemas, harness,
9193
policies, scripts, env — authoritative in `fullsend-ai/fullsend`, provided
92-
at runtime via sparse checkout of the release tag.
94+
at runtime via checkout of the release tag.
9395
- **Upstream infrastructure** (~5 files): composite actions,
9496
`setup-agent-env.sh` — referenced directly from upstream, never in `.fullsend`.
9597

@@ -114,7 +116,7 @@ File categories after this change:
114116
- Overrides are explicit and auditable: in `customized/` for per-org, in
115117
`.fullsend/customized/` for per-repo.
116118
- Requires a public upstream repo (`fullsend-ai/fullsend` is already public).
117-
- Runtime availability: sparse checkout of upstream defaults requires
119+
- Runtime availability: checkout of upstream defaults requires
118120
github.com to be reachable at workflow execution time. GitHub Actions already
119121
depends on github.com, so this adds no new availability boundary.
120122
- Migration for existing orgs: orgs that customized files in top-level dirs

docs/architecture.md

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,11 @@ Infrastructure platform choice and configuration are specified in the adopting o
3939

4040
- Forge abstraction: all forge operations go through the `forge.Client` interface, keeping the rest of the codebase forge-agnostic ([ADR 0005](ADRs/0005-forge-abstraction-layer.md)).
4141
- Installation model: ordered layer stack (install forward, uninstall reverse, analyze for status reporting) with idempotent operations. Current stack: config-repo → workflows → secrets → inference → dispatch → enrollment ([ADR 0006](ADRs/0006-ordered-layer-model.md)).
42-
- Cross-repo dispatch: enrolled repos call `.fullsend` via `workflow_call`; a dispatch workflow mints OIDC tokens exchanged at a central token mint (GCP Cloud Function) for scoped GitHub App installation tokens per agent role. App PEM secrets are stored in Secret Manager, not the config repo ([ADR 0008](ADRs/0008-workflow-dispatch-for-cross-repo-dispatch.md)).
42+
- Cross-repo dispatch: enrolled repos call `.fullsend` via `workflow_call`; dispatch routes events to upstream reusable workflows via synchronous `workflow_call` jobs. Each reusable workflow mints OIDC tokens exchanged at a central token mint (GCP Cloud Function) for scoped GitHub App installation tokens per agent role. App PEM secrets are stored in Secret Manager, not the config repo ([ADR 0008](ADRs/0008-workflow-dispatch-for-cross-repo-dispatch.md), [ADR 0041](ADRs/0041-synchronous-workflow-call-event-dispatch.md)).
4343
- Shim workflow security: `pull_request_target` prevents PR authors from modifying the shim workflow. No long-lived secrets flow through the shim — OIDC tokens are issued by the GitHub runtime and scoped to the workflow run ([ADR 0009](ADRs/0009-pull-request-target-in-shim-workflows.md)).
4444
- Repo maintenance: a workflow in `.fullsend` (`.github/workflows/repo-maintenance.yml`) reconciles enrollment shims in target repos when `config.yaml` changes or on manual dispatch. The CLI's `EnrollmentLayer.Install()` dispatches this workflow via `workflow_dispatch` and monitors it for completion, then reports any enrollment PRs created in target repos.
4545
- Installer scaffold: the `WorkflowsLayer` deploys content from an embedded scaffold (`internal/scaffold/`), keeping deployable files as real files under version control rather than Go string constants.
46-
- Reusable workflows: agent workflows in `.fullsend` are thin callers (~40-70 lines) that delegate infrastructure logic to upstream reusable workflows (`fullsend-ai/fullsend/.github/workflows/reusable-*.yml`) via `workflow_call`. Infrastructure patches ship once upstream and propagate to all orgs without re-install ([ADR 0031](ADRs/0031-reusable-workflows-for-action-installed-distribution.md)).
47-
- Event-driven stage dispatch: eliminate `workflow_dispatch` + `gh workflow run` fan-out from `dispatch.yml` in favor of synchronous `workflow_call` so the dispatched run stays linked to the caller ([ADR 0041](ADRs/0041-synchronous-workflow-call-event-dispatch.md)).
46+
- Reusable workflows: `dispatch.yml` in `.fullsend` calls upstream reusable workflows (`fullsend-ai/fullsend/.github/workflows/reusable-*.yml`) directly via `workflow_call` jobs (no per-stage thin callers). Infrastructure patches ship once upstream and propagate to all orgs without re-install ([ADR 0031](ADRs/0031-reusable-workflows-for-action-installed-distribution.md), [ADR 0041](ADRs/0041-synchronous-workflow-call-event-dispatch.md)).
4847

4948
**Open questions:**
5049

@@ -131,10 +130,6 @@ The mechanism that assigns work to agents and prevents conflicts. Responsible fo
131130

132131
The existing design principle is that [the repo is the coordinator](problems/agent-architecture.md#interaction-model-the-repo-as-coordinator) — branch protection, CODEOWNERS, status checks, and GitHub events provide coordination without a central orchestrator. The agent dispatch and coordination layer may be nothing more than the glue that connects GitHub webhooks to agent infrastructure. Or it may need to be more.
133132

134-
**Decided:**
135-
136-
- Event-driven stage dispatch runs synchronously via `workflow_call` to preserve run correlation in the GitHub Actions UI (see [ADR 0041](ADRs/0041-synchronous-workflow-call-event-dispatch.md)).
137-
138133
**Open questions:**
139134

140135
- Is GitHub's event system sufficient, or do we need additional coordination logic (e.g. to prevent two code agents from picking up the same issue)?
@@ -180,7 +175,6 @@ Observability is a cross-cutting concern that touches every other component. Eac
180175
**Decided:**
181176

182177
- JSONL reasoning trace exposure: raw JSONL conversation transcripts are extracted from sandboxes and stored with owner-scoped access. Credential scanning acts as an invariant check on [ADR 0017](ADRs/0017-credential-isolation-for-sandboxed-agents.md)'s isolation model. Agents handling data from protected sources beyond the target repo can opt in to JSONL suppression via configuration ([ADR 0021](ADRs/0021-jsonl-reasoning-trace-exposure.md)).
183-
- Event-driven stage dispatch remains traceable end-to-end in the GitHub Actions UI by using synchronous `workflow_call` dispatch (see [ADR 0041](ADRs/0041-synchronous-workflow-call-event-dispatch.md)).
184178

185179
**Open questions:**
186180

@@ -334,10 +328,10 @@ See [ADR 0003](ADRs/0003-org-config-repo-convention.md) for the config repo conv
334328
**Decided:**
335329

336330
- Layered content resolution: upstream defaults (agents, skills, schemas,
337-
harness, policies, scripts) are provided at runtime via reusable workflow
338-
sparse-checkout of `fullsend-ai/fullsend@v0`. The scaffold installs only
339-
org-specific files and a `customized/` directory for org overrides. Org files
340-
in `customized/` overwrite upstream defaults at runtime
331+
harness, policies, scripts) are provided at runtime via a full checkout of
332+
`fullsend-ai/fullsend` at the ref passed via `fullsend_ai_ref`. The scaffold
333+
installs only org-specific files and a `customized/` directory for org
334+
overrides. Org files in `customized/` overwrite upstream defaults at runtime
341335
([ADR 0035](ADRs/0035-layered-content-resolution.md)).
342336

343337
## Multi-org deployment model
@@ -518,14 +512,13 @@ GitHub event ──► SHIM WORKFLOW (fullsend.yml in enrolled repo)
518512
╔═══════════════════════════════════════════════════════════════╗
519513
║ DISPATCH WORKFLOW (.fullsend repo, dispatch.yml) ║
520514
║ ║
521-
║ Mints OIDC token → Cloud Function (token mint) → scoped ║
522-
║ GitHub App installation token per agent role. ║
523-
║ Dispatches per-role agent workflows (code.yml, triage.yml). ║
515+
║ Routes to stage; synchronous workflow_call to upstream ║
516+
║ reusable-{stage}.yml (or prioritize.yml in .fullsend). ║
524517
╚═══════════════════════════════════════════════════════════════╝
525518
526519
527520
╔═══════════════════════════════════════════════════════════════╗
528-
║ AGENT WORKFLOW (.fullsend repo, e.g. code.yml)
521+
║ AGENT WORKFLOW (fullsend-ai/fullsend reusable-*.yml) ║
529522
║ ║
530523
║ Validates source repo is enrolled in config.yaml. ║
531524
║ Uses scoped GitHub App tokens: ║
@@ -607,8 +600,8 @@ GitHub event ──► SHIM WORKFLOW (fullsend.yml in enrolled repo)
607600

608601
| Abstract layer | MVP technology | ADR |
609602
|---|---|---|
610-
| Dispatcher | Shim workflow (`fullsend.yml`) in enrolled repo → `workflow_call` to `.fullsend/dispatch.yml`OIDC mint → per-role agent workflows (thin callers → upstream reusable workflows) | [ADR 0008](ADRs/0008-workflow-dispatch-for-cross-repo-dispatch.md), [ADR 0031](ADRs/0031-reusable-workflows-for-action-installed-distribution.md) |
611-
| Agent runner | GitHub Actions job → `fullsend run` CLI (via `fullsend-ai/fullsend@v0` composite action) | |
603+
| Dispatcher | Shim workflow (`fullsend.yml`) in enrolled repo → `workflow_call` to `.fullsend/dispatch.yml`synchronous `workflow_call` to upstream reusable workflows at `fullsend_ai_ref` | [ADR 0008](ADRs/0008-workflow-dispatch-for-cross-repo-dispatch.md), [ADR 0031](ADRs/0031-reusable-workflows-for-action-installed-distribution.md), [ADR 0041](ADRs/0041-synchronous-workflow-call-event-dispatch.md) |
604+
| Agent runner | GitHub Actions job → `fullsend run` CLI (via `fullsend-ai/fullsend@<version>` composite action) | |
612605
| Harness store | YAML files in `.fullsend/harness/` (e.g. `code.yaml`, `triage.yaml`) | |
613606
| Sandbox | OpenShell with per-agent L7 network policies (endpoint + binary restrictions) | |
614607
| Agent runtime | Claude Code (`claude --agent --dangerously-skip-permissions`) | |

docs/guides/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,5 @@ Guides for developers working in repositories where fullsend is active.
3434
Guides for contributors developing and testing fullsend itself.
3535

3636
- [Local development](dev/local-dev.md) — Run fullsend agents locally on macOS and Linux (amd64 + arm64)
37+
- [Testing workflow changes](dev/testing-workflows.md) — Point a live GitHub org at a branch to test workflow, action, and agent changes before release
3738
- [CLI internals](dev/cli-internals.md) — Command structure, installation pipeline, and sandbox runtime
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Testing workflow changes
2+
3+
This guide explains how to test changes to Fullsend's GitHub Actions workflows.
4+
5+
## Per-repo mode
6+
7+
In your repository modify the dispatch job at `.github/workflows/fullsend.yaml` to
8+
use the ref you want to test. Change the reference `uses` and
9+
`fullsend_ai_ref` to the same value.
10+
11+
```yaml
12+
# .github/workflows/fullsend.yaml
13+
# [...]
14+
jobs:
15+
dispatch:
16+
# [...]
17+
uses: fullsend-ai/fullsend/.github/workflows/reusable-dispatch.yml@<YOUR_VERSION>
18+
with:
19+
# [...]
20+
fullsend_ai_ref: <YOUR_VERSION>
21+
# [...]
22+
```
23+
24+
Then push this change and trigger a Fullsend action: `/fs-triage`, `/fs-code`, ... When the ref is
25+
deleted from fullsend-ai/fullsend (branch deleted or commit amended), revert this back to the
26+
desired reference.
27+
28+
## Per-org mode
29+
30+
**WARNING**: this impacts all repositories, so proceed with care. You can install your test repository
31+
using the repository install mode to avoid this problem.
32+
33+
For org installs, the shim passes `fullsend_ai_ref` from the `FULLSEND_AI_REF` org variable (default
34+
`v0`). To test a branch across the org without editing every enrolled repo, set the org variable
35+
(for example via `gh variable set FULLSEND_AI_REF --org <ORG> --body <YOUR_VERSION>`) and ensure
36+
selected repos include your test repo.
37+
38+
Alternatively, edit `.fullsend/.github/workflows/dispatch.yml` and change both the `uses:` ref on
39+
each `reusable-<stage>.yml` call and the `fullsend_ai_ref` input passed to it:
40+
41+
```yaml
42+
# .fullsend/.github/workflows/dispatch.yml
43+
# [...]
44+
triage:
45+
uses: fullsend-ai/fullsend/.github/workflows/reusable-triage.yml@<YOUR_VERSION>
46+
with:
47+
# [...]
48+
fullsend_ai_ref: <YOUR_VERSION>
49+
# [...]
50+
```
51+
52+
Then push this change and trigger a Fullsend action on your test repository: `/fs-triage`, `/fs-code`, ...
53+
When the ref is deleted from fullsend-ai/fullsend (branch deleted or commit amended), revert this back
54+
to the desired reference.

docs/guides/getting-started/installation.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -352,10 +352,10 @@ Per-repo mode installs fullsend for a single repository without requiring an org
352352
> **Installing fullsend in the `fullsend-ai` org:** When installing fullsend for
353353
> `fullsend-ai/fullsend` itself, prefer **per-org mode** (`fullsend admin install fullsend-ai`).
354354
> Per-repo mode technically works but creates a circular reference: the per-repo
355-
> shim workflow calls `fullsend-ai/fullsend/.github/workflows/reusable-dispatch.yml@v0`,
356-
> which in turn calls reusable stage workflows in the same repo, which check out
357-
> `fullsend-ai/fullsend@v0` again for upstream defaults and use
358-
> `fullsend-ai/fullsend@v0` as the composite action. The repo ends up
355+
> shim workflow calls `fullsend-ai/fullsend/.github/workflows/reusable-dispatch.yml@<ref>`,
356+
> which resolves stage workflows via relative paths in the same ref, which in
357+
> turn check out `fullsend-ai/fullsend` at `fullsend_ai_ref` for upstream
358+
> defaults and load the composite action from that checkout. The repo ends up
359359
> simultaneously serving as the source of reusable workflows, the source of the
360360
> composite action, the caller repo, and the target repo being acted on. Per-org
361361
> mode avoids this by placing the shim in `fullsend-ai/fullsend` and the agent

internal/scaffold/fullsend-repo/skills/finding-agent-runs/SKILL.md

Lines changed: 24 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,15 @@ Given an issue or PR, find the fullsend agent workflow runs using `gh` CLI.
1313
## Setup
1414

1515
```bash
16-
ORG=$(echo "${REPO_FULL_NAME:-$(gh repo view --json owner -q .owner.login)}" | cut -d/ -f1)
17-
DISPATCH_REPO="${ORG}/.fullsend"
16+
SOURCE_REPO="${REPO_FULL_NAME:-$(gh repo view --json nameWithOwner -q .nameWithOwner)}"
17+
ORG=$(echo "${SOURCE_REPO}" | cut -d/ -f1)
18+
CONFIG_REPO="${ORG}/.fullsend"
1819
```
1920

20-
The shim workflow (`fullsend.yaml`) runs in the source repo on `main`. It
21-
dispatches to `${DISPATCH_REPO}` which runs the agent workflows
22-
(`triage.yml`, `code.yml`, `review.yml`, `retro.yml`).
21+
Per-org installs use synchronous `workflow_call`: the enrolled-repo shim
22+
(`fullsend.yaml`) calls `${CONFIG_REPO}` `dispatch.yml`, which runs agent
23+
stages as jobs in the **same** Actions run on `SOURCE_REPO`. There are no
24+
separate `dispatch.yml` runs in `.fullsend` to look up.
2325

2426
## Issue → Agent Runs
2527

@@ -28,15 +30,15 @@ dispatches to `${DISPATCH_REPO}` which runs the agent workflows
2830
Triage dispatches from `issue_comment` events (the `/fs-triage` command):
2931

3032
```bash
31-
gh run list --workflow=fullsend.yaml \
33+
gh run list --repo "${SOURCE_REPO}" --workflow=fullsend.yaml \
3234
--json databaseId,status,conclusion,event,createdAt \
3335
-q '.[] | select(.event == "issue_comment")'
3436
```
3537

36-
Match by timestamp against the `/fs-triage` comment (`gh issue view <N> --json comments`), then confirm `dispatch-triage` succeeded:
38+
Match by timestamp against the `/fs-triage` comment (`gh issue view <N> --json comments`), then confirm the **Triage** stage job succeeded:
3739

3840
```bash
39-
gh run view <RUN_ID> --json jobs \
41+
gh run view <RUN_ID> --repo "${SOURCE_REPO}" --json jobs \
4042
-q '.jobs[] | "\(.name) \(.status)/\(.conclusion)"'
4143
```
4244

@@ -45,47 +47,35 @@ gh run view <RUN_ID> --json jobs \
4547
Code dispatches from `issues` events when `ready-to-code` is applied:
4648

4749
```bash
48-
gh run list --workflow=fullsend.yaml \
50+
gh run list --repo "${SOURCE_REPO}" --workflow=fullsend.yaml \
4951
--json databaseId,status,conclusion,event,createdAt \
5052
-q '.[] | select(.event == "issues")'
5153
```
5254

53-
Confirm `dispatch-code completed/success` in the jobs list.
54-
55-
### Find the actual agent run
56-
57-
Match by timestamp in the dispatch repo (runs start within seconds):
58-
59-
```bash
60-
gh run list --repo "${DISPATCH_REPO}" --workflow=triage.yml --limit 5 \
61-
--json databaseId,status,conclusion,createdAt
62-
63-
gh run list --repo "${DISPATCH_REPO}" --workflow=code.yml --limit 5 \
64-
--json databaseId,status,conclusion,createdAt
65-
```
55+
Confirm the **Code** job completed successfully in that run's job list.
6656

6757
## PR → Agent Runs
6858

6959
### Code agent run
7060

7161
The PR branch follows `agent/{issue}-{slug}`. Extract the issue number and
72-
use the issue recipe above to find the code dispatch.
62+
use the issue recipe above to find the code dispatch run on `SOURCE_REPO`.
7363

7464
### Review dispatch
7565

7666
Review dispatches from `pull_request_target` events. Match by `headBranch`:
7767

7868
```bash
79-
gh run list --workflow=fullsend.yaml \
69+
gh run list --repo "${SOURCE_REPO}" --workflow=fullsend.yaml \
8070
--json databaseId,status,conclusion,event,headBranch,createdAt \
8171
-q '.[] | select(.event == "pull_request_target")'
8272
```
8373

84-
Confirm `dispatch-review completed/success`, then find the run:
74+
Confirm the **Review** job completed successfully:
8575

8676
```bash
87-
gh run list --repo "${DISPATCH_REPO}" --workflow=review.yml --limit 5 \
88-
--json databaseId,status,conclusion,createdAt
77+
gh run view <RUN_ID> --repo "${SOURCE_REPO}" --json jobs \
78+
-q '.jobs[] | "\(.name) \(.status)/\(.conclusion)"'
8979
```
9080

9181
### Retro dispatch
@@ -94,29 +84,26 @@ Retro dispatches from `pull_request_target` (on PR close) and from
9484
`issue_comment` events (the `/fs-retro` command):
9585

9686
```bash
97-
gh run list --workflow=fullsend.yaml \
87+
gh run list --repo "${SOURCE_REPO}" --workflow=fullsend.yaml \
9888
--json databaseId,status,conclusion,event,createdAt \
9989
-q '.[] | select(.event == "pull_request_target" or .event == "issue_comment")'
10090
```
10191

102-
Find the actual retro agent run:
103-
104-
```bash
105-
gh run list --repo "${DISPATCH_REPO}" --workflow=retro.yml --limit 5 \
106-
--json databaseId,status,conclusion,createdAt
107-
```
92+
Confirm the **Retro** job completed successfully in the same run.
10893

10994
## Reference
11095

11196
### Logs and artifacts
11297

98+
Use the shim run ID on `SOURCE_REPO` (not `${CONFIG_REPO}`):
99+
113100
```bash
114101
# Search logs for errors
115-
gh run view <RUN_ID> --repo "${DISPATCH_REPO}" --log 2>&1 \
102+
gh run view <RUN_ID> --repo "${SOURCE_REPO}" --log 2>&1 \
116103
| grep -i "error\|fail\|exit code"
117104

118-
# Download session artifact
119-
gh run download <RUN_ID> --repo "${DISPATCH_REPO}"
105+
# Download session artifact (uploaded by the stage job in this run)
106+
gh run download <RUN_ID> --repo "${SOURCE_REPO}"
120107
```
121108

122109
### Common failure signatures

0 commit comments

Comments
 (0)