create-agentic-pdlc 2.3.0 → 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/metrics/raw/2026-W22.jsonl +114 -0
- 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 +150 -27
- package/.github/workflows/ci.yml +1 -1
- package/.github/workflows/npm-publish.yml +2 -2
- package/.github/workflows/pdlc-health-check.yml +1 -1
- package/.github/workflows/pdlc-stage-gate.yml +2 -2
- package/.github/workflows/project-automation.yml +51 -12
- package/.github/workflows/qa-agent.yml +22 -11
- package/.github/workflows/qa-gate.yml +51 -0
- package/AGENTS.md +50 -8
- package/CLAUDE.md +2 -0
- package/SETUP.md +2 -1
- package/adapters/claude-code/skill.md +32 -11
- package/adapters/hooks/pdlc-stage-gate.sh +3 -8
- package/bin/cli.js +23 -2
- package/docs/pdlc.md +5 -5
- 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 +22 -13
- package/{.agentic-pdlc/templates → templates}/.github/workflows/agentic-metrics.yml +150 -27
- package/templates/.github/workflows/ci.yml +1 -1
- package/templates/.github/workflows/pdlc-health-check.yml +1 -1
- package/templates/.github/workflows/pdlc-stage-gate.yml +2 -2
- package/templates/.github/workflows/project-automation.yml +71 -32
- package/templates/.github/workflows/qa-agent.yml +32 -18
- package/templates/.github/workflows/qa-gate.yml +51 -0
- package/templates/AGENTS.md +57 -29
- package/templates/docs/pdlc.md +4 -4
- 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 -54
- package/.agentic-pdlc/templates/.github/workflows/pdlc-health-check.yml +0 -121
- package/.agentic-pdlc/templates/.github/workflows/pdlc-stage-gate.yml +0 -51
- package/.agentic-pdlc/templates/.github/workflows/project-automation.yml +0 -274
- 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 -104
- package/.agentic-pdlc/templates/docs/pdlc.md +0 -123
|
@@ -31,7 +31,7 @@ jobs:
|
|
|
31
31
|
steps:
|
|
32
32
|
- name: Detect Label and Move Issue
|
|
33
33
|
if: ${{ env.PROJECT_TOKEN != '' && env.PROJECT_ID != '{{PROJECT_ID}}' }}
|
|
34
|
-
uses: actions/github-script@
|
|
34
|
+
uses: actions/github-script@v8
|
|
35
35
|
with:
|
|
36
36
|
github-token: ${{ env.PROJECT_TOKEN }}
|
|
37
37
|
script: |
|
|
@@ -51,9 +51,6 @@ jobs:
|
|
|
51
51
|
} else if (labelName === 'stage:development') {
|
|
52
52
|
targetStatusId = process.env.STATUS_DEVELOPMENT;
|
|
53
53
|
stageName = 'Development';
|
|
54
|
-
} else if (labelName === 'stage:testing') {
|
|
55
|
-
targetStatusId = process.env.STATUS_TESTING;
|
|
56
|
-
stageName = 'Testing';
|
|
57
54
|
}
|
|
58
55
|
|
|
59
56
|
if (!targetStatusId) {
|
|
@@ -83,6 +80,42 @@ jobs:
|
|
|
83
80
|
await moveItem(node_id, targetStatusId);
|
|
84
81
|
console.log(`Issue #${number} moved to ${stageName}`);
|
|
85
82
|
|
|
83
|
+
# human-approved on issue → qa:approved on linked open PRs
|
|
84
|
+
handle-human-approved:
|
|
85
|
+
name: human-approved → qa:approved on linked PRs
|
|
86
|
+
if: github.event_name == 'issues' && github.event.action == 'labeled' && github.event.label.name == 'human-approved'
|
|
87
|
+
runs-on: ubuntu-latest
|
|
88
|
+
permissions:
|
|
89
|
+
issues: write
|
|
90
|
+
pull-requests: write
|
|
91
|
+
steps:
|
|
92
|
+
- name: Add qa:approved to linked open PRs
|
|
93
|
+
uses: actions/github-script@v8
|
|
94
|
+
with:
|
|
95
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
96
|
+
script: |
|
|
97
|
+
const { owner, repo } = context.repo;
|
|
98
|
+
const issueNumber = context.payload.issue.number;
|
|
99
|
+
const pattern = new RegExp(`(?:Closes?|Fixes?|Resolves?)\\s+#${issueNumber}\\b`, 'i');
|
|
100
|
+
|
|
101
|
+
const prs = await github.paginate(github.rest.pulls.list, {
|
|
102
|
+
owner, repo, state: 'open', per_page: 100
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
const linked = prs.filter(pr => pattern.test(pr.body ?? ''));
|
|
106
|
+
|
|
107
|
+
if (linked.length === 0) {
|
|
108
|
+
console.log(`No open PRs linking issue #${issueNumber}. Exiting.`);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
for (const pr of linked) {
|
|
113
|
+
await github.rest.issues.addLabels({
|
|
114
|
+
owner, repo, issue_number: pr.number, labels: ['qa:approved']
|
|
115
|
+
}).catch(() => {});
|
|
116
|
+
console.log(`PR #${pr.number} → qa:approved`);
|
|
117
|
+
}
|
|
118
|
+
|
|
86
119
|
# OPTIONAL: Uncomment to enable architecture-violation → Idea
|
|
87
120
|
# move-violation-to-board:
|
|
88
121
|
# name: architecture-violation → 💡 Idea
|
|
@@ -91,7 +124,7 @@ jobs:
|
|
|
91
124
|
# steps:
|
|
92
125
|
# - name: Move issue to Idea
|
|
93
126
|
# if: ${{ env.PROJECT_TOKEN != '' && env.PROJECT_ID != '{{PROJECT_ID}}' }}
|
|
94
|
-
# uses: actions/github-script@
|
|
127
|
+
# uses: actions/github-script@v8
|
|
95
128
|
# with:
|
|
96
129
|
# github-token: ${{ env.PROJECT_TOKEN }}
|
|
97
130
|
# script: |
|
|
@@ -121,7 +154,7 @@ jobs:
|
|
|
121
154
|
steps:
|
|
122
155
|
- name: Move linked issue to Testing
|
|
123
156
|
if: ${{ env.PROJECT_TOKEN != '' && env.PROJECT_ID != '{{PROJECT_ID}}' }}
|
|
124
|
-
uses: actions/github-script@
|
|
157
|
+
uses: actions/github-script@v8
|
|
125
158
|
with:
|
|
126
159
|
github-token: ${{ env.PROJECT_TOKEN }}
|
|
127
160
|
script: |
|
|
@@ -152,7 +185,7 @@ jobs:
|
|
|
152
185
|
});
|
|
153
186
|
};
|
|
154
187
|
|
|
155
|
-
const stageLabelsToRemove = ['stage:development', 'stage:brainstorming', 'stage:detailing', '
|
|
188
|
+
const stageLabelsToRemove = ['stage:development', 'stage:brainstorming', 'stage:detailing', 'jules'];
|
|
156
189
|
|
|
157
190
|
if (linkedIssues.length > 0) {
|
|
158
191
|
for (const n of linkedIssues) {
|
|
@@ -181,7 +214,7 @@ jobs:
|
|
|
181
214
|
steps:
|
|
182
215
|
- name: Move linked issue to Code Review / PR
|
|
183
216
|
if: ${{ env.PROJECT_TOKEN != '' && env.PROJECT_ID != '{{PROJECT_ID}}' }}
|
|
184
|
-
uses: actions/github-script@
|
|
217
|
+
uses: actions/github-script@v8
|
|
185
218
|
with:
|
|
186
219
|
github-token: ${{ env.PROJECT_TOKEN }}
|
|
187
220
|
script: |
|
|
@@ -215,6 +248,9 @@ jobs:
|
|
|
215
248
|
for (const n of linkedIssues) {
|
|
216
249
|
const { data: issue } = await github.rest.issues.get({ owner, repo, issue_number: n });
|
|
217
250
|
await moveItem(issue.node_id);
|
|
251
|
+
if (issue.labels?.some(l => l.name === 'stage:testing')) {
|
|
252
|
+
await github.rest.issues.removeLabel({ owner, repo, issue_number: n, name: 'stage:testing' }).catch(() => {});
|
|
253
|
+
}
|
|
218
254
|
console.log(`Issue #${n} → Code Review / PR`);
|
|
219
255
|
}
|
|
220
256
|
} else {
|
|
@@ -232,7 +268,7 @@ jobs:
|
|
|
232
268
|
steps:
|
|
233
269
|
- name: Swap PR labels
|
|
234
270
|
if: ${{ env.PROJECT_TOKEN != '' && env.PROJECT_ID != '{{PROJECT_ID}}' }}
|
|
235
|
-
uses: actions/github-script@
|
|
271
|
+
uses: actions/github-script@v8
|
|
236
272
|
with:
|
|
237
273
|
github-token: ${{ env.PROJECT_TOKEN }}
|
|
238
274
|
script: |
|
|
@@ -251,7 +287,7 @@ jobs:
|
|
|
251
287
|
steps:
|
|
252
288
|
- name: Move issue to Production
|
|
253
289
|
if: ${{ env.PROJECT_TOKEN != '' && env.PROJECT_ID != '{{PROJECT_ID}}' }}
|
|
254
|
-
uses: actions/github-script@
|
|
290
|
+
uses: actions/github-script@v8
|
|
255
291
|
with:
|
|
256
292
|
github-token: ${{ env.PROJECT_TOKEN }}
|
|
257
293
|
script: |
|
|
@@ -279,6 +315,9 @@ jobs:
|
|
|
279
315
|
for (const n of linkedIssues) {
|
|
280
316
|
const { data: issue } = await github.rest.issues.get({ owner, repo, issue_number: n });
|
|
281
317
|
await moveItem(issue.node_id);
|
|
318
|
+
if (issue.labels?.some(l => l.name === 'stage:approval')) {
|
|
319
|
+
await github.rest.issues.removeLabel({ owner, repo, issue_number: n, name: 'stage:approval' }).catch(() => {});
|
|
320
|
+
}
|
|
282
321
|
console.log(`Issue #${n} → Production`);
|
|
283
322
|
}
|
|
284
323
|
} else {
|
|
@@ -291,7 +330,7 @@ jobs:
|
|
|
291
330
|
runs-on: ubuntu-latest
|
|
292
331
|
steps:
|
|
293
332
|
- name: Remove transient labels
|
|
294
|
-
uses: actions/github-script@
|
|
333
|
+
uses: actions/github-script@v8
|
|
295
334
|
with:
|
|
296
335
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
297
336
|
script: |
|
|
@@ -300,7 +339,7 @@ jobs:
|
|
|
300
339
|
const toRemove = [
|
|
301
340
|
'stage:brainstorming', 'stage:detailing',
|
|
302
341
|
'stage:approval', 'stage:development', 'stage:testing',
|
|
303
|
-
'
|
|
342
|
+
'qa:needs-work', 'pr:in-review', 'jules'
|
|
304
343
|
];
|
|
305
344
|
for (const label of toRemove) {
|
|
306
345
|
await github.rest.issues.removeLabel({ owner, repo, issue_number, name: label }).catch(() => {});
|
|
@@ -19,7 +19,7 @@ jobs:
|
|
|
19
19
|
STATUS_FIELD_ID: "PVTSSF_lAHODpFFL84BXg7hzhStRHI"
|
|
20
20
|
STATUS_CODE_REVIEW_PR: "86ca9720"
|
|
21
21
|
steps:
|
|
22
|
-
- uses: actions/checkout@
|
|
22
|
+
- uses: actions/checkout@v5.0.1
|
|
23
23
|
with:
|
|
24
24
|
fetch-depth: 0
|
|
25
25
|
|
|
@@ -34,7 +34,7 @@ jobs:
|
|
|
34
34
|
HEAD="${{ github.event.pull_request.head.sha }}"
|
|
35
35
|
|
|
36
36
|
# Get PR diff (truncated to 8000 chars to stay within context limits)
|
|
37
|
-
DIFF=$(git diff "$BASE" "$HEAD" | head -c
|
|
37
|
+
DIFF=$(git diff "$BASE" "$HEAD" | head -c 8000)
|
|
38
38
|
|
|
39
39
|
# Extract linked issues from PR body
|
|
40
40
|
PR_BODY=$(gh pr view "$PR_NUMBER" --json body --jq '.body // ""')
|
|
@@ -53,20 +53,30 @@ jobs:
|
|
|
53
53
|
AC_CONTEXT="No linked issue found. Evaluate if the PR description is self-contained."
|
|
54
54
|
fi
|
|
55
55
|
|
|
56
|
-
# Serialize prompt as JSON string and call GitHub Models API (
|
|
56
|
+
# Serialize prompt as JSON string and call GitHub Models API (3 attempts, 20s backoff)
|
|
57
57
|
PROMPT_JSON=$(printf '%s' "You are a senior QA engineer. Review whether this PR diff satisfies the Acceptance Criteria below.\n\nACCEPTANCE CRITERIA:\n${AC_CONTEXT}\n\nPR DIFF:\n${DIFF}\n\nFirst line of your response must be exactly one word: PASS or FAIL. Second line: brief explanation (max 3 sentences)." | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()))')
|
|
58
58
|
|
|
59
|
-
RESPONSE
|
|
60
|
-
|
|
61
|
-
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
59
|
+
RESPONSE="API_ERROR"
|
|
60
|
+
for attempt in 1 2 3; do
|
|
61
|
+
RESULT=$(curl -s -X POST \
|
|
62
|
+
"https://models.github.ai/inference/chat/completions" \
|
|
63
|
+
-H "Authorization: Bearer ${GITHUB_TOKEN}" \
|
|
64
|
+
-H "Content-Type: application/json" \
|
|
65
|
+
-d "{\"model\":\"gpt-4o-mini\",\"messages\":[{\"role\":\"user\",\"content\":${PROMPT_JSON}}]}" \
|
|
66
|
+
-w "\n__HTTP_STATUS__:%{http_code}" \
|
|
67
|
+
--max-time 45 2>/dev/null)
|
|
68
|
+
HTTP_STATUS=$(echo "$RESULT" | grep -o '__HTTP_STATUS__:[0-9]*' | cut -d: -f2)
|
|
69
|
+
BODY=$(echo "$RESULT" | sed 's/__HTTP_STATUS__:[0-9]*$//')
|
|
70
|
+
echo "Attempt $attempt: HTTP $HTTP_STATUS"
|
|
71
|
+
if [ "$HTTP_STATUS" = "200" ]; then RESPONSE="$BODY"; break; fi
|
|
72
|
+
[ $attempt -lt 3 ] && sleep 20
|
|
73
|
+
done
|
|
65
74
|
|
|
66
75
|
if [ "$RESPONSE" = "API_ERROR" ]; then
|
|
67
76
|
GH_TOKEN="$PROJECT_TOKEN" gh api "repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/labels" --method POST -f 'labels[]=infra:qa-broken'
|
|
68
77
|
gh pr comment "$PR_NUMBER" --body "🤖 **QA Agent:** Could not reach GitHub Models API. Manual review required."
|
|
69
|
-
exit
|
|
78
|
+
# exit 1 marks this check as failed; qa-gate.yml enforces the merge block via infra:qa-broken label
|
|
79
|
+
exit 1
|
|
70
80
|
fi
|
|
71
81
|
|
|
72
82
|
VERDICT=$(echo "$RESPONSE" | python3 -c 'import json,sys,re; d=json.load(sys.stdin); t=d.get("choices",[{}])[0].get("message",{}).get("content","").strip(); first=t.split("\n")[0].upper() if t else ""; print("FAIL" if re.search(r"\bFAIL\b",first) else "PASS" if re.search(r"\bPASS\b",first) else "API_ERROR")')
|
|
@@ -81,11 +91,12 @@ jobs:
|
|
|
81
91
|
else
|
|
82
92
|
GH_TOKEN="$PROJECT_TOKEN" gh api "repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/labels" --method POST -f 'labels[]=infra:qa-broken'
|
|
83
93
|
gh pr comment "$PR_NUMBER" --body "🤖 **QA Agent:** Could not parse GitHub Models response. Manual review required."
|
|
94
|
+
exit 1
|
|
84
95
|
fi
|
|
85
96
|
|
|
86
97
|
- name: Move board card to Code Review on qa:approved
|
|
87
98
|
if: ${{ env.PROJECT_TOKEN != '' && env.PROJECT_ID != '{{PROJECT_ID}}' }}
|
|
88
|
-
uses: actions/github-script@
|
|
99
|
+
uses: actions/github-script@v8
|
|
89
100
|
with:
|
|
90
101
|
github-token: ${{ env.PROJECT_TOKEN }}
|
|
91
102
|
script: |
|
|
@@ -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/AGENTS.md
CHANGED
|
@@ -27,14 +27,55 @@ Always start from the current `main` HEAD. Never work over stale snapshots.
|
|
|
27
27
|
## Mandatory Workflow
|
|
28
28
|
|
|
29
29
|
0. **Identity**: Always prefix your GitHub comments with `🤖 **Agent:** ` to distinguish yourself.
|
|
30
|
-
1. **
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
30
|
+
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**:
|
|
31
|
+
- `spec:approved` → begin implementation (gate already passed)
|
|
32
|
+
- `stage:development` or `stage:testing` → issue is owned by automation; do not intervene unless explicitly asked to fix a specific problem
|
|
33
|
+
- `stage:approval` → spec already written; wait for PM to add `spec:approved` before doing anything
|
|
34
|
+
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`.
|
|
35
|
+
3. Read the issue entirely — understand its type (US/BUG/TASK/SPIKE) and the Acceptance Criteria.
|
|
36
|
+
4. Read `docs/pdlc.md` — understand the PDLC and the Definition of Done in this project.
|
|
37
|
+
5. Read all files mentioned in the issue's technical context.
|
|
38
|
+
6. Implement the **minimum viable change** that satisfies the ACs — do not refactor beyond scope.
|
|
39
|
+
7. Run tests: `echo "No tests/build needed."`
|
|
40
|
+
8. Run typecheck: `echo "No typecheck needed."`
|
|
41
|
+
9. Create a Pull Request with `Closes #N` in the body — automation moves the board.
|
|
42
|
+
|
|
43
|
+
## Spec Format
|
|
44
|
+
|
|
45
|
+
When writing or rewriting an issue body during detailing, include ALL sections below. Omitting any section blocks `stage:approval`.
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
## Problem
|
|
49
|
+
[1-3 sentences. What fails. Who affected. Measured impact.]
|
|
50
|
+
|
|
51
|
+
## Sprint Goal / Success Metrics
|
|
52
|
+
| Metric | Baseline | Target | When |
|
|
53
|
+
|--------|----------|--------|------|
|
|
54
|
+
|
|
55
|
+
## Solution
|
|
56
|
+
[Behavioral description of what is built. No implementation details.]
|
|
57
|
+
|
|
58
|
+
## Acceptance Criteria
|
|
59
|
+
**AC1 — [name]**
|
|
60
|
+
- Given [precondition]
|
|
61
|
+
- When [action]
|
|
62
|
+
- Then [outcome]
|
|
63
|
+
|
|
64
|
+
## Edge Cases
|
|
65
|
+
- EC1: [condition] → [expected behavior]
|
|
66
|
+
|
|
67
|
+
## Out of Scope
|
|
68
|
+
- [item] — reason
|
|
69
|
+
|
|
70
|
+
## Non-Functional Requirements
|
|
71
|
+
- Performance: [metric with number]
|
|
72
|
+
- Security: [constraint]
|
|
73
|
+
- Reliability: [constraint]
|
|
74
|
+
> For pure docs/markdown issues with zero runtime behavior, include the NFRs section and state "N/A".
|
|
75
|
+
|
|
76
|
+
## Files to Modify
|
|
77
|
+
- `path/to/file` — what changes
|
|
78
|
+
```
|
|
38
79
|
|
|
39
80
|
## What NOT to do
|
|
40
81
|
|
|
@@ -50,3 +91,4 @@ Always start from the current `main` HEAD. Never work over stale snapshots.
|
|
|
50
91
|
- **Lint/Types:** `echo "No tests/build needed."`
|
|
51
92
|
- **Typecheck:** `echo "No typecheck needed."`
|
|
52
93
|
- **Build:** `echo "No tests/build needed."`
|
|
94
|
+
- **Canonical secret name:** `PROJECT_TOKEN` (not `PROJECT_PAT`) — use this in all workflow files, both live and templates
|
package/CLAUDE.md
CHANGED
|
@@ -33,6 +33,8 @@ The PreToolUse hook will block `gh pr create` automatically if this rule is viol
|
|
|
33
33
|
|
|
34
34
|
**Detailing is fully autonomous.** Write the complete spec, add it to the issue, advance to `stage:approval` — no confirmation needed. Then **stop and wait** for human to add `spec:approved` before any implementation.
|
|
35
35
|
|
|
36
|
+
**Spec 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.
|
|
37
|
+
|
|
36
38
|
## Stage Transition Rules (non-negotiable)
|
|
37
39
|
|
|
38
40
|
MUST apply `stage:brainstorming` label immediately on starting work — before
|
package/SETUP.md
CHANGED
|
@@ -63,7 +63,7 @@ npx create-agentic-pdlc
|
|
|
63
63
|
The CLI will:
|
|
64
64
|
1. Ask you which AI Agent you use (Claude Code, Cursor, etc.).
|
|
65
65
|
2. Copy the system instructions pointing to our interactive Setup Mode.
|
|
66
|
-
3. Automatically
|
|
66
|
+
3. Automatically install the base templates into your project.
|
|
67
67
|
|
|
68
68
|
Once the CLI finishes, it will instruct you to open your AI agent and run the **Setup Mode**. Your AI agent will then ask you the required project variables interactively and generate `AGENTS.md`, `docs/pdlc.md`, and the GitHub Actions for you!
|
|
69
69
|
|
|
@@ -169,6 +169,7 @@ The issue appears in your GitHub notifications automatically — zero extra setu
|
|
|
169
169
|
```bash
|
|
170
170
|
cp .agentic-pdlc/templates/.github/workflows/agentic-metrics.yml .github/workflows/agentic-metrics.yml
|
|
171
171
|
```
|
|
172
|
+
> If the file is missing, re-run `npx create-agentic-pdlc` to update your installed templates.
|
|
172
173
|
|
|
173
174
|
2. Commit and push. The first pulse runs next Sunday, or trigger it manually:
|
|
174
175
|
```bash
|
|
@@ -106,21 +106,21 @@ If `AGENTS.md` and `docs/pdlc.md` are present, you are in **Execution Mode**.
|
|
|
106
106
|
|
|
107
107
|
Reading the issue title and body for type inference is exempt from the initial label requirement: it is metadata already present in the request, not code reading or skill invocation.
|
|
108
108
|
|
|
109
|
-
1. Check if issue already has a `type:*` label (`type:
|
|
109
|
+
1. Check if issue already has a `type:*` label (`type:feature`, `type:task`, `type:bug`, `type:spike`) → if yes, skip to Section 0.1.
|
|
110
110
|
2. Read issue title + body (metadata only — no code reading at this step).
|
|
111
111
|
3. Classify using these rules:
|
|
112
112
|
- `type:task` — operational change, config, rename, docs update, non-functional (no user-facing behavior change)
|
|
113
113
|
- `type:bug` — something broken that should work
|
|
114
114
|
- `type:spike` — research/evaluation spike, never reaches Development
|
|
115
|
-
- `type:
|
|
115
|
+
- `type:feature` — new feature, behavioral change, anything product-facing
|
|
116
116
|
4. Confidence ≥ 85% → add inferred label: `gh issue edit <N> --add-label "type:<inferred>"`
|
|
117
|
-
5. Confidence < 85% → default to `type:
|
|
117
|
+
5. Confidence < 85% → default to `type:feature`: `gh issue edit <N> --add-label "type:feature"`
|
|
118
118
|
|
|
119
119
|
**Type drives the PDLC flow:**
|
|
120
120
|
|
|
121
121
|
| Type | Flow |
|
|
122
122
|
|---|---|
|
|
123
|
-
| `type:
|
|
123
|
+
| `type:feature` | brainstorming → Gate 1 → detailing → approval |
|
|
124
124
|
| `type:task` | brainstorming → Gate 1 → detailing → approval |
|
|
125
125
|
| `type:bug` | brainstorming → Gate 1 → detailing → approval |
|
|
126
126
|
| `type:spike` | brainstorming → Gate 1 → detailing → conclusion comment (never reaches Development) |
|
|
@@ -157,18 +157,39 @@ When asked to work on a feature, you will:
|
|
|
157
157
|
- Stop and wait for the human PM's explicit approval (Gate 1).
|
|
158
158
|
|
|
159
159
|
### 2. Creating the Spec
|
|
160
|
-
Once approved,
|
|
161
|
-
**IMPORTANT:** You must always rewrite the full issue body to include both the user story and the Acceptance Criteria. Do not simply append the ACs to the existing text. Use this format:
|
|
160
|
+
Once approved, detail the solution directly into the GitHub Issue body. Always rewrite the full issue body — never append only ACs to existing text. Include ALL sections below. Omitting any section blocks `stage:approval`. Use this format:
|
|
162
161
|
|
|
163
162
|
```
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
**so that** [benefit].
|
|
163
|
+
## Problem
|
|
164
|
+
[1-3 sentences. What fails. Who affected. Measured impact.]
|
|
167
165
|
|
|
168
|
-
|
|
166
|
+
## Sprint Goal / Success Metrics
|
|
167
|
+
| Metric | Baseline | Target | When |
|
|
168
|
+
|--------|----------|--------|------|
|
|
169
|
+
|
|
170
|
+
## Solution
|
|
171
|
+
[Behavioral description of what is built. No implementation details.]
|
|
169
172
|
|
|
170
173
|
## Acceptance Criteria
|
|
171
|
-
|
|
174
|
+
**AC1 — [name]**
|
|
175
|
+
- Given [precondition]
|
|
176
|
+
- When [action]
|
|
177
|
+
- Then [outcome]
|
|
178
|
+
|
|
179
|
+
## Edge Cases
|
|
180
|
+
- EC1: [condition] → [expected behavior]
|
|
181
|
+
|
|
182
|
+
## Out of Scope
|
|
183
|
+
- [item] — reason
|
|
184
|
+
|
|
185
|
+
## Non-Functional Requirements
|
|
186
|
+
- Performance: [metric with number]
|
|
187
|
+
- Security: [constraint]
|
|
188
|
+
- Reliability: [constraint]
|
|
189
|
+
> For pure docs/markdown issues with zero runtime behavior, include the NFRs section and state "N/A".
|
|
190
|
+
|
|
191
|
+
## Files to Modify
|
|
192
|
+
- `path/to/file` — what changes
|
|
172
193
|
```
|
|
173
194
|
|
|
174
195
|
### 3. Handoff
|
|
@@ -26,19 +26,14 @@ fi
|
|
|
26
26
|
|
|
27
27
|
LABELS=$(gh issue view "$ISSUE_NUM" --json labels --jq '[.labels[].name] | join(" ")' 2>/dev/null || echo "")
|
|
28
28
|
|
|
29
|
-
if echo "$LABELS" | grep -qw "spec:approved"; then
|
|
30
|
-
echo "✅ PDLC: Issue #$ISSUE_NUM
|
|
31
|
-
exit 0
|
|
32
|
-
fi
|
|
33
|
-
|
|
34
|
-
if echo "$LABELS" | grep -qw "stage:development"; then
|
|
35
|
-
echo "✅ PDLC: Issue #$ISSUE_NUM in development (spec already approved) — gate passed."
|
|
29
|
+
if echo "$LABELS" | grep -qw "spec:approved" || echo "$LABELS" | grep -qw "stage:approval" || echo "$LABELS" | grep -qw "stage:development" || echo "$LABELS" | grep -qw "stage:testing" || echo "$LABELS" | grep -qw "human-approved"; then
|
|
30
|
+
echo "✅ PDLC: Issue #$ISSUE_NUM approved — gate passed."
|
|
36
31
|
exit 0
|
|
37
32
|
fi
|
|
38
33
|
|
|
39
34
|
STAGE=$(echo "$LABELS" | tr ' ' '\n' | grep "^stage:" | head -1 || echo "none")
|
|
40
35
|
echo "❌ PDLC Stage Gate: Issue #$ISSUE_NUM is not approved."
|
|
41
36
|
echo " Current stage: $STAGE"
|
|
42
|
-
echo " Required: spec:approved
|
|
37
|
+
echo " Required: stage:approval OR spec:approved OR stage:development OR stage:testing OR human-approved label on the issue."
|
|
43
38
|
echo " Emergency bypass: rename branch to hotfix/<issue-number>-<description>."
|
|
44
39
|
exit 1
|
package/bin/cli.js
CHANGED
|
@@ -41,10 +41,10 @@ const i18n = {
|
|
|
41
41
|
invalid_repo: t('❌ Invalid repository URL. Expected format: https://github.com/OWNER/REPO', '❌ URL de repositório inválida. Formato esperado: https://github.com/OWNER/REPO', '❌ URL de repositorio inválida. Formato esperado: https://github.com/OWNER/REPO'),
|
|
42
42
|
ask_org: t('Does this repository belong to a personal User account (e.g., github.com/rafaeltcosta86) or an Organization (e.g., github.com/google-labs)? (user/org): ', 'Esse repositório pertence a um Usuário pessoal (ex: github.com/rafaeltcosta86) ou a uma Organização (ex: github.com/google-labs)? (user/org): ', '¿Este repositorio pertenece a un Usuario personal (ej: github.com/rafaeltcosta86) o a una Organización (ej: github.com/google-labs)? (user/org): '),
|
|
43
43
|
starting_setup: t('Starting automated repository setup...', 'Iniciando o setup automatizado do repositório...', 'Iniciando la configuración automatizada del repositorio...'),
|
|
44
|
-
creating_labels: t('[1/
|
|
44
|
+
creating_labels: t('[1/3] Creating repository labels...', '[1/3] Criando labels no repositório...', '[1/3] Creando etiquetas (labels) en el repositorio...'),
|
|
45
45
|
label_ok: t('✅ Label created: ', '✅ Label criada: ', '✅ Etiqueta creada: '),
|
|
46
46
|
label_warn: t('⚠️ Failed to create label (might already exist): ', '⚠️ Falha ao criar label (talvez já exista): ', '⚠️ Fallo al crear etiqueta (quizás ya exista): '),
|
|
47
|
-
creating_project: t('[2/
|
|
47
|
+
creating_project: t('[2/3] Creating Project V2 Board...', '[2/3] Criando Project V2 Board...', '[2/3] Creando Project V2 Board...'),
|
|
48
48
|
project_ok: t('✅ Project created (ID: ', '✅ Projeto criado (ID: ', '✅ Proyecto creado (ID: '),
|
|
49
49
|
project_err: t('❌ Failed to create project. Error: ', '❌ Falha ao criar o projeto. Erro: ', '❌ Fallo al crear el proyecto. Error: '),
|
|
50
50
|
link_project_ok: t('✅ Project linked to repository.', '✅ Projeto vinculado ao repositório.', '✅ Proyecto vinculado al repositorio.'),
|
|
@@ -72,6 +72,9 @@ const i18n = {
|
|
|
72
72
|
update_qa_ask: t(' Activate? Uses GITHUB_TOKEN — no extra secrets needed. (Y/n): ', ' Ativar? Usa GITHUB_TOKEN — nenhum secret extra necessário. (S/n): ', ' ¿Activar? Usa GITHUB_TOKEN — sin secrets adicionales. (S/n): '),
|
|
73
73
|
update_sentinel_header: t('— Sentinel (architecture audit via Gemini Code Assist) —', '— Sentinel (auditoria de arquitetura via Gemini Code Assist) —', '— Sentinel (auditoría de arquitectura via Gemini Code Assist) —'),
|
|
74
74
|
update_sentinel_ask: t(' Activate? Requires Gemini Code Assist CI job. (Y/n): ', ' Ativar? Requer CI job do Gemini Code Assist. (S/n): ', ' ¿Activar? Requiere CI job de Gemini Code Assist. (S/n): '),
|
|
75
|
+
configuring_protection: t('[3/3] Configuring branch protection...', '[3/3] Configurando proteção de branch...', '[3/3] Configurando protección de rama...'),
|
|
76
|
+
protection_ok: t('✅ Branch protection set — required checks: PDLC Stage Gate, QA Gate.', '✅ Proteção de branch configurada — checks obrigatórios: PDLC Stage Gate, QA Gate.', '✅ Protección de rama configurada — checks requeridos: PDLC Stage Gate, QA Gate.'),
|
|
77
|
+
protection_warn: t('⚠️ Branch protection could not be set automatically.\n Set required checks manually: Settings → Branches → main → Required status checks.\n Required: "PDLC Stage Gate" and "QA Gate"', '⚠️ Proteção de branch não pôde ser configurada automaticamente.\n Configure manualmente: Settings → Branches → main → Required status checks.\n Obrigatórios: "PDLC Stage Gate" e "QA Gate"', '⚠️ No se pudo configurar la protección de rama automáticamente.\n Configúralo en: Settings → Branches → main → Required status checks.\n Requeridos: "PDLC Stage Gate" y "QA Gate"'),
|
|
75
78
|
};
|
|
76
79
|
|
|
77
80
|
const cyan = '\x1b[36m';
|
|
@@ -355,6 +358,24 @@ async function runSetup() {
|
|
|
355
358
|
console.log(`\n${yellow}ℹ️ Org repo detected — PROJECT_PAT will require manual setup for security.${reset}`);
|
|
356
359
|
}
|
|
357
360
|
|
|
361
|
+
// Branch protection — require PDLC Stage Gate + QA Gate on default branch
|
|
362
|
+
console.log(`\n${cyan}${i18n.configuring_protection}${reset}`);
|
|
363
|
+
try {
|
|
364
|
+
const defaultBranch = execFileSync('gh', ['api', `repos/${repo}`, '--jq', '.default_branch'],
|
|
365
|
+
{ stdio: ['ignore', 'pipe', 'ignore'], encoding: 'utf8' }).trim() || 'main';
|
|
366
|
+
const protectionPayload = JSON.stringify({
|
|
367
|
+
required_status_checks: { strict: false, contexts: ['PDLC Stage Gate', 'QA Gate'] },
|
|
368
|
+
enforce_admins: false,
|
|
369
|
+
required_pull_request_reviews: null,
|
|
370
|
+
restrictions: null
|
|
371
|
+
});
|
|
372
|
+
execFileSync('gh', ['api', `repos/${repo}/branches/${defaultBranch}/protection`, '--method', 'PUT', '--input', '-'],
|
|
373
|
+
{ input: protectionPayload, stdio: ['pipe', 'ignore', 'pipe'] });
|
|
374
|
+
console.log(` ${green}${i18n.protection_ok}${reset}`);
|
|
375
|
+
} catch (_) {
|
|
376
|
+
console.log(` ${yellow}${i18n.protection_warn}${reset}`);
|
|
377
|
+
}
|
|
378
|
+
|
|
358
379
|
console.log(`\n${yellow}${i18n.scaffolding}${reset}`);
|
|
359
380
|
|
|
360
381
|
// We copy the templates folder so the agent has the real text logic to replace and rename
|
package/docs/pdlc.md
CHANGED
|
@@ -53,7 +53,7 @@ REPO = {{REPO_OWNER}}/{{REPO_NAME}}
|
|
|
53
53
|
```
|
|
54
54
|
[icon] [PREFIX]: [short description, imperative tense]
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
✨ feat: new feature or behavioral change
|
|
57
57
|
🐛 BUG: bug
|
|
58
58
|
🔧 TASK: operational task
|
|
59
59
|
🔬 SPIKE: exploration/evaluation spike
|
|
@@ -69,7 +69,7 @@ REPO = {{REPO_OWNER}}/{{REPO_NAME}}
|
|
|
69
69
|
| `spec:approved` | Issue | Green | Gate 2 — agent is cleared to implement |
|
|
70
70
|
| `pr:in-review` | PR | Yellow | Awaiting code review |
|
|
71
71
|
| `pr:approved` | PR | Green | Code review approved |
|
|
72
|
-
| `type:
|
|
72
|
+
| `type:feature` | Issue | Blue | New feature or behavioral change — full flow |
|
|
73
73
|
| `type:task` | Issue | Yellow | Operational/non-functional change — full flow |
|
|
74
74
|
| `type:bug` | Issue | Red | Something broken — full flow |
|
|
75
75
|
| `type:spike` | Issue | Gray | Research/evaluation — never reaches Development |
|
|
@@ -90,12 +90,12 @@ The `type:*` label is the authoritative signal — set automatically by the agen
|
|
|
90
90
|
|
|
91
91
|
| Label | Flow |
|
|
92
92
|
|---|---|
|
|
93
|
-
| `type:
|
|
93
|
+
| `type:feature` | brainstorming → Gate 1 → detailing → approval |
|
|
94
94
|
| `type:task` | brainstorming → Gate 1 → detailing → approval |
|
|
95
95
|
| `type:bug` | brainstorming → Gate 1 → detailing → approval |
|
|
96
96
|
| `type:spike` | brainstorming → Gate 1 → detailing → conclusion comment (never reaches Development) |
|
|
97
97
|
|
|
98
|
-
If no `type:*` label present and agent confidence < 85%, defaults to `type:
|
|
98
|
+
If no `type:*` label present and agent confidence < 85%, defaults to `type:feature` (safe fallback — never skips gates by omission).
|
|
99
99
|
|
|
100
100
|
## Bypass Mechanism
|
|
101
101
|
|
|
@@ -103,7 +103,7 @@ Agents MUST NOT skip any stage. The ONLY authorized bypasses are:
|
|
|
103
103
|
|
|
104
104
|
| Mechanism | Who authorizes | What it bypasses |
|
|
105
105
|
|---|---|---|
|
|
106
|
-
| `human-approved` label on issue | PM (human) only | All stage gates |
|
|
106
|
+
| `human-approved` label on issue | PM (human) only | All stage gates — automation adds `qa:approved` to linked PRs, moving card to Code Review / PR. `pdlc-stage-gate` accepts `stage:testing` as a valid post-gate state. |
|
|
107
107
|
| Branch prefix `hotfix/` | PM (human) only | PR gate only |
|
|
108
108
|
|
|
109
109
|
Agents MUST NOT self-authorize a bypass. Stop and ask the PM explicitly.
|