create-agentic-pdlc 2.1.3 → 2.1.4
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.
|
@@ -42,7 +42,7 @@ If any of these files are missing, you are in **Setup Mode**. Do not proceed wit
|
|
|
42
42
|
- c) **Yes, activate** — *Uncomment the `move-violation-to-board` job in `project-automation.yml`.* → Activate immediately.
|
|
43
43
|
- **QA Agent:** Ask: *"Do you want to use a QA agent to verify PRs automatically before Code Review?"* Present the options:
|
|
44
44
|
- a) **No (Variant A)** — *PRs go straight to Code Review. Standard and simpler.*
|
|
45
|
-
- b) **Yes (Variant B), but I need help configuring it** — *PRs pass through a QA Agent before being reviewed. Requires a
|
|
45
|
+
- b) **Yes (Variant B), but I need help configuring it** — *PRs pass through a QA Agent before being reviewed. Requires a `GEMINI_API_KEY` secret (free tier available at ai.google.dev).* → Guide the user through configuration.
|
|
46
46
|
- c) **Yes (Variant B), I already have it configured** — *PRs pass through a QA Agent before being reviewed.* → Activate Variant B immediately: change `STATUS_CODE_REVIEW_PR` to `STATUS_TESTING` in the `move-card-on-pr-open` job and uncomment the `move-card-on-qa-pass` job in `project-automation.yml`.
|
|
47
47
|
- **Implementation Agent:** Ask: *"Do you use an autonomous implementation agent? (It implements the features you approve for development)"* Present the options:
|
|
48
48
|
- a) **No** — *No autonomous implementation agent.*
|
package/bin/cli.js
CHANGED
|
@@ -159,6 +159,7 @@ async function runSetup() {
|
|
|
159
159
|
{ name: 'architecture-violation', color: 'd93f0b', description: 'Invariant violation detected by CI' },
|
|
160
160
|
{ name: 'qa:approved', color: '0e8a16', description: 'QA Agent approved the implementation' },
|
|
161
161
|
{ name: 'qa:needs-work', color: 'd93f0b', description: 'QA Agent found issues' },
|
|
162
|
+
{ name: 'infra:qa-broken', color: 'F97316', description: 'QA Agent failed to run — manual review required' },
|
|
162
163
|
{ name: 'jules', color: '5319e7', description: 'Jules AI Agent' }
|
|
163
164
|
];
|
|
164
165
|
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@ name: PDLC Board Automation
|
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
4
|
pull_request:
|
|
5
|
-
types: [opened, reopened, closed]
|
|
5
|
+
types: [opened, reopened, closed, labeled]
|
|
6
6
|
pull_request_review:
|
|
7
7
|
types: [submitted]
|
|
8
8
|
issues:
|
|
@@ -189,6 +189,52 @@ jobs:
|
|
|
189
189
|
try { await github.rest.issues.removeLabel({ owner, repo, issue_number: prNumber, name: 'pr:in-review' }); } catch {}
|
|
190
190
|
await github.rest.issues.addLabels({ owner, repo, issue_number: prNumber, labels: ['pr:approved'] }).catch(() => {});
|
|
191
191
|
|
|
192
|
+
# OPTIONAL: Uncomment for Variant B (QA Agent enabled)
|
|
193
|
+
# When qa:approved is added to a PR, move linked issue to Code Review / PR
|
|
194
|
+
# move-card-on-qa-pass:
|
|
195
|
+
# name: qa:approved → Code Review / PR
|
|
196
|
+
# if: github.event_name == 'pull_request' && github.event.action == 'labeled' && github.event.label.name == 'qa:approved'
|
|
197
|
+
# runs-on: ubuntu-latest
|
|
198
|
+
# env:
|
|
199
|
+
# PROJECT_PAT: ${{ secrets.PROJECT_PAT }}
|
|
200
|
+
# steps:
|
|
201
|
+
# - name: Move linked issue to Code Review / PR
|
|
202
|
+
# if: ${{ env.PROJECT_PAT != '' && env.PROJECT_ID != '{{PROJECT_ID}}' }}
|
|
203
|
+
# uses: actions/github-script@v7
|
|
204
|
+
# with:
|
|
205
|
+
# github-token: ${{ env.PROJECT_PAT }}
|
|
206
|
+
# script: |
|
|
207
|
+
# const prNumber = context.payload.pull_request.number;
|
|
208
|
+
# const { owner, repo } = context.repo;
|
|
209
|
+
# const { data: pr } = await github.rest.pulls.get({ owner, repo, pull_number: prNumber });
|
|
210
|
+
# const body = pr.body ?? '';
|
|
211
|
+
# const linkedIssues = [...body.matchAll(/(?:Closes?|Fixes?|Resolves?)\s+#(\d+)/gi)].map(m => parseInt(m[1]));
|
|
212
|
+
# const moveItem = async (nodeId) => {
|
|
213
|
+
# const { addProjectV2ItemById: { item } } = await github.graphql(`
|
|
214
|
+
# mutation($p: ID!, $c: ID!) {
|
|
215
|
+
# addProjectV2ItemById(input: {projectId: $p, contentId: $c}) { item { id } }
|
|
216
|
+
# }`, { p: process.env.PROJECT_ID, c: nodeId });
|
|
217
|
+
# await github.graphql(`
|
|
218
|
+
# mutation($p: ID!, $i: ID!, $f: ID!, $v: ProjectV2FieldValue!) {
|
|
219
|
+
# updateProjectV2ItemFieldValue(input: {projectId: $p, itemId: $i, fieldId: $f, value: $v}) {
|
|
220
|
+
# projectV2Item { id }
|
|
221
|
+
# }
|
|
222
|
+
# }`, {
|
|
223
|
+
# p: process.env.PROJECT_ID, i: item.id, f: process.env.STATUS_FIELD_ID,
|
|
224
|
+
# v: { singleSelectOptionId: process.env.STATUS_CODE_REVIEW_PR }
|
|
225
|
+
# });
|
|
226
|
+
# };
|
|
227
|
+
# if (linkedIssues.length > 0) {
|
|
228
|
+
# for (const n of linkedIssues) {
|
|
229
|
+
# const { data: issue } = await github.rest.issues.get({ owner, repo, issue_number: n });
|
|
230
|
+
# await moveItem(issue.node_id);
|
|
231
|
+
# console.log(`Issue #${n} → Code Review / PR`);
|
|
232
|
+
# }
|
|
233
|
+
# } else {
|
|
234
|
+
# await moveItem(pr.node_id);
|
|
235
|
+
# console.log(`PR #${prNumber} → Code Review / PR (no linked issue)`);
|
|
236
|
+
# }
|
|
237
|
+
|
|
192
238
|
# PR Merged → Production
|
|
193
239
|
move-card-on-pr-merge:
|
|
194
240
|
name: Merged PR → Production
|
|
@@ -6,22 +6,73 @@ on:
|
|
|
6
6
|
permissions:
|
|
7
7
|
pull-requests: write
|
|
8
8
|
contents: read
|
|
9
|
+
issues: read
|
|
9
10
|
|
|
10
11
|
jobs:
|
|
11
12
|
qa:
|
|
12
|
-
name:
|
|
13
|
+
name: AC Coverage Verification (Gemini)
|
|
13
14
|
runs-on: ubuntu-latest
|
|
14
15
|
steps:
|
|
15
16
|
- uses: actions/checkout@v4
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
echo "If tests pass: gh pr edit $PR_URL --add-label 'qa:pass'"
|
|
21
|
-
echo "If tests fail: gh pr edit $PR_URL --add-label 'qa:fail'"
|
|
22
|
-
|
|
23
|
-
# Example success:
|
|
24
|
-
# gh pr edit ${{ github.event.pull_request.html_url }} --add-label "qa:pass"
|
|
17
|
+
with:
|
|
18
|
+
fetch-depth: 0
|
|
19
|
+
|
|
20
|
+
- name: Verify AC Coverage via Gemini
|
|
25
21
|
env:
|
|
26
22
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
27
|
-
|
|
23
|
+
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
|
|
24
|
+
run: |
|
|
25
|
+
set -e
|
|
26
|
+
|
|
27
|
+
PR_NUMBER="${{ github.event.pull_request.number }}"
|
|
28
|
+
BASE="${{ github.event.pull_request.base.sha }}"
|
|
29
|
+
HEAD="${{ github.event.pull_request.head.sha }}"
|
|
30
|
+
|
|
31
|
+
# Get PR diff (truncated to 8000 chars to stay within context limits)
|
|
32
|
+
DIFF=$(git diff "$BASE" "$HEAD" | head -c 8000)
|
|
33
|
+
|
|
34
|
+
# Extract linked issues from PR body
|
|
35
|
+
PR_BODY=$(gh pr view "$PR_NUMBER" --json body --jq '.body // ""')
|
|
36
|
+
ISSUE_NUMS=$(echo "$PR_BODY" | grep -oiE '(Closes?|Fixes?|Resolves?)\s+#([0-9]+)' | grep -oE '[0-9]+' || true)
|
|
37
|
+
|
|
38
|
+
# Build acceptance criteria context
|
|
39
|
+
AC_CONTEXT=""
|
|
40
|
+
if [ -n "$ISSUE_NUMS" ]; then
|
|
41
|
+
for NUM in $ISSUE_NUMS; do
|
|
42
|
+
ISSUE_BODY=$(gh issue view "$NUM" --json body --jq '.body // ""' 2>/dev/null || echo "")
|
|
43
|
+
AC_CONTEXT="${AC_CONTEXT}\\n\\n--- Issue #${NUM} ---\\n${ISSUE_BODY}"
|
|
44
|
+
done
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
if [ -z "$AC_CONTEXT" ]; then
|
|
48
|
+
AC_CONTEXT="No linked issue found. Evaluate if the PR description is self-contained."
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
# Serialize prompt as JSON string and call Gemini API (30s timeout)
|
|
52
|
+
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\nRespond with exactly one word: PASS or FAIL, then on the next line explain briefly why (max 3 sentences)." | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()))')
|
|
53
|
+
|
|
54
|
+
RESPONSE=$(curl -s -X POST \
|
|
55
|
+
"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${GEMINI_API_KEY}" \
|
|
56
|
+
-H "Content-Type: application/json" \
|
|
57
|
+
-d "{\"contents\":[{\"parts\":[{\"text\":${PROMPT_JSON}}]}]}" \
|
|
58
|
+
--max-time 30 || echo "API_ERROR")
|
|
59
|
+
|
|
60
|
+
if [ "$RESPONSE" = "API_ERROR" ]; then
|
|
61
|
+
gh pr edit "$PR_NUMBER" --add-label "infra:qa-broken"
|
|
62
|
+
gh pr comment "$PR_NUMBER" --body "🤖 **QA Agent:** Could not reach Gemini API. Manual review required."
|
|
63
|
+
exit 0
|
|
64
|
+
fi
|
|
65
|
+
|
|
66
|
+
VERDICT=$(echo "$RESPONSE" | python3 -c 'import json,sys; d=json.load(sys.stdin); t=d.get("candidates",[{}])[0].get("content",{}).get("parts",[{}])[0].get("text","").strip(); print(t.split("\n")[0].upper() if t else "API_ERROR")')
|
|
67
|
+
EXPLANATION=$(echo "$RESPONSE" | python3 -c 'import json,sys; d=json.load(sys.stdin); t=d.get("candidates",[{}])[0].get("content",{}).get("parts",[{}])[0].get("text","").strip(); lines=t.split("\n",1); print(lines[1].strip() if len(lines)>1 else "")')
|
|
68
|
+
|
|
69
|
+
if echo "$VERDICT" | grep -q "^PASS"; then
|
|
70
|
+
gh pr edit "$PR_NUMBER" --add-label "qa:approved"
|
|
71
|
+
gh pr comment "$PR_NUMBER" --body "🤖 **QA Agent:** AC coverage verified. ${EXPLANATION}"
|
|
72
|
+
elif echo "$VERDICT" | grep -q "^FAIL"; then
|
|
73
|
+
gh pr edit "$PR_NUMBER" --add-label "qa:needs-work"
|
|
74
|
+
gh pr comment "$PR_NUMBER" --body "🤖 **QA Agent:** AC coverage insufficient. ${EXPLANATION}"
|
|
75
|
+
else
|
|
76
|
+
gh pr edit "$PR_NUMBER" --add-label "infra:qa-broken"
|
|
77
|
+
gh pr comment "$PR_NUMBER" --body "🤖 **QA Agent:** Could not parse Gemini response. Manual review required."
|
|
78
|
+
fi
|
package/templates/AGENTS.md
CHANGED
|
@@ -70,6 +70,7 @@ When detailing a solution in an issue body, you must **always** include both the
|
|
|
70
70
|
- Never open a PR without passing the tests.
|
|
71
71
|
- Never implement beyond the immediate scope of the issue.
|
|
72
72
|
- Never create future-proofing abstractions for hypothetical features.
|
|
73
|
+
- Never add or remove `stage:*` or `qa:*` labels manually. These are owned by GitHub Actions automation and the PM only.
|
|
73
74
|
{{EXTRA_DONT}}
|
|
74
75
|
|
|
75
76
|
## Project Standards
|
package/templates/docs/pdlc.md
CHANGED
|
@@ -22,7 +22,7 @@ Adapt columns as needed. The functional baseline is:
|
|
|
22
22
|
## Workflow Variants (QA Agent)
|
|
23
23
|
|
|
24
24
|
- **Variant A (Default):** PRs bypass the `Testing` column and land directly in `Code Review / PR`.
|
|
25
|
-
- **Variant B (QA Agent Enabled):** PRs land in the `Testing` column first. An AI QA agent verifies the PR, adding `qa:
|
|
25
|
+
- **Variant B (QA Agent Enabled):** PRs land in the `Testing` column first. An AI QA agent verifies the PR, adding `qa:approved` or `qa:needs-work`. Only after a `qa:approved` is the issue moved to `Code Review / PR`.
|
|
26
26
|
|
|
27
27
|
## Board Identifiers (GitHub Projects)
|
|
28
28
|
|
|
@@ -77,6 +77,9 @@ REPO = {{REPO_OWNER}}/{{REPO_NAME}}
|
|
|
77
77
|
| `spec:approved` | Issue | Green | Gate 2 — agent is cleared to implement |
|
|
78
78
|
| `pr:in-review` | PR | Yellow | Awaiting code review |
|
|
79
79
|
| `pr:approved` | PR | Green | Code review approved |
|
|
80
|
+
| `qa:approved` | PR | Green | QA Agent passed — AC coverage verified |
|
|
81
|
+
| `qa:needs-work` | PR | Red | QA Agent failed — PR needs changes |
|
|
82
|
+
| `infra:qa-broken` | PR | Orange | QA Agent error — manual review required |
|
|
80
83
|
|
|
81
84
|
## Approval Gates
|
|
82
85
|
|