create-agentic-pdlc 2.2.1 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agentic-pdlc/SETUP_PROMPT.md +3 -4
- package/.agentic-pdlc/metrics/raw/2026-W18.jsonl +2 -0
- package/.agentic-pdlc/metrics/raw/2026-W21.jsonl +68 -0
- package/.agentic-pdlc/metrics/raw/2026-W22.jsonl +114 -0
- package/.agentic-setup-prompt.md +3 -4
- package/.agentic-setup.md +3 -4
- package/.github/ISSUE_TEMPLATE/bug.md +53 -0
- package/.github/ISSUE_TEMPLATE/feature.md +54 -0
- package/.github/ISSUE_TEMPLATE/task.md +33 -0
- package/.github/workflows/add-to-board.yml +1 -1
- package/.github/workflows/agent-trigger.yml +4 -4
- package/.github/workflows/agentic-metrics.yml +171 -38
- package/.github/workflows/ci.yml +1 -1
- package/.github/workflows/npm-publish.yml +2 -2
- package/.github/workflows/pdlc-health-check.yml +1 -3
- package/.github/workflows/pdlc-stage-gate.yml +2 -2
- package/.github/workflows/project-automation.yml +79 -16
- package/.github/workflows/qa-agent.yml +26 -15
- package/.github/workflows/qa-gate.yml +51 -0
- package/AGENTS.md +50 -8
- package/CLAUDE.md +53 -3
- package/SETUP.md +4 -1
- package/adapters/claude-code/skill.md +44 -20
- package/adapters/hooks/pdlc-stage-gate.sh +2 -7
- package/bin/cli.js +41 -9
- package/docs/flow.md +8 -21
- package/docs/pdlc.md +24 -16
- package/docs/superpowers/plans/2026-05-28-jules-label-pat-split.md +240 -0
- package/docs/superpowers/plans/2026-05-29-agentic-pulse-rework-taxonomy.md +474 -0
- package/docs/superpowers/plans/2026-05-29-qa-gate-enforcement.md +354 -0
- package/docs/superpowers/specs/2026-05-29-agentic-pulse-rework-taxonomy-design.md +122 -0
- package/package.json +1 -1
- package/templates/.github/ISSUE_TEMPLATE/bug.md +53 -0
- package/templates/.github/ISSUE_TEMPLATE/feature.md +54 -0
- package/templates/.github/ISSUE_TEMPLATE/task.md +33 -0
- package/templates/.github/workflows/add-to-board.yml +4 -4
- package/templates/.github/workflows/agent-trigger.yml +24 -15
- package/{.agentic-pdlc/templates → templates}/.github/workflows/agentic-metrics.yml +166 -36
- package/templates/.github/workflows/ci.yml +15 -1
- package/templates/.github/workflows/pdlc-health-check.yml +1 -3
- package/templates/.github/workflows/pdlc-stage-gate.yml +2 -2
- package/templates/.github/workflows/project-automation.yml +93 -36
- package/templates/.github/workflows/qa-agent.yml +33 -17
- package/templates/.github/workflows/qa-gate.yml +51 -0
- package/templates/AGENTS.md +74 -23
- package/templates/docs/pdlc.md +24 -16
- package/.agentic-pdlc/templates/.github/CODEOWNERS +0 -5
- package/.agentic-pdlc/templates/.github/copilot-instructions.md +0 -12
- package/.agentic-pdlc/templates/.github/workflows/add-to-board.yml +0 -38
- package/.agentic-pdlc/templates/.github/workflows/agent-trigger.yml +0 -146
- package/.agentic-pdlc/templates/.github/workflows/auto-approve.yml +0 -16
- package/.agentic-pdlc/templates/.github/workflows/ci.yml +0 -40
- package/.agentic-pdlc/templates/.github/workflows/pdlc-health-check.yml +0 -123
- package/.agentic-pdlc/templates/.github/workflows/pdlc-stage-gate.yml +0 -51
- package/.agentic-pdlc/templates/.github/workflows/project-automation.yml +0 -278
- package/.agentic-pdlc/templates/.github/workflows/protect-workflows.yml +0 -21
- package/.agentic-pdlc/templates/.github/workflows/qa-agent.yml +0 -128
- package/.agentic-pdlc/templates/AGENTS.md +0 -81
- package/.agentic-pdlc/templates/docs/pdlc.md +0 -115
|
@@ -6,13 +6,12 @@ on:
|
|
|
6
6
|
pull_request_review:
|
|
7
7
|
types: [submitted]
|
|
8
8
|
issues:
|
|
9
|
-
types: [labeled, edited]
|
|
9
|
+
types: [labeled, edited, closed]
|
|
10
10
|
|
|
11
11
|
env:
|
|
12
12
|
PROJECT_ID: "{{PROJECT_ID}}"
|
|
13
13
|
STATUS_FIELD_ID: "{{STATUS_FIELD_ID}}"
|
|
14
14
|
STATUS_IDEA: "{{ID_IDEA}}"
|
|
15
|
-
STATUS_EXPLORATION: "{{ID_EXPLORATION}}"
|
|
16
15
|
STATUS_BRAINSTORMING: "{{ID_BRAINSTORMING}}"
|
|
17
16
|
STATUS_DETAILING: "{{ID_DETAILING}}"
|
|
18
17
|
STATUS_APPROVAL: "{{ID_APPROVAL}}"
|
|
@@ -28,22 +27,19 @@ jobs:
|
|
|
28
27
|
if: github.event_name == 'issues' && github.event.action == 'labeled'
|
|
29
28
|
runs-on: ubuntu-latest
|
|
30
29
|
env:
|
|
31
|
-
|
|
30
|
+
PROJECT_TOKEN: ${{ secrets.PROJECT_TOKEN }}
|
|
32
31
|
steps:
|
|
33
32
|
- name: Detect Label and Move Issue
|
|
34
|
-
if: ${{ env.
|
|
35
|
-
uses: actions/github-script@
|
|
33
|
+
if: ${{ env.PROJECT_TOKEN != '' && env.PROJECT_ID != '{{PROJECT_ID}}' }}
|
|
34
|
+
uses: actions/github-script@v8
|
|
36
35
|
with:
|
|
37
|
-
github-token: ${{ env.
|
|
36
|
+
github-token: ${{ env.PROJECT_TOKEN }}
|
|
38
37
|
script: |
|
|
39
38
|
const labelName = context.payload.label.name;
|
|
40
39
|
let targetStatusId = null;
|
|
41
40
|
let stageName = null;
|
|
42
41
|
|
|
43
|
-
if (labelName === 'stage:
|
|
44
|
-
targetStatusId = process.env.STATUS_EXPLORATION;
|
|
45
|
-
stageName = 'Exploration';
|
|
46
|
-
} else if (labelName === 'stage:brainstorming') {
|
|
42
|
+
if (labelName === 'stage:brainstorming') {
|
|
47
43
|
targetStatusId = process.env.STATUS_BRAINSTORMING;
|
|
48
44
|
stageName = 'Brainstorming';
|
|
49
45
|
} else if (labelName === 'stage:detailing') {
|
|
@@ -55,9 +51,6 @@ jobs:
|
|
|
55
51
|
} else if (labelName === 'stage:development') {
|
|
56
52
|
targetStatusId = process.env.STATUS_DEVELOPMENT;
|
|
57
53
|
stageName = 'Development';
|
|
58
|
-
} else if (labelName === 'stage:testing') {
|
|
59
|
-
targetStatusId = process.env.STATUS_TESTING;
|
|
60
|
-
stageName = 'Testing';
|
|
61
54
|
}
|
|
62
55
|
|
|
63
56
|
if (!targetStatusId) {
|
|
@@ -95,13 +88,13 @@ jobs:
|
|
|
95
88
|
contains(github.event.issue.labels.*.name, 'stage:detailing')
|
|
96
89
|
runs-on: ubuntu-latest
|
|
97
90
|
env:
|
|
98
|
-
|
|
91
|
+
PROJECT_TOKEN: ${{ secrets.PROJECT_TOKEN }}
|
|
99
92
|
steps:
|
|
100
93
|
- name: Check spec markers and swap labels
|
|
101
|
-
if: ${{ env.
|
|
102
|
-
uses: actions/github-script@
|
|
94
|
+
if: ${{ env.PROJECT_TOKEN != '' && env.PROJECT_ID != '{{PROJECT_ID}}' }}
|
|
95
|
+
uses: actions/github-script@v8
|
|
103
96
|
with:
|
|
104
|
-
github-token: ${{ env.
|
|
97
|
+
github-token: ${{ env.PROJECT_TOKEN }}
|
|
105
98
|
script: |
|
|
106
99
|
const body = context.payload.issue.body ?? '';
|
|
107
100
|
if (!body.includes('## Acceptance Criteria') || !body.includes('## Files to modify')) return;
|
|
@@ -113,6 +106,42 @@ jobs:
|
|
|
113
106
|
await github.rest.issues.removeLabel({ owner, repo, issue_number, name: 'stage:detailing' }).catch(() => {});
|
|
114
107
|
console.log(`Issue #${issue_number} auto-moved to stage:approval`);
|
|
115
108
|
|
|
109
|
+
# human-approved on issue → qa:approved on linked open PRs
|
|
110
|
+
handle-human-approved:
|
|
111
|
+
name: human-approved → qa:approved on linked PRs
|
|
112
|
+
if: github.event_name == 'issues' && github.event.action == 'labeled' && github.event.label.name == 'human-approved'
|
|
113
|
+
runs-on: ubuntu-latest
|
|
114
|
+
permissions:
|
|
115
|
+
issues: write
|
|
116
|
+
pull-requests: write
|
|
117
|
+
steps:
|
|
118
|
+
- name: Add qa:approved to linked open PRs
|
|
119
|
+
uses: actions/github-script@v8
|
|
120
|
+
with:
|
|
121
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
122
|
+
script: |
|
|
123
|
+
const { owner, repo } = context.repo;
|
|
124
|
+
const issueNumber = context.payload.issue.number;
|
|
125
|
+
const pattern = new RegExp(`(?:Closes?|Fixes?|Resolves?)\\s+#${issueNumber}\\b`, 'i');
|
|
126
|
+
|
|
127
|
+
const prs = await github.paginate(github.rest.pulls.list, {
|
|
128
|
+
owner, repo, state: 'open', per_page: 100
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
const linked = prs.filter(pr => pattern.test(pr.body ?? ''));
|
|
132
|
+
|
|
133
|
+
if (linked.length === 0) {
|
|
134
|
+
console.log(`No open PRs linking issue #${issueNumber}. Exiting.`);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
for (const pr of linked) {
|
|
139
|
+
await github.rest.issues.addLabels({
|
|
140
|
+
owner, repo, issue_number: pr.number, labels: ['qa:approved']
|
|
141
|
+
}).catch(() => {});
|
|
142
|
+
console.log(`PR #${pr.number} → qa:approved`);
|
|
143
|
+
}
|
|
144
|
+
|
|
116
145
|
# OPTIONAL: Uncomment to enable architecture-violation → Idea
|
|
117
146
|
# move-violation-to-board:
|
|
118
147
|
# name: architecture-violation → 💡 Idea
|
|
@@ -120,10 +149,10 @@ jobs:
|
|
|
120
149
|
# runs-on: ubuntu-latest
|
|
121
150
|
# steps:
|
|
122
151
|
# - name: Move issue to Idea
|
|
123
|
-
# if: ${{ env.
|
|
124
|
-
# uses: actions/github-script@
|
|
152
|
+
# if: ${{ env.PROJECT_TOKEN != '' && env.PROJECT_ID != '{{PROJECT_ID}}' }}
|
|
153
|
+
# uses: actions/github-script@v8
|
|
125
154
|
# with:
|
|
126
|
-
# github-token: ${{ env.
|
|
155
|
+
# github-token: ${{ env.PROJECT_TOKEN }}
|
|
127
156
|
# script: |
|
|
128
157
|
# const { issue: { number, node_id } } = context.payload;
|
|
129
158
|
# const { addProjectV2ItemById: { item } } = await github.graphql(`
|
|
@@ -147,13 +176,13 @@ jobs:
|
|
|
147
176
|
if: github.event_name == 'pull_request' && (github.event.action == 'opened' || github.event.action == 'reopened')
|
|
148
177
|
runs-on: ubuntu-latest
|
|
149
178
|
env:
|
|
150
|
-
|
|
179
|
+
PROJECT_TOKEN: ${{ secrets.PROJECT_TOKEN }}
|
|
151
180
|
steps:
|
|
152
181
|
- name: Move linked issue to Testing
|
|
153
|
-
if: ${{ env.
|
|
154
|
-
uses: actions/github-script@
|
|
182
|
+
if: ${{ env.PROJECT_TOKEN != '' && env.PROJECT_ID != '{{PROJECT_ID}}' }}
|
|
183
|
+
uses: actions/github-script@v8
|
|
155
184
|
with:
|
|
156
|
-
github-token: ${{ env.
|
|
185
|
+
github-token: ${{ env.PROJECT_TOKEN }}
|
|
157
186
|
script: |
|
|
158
187
|
const prNumber = context.payload.pull_request.number;
|
|
159
188
|
const { owner, repo } = context.repo;
|
|
@@ -201,13 +230,13 @@ jobs:
|
|
|
201
230
|
if: github.event_name == 'pull_request' && github.event.action == 'labeled' && github.event.label.name == 'qa:approved'
|
|
202
231
|
runs-on: ubuntu-latest
|
|
203
232
|
env:
|
|
204
|
-
|
|
233
|
+
PROJECT_TOKEN: ${{ secrets.PROJECT_TOKEN }}
|
|
205
234
|
steps:
|
|
206
235
|
- name: Move linked issue to Code Review / PR
|
|
207
|
-
if: ${{ env.
|
|
208
|
-
uses: actions/github-script@
|
|
236
|
+
if: ${{ env.PROJECT_TOKEN != '' && env.PROJECT_ID != '{{PROJECT_ID}}' }}
|
|
237
|
+
uses: actions/github-script@v8
|
|
209
238
|
with:
|
|
210
|
-
github-token: ${{ env.
|
|
239
|
+
github-token: ${{ env.PROJECT_TOKEN }}
|
|
211
240
|
script: |
|
|
212
241
|
const prNumber = context.payload.pull_request.number;
|
|
213
242
|
const { owner, repo } = context.repo;
|
|
@@ -233,6 +262,9 @@ jobs:
|
|
|
233
262
|
for (const n of linkedIssues) {
|
|
234
263
|
const { data: issue } = await github.rest.issues.get({ owner, repo, issue_number: n });
|
|
235
264
|
await moveItem(issue.node_id);
|
|
265
|
+
if (issue.labels?.some(l => l.name === 'stage:testing')) {
|
|
266
|
+
await github.rest.issues.removeLabel({ owner, repo, issue_number: n, name: 'stage:testing' }).catch(() => {});
|
|
267
|
+
}
|
|
236
268
|
console.log(`Issue #${n} → Code Review / PR`);
|
|
237
269
|
}
|
|
238
270
|
} else {
|
|
@@ -246,13 +278,13 @@ jobs:
|
|
|
246
278
|
if: github.event_name == 'pull_request_review' && github.event.review.state == 'approved'
|
|
247
279
|
runs-on: ubuntu-latest
|
|
248
280
|
env:
|
|
249
|
-
|
|
281
|
+
PROJECT_TOKEN: ${{ secrets.PROJECT_TOKEN }}
|
|
250
282
|
steps:
|
|
251
283
|
- name: Swap PR labels
|
|
252
|
-
if: ${{ env.
|
|
253
|
-
uses: actions/github-script@
|
|
284
|
+
if: ${{ env.PROJECT_TOKEN != '' && env.PROJECT_ID != '{{PROJECT_ID}}' }}
|
|
285
|
+
uses: actions/github-script@v8
|
|
254
286
|
with:
|
|
255
|
-
github-token: ${{ env.
|
|
287
|
+
github-token: ${{ env.PROJECT_TOKEN }}
|
|
256
288
|
script: |
|
|
257
289
|
const prNumber = context.payload.pull_request.number;
|
|
258
290
|
const { owner, repo } = context.repo;
|
|
@@ -265,13 +297,13 @@ jobs:
|
|
|
265
297
|
if: github.event_name == 'pull_request' && github.event.action == 'closed' && github.event.pull_request.merged == true
|
|
266
298
|
runs-on: ubuntu-latest
|
|
267
299
|
env:
|
|
268
|
-
|
|
300
|
+
PROJECT_TOKEN: ${{ secrets.PROJECT_TOKEN }}
|
|
269
301
|
steps:
|
|
270
302
|
- name: Move issue to Production
|
|
271
|
-
if: ${{ env.
|
|
272
|
-
uses: actions/github-script@
|
|
303
|
+
if: ${{ env.PROJECT_TOKEN != '' && env.PROJECT_ID != '{{PROJECT_ID}}' }}
|
|
304
|
+
uses: actions/github-script@v8
|
|
273
305
|
with:
|
|
274
|
-
github-token: ${{ env.
|
|
306
|
+
github-token: ${{ env.PROJECT_TOKEN }}
|
|
275
307
|
script: |
|
|
276
308
|
const prNumber = context.payload.pull_request.number;
|
|
277
309
|
const { owner, repo } = context.repo;
|
|
@@ -297,8 +329,33 @@ jobs:
|
|
|
297
329
|
for (const n of linkedIssues) {
|
|
298
330
|
const { data: issue } = await github.rest.issues.get({ owner, repo, issue_number: n });
|
|
299
331
|
await moveItem(issue.node_id);
|
|
332
|
+
if (issue.labels?.some(l => l.name === 'stage:approval')) {
|
|
333
|
+
await github.rest.issues.removeLabel({ owner, repo, issue_number: n, name: 'stage:approval' }).catch(() => {});
|
|
334
|
+
}
|
|
300
335
|
console.log(`Issue #${n} → Production`);
|
|
301
336
|
}
|
|
302
337
|
} else {
|
|
303
338
|
await moveItem(pr.node_id);
|
|
304
339
|
}
|
|
340
|
+
|
|
341
|
+
cleanup-labels-on-close:
|
|
342
|
+
name: Issue closed → strip stage/agent labels
|
|
343
|
+
if: github.event_name == 'issues' && github.event.action == 'closed'
|
|
344
|
+
runs-on: ubuntu-latest
|
|
345
|
+
steps:
|
|
346
|
+
- name: Remove transient labels
|
|
347
|
+
uses: actions/github-script@v8
|
|
348
|
+
with:
|
|
349
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
350
|
+
script: |
|
|
351
|
+
const { owner, repo } = context.repo;
|
|
352
|
+
const issue_number = context.payload.issue.number;
|
|
353
|
+
const toRemove = [
|
|
354
|
+
'stage:brainstorming', 'stage:detailing',
|
|
355
|
+
'stage:approval', 'stage:development', 'stage:testing',
|
|
356
|
+
'qa:needs-work', 'pr:in-review', 'jules'
|
|
357
|
+
];
|
|
358
|
+
for (const label of toRemove) {
|
|
359
|
+
await github.rest.issues.removeLabel({ owner, repo, issue_number, name: label }).catch(() => {});
|
|
360
|
+
}
|
|
361
|
+
console.log(`Issue #${issue_number} labels cleaned up`);
|
|
@@ -13,8 +13,10 @@ jobs:
|
|
|
13
13
|
qa:
|
|
14
14
|
name: AC Coverage Verification (GitHub Models)
|
|
15
15
|
runs-on: ubuntu-latest
|
|
16
|
+
env:
|
|
17
|
+
PROJECT_TOKEN: ${{ secrets.PROJECT_TOKEN }}
|
|
16
18
|
steps:
|
|
17
|
-
- uses: actions/checkout@
|
|
19
|
+
- uses: actions/checkout@v5.0.1
|
|
18
20
|
with:
|
|
19
21
|
fetch-depth: 0
|
|
20
22
|
|
|
@@ -29,7 +31,7 @@ jobs:
|
|
|
29
31
|
HEAD="${{ github.event.pull_request.head.sha }}"
|
|
30
32
|
|
|
31
33
|
# Get PR diff (truncated to 8000 chars to stay within context limits)
|
|
32
|
-
DIFF=$(git diff "$BASE" "$HEAD" | head -c
|
|
34
|
+
DIFF=$(git diff "$BASE" "$HEAD" | head -c 8000)
|
|
33
35
|
|
|
34
36
|
# Extract linked issues from PR body
|
|
35
37
|
PR_BODY=$(gh pr view "$PR_NUMBER" --json body --jq '.body // ""')
|
|
@@ -49,31 +51,45 @@ jobs:
|
|
|
49
51
|
fi
|
|
50
52
|
|
|
51
53
|
# Serialize prompt as JSON string and call GitHub Models API (30s timeout)
|
|
52
|
-
PROMPT_JSON=$(printf '%s' "You are
|
|
54
|
+
PROMPT_JSON=$(printf '%s' "You are an adversarial product tester. Your mission is to find what the Acceptance Criteria do NOT cover — undefined edge cases, ambiguous states, missing user scenarios. Do NOT review code quality, file structure, or technical consistency.\n\nACCEPTANCE CRITERIA:\n${AC_CONTEXT}\n\nPR DIFF:\n${DIFF}\n\nRespond in exactly 3 lines (do NOT wrap your response in markdown code blocks or any other formatting):\nLine 1: PASS or FAIL (PASS if the PR diff fully satisfies the stated Acceptance Criteria, FAIL if it falls short of covering them)\nLine 2: Gaps: [one-line summary of AC gaps found, or \"none\"]\nLine 3: Not covered: [AC refs where diff falls short, or \"all covered\"]" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()))')
|
|
53
55
|
|
|
54
|
-
RESPONSE
|
|
55
|
-
|
|
56
|
-
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
56
|
+
RESPONSE="API_ERROR"
|
|
57
|
+
for attempt in 1 2 3; do
|
|
58
|
+
RESULT=$(curl -s -X POST \
|
|
59
|
+
"https://models.github.ai/inference/chat/completions" \
|
|
60
|
+
-H "Authorization: Bearer ${GITHUB_TOKEN}" \
|
|
61
|
+
-H "Content-Type: application/json" \
|
|
62
|
+
-d "{\"model\":\"gpt-4o-mini\",\"messages\":[{\"role\":\"user\",\"content\":${PROMPT_JSON}}]}" \
|
|
63
|
+
-w "\n__HTTP_STATUS__:%{http_code}" \
|
|
64
|
+
--max-time 45 2>/dev/null)
|
|
65
|
+
HTTP_STATUS=$(echo "$RESULT" | grep -o '__HTTP_STATUS__:[0-9]*' | cut -d: -f2)
|
|
66
|
+
BODY=$(echo "$RESULT" | sed 's/__HTTP_STATUS__:[0-9]*$//')
|
|
67
|
+
echo "Attempt $attempt: HTTP $HTTP_STATUS"
|
|
68
|
+
if [ "$HTTP_STATUS" = "200" ]; then RESPONSE="$BODY"; break; fi
|
|
69
|
+
[ $attempt -lt 3 ] && sleep 20
|
|
70
|
+
done
|
|
60
71
|
|
|
61
72
|
if [ "$RESPONSE" = "API_ERROR" ]; then
|
|
62
|
-
gh
|
|
73
|
+
GH_TOKEN="$PROJECT_TOKEN" gh api "repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/labels" --method POST -f 'labels[]=infra:qa-broken'
|
|
63
74
|
gh pr comment "$PR_NUMBER" --body "🤖 **QA Agent:** Could not reach GitHub Models API. Manual review required."
|
|
64
|
-
exit
|
|
75
|
+
exit 1
|
|
65
76
|
fi
|
|
66
77
|
|
|
67
|
-
VERDICT=$(echo "$RESPONSE" | python3 -c 'import json,sys,re; d=json.load(sys.stdin); t=d.get("choices",[{}])[0].get("message",{}).get("content","").strip();
|
|
78
|
+
VERDICT=$(echo "$RESPONSE" | python3 -c 'import json,sys,re; d=json.load(sys.stdin); t=d.get("choices",[{}])[0].get("message",{}).get("content","").strip(); lines=[l for l in t.split("\n") if not l.strip().startswith("```")]; first=lines[0].upper() if lines else ""; print("FAIL" if re.search(r"\bFAIL\b",first) else "PASS" if re.search(r"\bPASS\b",first) else "API_ERROR")')
|
|
68
79
|
EXPLANATION=$(echo "$RESPONSE" | python3 -c 'import json,sys; d=json.load(sys.stdin); t=d.get("choices",[{}])[0].get("message",{}).get("content","").strip(); lines=t.split("\n",1); print(lines[1].strip() if len(lines)>1 else "")')
|
|
69
80
|
|
|
70
81
|
if echo "$VERDICT" | grep -q "^PASS"; then
|
|
71
|
-
gh
|
|
72
|
-
gh pr comment "$PR_NUMBER" --body "🤖 **QA Agent:**
|
|
82
|
+
GH_TOKEN="$PROJECT_TOKEN" gh api "repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/labels" --method POST -f 'labels[]=qa:approved'
|
|
83
|
+
gh pr comment "$PR_NUMBER" --body "🤖 **QA Agent:** PASS
|
|
84
|
+
|
|
85
|
+
${EXPLANATION}"
|
|
73
86
|
elif echo "$VERDICT" | grep -q "^FAIL"; then
|
|
74
|
-
gh
|
|
75
|
-
gh pr comment "$PR_NUMBER" --body "🤖 **QA Agent:**
|
|
87
|
+
GH_TOKEN="$PROJECT_TOKEN" gh api "repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/labels" --method POST -f 'labels[]=qa:needs-work'
|
|
88
|
+
gh pr comment "$PR_NUMBER" --body "🤖 **QA Agent:** FAIL
|
|
89
|
+
|
|
90
|
+
${EXPLANATION}"
|
|
76
91
|
else
|
|
77
|
-
gh
|
|
92
|
+
GH_TOKEN="$PROJECT_TOKEN" gh api "repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/labels" --method POST -f 'labels[]=infra:qa-broken'
|
|
78
93
|
gh pr comment "$PR_NUMBER" --body "🤖 **QA Agent:** Could not parse GitHub Models response. Manual review required."
|
|
94
|
+
exit 1
|
|
79
95
|
fi
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
name: QA Gate
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
types: [opened, synchronize, reopened, labeled, unlabeled]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
pull-requests: read
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
qa-gate:
|
|
12
|
+
name: QA Gate
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- name: Check QA status label
|
|
16
|
+
env:
|
|
17
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
18
|
+
run: |
|
|
19
|
+
set -e
|
|
20
|
+
PR_NUMBER="${{ github.event.pull_request.number }}"
|
|
21
|
+
REPO="${{ github.repository }}"
|
|
22
|
+
|
|
23
|
+
PR_LABELS=$(gh pr view "$PR_NUMBER" --repo "$REPO" --json labels --jq '.labels[].name')
|
|
24
|
+
|
|
25
|
+
if echo "$PR_LABELS" | grep -qx "hotfix"; then
|
|
26
|
+
echo "✅ QA Gate: hotfix label — bypassed."
|
|
27
|
+
exit 0
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
if echo "$PR_LABELS" | grep -qx "human-approved"; then
|
|
31
|
+
echo "✅ QA Gate: human-approved label — manual QA sign-off, bypassed."
|
|
32
|
+
exit 0
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
if echo "$PR_LABELS" | grep -qx "qa:approved"; then
|
|
36
|
+
echo "✅ QA Gate: qa:approved — merge allowed."
|
|
37
|
+
exit 0
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
if echo "$PR_LABELS" | grep -qx "infra:qa-broken"; then
|
|
41
|
+
echo "❌ QA Gate: infra:qa-broken — GitHub Models API unreachable. Manual QA review required before merge."
|
|
42
|
+
exit 1
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
if echo "$PR_LABELS" | grep -qx "qa:needs-work"; then
|
|
46
|
+
echo "❌ QA Gate: qa:needs-work — acceptance criteria not fully met. Fix required before merge."
|
|
47
|
+
exit 1
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
echo "❌ QA Gate: no QA label found — AC Coverage Verification has not completed. Wait for the check to finish."
|
|
51
|
+
exit 1
|
package/templates/AGENTS.md
CHANGED
|
@@ -30,40 +30,87 @@ Always start from the current `main` HEAD. Never work over stale snapshots.
|
|
|
30
30
|
## Mandatory Workflow
|
|
31
31
|
|
|
32
32
|
0. **Identity**: Always prefix your GitHub comments with `🤖 **Agent:** ` to distinguish yourself.
|
|
33
|
-
1. **
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
33
|
+
1. **Stage Check**: Before applying any label or taking any action, run `gh issue view <N> --json labels,title` to determine the issue's current stage. State: *"Issue #N — [title] — is currently at `<stage>`. Requesting confirmation to advance to `<next>`."* Wait for an explicit stage-advancement signal in this conversation turn. A prioritization signal ("work on X", "tackle X next") does **not** count as confirmation — only an explicit signal counts (e.g. "start brainstorming", "yes advance", "go"). **Exceptions — skip this step and proceed directly**:
|
|
34
|
+
- `spec:approved` → begin implementation (gate already passed)
|
|
35
|
+
- `stage:development` or `stage:testing` → issue is owned by automation; do not intervene unless explicitly asked to fix a specific problem
|
|
36
|
+
- `stage:approval` → spec already written; wait for PM to add `spec:approved` before doing anything
|
|
37
|
+
2. **Initial State**: Apply the `stage:brainstorming` label using the GitHub CLI (`gh issue edit <N> --add-label "stage:brainstorming"`). **Exception — pre-spec'd issue**: if the issue body already contains all required spec sections (`## Problem`, `## Solution`, `## Acceptance Criteria`, `## Edge Cases`, `## Out of Scope`, `## Files to Modify`) — all present and non-empty — apply `stage:approval` directly in a single call instead, skipping `stage:brainstorming` and `stage:detailing`.
|
|
38
|
+
3. Read the issue entirely — understand its type (US/BUG/TASK/SPIKE) and the Acceptance Criteria.
|
|
39
|
+
4. Read `docs/pdlc.md` — understand the PDLC and the Definition of Done in this project.
|
|
40
|
+
5. Read all files mentioned in the issue's technical context.
|
|
41
|
+
6. Implement the **minimum viable change** that satisfies the ACs — do not refactor beyond scope.
|
|
42
|
+
7. Run tests: `{{TEST_COMMAND}}`
|
|
43
|
+
8. Run typecheck (if applicable): `{{TYPECHECK_COMMAND}}`
|
|
44
|
+
9. Create a Pull Request with `Closes #N` in the body — automation moves the board.
|
|
41
45
|
|
|
42
|
-
|
|
46
|
+
## Spec Format
|
|
43
47
|
|
|
44
|
-
When
|
|
48
|
+
When writing or rewriting an issue body during detailing, include ALL sections below. Omitting any section blocks `stage:approval`.
|
|
49
|
+
|
|
50
|
+
**Destination: the issue body.** Write spec content to the issue body using `gh issue edit <N> --body "..."` — not to a file. A file is acceptable as optional reference only. Automation checks the issue body for `## Acceptance Criteria` and `## Files to Modify` to advance the stage; content that exists only in a file is invisible to it.
|
|
45
51
|
|
|
46
52
|
```
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
53
|
+
## Problem
|
|
54
|
+
[1-3 sentences. What fails. Who affected. Measured impact.]
|
|
55
|
+
|
|
56
|
+
## Sprint Goal / Success Metrics
|
|
57
|
+
| Metric | Baseline | Target | When |
|
|
58
|
+
|--------|----------|--------|------|
|
|
50
59
|
|
|
51
|
-
|
|
60
|
+
## Solution
|
|
61
|
+
[Behavioral description of what is built. No implementation details.]
|
|
52
62
|
|
|
53
63
|
## Acceptance Criteria
|
|
64
|
+
**AC1 — [name]**
|
|
65
|
+
- Given [precondition]
|
|
66
|
+
- When [action]
|
|
67
|
+
- Then [outcome]
|
|
68
|
+
|
|
69
|
+
## Edge Cases
|
|
70
|
+
- EC1: [condition] → [expected behavior]
|
|
54
71
|
|
|
55
|
-
|
|
56
|
-
-
|
|
57
|
-
- When ...
|
|
58
|
-
- Then ...
|
|
72
|
+
## Out of Scope
|
|
73
|
+
- [item] — reason
|
|
59
74
|
|
|
60
|
-
|
|
61
|
-
|
|
75
|
+
## Non-Functional Requirements
|
|
76
|
+
- Performance: [metric with number]
|
|
77
|
+
- Security: [constraint]
|
|
78
|
+
- Reliability: [constraint]
|
|
79
|
+
> For pure docs/markdown issues with zero runtime behavior, include the NFRs section and state "N/A".
|
|
62
80
|
|
|
63
|
-
## Files to
|
|
64
|
-
- `path/to/file
|
|
81
|
+
## Files to Modify
|
|
82
|
+
- `path/to/file` — what changes
|
|
65
83
|
```
|
|
66
84
|
|
|
85
|
+
## Stage Transition Rules (non-negotiable)
|
|
86
|
+
|
|
87
|
+
MUST apply `stage:brainstorming` label immediately on starting work — before reading
|
|
88
|
+
any code, before invoking any skill. Then read context and present problem summary
|
|
89
|
+
+ 2–3 solution options in a single message.
|
|
90
|
+
|
|
91
|
+
MUST NOT add `stage:detailing` label until the user has explicitly selected
|
|
92
|
+
an approach in the current conversation turn. Work done in a prior
|
|
93
|
+
planning session does NOT count as confirmation.
|
|
94
|
+
|
|
95
|
+
MUST NOT add `spec:approved` or `stage:development` — these represent final
|
|
96
|
+
human approval or automation output. Adding them manually triggers irreversible
|
|
97
|
+
automation (Jules dispatch, board move).
|
|
98
|
+
|
|
99
|
+
MUST NOT manually add `stage:approval` except via the pre-spec'd exception
|
|
100
|
+
below. In the standard flow, `stage:approval` is set after you write a complete
|
|
101
|
+
spec and the user confirms; it is not applied before the spec exists.
|
|
102
|
+
|
|
103
|
+
Each stage transition requires a fresh explicit signal from the user in the same
|
|
104
|
+
session where the transition happens. The pre-spec'd exception is the only
|
|
105
|
+
deviation from this rule.
|
|
106
|
+
|
|
107
|
+
**Pre-spec'd exception**: if the issue body already contains all required spec
|
|
108
|
+
sections (`## Problem`, `## Solution`, `## Acceptance Criteria`, `## Edge Cases`,
|
|
109
|
+
`## Out of Scope`, `## Files to Modify`) — all present and non-empty — apply
|
|
110
|
+
`stage:approval` directly in a single `gh issue edit` call, skipping
|
|
111
|
+
`stage:brainstorming` and `stage:detailing`. One label event eliminates the
|
|
112
|
+
race condition that causes the project board to land on the wrong column.
|
|
113
|
+
|
|
67
114
|
## Pipeline Updates
|
|
68
115
|
|
|
69
116
|
To add or configure optional agents (Jules, QA Agent, Sentinel) at any time:
|
|
@@ -80,7 +127,11 @@ Run this when the user says anything like "update the pipeline", "update the boa
|
|
|
80
127
|
- Never open a PR without passing the tests.
|
|
81
128
|
- Never implement beyond the immediate scope of the issue.
|
|
82
129
|
- Never create future-proofing abstractions for hypothetical features.
|
|
83
|
-
-
|
|
130
|
+
- The agent MUST NOT apply these labels under any circumstances (PM only):
|
|
131
|
+
- `spec:approved`: triggers Jules dispatch + board move to Development.
|
|
132
|
+
- `qa:approved`: triggers board move to Code Review.
|
|
133
|
+
- `qa:needs-work`: signals the PR requires changes and halts the flow.
|
|
134
|
+
- Never add or remove stage:* labels manually, except: `stage:brainstorming` as the initial label when starting work, or `stage:approval` directly when applying the pre-spec'd exception. All other stage transitions are owned by GitHub Actions automation and the PM.
|
|
84
135
|
{{EXTRA_DONT}}
|
|
85
136
|
|
|
86
137
|
## Project Standards
|
package/templates/docs/pdlc.md
CHANGED
|
@@ -4,9 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
| Column | Meaning | Who moves the card |
|
|
6
6
|
|---|---|---|
|
|
7
|
-
| 💡 Idea
|
|
8
|
-
|
|
|
9
|
-
| 🧠 Brainstorming | Claude proposed approaches, awaiting PM gate | Label `stage:brainstorming` |
|
|
7
|
+
| 💡 Idea | Backlog — tell agent: "work on issue #XX" | Don't move manually |
|
|
8
|
+
| 🧠 Brainstorming | AI reading context, proposing approaches and trade-offs | Label `stage:brainstorming` |
|
|
10
9
|
| 📐 Detail Solution | Claude is writing the technical spec | Label `stage:detailing` |
|
|
11
10
|
| ✅ Approval | Spec ready, awaiting `spec:approved` label | Label `spec:approved` |
|
|
12
11
|
| ⚙️ Development | Agent implementing the spec | Label `stage:development` |
|
|
@@ -37,7 +36,6 @@ REPO = {{REPO_OWNER}}/{{REPO_NAME}}
|
|
|
37
36
|
| Column | Option ID |
|
|
38
37
|
|---|---|
|
|
39
38
|
| 💡 Idea | `{{ID_IDEA}}` |
|
|
40
|
-
| 🔍 Exploration | `{{ID_EXPLORATION}}` |
|
|
41
39
|
| 🧠 Brainstorming | `{{ID_BRAINSTORMING}}` |
|
|
42
40
|
| 📐 Detail Solution | `{{ID_DETAIL}}` |
|
|
43
41
|
| ✅ Approval | `{{ID_APPROVAL}}` |
|
|
@@ -60,7 +58,7 @@ REPO = {{REPO_OWNER}}/{{REPO_NAME}}
|
|
|
60
58
|
```
|
|
61
59
|
[icon] [PREFIX]: [short description, imperative tense]
|
|
62
60
|
|
|
63
|
-
|
|
61
|
+
✨ feat: new feature or behavioral change
|
|
64
62
|
🐛 BUG: bug
|
|
65
63
|
🔧 TASK: operational task
|
|
66
64
|
🔬 SPIKE: exploration/evaluation spike
|
|
@@ -70,7 +68,6 @@ REPO = {{REPO_OWNER}}/{{REPO_NAME}}
|
|
|
70
68
|
|
|
71
69
|
| Label | Entity | Color | Meaning |
|
|
72
70
|
|---|---|---|---|
|
|
73
|
-
| `stage:exploration` | Issue | Purple | Issue is being evaluated |
|
|
74
71
|
| `stage:brainstorming` | Issue | Pink | Proposed approaches awaiting PM gate |
|
|
75
72
|
| `stage:detailing` | Issue | Blue | Technical spec is being written |
|
|
76
73
|
| `stage:development` | Issue | Orange | Agent is implementing the spec |
|
|
@@ -80,16 +77,16 @@ REPO = {{REPO_OWNER}}/{{REPO_NAME}}
|
|
|
80
77
|
| `qa:approved` | PR | Green | QA Agent passed — AC coverage verified |
|
|
81
78
|
| `qa:needs-work` | PR | Red | QA Agent failed — PR needs changes |
|
|
82
79
|
| `infra:qa-broken` | PR | Orange | QA Agent error — manual review required |
|
|
83
|
-
| `type:
|
|
84
|
-
| `type:task` | Issue | Yellow | Operational/non-functional change —
|
|
85
|
-
| `type:bug` | Issue | Red | Something broken —
|
|
80
|
+
| `type:feature` | Issue | Blue | New feature or behavioral change — full flow |
|
|
81
|
+
| `type:task` | Issue | Yellow | Operational/non-functional change — full flow |
|
|
82
|
+
| `type:bug` | Issue | Red | Something broken — full flow |
|
|
86
83
|
| `type:spike` | Issue | Gray | Research/evaluation — never reaches Development |
|
|
87
84
|
|
|
88
85
|
## Approval Gates
|
|
89
86
|
|
|
90
87
|
**Gate 1 — PM/Ideation (Brainstorming):**
|
|
91
|
-
|
|
92
|
-
Format: *"Approved — proceed with option X."*
|
|
88
|
+
Agent presents problem summary + 2–3 solution options in a single message. You select an approach.
|
|
89
|
+
Format: *"Option X"* or *"Go with B"* or *"Approved — proceed with option X."*
|
|
93
90
|
|
|
94
91
|
**Gate 2 — Tech Lead (Spec):**
|
|
95
92
|
You add the `spec:approved` label to the issue after reviewing the technical spec in the body.
|
|
@@ -101,12 +98,23 @@ The `type:*` label is the authoritative signal — set automatically by the agen
|
|
|
101
98
|
|
|
102
99
|
| Label | Flow |
|
|
103
100
|
|---|---|
|
|
104
|
-
| `type:
|
|
105
|
-
| `type:task` |
|
|
106
|
-
| `type:bug` |
|
|
107
|
-
| `type:spike` |
|
|
101
|
+
| `type:feature` | brainstorming → Gate 1 → detailing → approval |
|
|
102
|
+
| `type:task` | brainstorming → Gate 1 → detailing → approval |
|
|
103
|
+
| `type:bug` | brainstorming → Gate 1 → detailing → approval |
|
|
104
|
+
| `type:spike` | brainstorming → Gate 1 → detailing → conclusion comment (never reaches Development) |
|
|
108
105
|
|
|
109
|
-
If no `type:*` label present and agent confidence < 85%, defaults to `type:
|
|
106
|
+
If no `type:*` label present and agent confidence < 85%, defaults to `type:feature` (safe fallback — never skips gates by omission).
|
|
107
|
+
|
|
108
|
+
## Bypass Mechanism
|
|
109
|
+
|
|
110
|
+
Agents MUST NOT skip any stage. The ONLY authorized bypasses are:
|
|
111
|
+
|
|
112
|
+
| Mechanism | Who authorizes | What it bypasses |
|
|
113
|
+
|---|---|---|
|
|
114
|
+
| `human-approved` label on issue | PM (human) only | All stage gates |
|
|
115
|
+
| Branch prefix `hotfix/` | PM (human) only | PR gate only |
|
|
116
|
+
|
|
117
|
+
Agents MUST NOT self-authorize a bypass. Stop and ask the PM explicitly.
|
|
110
118
|
|
|
111
119
|
## Definition of Done
|
|
112
120
|
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
# Agentic PDLC Instructions for GitHub Copilot Workspace
|
|
2
|
-
|
|
3
|
-
Hello! You are operating within the Agentic PDLC framework.
|
|
4
|
-
|
|
5
|
-
Before addressing the user's prompt or executing any task in this workspace, you MUST:
|
|
6
|
-
|
|
7
|
-
1. Read the `AGENTS.md` file located at the root of this repository. It contains the primary instructions, definitions of done, and absolute invariants you must respect.
|
|
8
|
-
2. Read `docs/pdlc.md` to understand your role in the project lifecycle.
|
|
9
|
-
|
|
10
|
-
Never violate the invariants described in those files. If a user asks you to do something that contradicts `AGENTS.md`, you must refuse and point out the conflict.
|
|
11
|
-
|
|
12
|
-
Focus on delivering the absolute minimum required to satisfy the immediate technical specs. Start!
|