Skip to content

Commit 45c3345

Browse files
EMaherCopilotCopilot
authored
fix(squad): start coding-agent sessions for squad:copilot issues; rename go/no-go workflows (#219)
* fix(squad): start coding-agent sessions for squad:copilot issues; rename go/no-go workflows Also rename the issue go/no-go decision workflows for discoverability: - issue-nogo.yml -> issue-go-no.yml ("Issue Go: No") - issue-assign.md -> issue-go-yes.md ("Issue Go: Yes") - issue-clarify.md -> issue-go-clarify.md ("Issue Go: Clarify") Display names set via gh-aw frontmatter `name:`; agentic .lock.yml recompiled. NOTE: requires a COPILOT_ASSIGN_TOKEN repo secret (a PAT that can assign the coding agent) for the assignment step to succeed. Refs #194 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * docs(squad): link COPILOT_ASSIGN_TOKEN setup instructions in team.md Add a "Setting up COPILOT_ASSIGN_TOKEN" note to the Coding Agent section with links to fine-grained PAT creation, repository-secret setup, and the GitHub docs describing the exact token/permissions required to assign the Copilot coding agent. Docs-only; no workflow/code changes. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * feat(squad): nudge maintainer to assign Copilot when auto-assign is off Add a deterministic post-check step to the "Issue Go: Yes" workflow that checks whether the approved (go:yes, squad:copilot) issue is actually assigned to the Copilot coding agent. When the COPILOT_ASSIGN_TOKEN secret is not configured (so squad-heartbeat cannot auto-assign), it posts a comment tagging the human assignee(s) explaining they must assign the issue to Copilot to start the work. When the token IS configured, the step is a no-op to avoid noise, since Ralph will auto-assign shortly. Runs in the injection-safe post-check job (not the agentic prompt), so the logic is deterministic. Grants that job issues:write to post the comment. Security note: issue-go-yes.lock.yml now references the COPILOT_ASSIGN_TOKEN secret only inside an expression (secrets.COPILOT_ASSIGN_TOKEN != '') to detect presence; the secret value is never read, logged, or sent anywhere. Refs #194 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * refactor(squad): post Copilot-assignment nudge via agent, not post-check Move the "assign this issue to Copilot" nudge out of the deterministic post-check job (which required issues:write) into the agent's single assignment comment, posted through the existing add-comment safe output. The post-check job is back to issues:read. Because the agent cannot read secrets, the gating is computed deterministically in the trusted "Prepare assignment context" shell step: NUDGE_NEEDED is true only when COPILOT_ASSIGN_TOKEN is absent (evaluated as the boolean `secrets.COPILOT_ASSIGN_TOKEN != ''`, never the value) AND the coding agent is not already an assignee. That result is injected into the trusted system-policy.md so the agent includes or omits the "### 🤖 Action needed" section accordingly. Security note: the raw COPILOT_ASSIGN_TOKEN value is materialized in exactly one place in the compiled lock — gh-aw's "Redact secrets in logs" step — so its value is scrubbed from agent logs and is never placed in the agent inference step or the prepare step (which only see the boolean). Refs #194 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * fix(squad): keep COPILOT_ASSIGN_TOKEN out of the posted issue comment The Copilot-assignment nudge must not expose workflow internals in the public issue thread. Remove the ".squad/team.md / COPILOT_ASSIGN_TOKEN setup" reference from the nudge the agent posts, and add explicit guards (system policy + comment-format template) instructing the agent to never mention COPILOT_ASSIGN_TOKEN, repository secrets, or workflow internals in the comment. The nudge now simply asks the assignee to assign the issue to Copilot from the Assignees menu. Refs #194 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
1 parent 926f2f1 commit 45c3345

8 files changed

Lines changed: 199 additions & 121 deletions

File tree

.github/workflows/issue-clarify.lock.yml renamed to .github/workflows/issue-go-clarify.lock.yml

Lines changed: 52 additions & 52 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
---
2+
name: "Issue Go: Clarify"
23
description: >
34
When a maintainer marks an issue go:needs-research, help scope it by asking focused
45
clarifying questions — but only if the maintainer has not already asked them. If the
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Issue No-Go
1+
name: "Issue Go: No"
22

33
# Deterministic Workflow: No-Go decision
44
# Trigger: a maintainer applies the "go:no" label to reject an issue.
Lines changed: 60 additions & 57 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
---
2+
name: "Issue Go: Yes"
23
description: >
34
Assign an approved issue (go:yes) to the maintainer who approved it and route it to
45
squad areas. Reads the prior triage analysis and the squad routing table, applies the
56
squad label plus matched squad:{member} labels (and squad:copilot to hand the issue
6-
off to the Copilot coding agent), and posts an assignment rationale comment.
7+
off to the Copilot coding agent), and posts an assignment rationale comment that,
8+
when auto-assign is not configured (no COPILOT_ASSIGN_TOKEN), also nudges the
9+
assignee to assign the Copilot coding agent so work can begin.
710
811
on:
912
issues:
@@ -44,6 +47,10 @@ steps:
4447
ISSUE_TITLE: ${{ github.event.issue.title }}
4548
GO_YES_SENDER: ${{ github.event.sender.login }}
4649
GH_TOKEN: ${{ github.token }}
50+
# 'true' only when the repo secret is set — i.e. squad-heartbeat can
51+
# auto-assign the coding agent. The secret VALUE is never exposed; only
52+
# this boolean is placed in the environment.
53+
HAS_ASSIGN_TOKEN: ${{ secrets.COPILOT_ASSIGN_TOKEN != '' }}
4754
run: |
4855
mkdir -p /tmp/gh-aw/agent
4956
@@ -70,6 +77,22 @@ steps:
7077
printf '%s\n' '_No prior triage analysis comment found — route from the issue content alone._' >> "$USER_FILE"
7178
fi
7279
80+
# ---------------------------------------------------------------------
81+
# Copilot coding-agent hand-off status (deterministic, trusted context).
82+
# NUDGE_NEEDED is true only when auto-assign is unavailable AND the coding
83+
# agent is not already an assignee — so the agent should nudge the human.
84+
# ---------------------------------------------------------------------
85+
CURRENT_ASSIGNEES=$(gh issue view "$ISSUE_NUMBER" --repo "$GITHUB_REPOSITORY" --json assignees \
86+
--jq '[.assignees[].login] | join(",")' 2>/dev/null | tr 'A-Z' 'a-z' || printf '')
87+
AGENT_ALREADY_ASSIGNED=false
88+
if printf ',%s,' "$CURRENT_ASSIGNEES" | grep -qE ',(copilot-swe-agent\[bot\]|copilot-swe-agent|copilot),'; then
89+
AGENT_ALREADY_ASSIGNED=true
90+
fi
91+
NUDGE_NEEDED=false
92+
if [ "${HAS_ASSIGN_TOKEN:-false}" != "true" ] && [ "$AGENT_ALREADY_ASSIGNED" != "true" ]; then
93+
NUDGE_NEEDED=true
94+
fi
95+
7396
# ---------------------------------------------------------------------
7497
# SYSTEM context (trusted): assignment policy + routing/team tables.
7598
# Single-quoted printf args keep backticks/`$` literal (no expansion).
@@ -85,6 +108,11 @@ steps:
85108
printf '%s\n' '- Apply one `squad:{member}` label for every squad area the issue touches, per the routing table below.'
86109
printf '%s\n' '- Apply the `squad:copilot` label to hand the issue off to the Copilot coding agent to begin implementation.'
87110
printf '%s\n' '- Never apply `go:*`, `priority:*`, `override:*`, or `type:*` labels.'
111+
if [ "$NUDGE_NEEDED" = "true" ]; then
112+
printf -- '- **Copilot hand-off nudge REQUIRED:** no coding agent is assigned to this issue yet. In your single assignment comment, add an `### 🤖 Action needed` section that @-mentions **@%s** and tells them to assign this issue to Copilot from the Assignees menu so implementation can begin. Do NOT mention COPILOT_ASSIGN_TOKEN, repository secrets, `.squad/team.md`, or any workflow internals in the comment.\n' "$GO_YES_SENDER"
113+
else
114+
printf '%s\n' '- **Copilot hand-off nudge:** NOT required — do not add any Copilot-assignment nudge to the comment (auto-assign is configured or the coding agent is already assigned).'
115+
fi
88116
printf '\n'
89117
} > "$SYS_FILE"
90118
@@ -226,6 +254,9 @@ context — you do not decide the assignee yourself.
226254
`squad` label, `squad:copilot`, and the most relevant matched member labels.
227255
6. **Comment** exactly once with `add_comment`, explaining the assignee, the matched
228256
squad areas, and the routing evidence (which routing-table entries matched and why).
257+
If the system policy says a **Copilot hand-off nudge** is REQUIRED, append the
258+
`### 🤖 Action needed` section (described in that policy) to this same comment. If it
259+
says the nudge is NOT required, omit that section entirely.
229260

230261
## Assignment comment format
231262

@@ -241,6 +272,11 @@ context — you do not decide the assignee yourself.
241272
242273
#### Notes
243274
<Any routing uncertainty, or "none".>
275+
276+
<!-- Include the section below ONLY when the system policy says a Copilot hand-off nudge is REQUIRED. Omit it otherwise. Never mention COPILOT_ASSIGN_TOKEN, secrets, or workflow internals here. -->
277+
### 🤖 Action needed
278+
@<assignee> — this issue is not assigned to the Copilot coding agent yet, so no agent
279+
session has started. Please assign it to Copilot from the Assignees menu to begin work.
244280
```
245281

246282
## Security Rules

.github/workflows/squad-heartbeat.yml

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -129,12 +129,19 @@ jobs:
129129
per_page: 5
130130
});
131131
132-
const unassigned = copilotIssues.filter(i =>
133-
!i.assignees || i.assignees.length === 0
132+
// Include issues that don't yet have the coding agent assigned.
133+
// issue-go-yes always assigns the go:yes maintainer as owner, so an
134+
// issue almost always has a (human) assignee — filtering on "zero
135+
// assignees" would skip every routed issue. Filter on the agent's
136+
// presence instead; the POST below ADDS the bot as an extra assignee
137+
// without removing the human owner.
138+
const COPILOT_LOGINS = new Set(['copilot-swe-agent[bot]', 'copilot-swe-agent', 'copilot']);
139+
const needsAgent = copilotIssues.filter(i =>
140+
!(i.assignees || []).some(a => COPILOT_LOGINS.has((a.login || '').toLowerCase()))
134141
);
135142
136-
if (unassigned.length === 0) {
137-
core.info('No unassigned squad:copilot issues');
143+
if (needsAgent.length === 0) {
144+
core.info('No squad:copilot issues awaiting the coding agent');
138145
return;
139146
}
140147
@@ -144,7 +151,7 @@ jobs:
144151
repo: context.repo.repo
145152
});
146153
147-
for (const issue of unassigned) {
154+
for (const issue of needsAgent) {
148155
try {
149156
await github.request('POST /repos/{owner}/{repo}/issues/{issue_number}/assignees', {
150157
owner: context.repo.owner,

.squad/team.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,30 @@
2626
| SecurityExpert | 🔒 Security Expert | [charter](.squad/agents/securityexpert/charter.md) | ✅ Active |
2727
| Ralph | 🔄 Work Monitor || ✅ Active |
2828

29+
## Coding Agent
30+
31+
<!-- copilot-auto-assign: true -->
32+
33+
GitHub's Copilot coding agent (`@copilot`) autonomously implements issues labeled
34+
`squad:copilot`. Ralph (`squad-heartbeat.yml`) assigns `copilot-swe-agent[bot]` to
35+
every open `squad:copilot` issue that does not already have the agent assigned —
36+
in addition to the human maintainer who owns it — which is what actually starts an
37+
agent session (the label alone does not).
38+
39+
| Name | Role | Auto-assign | Notes |
40+
|------|------|-------------|-------|
41+
| Copilot | 🤖 Coding Agent | ✅ Enabled | Requires the `COPILOT_ASSIGN_TOKEN` repo secret (a PAT that can assign the coding agent). Without it the assign step is a no-op. |
42+
43+
**Setting up `COPILOT_ASSIGN_TOKEN`:** the default `GITHUB_TOKEN` cannot assign the
44+
coding agent, so the assign step needs a user token stored as a repo secret. Create a
45+
[fine-grained personal access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens)
46+
(ideally on a machine/bot account) with **Repository permissions***Metadata*: read,
47+
and *Actions*, *Contents*, *Issues*, and *Pull requests*: read & write — then add it as a
48+
[repository secret](https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/use-secrets#creating-secrets-for-a-repository)
49+
named `COPILOT_ASSIGN_TOKEN`. A classic PAT with the `repo` scope also works. The exact
50+
token requirements are documented under
51+
[Assigning issues to Copilot via the API](https://docs.github.com/en/copilot/how-tos/use-copilot-agents/cloud-agent/use-cloud-agent-via-the-api#using-the-issues-api).
52+
2953
## Issue Source
3054

3155
- **Repository:** Azure/apiops-cli

.squad/templates/workflows/squad-heartbeat.yml

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -133,12 +133,19 @@ jobs:
133133
per_page: 5
134134
});
135135
136-
const unassigned = copilotIssues.filter(i =>
137-
!i.assignees || i.assignees.length === 0
136+
// Include issues that don't yet have the coding agent assigned.
137+
// issue-go-yes always assigns the go:yes maintainer as owner, so an
138+
// issue almost always has a (human) assignee — filtering on "zero
139+
// assignees" would skip every routed issue. Filter on the agent's
140+
// presence instead; the POST below ADDS the bot as an extra assignee
141+
// without removing the human owner.
142+
const COPILOT_LOGINS = new Set(['copilot-swe-agent[bot]', 'copilot-swe-agent', 'copilot']);
143+
const needsAgent = copilotIssues.filter(i =>
144+
!(i.assignees || []).some(a => COPILOT_LOGINS.has((a.login || '').toLowerCase()))
138145
);
139146
140-
if (unassigned.length === 0) {
141-
core.info('No unassigned squad:copilot issues');
147+
if (needsAgent.length === 0) {
148+
core.info('No squad:copilot issues awaiting the coding agent');
142149
return;
143150
}
144151
@@ -148,7 +155,7 @@ jobs:
148155
repo: context.repo.repo
149156
});
150157
151-
for (const issue of unassigned) {
158+
for (const issue of needsAgent) {
152159
try {
153160
await github.request('POST /repos/{owner}/{repo}/issues/{issue_number}/assignees', {
154161
owner: context.repo.owner,

0 commit comments

Comments
 (0)