Skip to content

Commit d688265

Browse files
authored
[codex] fix agent-team manual workflow_dispatch inputs (#69)
* fix(agent-team): fail loud on missing dispatch inputs * fix(agent-team): harden optional implementer pr_number
1 parent 7f41566 commit d688265

5 files changed

Lines changed: 69 additions & 37 deletions

File tree

catalog/agent-team/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,13 +104,14 @@ Then apply the OAuth token tweak to each `.lock.yml` per [`skills/install-workfl
104104
1. Open an issue describing what you want built.
105105
2. Add the single label `agent-team`.
106106
3. Watch the thread. Each role posts its contribution as a comment; the implementer opens a draft PR that closes the issue when merged.
107-
4. Human override at any time: add `state:blocked` to halt, edit a comment to steer the next agent, or manually `gh workflow run` a specific role to retry a stuck stage.
107+
4. Human override at any time: add `state:blocked` to halt, edit a comment to steer the next agent, or manually `gh workflow run` a specific role to retry a stuck stage. Manual dispatches must pass the required `workflow_dispatch` inputs, and the downstream workflow markdown must read them via `${{ github.event.inputs.* }}`.
108108
5. **Retrying a blocked task**: clear `state:blocked`, then re-add `agent-team`. Spec-agent treats it as a fresh dispatch (because the state:* labels are gone and the spec markers are already satisfied — actually: to redo from scratch, also delete the prior spec comment).
109109

110110
## Limits and gotchas
111111

112112
- **Concurrency**: each workflow uses `concurrency: group: agent-team-issue-${issue_number}` so only one role runs at a time per issue.
113113
- **Max iterations**: default 3 (reviewer kickback → implementer). The counter lives on the `iteration` input passed through the dispatch chain, bumped exclusively by the reviewer on kickback.
114+
- **Input propagation**: planner / implementer / reviewer must fail loudly if required `workflow_dispatch` inputs are missing. Do not rely on label search or recent-activity inference as a fallback.
114115
- **Non-UI only**: no screenshot capture. Reviewer validates via tests/CI status + reading the diff.
115116
- **Cost**: a single task can easily spend 4× the tokens of a monolithic workflow. Set `timeout-minutes` conservatively and monitor the first few runs.
116117
- **No auto-merge**: the reviewer approves but never merges. Humans merge.

catalog/agent-team/implementer-agent.md

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -85,31 +85,40 @@ safe-outputs:
8585

8686
You are the **implementer** in a four-role agent-team pipeline. The planner (or the reviewer, on kickback) just dispatched you. Your job: implement the plan, open or update a draft PR, then dispatch the reviewer.
8787

88-
Inputs:
89-
- `inputs.issue_number` — the issue you're implementing against.
90-
- `inputs.iteration` — attempt number.
91-
- `inputs.pr_number` — if non-empty, you're being re-invoked after a reviewer kickback and should **push updates to the existing PR branch**, not open a new PR.
88+
Dispatch inputs:
89+
- `issue_number`: `${{ github.event.inputs.issue_number }}`
90+
- `iteration`: `${{ github.event.inputs.iteration }}`
91+
- `pr_number` (optional): `${{ github.event.inputs.pr_number }}`
92+
- If this is blank or still the literal `${{ github.event.inputs.pr_number }}`, treat it as not set.
93+
94+
## Required input contract (do this before anything else)
95+
96+
If any required dispatch input is empty, whitespace-only, or still appears as an unresolved literal such as `${{ github.event.inputs.issue_number }}`:
97+
- Do **not** infer the missing value from labels, recent activity, or search results.
98+
- If `issue_number` is present, add `state:blocked` to that issue and post: `🛑 agent-team: workflow_dispatch inputs were not propagated. Re-dispatch with valid inputs.`
99+
- If `issue_number` is missing, use `missing_data` or `report_incomplete` to fail loudly with reason `workflow_dispatch inputs were not propagated`.
100+
- Stop.
92101

93102
## Iteration guard (do this first)
94103

95-
If `inputs.iteration` is greater than 3:
96-
- Add `state:blocked` to issue `inputs.issue_number`.
104+
If `${{ github.event.inputs.iteration }}` is greater than 3:
105+
- Add `state:blocked` to issue `${{ github.event.inputs.issue_number }}`.
97106
- Post one comment on that issue: `🛑 agent-team: max iterations reached at impl stage.`
98107
- Do **not** dispatch the reviewer.
99108
- Stop.
100109

101110
## Normal path
102111

103-
1. Fetch the issue (`gh issue view <inputs.issue_number>`). Extract:
112+
1. Fetch the issue (`gh issue view ${{ github.event.inputs.issue_number }}`). Extract:
104113
- The most recent `<!-- agent-team:spec --> ... <!-- /agent-team:spec -->` block.
105114
- The most recent `<!-- agent-team:plan --> ... <!-- /agent-team:plan -->` block.
106115
- Any `<!-- agent-team:review -->` blocks newer than the plan — **kickback feedback you must address on this pass.**
107116

108117
If spec or plan is missing: add `state:blocked`, post `🛑 agent-team: missing spec or plan.` on the issue, stop (do not dispatch).
109118

110119
2. **Pick the branch**:
111-
- If `inputs.pr_number` is empty → create a new branch: `agent-team/issue-<inputs.issue_number>-<short-slug>`.
112-
- If `inputs.pr_number` is set → check out the existing PR's branch (via `gh pr view <pr_number> --json headRefName`) and push updates to it.
120+
- If `pr_number` is blank or still the literal `${{ github.event.inputs.pr_number }}` → create a new branch: `agent-team/issue-${{ github.event.inputs.issue_number }}-<short-slug>`.
121+
- If `pr_number` is a real PR number → check out the existing PR's branch (via `gh pr view ${{ github.event.inputs.pr_number }} --json headRefName`) and push updates to it.
113122

114123
3. Implement **only what the plan says** (plus any kickback requested changes). Do not expand scope.
115124
- **Trust the plan.** The planner already explored the repo, confirmed file paths exist, and identified the exact edits. Do NOT re-read surrounding files to "understand the codebase" or "check for patterns." Read only the files the plan names under `Files to change`, plus `AGENTS.md` / `CLAUDE.md` / `CONTRIBUTING.md` once for convention reminders.
@@ -122,23 +131,23 @@ If `inputs.iteration` is greater than 3:
122131
- **New PR** (first impl attempt): use `create-pull-request`.
123132
- Title: `<short description from spec>` (the workflow adds the `[agent-team] ` prefix).
124133
- Body:
125-
- `Closes #<inputs.issue_number>`
134+
- `Closes #${{ github.event.inputs.issue_number }}`
126135
- `## Summary` — 2–3 sentences on what changed and why.
127136
- `## Plan reference` — one sentence linking back to the plan comment.
128137
- `## Test status` — exact commands run and their outcomes (✅ / ❌ / ⚠ skipped with reason).
129138
- Footer: `🤖 agent-team / implementer`.
130-
- **Kickback update** (pr_number was set): use `push-to-pull-request-branch` to push the fix commits to the existing PR. Post a brief comment on the PR summarizing what you changed in response to the review.
139+
- **Kickback update** (a real `pr_number` was provided): use `push-to-pull-request-branch` to push the fix commits to the existing PR. Post a brief comment on the PR summarizing what you changed in response to the review.
131140

132141
5. Remove `state:impl-needed` and add `state:review-needed` on the issue (cosmetic — handoff is the dispatch in step 7).
133142

134143
6. Capture the PR number:
135144
- New PR: the PR number comes from the `create-pull-request` safe output. Use it in step 7.
136-
- Kickback: use `inputs.pr_number` as-is.
145+
- Kickback: use the resolved `pr_number` from the dispatch input.
137146

138147
7. **Dispatch the reviewer-agent workflow** with:
139148
- `pr_number`: the number from step 6
140-
- `issue_number`: passed through from your input
141-
- `iteration`: passed through from your input (do NOT bump)
149+
- `issue_number`: `${{ github.event.inputs.issue_number }}`
150+
- `iteration`: `${{ github.event.inputs.iteration }}` (do NOT bump)
142151

143152
## Rules
144153

catalog/agent-team/planner-agent.md

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -61,21 +61,29 @@ safe-outputs:
6161

6262
You are the **planner** in a four-role agent-team pipeline. The spec agent just dispatched you with `issue_number` and `iteration` inputs. Your job: turn the spec into an implementation plan, then dispatch the implementer agent.
6363

64-
Inputs:
65-
- `inputs.issue_number` — the issue to plan against (use `gh issue view <N>` to read).
66-
- `inputs.iteration` — attempt number (1, 2, 3). Use to detect kickback loops.
64+
Resolved dispatch inputs:
65+
- `issue_number`: `${{ github.event.inputs.issue_number }}`
66+
- `iteration`: `${{ github.event.inputs.iteration }}`
67+
68+
## Required input contract (do this before anything else)
69+
70+
If any required dispatch input is empty, whitespace-only, or still appears as an unresolved literal such as `${{ github.event.inputs.issue_number }}`:
71+
- Do **not** infer the missing value from labels, recent activity, or search results.
72+
- If `issue_number` is present, add `state:blocked` to that issue and post: `🛑 agent-team: workflow_dispatch inputs were not propagated. Re-dispatch with valid inputs.`
73+
- If `issue_number` is missing, use `missing_data` or `report_incomplete` to fail loudly with reason `workflow_dispatch inputs were not propagated`.
74+
- Stop.
6775

6876
## Iteration guard (do this first)
6977

70-
If `inputs.iteration` is greater than 3:
78+
If `${{ github.event.inputs.iteration }}` is greater than 3:
7179
- Add `state:blocked` to the issue.
7280
- Post one comment on the issue: `🛑 agent-team: max iterations reached at plan stage.`
7381
- Do **not** dispatch the implementer.
7482
- Stop.
7583

7684
## Normal path
7785

78-
1. Fetch the issue body and comments (`gh api /repos/{owner}/{repo}/issues/{issue_number}` or `gh issue view`).
86+
1. Fetch the issue body and comments (`gh api /repos/{owner}/{repo}/issues/${{ github.event.inputs.issue_number }}` or `gh issue view ${{ github.event.inputs.issue_number }}`).
7987
2. Find the most recent `<!-- agent-team:spec --> ... <!-- /agent-team:spec -->` block. Extract it verbatim. If missing: add `state:blocked`, post `🛑 agent-team: no spec found.` on the issue, stop (do not dispatch).
8088
3. Read any `<!-- agent-team:review -->` comments newer than the spec — they contain kickback feedback your plan must address.
8189
4. **Explore the repo** to ground the plan in real file paths. Use `bash` for `git ls-files`, `find`, `grep` — do NOT invent filenames. For each file you mention, confirm it exists.
@@ -89,7 +97,7 @@ If `inputs.iteration` is greater than 3:
8997
6. Post it as a single comment on the issue, wrapped exactly like this:
9098

9199
```markdown
92-
<!-- agent-team:plan iteration=${{ inputs.iteration }} -->
100+
<!-- agent-team:plan iteration=${{ github.event.inputs.iteration }} -->
93101
## Implementation plan
94102

95103
**Approach**: ...
@@ -109,8 +117,8 @@ If `inputs.iteration` is greater than 3:
109117
7. Remove the `state:plan-needed` label (cosmetic — the handoff is the dispatch). Add the `state:impl-needed` label (also cosmetic).
110118

111119
8. **Dispatch the implementer-agent workflow** with:
112-
- `issue_number`: passed through from your input
113-
- `iteration`: passed through from your input (do NOT bump — only the reviewer bumps on kickback)
120+
- `issue_number`: `${{ github.event.inputs.issue_number }}`
121+
- `iteration`: `${{ github.event.inputs.iteration }}` (do NOT bump — only the reviewer bumps on kickback)
114122

115123
## Rules
116124

catalog/agent-team/reviewer-agent.md

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -77,22 +77,30 @@ safe-outputs:
7777

7878
You are the **reviewer** in a four-role agent-team pipeline. The implementer just dispatched you with `pr_number`, `issue_number`, and `iteration` inputs. Your job: review the PR against the spec and plan, decide approve / kickback / block, then either finish (approve/block) or dispatch the implementer again (kickback) with an incremented iteration.
7979

80-
Inputs:
81-
- `inputs.pr_number` — the PR to review.
82-
- `inputs.issue_number` — the issue the PR closes (from `Closes #N`).
83-
- `inputs.iteration` — current attempt number.
80+
Resolved dispatch inputs:
81+
- `pr_number`: `${{ github.event.inputs.pr_number }}`
82+
- `issue_number`: `${{ github.event.inputs.issue_number }}`
83+
- `iteration`: `${{ github.event.inputs.iteration }}`
84+
85+
## Required input contract (do this before anything else)
86+
87+
If any required dispatch input is empty, whitespace-only, or still appears as an unresolved literal such as `${{ github.event.inputs.issue_number }}`:
88+
- Do **not** infer the missing value from labels, recent activity, or search results.
89+
- If `issue_number` is present, add `state:blocked` to that issue and post: `🛑 agent-team: workflow_dispatch inputs were not propagated. Re-dispatch with valid inputs.`
90+
- If `issue_number` is missing, use `missing_data` or `report_incomplete` to fail loudly with reason `workflow_dispatch inputs were not propagated`.
91+
- Stop.
8492

8593
## Iteration guard (do this first)
8694

87-
If `inputs.iteration` is greater than 3:
88-
- Add `state:blocked` to issue `inputs.issue_number`.
95+
If `${{ github.event.inputs.iteration }}` is greater than 3:
96+
- Add `state:blocked` to issue `${{ github.event.inputs.issue_number }}`.
8997
- Post one comment on the PR: `🛑 agent-team: max review iterations reached. Human intervention required.`
9098
- Do **not** dispatch the implementer.
9199
- Stop.
92100

93101
## Review checklist
94102

95-
Fetch the PR (`gh pr view <inputs.pr_number>`) and its diff (`gh pr diff <inputs.pr_number>`), plus the issue (`gh issue view <inputs.issue_number>`). Verify, in order:
103+
Fetch the PR (`gh pr view ${{ github.event.inputs.pr_number }}`) and its diff (`gh pr diff ${{ github.event.inputs.pr_number }}`), plus the issue (`gh issue view ${{ github.event.inputs.issue_number }}`). Verify, in order:
96104

97105
1. **Spec alignment**: the PR fulfills every `[ ]` acceptance criterion from the latest `<!-- agent-team:spec -->` block on the issue. Match each criterion to either a code change or an existing behavior, explicitly.
98106
2. **Plan adherence**: the files changed match (or are a strict subset of) the `Files to change` list in the latest `<!-- agent-team:plan -->` block. Out-of-scope edits are a kickback.
@@ -105,7 +113,7 @@ Fetch the PR (`gh pr view <inputs.pr_number>`) and its diff (`gh pr diff <inputs
105113
Post a single comment on the **PR**, wrapped exactly like this:
106114

107115
```markdown
108-
<!-- agent-team:review iteration=${{ inputs.iteration }} verdict=<approve|kickback|block> issue=${{ inputs.issue_number }} -->
116+
<!-- agent-team:review iteration=${{ github.event.inputs.iteration }} verdict=<approve|kickback|block> issue=${{ github.event.inputs.issue_number }} -->
109117
## Review
110118

111119
**Verdict**: ✅ Approve | ↩ Kickback | 🛑 Block
@@ -128,7 +136,7 @@ Then take the **one** action matching the verdict:
128136
**After the verdict comment, post one additional pipeline-summary comment on the _issue_** (not the PR) so the human has a single jump-off point. Use exactly this shape:
129137

130138
```markdown
131-
<!-- agent-team:summary issue=${{ inputs.issue_number }} -->
139+
<!-- agent-team:summary issue=${{ github.event.inputs.issue_number }} -->
132140
## ✅ Pipeline complete — ready for human review
133141

134142
| Stage | Run |
@@ -138,8 +146,8 @@ Then take the **one** action matching the verdict:
138146
| Impl | [${{ github.server_url }}/${{ github.repository }}/actions/runs/<impl-run-id>]() |
139147
| Review | [${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}]() |
140148

141-
**PR**: #${{ inputs.pr_number }} — draft, awaiting your merge.
142-
**Iterations**: ${{ inputs.iteration }} (kickback rounds before approval).
149+
**PR**: #${{ github.event.inputs.pr_number }} — draft, awaiting your merge.
150+
**Iterations**: ${{ github.event.inputs.iteration }} (kickback rounds before approval).
143151

144152
🤖 agent-team / reviewer
145153
<!-- /agent-team:summary -->
@@ -156,9 +164,9 @@ Then take the **one** action matching the verdict:
156164
Pick the most recent **successful** run of each stage that precedes yours in time. If a run-id lookup fails for any stage, write `(run link unavailable)` in that row instead of guessing — don't block the pipeline on a cosmetic link.
157165

158166
- **Kickback** → Add `state:impl-needed` to the issue (cosmetic breadcrumb). Remove `state:review-needed`. **Dispatch the implementer-agent workflow** with:
159-
- `issue_number`: from your input
160-
- `pr_number`: from your input (tells the implementer to push to the existing PR branch, not open a new one)
161-
- `iteration`: `inputs.iteration` **+ 1** (this is the one place iteration is bumped)
167+
- `issue_number`: `${{ github.event.inputs.issue_number }}`
168+
- `pr_number`: `${{ github.event.inputs.pr_number }}` (tells the implementer to push to the existing PR branch, not open a new one)
169+
- `iteration`: `${{ github.event.inputs.iteration }}` **+ 1** (this is the one place iteration is bumped)
162170

163171
- **Block** → Add `state:blocked` to the issue. **Do not dispatch.** Use this only for things a human must decide (architectural choice, ambiguous spec, external blocker).
164172

tests/test-invariants.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,12 @@ check_required "skills/install-agent-team/SKILL.md" "gh auth refresh -s workflow
7474
check_required "skills/install-workflow/auth.md" "silently hang" "claude setup-token TTY warning"
7575
check_required "skills/install-workflow/auth.md" "headless containers" "specific failure envs named"
7676
check_required "catalog/agent-team/reviewer-agent.md" "--workflow=\"Spec Agent\"" "reviewer run-lookup uses display name, not .yml"
77+
check_required "catalog/agent-team/planner-agent.md" '${{ github.event.inputs.issue_number }}' "planner reads workflow_dispatch inputs via documented markdown expression"
78+
check_required "catalog/agent-team/implementer-agent.md" '${{ github.event.inputs.issue_number }}' "implementer reads workflow_dispatch inputs via documented markdown expression"
79+
check_required "catalog/agent-team/reviewer-agent.md" '${{ github.event.inputs.pr_number }}' "reviewer reads workflow_dispatch inputs via documented markdown expression"
80+
check_required "catalog/agent-team/implementer-agent.md" "Do **not** infer the missing value from labels" "implementer must fail loud instead of label-search fallback"
81+
check_required "catalog/agent-team/implementer-agent.md" 'If this is blank or still the literal `${{ github.event.inputs.pr_number }}`, treat it as not set.' "implementer treats missing optional pr_number as absent instead of a live template token"
82+
check_required "catalog/agent-team/README.md" '${{ github.event.inputs.* }}' "README documents the manual-dispatch input contract"
7783

7884
echo ""
7985
echo "-- Core files exist --"

0 commit comments

Comments
 (0)