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.
Files changed (53) hide show
  1. package/.agentic-pdlc/metrics/raw/2026-W22.jsonl +114 -0
  2. package/.github/ISSUE_TEMPLATE/bug.md +53 -0
  3. package/.github/ISSUE_TEMPLATE/feature.md +54 -0
  4. package/.github/ISSUE_TEMPLATE/task.md +33 -0
  5. package/.github/workflows/add-to-board.yml +1 -1
  6. package/.github/workflows/agent-trigger.yml +4 -4
  7. package/.github/workflows/agentic-metrics.yml +150 -27
  8. package/.github/workflows/ci.yml +1 -1
  9. package/.github/workflows/npm-publish.yml +2 -2
  10. package/.github/workflows/pdlc-health-check.yml +1 -1
  11. package/.github/workflows/pdlc-stage-gate.yml +2 -2
  12. package/.github/workflows/project-automation.yml +51 -12
  13. package/.github/workflows/qa-agent.yml +22 -11
  14. package/.github/workflows/qa-gate.yml +51 -0
  15. package/AGENTS.md +50 -8
  16. package/CLAUDE.md +2 -0
  17. package/SETUP.md +2 -1
  18. package/adapters/claude-code/skill.md +32 -11
  19. package/adapters/hooks/pdlc-stage-gate.sh +3 -8
  20. package/bin/cli.js +23 -2
  21. package/docs/pdlc.md +5 -5
  22. package/docs/superpowers/plans/2026-05-28-jules-label-pat-split.md +240 -0
  23. package/docs/superpowers/plans/2026-05-29-agentic-pulse-rework-taxonomy.md +474 -0
  24. package/docs/superpowers/plans/2026-05-29-qa-gate-enforcement.md +354 -0
  25. package/docs/superpowers/specs/2026-05-29-agentic-pulse-rework-taxonomy-design.md +122 -0
  26. package/package.json +1 -1
  27. package/templates/.github/ISSUE_TEMPLATE/bug.md +53 -0
  28. package/templates/.github/ISSUE_TEMPLATE/feature.md +54 -0
  29. package/templates/.github/ISSUE_TEMPLATE/task.md +33 -0
  30. package/templates/.github/workflows/add-to-board.yml +4 -4
  31. package/templates/.github/workflows/agent-trigger.yml +22 -13
  32. package/{.agentic-pdlc/templates β†’ templates}/.github/workflows/agentic-metrics.yml +150 -27
  33. package/templates/.github/workflows/ci.yml +1 -1
  34. package/templates/.github/workflows/pdlc-health-check.yml +1 -1
  35. package/templates/.github/workflows/pdlc-stage-gate.yml +2 -2
  36. package/templates/.github/workflows/project-automation.yml +71 -32
  37. package/templates/.github/workflows/qa-agent.yml +32 -18
  38. package/templates/.github/workflows/qa-gate.yml +51 -0
  39. package/templates/AGENTS.md +57 -29
  40. package/templates/docs/pdlc.md +4 -4
  41. package/.agentic-pdlc/templates/.github/CODEOWNERS +0 -5
  42. package/.agentic-pdlc/templates/.github/copilot-instructions.md +0 -12
  43. package/.agentic-pdlc/templates/.github/workflows/add-to-board.yml +0 -38
  44. package/.agentic-pdlc/templates/.github/workflows/agent-trigger.yml +0 -146
  45. package/.agentic-pdlc/templates/.github/workflows/auto-approve.yml +0 -16
  46. package/.agentic-pdlc/templates/.github/workflows/ci.yml +0 -54
  47. package/.agentic-pdlc/templates/.github/workflows/pdlc-health-check.yml +0 -121
  48. package/.agentic-pdlc/templates/.github/workflows/pdlc-stage-gate.yml +0 -51
  49. package/.agentic-pdlc/templates/.github/workflows/project-automation.yml +0 -274
  50. package/.agentic-pdlc/templates/.github/workflows/protect-workflows.yml +0 -21
  51. package/.agentic-pdlc/templates/.github/workflows/qa-agent.yml +0 -128
  52. package/.agentic-pdlc/templates/AGENTS.md +0 -104
  53. package/.agentic-pdlc/templates/docs/pdlc.md +0 -123
@@ -0,0 +1,114 @@
1
+ {"issueNumber":84,"stage":"stage:brainstorming","durationDays":0}
2
+ {"issueNumber":84,"stage":"stage:detailing","durationDays":0}
3
+ {"issueNumber":84,"stage":"stage:approval","durationDays":0}
4
+ {"issueNumber":84,"stage":"stage:development","durationDays":0}
5
+ {"issueNumber":85,"stage":"stage:brainstorming","durationDays":0}
6
+ {"issueNumber":85,"stage":"stage:detailing","durationDays":0}
7
+ {"issueNumber":85,"stage":"stage:approval","durationDays":0}
8
+ {"issueNumber":85,"stage":"stage:approval","durationDays":0}
9
+ {"issueNumber":87,"stage":"stage:approval","durationDays":0}
10
+ {"issueNumber":87,"stage":"stage:development","durationDays":0}
11
+ {"issueNumber":87,"stage":"stage:brainstorming","durationDays":0}
12
+ {"issueNumber":87,"stage":"stage:detailing","durationDays":0}
13
+ {"issueNumber":87,"stage":"stage:approval","durationDays":0}
14
+ {"issueNumber":88,"stage":"stage:brainstorming","durationDays":0}
15
+ {"issueNumber":88,"stage":"stage:detailing","durationDays":0}
16
+ {"issueNumber":88,"stage":"stage:approval","durationDays":0}
17
+ {"issueNumber":88,"stage":"stage:development","durationDays":0}
18
+ {"issueNumber":89,"stage":"stage:brainstorming","durationDays":0}
19
+ {"issueNumber":90,"stage":"stage:brainstorming","durationDays":0}
20
+ {"issueNumber":90,"stage":"stage:detailing","durationDays":0}
21
+ {"issueNumber":90,"stage":"stage:approval","durationDays":0}
22
+ {"issueNumber":90,"stage":"stage:approval","durationDays":0}
23
+ {"issueNumber":91,"stage":"stage:brainstorming","durationDays":0}
24
+ {"issueNumber":91,"stage":"stage:detailing","durationDays":0}
25
+ {"issueNumber":91,"stage":"stage:approval","durationDays":0}
26
+ {"issueNumber":91,"stage":"stage:development","durationDays":0}
27
+ {"issueNumber":92,"stage":"stage:brainstorming","durationDays":0}
28
+ {"issueNumber":92,"stage":"stage:detailing","durationDays":0}
29
+ {"issueNumber":92,"stage":"stage:approval","durationDays":0}
30
+ {"issueNumber":92,"stage":"stage:development","durationDays":0}
31
+ {"issueNumber":93,"stage":"stage:brainstorming","durationDays":0}
32
+ {"issueNumber":93,"stage":"stage:detailing","durationDays":0}
33
+ {"issueNumber":93,"stage":"stage:approval","durationDays":0}
34
+ {"issueNumber":93,"stage":"stage:development","durationDays":0}
35
+ {"issueNumber":94,"stage":"stage:brainstorming","durationDays":0}
36
+ {"issueNumber":94,"stage":"stage:detailing","durationDays":0}
37
+ {"issueNumber":94,"stage":"stage:approval","durationDays":0}
38
+ {"issueNumber":96,"stage":"stage:brainstorming","durationDays":0}
39
+ {"issueNumber":96,"stage":"stage:detailing","durationDays":0}
40
+ {"issueNumber":96,"stage":"stage:approval","durationDays":0}
41
+ {"issueNumber":96,"stage":"stage:development","durationDays":0}
42
+ {"issueNumber":97,"stage":"stage:brainstorming","durationDays":0}
43
+ {"issueNumber":97,"stage":"stage:detailing","durationDays":0}
44
+ {"issueNumber":97,"stage":"stage:approval","durationDays":0}
45
+ {"issueNumber":109,"stage":"stage:brainstorming","durationDays":0}
46
+ {"issueNumber":109,"stage":"stage:detailing","durationDays":0}
47
+ {"issueNumber":109,"stage":"stage:approval","durationDays":0}
48
+ {"issueNumber":109,"stage":"stage:development","durationDays":0}
49
+ {"issueNumber":110,"stage":"stage:brainstorming","durationDays":0}
50
+ {"issueNumber":110,"stage":"stage:detailing","durationDays":0}
51
+ {"issueNumber":110,"stage":"stage:approval","durationDays":0.3}
52
+ {"issueNumber":110,"stage":"stage:development","durationDays":0}
53
+ {"issueNumber":110,"stage":"stage:testing","durationDays":0}
54
+ {"issueNumber":111,"stage":"stage:brainstorming","durationDays":0}
55
+ {"issueNumber":111,"stage":"stage:detailing","durationDays":0}
56
+ {"issueNumber":111,"stage":"stage:approval","durationDays":0.3}
57
+ {"issueNumber":111,"stage":"stage:development","durationDays":0}
58
+ {"issueNumber":111,"stage":"stage:testing","durationDays":0}
59
+ {"issueNumber":113,"stage":"stage:brainstorming","durationDays":0}
60
+ {"issueNumber":113,"stage":"stage:detailing","durationDays":0}
61
+ {"issueNumber":113,"stage":"stage:approval","durationDays":0}
62
+ {"issueNumber":113,"stage":"stage:development","durationDays":0}
63
+ {"issueNumber":116,"stage":"stage:brainstorming","durationDays":3.7}
64
+ {"issueNumber":116,"stage":"stage:detailing","durationDays":0}
65
+ {"issueNumber":116,"stage":"stage:approval","durationDays":0}
66
+ {"issueNumber":116,"stage":"stage:development","durationDays":0}
67
+ {"issueNumber":119,"stage":"stage:brainstorming","durationDays":0}
68
+ {"issueNumber":119,"stage":"stage:detailing","durationDays":0}
69
+ {"issueNumber":119,"stage":"stage:approval","durationDays":0}
70
+ {"issueNumber":119,"stage":"stage:approval","durationDays":0.3}
71
+ {"issueNumber":119,"stage":"stage:development","durationDays":0}
72
+ {"issueNumber":120,"stage":"stage:brainstorming","durationDays":0}
73
+ {"issueNumber":120,"stage":"stage:detailing","durationDays":0}
74
+ {"issueNumber":120,"stage":"stage:approval","durationDays":0}
75
+ {"issueNumber":120,"stage":"stage:development","durationDays":0}
76
+ {"issueNumber":122,"stage":"stage:brainstorming","durationDays":0.1}
77
+ {"issueNumber":122,"stage":"stage:detailing","durationDays":0}
78
+ {"issueNumber":122,"stage":"stage:approval","durationDays":0}
79
+ {"issueNumber":122,"stage":"stage:development","durationDays":0}
80
+ {"issueNumber":125,"stage":"stage:brainstorming","durationDays":0}
81
+ {"issueNumber":125,"stage":"stage:detailing","durationDays":0}
82
+ {"issueNumber":125,"stage":"stage:approval","durationDays":0.1}
83
+ {"issueNumber":125,"stage":"stage:development","durationDays":0}
84
+ {"issueNumber":128,"stage":"stage:brainstorming","durationDays":0.6}
85
+ {"issueNumber":128,"stage":"stage:detailing","durationDays":0}
86
+ {"issueNumber":128,"stage":"stage:approval","durationDays":0}
87
+ {"issueNumber":128,"stage":"stage:development","durationDays":0}
88
+ {"issueNumber":130,"stage":"stage:brainstorming","durationDays":0}
89
+ {"issueNumber":130,"stage":"stage:detailing","durationDays":0}
90
+ {"issueNumber":130,"stage":"stage:approval","durationDays":0}
91
+ {"issueNumber":130,"stage":"stage:development","durationDays":0}
92
+ {"issueNumber":132,"stage":"stage:brainstorming","durationDays":0}
93
+ {"issueNumber":132,"stage":"stage:detailing","durationDays":0}
94
+ {"issueNumber":132,"stage":"stage:approval","durationDays":0}
95
+ {"issueNumber":132,"stage":"stage:development","durationDays":0}
96
+ {"issueNumber":132,"stage":"stage:testing","durationDays":0}
97
+ {"issueNumber":135,"stage":"stage:brainstorming","durationDays":0}
98
+ {"issueNumber":135,"stage":"stage:detailing","durationDays":0}
99
+ {"issueNumber":135,"stage":"stage:approval","durationDays":0}
100
+ {"issueNumber":135,"stage":"stage:development","durationDays":0}
101
+ {"issueNumber":136,"stage":"stage:brainstorming","durationDays":0}
102
+ {"issueNumber":136,"stage":"stage:detailing","durationDays":0}
103
+ {"issueNumber":136,"stage":"stage:approval","durationDays":0}
104
+ {"issueNumber":136,"stage":"stage:development","durationDays":0}
105
+ {"issueNumber":136,"stage":"stage:testing","durationDays":0.1}
106
+ {"issueNumber":137,"stage":"stage:brainstorming","durationDays":0}
107
+ {"issueNumber":137,"stage":"stage:detailing","durationDays":0}
108
+ {"issueNumber":137,"stage":"stage:approval","durationDays":0}
109
+ {"issueNumber":137,"stage":"stage:development","durationDays":0}
110
+ {"issueNumber":137,"stage":"stage:testing","durationDays":0}
111
+ {"issueNumber":141,"stage":"stage:brainstorming","durationDays":0}
112
+ {"issueNumber":142,"stage":"stage:brainstorming","durationDays":0}
113
+ {"issueNumber":142,"stage":"stage:detailing","durationDays":0}
114
+ {"issueNumber":142,"stage":"stage:approval","durationDays":0}
@@ -0,0 +1,53 @@
1
+ ---
2
+ name: Bug
3
+ about: Broken behavior with root cause and fix criteria
4
+ title: 'bug: '
5
+ labels: type:bug
6
+ assignees: ''
7
+ ---
8
+
9
+ ## Problem
10
+
11
+ <!-- What fails. Who affected. Measured impact. -->
12
+
13
+ ## Root Cause
14
+
15
+ <!-- Why it fails. Link to code/config if known. -->
16
+
17
+ ## Reproduction Steps
18
+
19
+ 1.
20
+ 2.
21
+ 3.
22
+
23
+ **Expected:**
24
+ **Actual:**
25
+
26
+ ## Acceptance Criteria
27
+
28
+ <!-- agent fills during detailing -->
29
+
30
+ **AC1 β€” [name]**
31
+ - Given
32
+ - When
33
+ - Then
34
+
35
+ ## Edge Cases
36
+
37
+ <!-- agent fills during detailing -->
38
+ - EC1: [condition] β†’ [expected behavior]
39
+
40
+ ## Out of Scope
41
+
42
+ <!-- agent fills during detailing -->
43
+ -
44
+
45
+ ## Non-Functional Requirements
46
+
47
+ <!-- agent fills during detailing β€” omit if pure docs/markdown -->
48
+ - Reliability: idempotent β€” safe to re-run
49
+
50
+ ## Files to Modify
51
+
52
+ <!-- agent fills during detailing -->
53
+ - `path/to/file` β€” what changes
@@ -0,0 +1,54 @@
1
+ ---
2
+ name: Feature
3
+ about: New capability with spec scaffold for detailing agent
4
+ title: 'feat: '
5
+ labels: type:feature
6
+ assignees: ''
7
+ ---
8
+
9
+ ## Problem
10
+
11
+ <!-- What fails or is missing. Who affected. Measured impact. -->
12
+
13
+ ## Sprint Goal / Success Metrics
14
+
15
+ <!-- agent fills during detailing -->
16
+
17
+ | Metric | Baseline | Target | When |
18
+ |--------|----------|--------|------|
19
+ | | | | |
20
+
21
+ ## Solution
22
+
23
+ <!-- agent fills during detailing β€” behavioral description, no implementation details -->
24
+
25
+ ## Acceptance Criteria
26
+
27
+ <!-- agent fills during detailing -->
28
+
29
+ **AC1 β€” [name]**
30
+ - Given
31
+ - When
32
+ - Then
33
+
34
+ ## Edge Cases
35
+
36
+ <!-- agent fills during detailing -->
37
+ - EC1: [condition] β†’ [expected behavior]
38
+
39
+ ## Out of Scope
40
+
41
+ <!-- agent fills during detailing -->
42
+ -
43
+
44
+ ## Non-Functional Requirements
45
+
46
+ <!-- agent fills during detailing β€” omit if pure docs/markdown -->
47
+ - Performance:
48
+ - Security:
49
+ - Reliability:
50
+
51
+ ## Files to Modify
52
+
53
+ <!-- agent fills during detailing -->
54
+ - `path/to/file` β€” what changes
@@ -0,0 +1,33 @@
1
+ ---
2
+ name: Task
3
+ about: Defined work unit with clear done criteria
4
+ title: 'task: '
5
+ labels: type:task
6
+ assignees: ''
7
+ ---
8
+
9
+ ## Objective
10
+
11
+ <!-- What must be done and why. -->
12
+
13
+ ## Steps
14
+
15
+ <!-- agent fills during detailing -->
16
+ 1.
17
+ 2.
18
+
19
+ ## Acceptance Criteria
20
+
21
+ <!-- agent fills during detailing -->
22
+ - [ ]
23
+ - [ ]
24
+
25
+ ## Out of Scope
26
+
27
+ <!-- agent fills during detailing -->
28
+ -
29
+
30
+ ## Files to Modify
31
+
32
+ <!-- agent fills during detailing -->
33
+ - `path/to/file` β€” what changes
@@ -18,7 +18,7 @@ jobs:
18
18
  steps:
19
19
  - name: Add issue to project board
20
20
  if: ${{ env.PROJECT_TOKEN != '' && env.PROJECT_ID != '{{PROJECT_ID}}' }}
21
- uses: actions/github-script@v7
21
+ uses: actions/github-script@v8
22
22
  with:
23
23
  github-token: ${{ env.PROJECT_TOKEN }}
24
24
  script: |
@@ -22,7 +22,7 @@ jobs:
22
22
  STATUS_DEVELOPMENT: "2c9e78e6"
23
23
  steps:
24
24
  - name: Swap labels β€” stage:approval β†’ stage:development
25
- uses: actions/github-script@v7
25
+ uses: actions/github-script@v8
26
26
  with:
27
27
  github-token: ${{ secrets.GITHUB_TOKEN }}
28
28
  script: |
@@ -44,13 +44,13 @@ jobs:
44
44
  owner,
45
45
  repo,
46
46
  issue_number,
47
- labels: ['stage:development', 'agent:working']
47
+ labels: ['stage:development']
48
48
  });
49
49
 
50
50
  - name: Move board card to Development
51
51
  if: ${{ env.PROJECT_TOKEN != '' }}
52
52
  continue-on-error: true
53
- uses: actions/github-script@v7
53
+ uses: actions/github-script@v8
54
54
  with:
55
55
  github-token: ${{ env.PROJECT_TOKEN }}
56
56
  script: |
@@ -79,7 +79,7 @@ jobs:
79
79
  contents: read
80
80
  steps:
81
81
  - name: Comment on issue
82
- uses: actions/github-script@v7
82
+ uses: actions/github-script@v8
83
83
  with:
84
84
  github-token: ${{ secrets.GITHUB_TOKEN }}
85
85
  script: |
@@ -9,15 +9,20 @@ permissions:
9
9
  contents: write
10
10
  issues: write
11
11
 
12
+ env:
13
+ AGENTIC_PULSE_REVIEWERS: |
14
+ code_reviewer=gemini-code-assist[bot]
15
+ qa_agent=github-actions[bot]
16
+
12
17
  jobs:
13
18
  generate-pulse:
14
19
  name: Generate Weekly Agentic Pulse
15
20
  runs-on: ubuntu-latest
16
21
  steps:
17
- - uses: actions/checkout@v4
22
+ - uses: actions/checkout@v5.0.1
18
23
 
19
24
  - name: Collect Stage Residence Time
20
- uses: actions/github-script@v7
25
+ uses: actions/github-script@v8
21
26
  with:
22
27
  script: |
23
28
  const fs = require('fs');
@@ -104,13 +109,26 @@ jobs:
104
109
  git push
105
110
 
106
111
  - name: Collect PR and Issue Insights
107
- uses: actions/github-script@v7
112
+ uses: actions/github-script@v8
108
113
  with:
109
114
  script: |
110
115
  const fs = require('fs');
111
116
  const { owner, repo } = context.repo;
112
117
  const weekKey = process.env.WEEK_KEY;
113
118
 
119
+ // ── Preload stage:detailing times for stage correlation ──────────
120
+ const detailingByIssue = {};
121
+ const jsonlPath = `.agentic-pdlc/metrics/raw/${weekKey}.jsonl`;
122
+ if (fs.existsSync(jsonlPath)) {
123
+ const rawLines = fs.readFileSync(jsonlPath, 'utf8').trim().split('\n').filter(Boolean);
124
+ for (const line of rawLines) {
125
+ const r = JSON.parse(line);
126
+ if (r.stage === 'stage:detailing') {
127
+ detailingByIssue[r.issueNumber] = round1((detailingByIssue[r.issueNumber] || 0) + r.durationDays);
128
+ }
129
+ }
130
+ }
131
+
114
132
  // ── Helper ──────────────────────────────────────────────────────
115
133
  function daysSince(isoStr) {
116
134
  return (Date.now() - new Date(isoStr).getTime()) / 864e5;
@@ -138,6 +156,22 @@ jobs:
138
156
  // ── Signal collection ───────────────────────────────────────────
139
157
  const signals = [];
140
158
 
159
+ // ── Review actor map (from AGENTIC_PULSE_REVIEWERS env var) ─────
160
+ const actorMap = {}; // login β†’ role
161
+ const reviewersEnv = (process.env.AGENTIC_PULSE_REVIEWERS || '').trim();
162
+ if (reviewersEnv) {
163
+ for (const line of reviewersEnv.split('\n')) {
164
+ const eq = line.indexOf('=');
165
+ if (eq < 0) continue;
166
+ const role = line.slice(0, eq).trim();
167
+ const logins = line.slice(eq + 1).trim();
168
+ for (const login of logins.split(',').map(l => l.trim()).filter(Boolean)) {
169
+ actorMap[login] = role;
170
+ }
171
+ }
172
+ }
173
+ const taxonomyEnabled = Object.keys(actorMap).length > 0;
174
+
141
175
  // 1. Orphan issues: open >14 days with no linked PR
142
176
  const closeRe = /(?:closes?|fixes?|resolves?)\s+#(\d+)/gi;
143
177
  const openIssues = await github.paginate(github.rest.issues.listForRepo, {
@@ -223,29 +257,68 @@ jobs:
223
257
  }
224
258
  }
225
259
 
226
- // 3. Rework rate: commits per PR β€” single push session = first-shot
260
+ // 3. Rework rate with actor taxonomy (if AGENTIC_PULSE_REVIEWERS configured)
227
261
  const weekAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
228
262
  const weekMerged = recentPRs.filter(pr => pr.merged_at && new Date(pr.merged_at) > weekAgo);
229
263
 
230
264
  if (weekMerged.length > 0) {
231
265
  let firstShots = 0;
266
+ const reworkByRole = {};
267
+ const reworkDetails = []; // { pr_number, issue_number } for stage correlation
268
+ const issueRe = /(?:closes?|fixes?|resolves?)\s+#(\d+)/i;
269
+
232
270
  for (const pr of weekMerged.slice(0, 10)) {
233
271
  try {
234
272
  const commits = await github.rest.pulls.listCommits({
235
273
  owner, repo, pull_number: pr.number, per_page: 100
236
274
  });
237
- const times = commits.data.map(c => new Date(c.commit.committer.date).getTime()).sort();
275
+ const times = commits.data
276
+ .map(c => new Date(c.commit.committer.date).getTime())
277
+ .sort((a, b) => a - b);
278
+
238
279
  let sessions = 1;
239
280
  for (let i = 1; i < times.length; i++) {
240
281
  if (times[i] - times[i-1] > 10 * 60 * 1000) sessions++;
241
282
  }
242
- if (sessions === 1) firstShots++;
283
+
284
+ if (sessions === 1) { firstShots++; continue; }
285
+
286
+ if (!taxonomyEnabled) continue;
287
+
288
+ let reviewTriggered = false;
289
+ try {
290
+ const reviews = await github.rest.pulls.listReviews({
291
+ owner, repo, pull_number: pr.number, per_page: 100
292
+ });
293
+ const attributedRoles = new Set();
294
+ for (const review of reviews.data) {
295
+ if (!review.user) continue;
296
+ const role = actorMap[review.user.login];
297
+ if (!role || review.state === 'APPROVED' || attributedRoles.has(role)) continue;
298
+ const reviewTime = new Date(review.submitted_at).getTime();
299
+ if (times.some(t => t > reviewTime)) {
300
+ reworkByRole[role] = (reworkByRole[role] || 0) + 1;
301
+ reviewTriggered = true;
302
+ attributedRoles.add(role);
303
+ if (!reworkDetails.some(d => d.pr_number === pr.number)) {
304
+ const m = issueRe.exec(pr.body || '');
305
+ if (m) reworkDetails.push({ pr_number: pr.number, issue_number: parseInt(m[1]) });
306
+ }
307
+ }
308
+ }
309
+ } catch(e) { /* reviews not accessible β€” skip taxonomy for this PR */ }
310
+
311
+ if (!reviewTriggered) {
312
+ reworkByRole.self_correction = (reworkByRole.self_correction || 0) + 1;
313
+ }
314
+
243
315
  } catch (e) { /* skip if commits not accessible */ }
244
316
  }
317
+
245
318
  const total = Math.min(weekMerged.length, 10);
246
319
  const pct = Math.round(firstShots / total * 100);
320
+ const reworkCount = total - firstShots;
247
321
 
248
- // Detect if repo uses an agent label (jules, sweep, codex, etc.)
249
322
  const agentLabels = new Set(['jules', 'sweep', 'codex', 'copilot']);
250
323
  const usesAgent = weekMerged.some(pr =>
251
324
  (pr.labels || []).some(l => agentLabels.has(l.name.toLowerCase()))
@@ -253,27 +326,78 @@ jobs:
253
326
  const subject = usesAgent ? 'Agent first-shot rate' : 'PRs sem rework';
254
327
  const verb = usesAgent ? 'acertaram de primeira' : 'foram mergeados sem rework';
255
328
 
256
- if (pct >= 80) {
257
- signals.push({
258
- level: 'green',
259
- emoji: '🟒',
260
- title: `**${subject}: ${pct}%**`,
261
- body: `${firstShots} de ${total} PRs ${verb} esta semana. βœ…`
262
- });
263
- } else if (pct < 50) {
329
+ if (taxonomyEnabled && reworkCount > 0) {
330
+ const lines = [];
331
+ for (const [role, count] of Object.entries(reworkByRole).sort((a, b) => b[1] - a[1])) {
332
+ const s = count > 1 ? 's' : '';
333
+ if (role === 'code_reviewer') lines.push(` ↳ Code reviewer: **${count} PR${s}** β†’ revisar DoD em stage:development`);
334
+ else if (role === 'qa_agent') lines.push(` ↳ QA Agent: **${count} PR${s}** β†’ spec com lacunas funcionais em stage:detailing`);
335
+ else if (role === 'self_correction') lines.push(` ↳ Self-correction: **${count} PR${s}** (causa nΓ£o determinada automaticamente)`);
336
+ else lines.push(` ↳ ${role}: **${count} PR${s}**`);
337
+ }
338
+
339
+ const reviewerRework = reworkByRole.code_reviewer || 0;
340
+ const level = reviewerRework >= Math.ceil(reworkCount * 0.8) ? 'red'
341
+ : (reviewerRework >= Math.ceil(reworkCount * 0.5) || (reworkByRole.qa_agent || 0) > 0) ? 'yellow'
342
+ : 'neutral';
343
+ const emoji = level === 'red' ? 'πŸ”΄' : level === 'yellow' ? '🟑' : 'πŸ”΅';
344
+
264
345
  signals.push({
265
- level: 'yellow',
266
- emoji: '🟑',
267
- title: `**${subject}: ${pct}% β€” rework alto**`,
268
- body: `Apenas ${firstShots} de ${total} PRs sem commits extras.\n→ Specs incompletas ou mudanças de requisito durante implementação.`
346
+ level,
347
+ emoji,
348
+ title: `**Rework: ${100 - pct}%** β€” ${reworkCount} de ${total} PRs tiveram commits extras`,
349
+ body: lines.join('\n')
269
350
  });
351
+
352
+ // ── Stage correlation ────────────────────────────────────────
353
+ if (reworkDetails.length > 0 && Object.keys(detailingByIssue).length > 0) {
354
+ const reworkIssueNums = new Set(reworkDetails.map(d => d.issue_number));
355
+
356
+ const reworkGroup = reworkDetails
357
+ .map(d => detailingByIssue[d.issue_number])
358
+ .filter(t => t !== undefined);
359
+
360
+ const cleanGroup = weekMerged.slice(0, 10)
361
+ .map(pr => { const m = issueRe.exec(pr.body || ''); return m ? parseInt(m[1]) : null; })
362
+ .filter(n => n !== null && !reworkIssueNums.has(n))
363
+ .map(n => detailingByIssue[n])
364
+ .filter(t => t !== undefined);
365
+
366
+ if (reworkGroup.length >= 3 && cleanGroup.length >= 3) {
367
+ const avgRework = round1(reworkGroup.reduce((a, b) => a + b, 0) / reworkGroup.length);
368
+ const avgClean = round1(cleanGroup.reduce((a, b) => a + b, 0) / cleanGroup.length);
369
+ if (avgRework < avgClean * 0.75) {
370
+ signals.push({
371
+ level: 'neutral',
372
+ emoji: 'πŸ’‘',
373
+ title: `**Stage correlation:** PRs com reviewer rework tiveram Detailing mΓ©dio de ${avgRework}d vs ${avgClean}d (N=${reworkGroup.length} vs ${cleanGroup.length})`,
374
+ body: 'β†’ Specs rΓ‘pidas correlacionam com mais rework de review'
375
+ });
376
+ }
377
+ }
378
+ }
379
+
270
380
  } else {
271
- signals.push({
272
- level: 'neutral',
273
- emoji: 'πŸ”΅',
274
- title: `**${subject}: ${pct}%**`,
275
- body: `${firstShots} de ${total} PRs sem rework commits.`
276
- });
381
+ // Taxonomy disabled or no rework β€” existing signal unchanged
382
+ if (pct >= 80) {
383
+ signals.push({
384
+ level: 'green', emoji: '🟒',
385
+ title: `**${subject}: ${pct}%**`,
386
+ body: `${firstShots} de ${total} PRs ${verb} esta semana. βœ…`
387
+ });
388
+ } else if (pct < 50) {
389
+ signals.push({
390
+ level: 'yellow', emoji: '🟑',
391
+ title: `**${subject}: ${pct}% β€” rework alto**`,
392
+ body: `Apenas ${firstShots} de ${total} PRs sem commits extras.\n→ Specs incompletas ou mudanças de requisito durante implementação.`
393
+ });
394
+ } else {
395
+ signals.push({
396
+ level: 'neutral', emoji: 'πŸ”΅',
397
+ title: `**${subject}: ${pct}%**`,
398
+ body: `${firstShots} de ${total} PRs sem rework commits.`
399
+ });
400
+ }
277
401
  }
278
402
  }
279
403
 
@@ -292,7 +416,6 @@ jobs:
292
416
 
293
417
  // ── Stage Residence Time section (conditional) ──────────────────
294
418
  let stageSection = '';
295
- const jsonlPath = `.agentic-pdlc/metrics/raw/${weekKey}.jsonl`;
296
419
  if (fs.existsSync(jsonlPath)) {
297
420
  const records = fs.readFileSync(jsonlPath, 'utf8').trim().split('\n').filter(Boolean).map(JSON.parse);
298
421
  if (records.length > 0) {
@@ -376,7 +499,7 @@ jobs:
376
499
  console.log(`Built pulse issue for ${weekKey} β€” ${signals.length} signals, ${reds} red, ${yellows} yellow`);
377
500
 
378
501
  - name: Create Weekly Pulse Issue
379
- uses: actions/github-script@v7
502
+ uses: actions/github-script@v8
380
503
  with:
381
504
  script: |
382
505
  const { owner, repo } = context.repo;
@@ -11,7 +11,7 @@ jobs:
11
11
  name: Run tests and linters
12
12
  runs-on: ubuntu-latest
13
13
  steps:
14
- - uses: actions/checkout@v4
14
+ - uses: actions/checkout@v5.0.1
15
15
 
16
16
  - name: Setup environment
17
17
  run: echo "Replace this with your language/toolchain setup (e.g., actions/setup-node)"
@@ -14,10 +14,10 @@ jobs:
14
14
 
15
15
  steps:
16
16
  - name: Checkout Code
17
- uses: actions/checkout@v4
17
+ uses: actions/checkout@v5.0.1
18
18
 
19
19
  - name: Setup Node.js
20
- uses: actions/setup-node@v4
20
+ uses: actions/setup-node@v6.4.0
21
21
  with:
22
22
  node-version: '24'
23
23
  registry-url: 'https://registry.npmjs.org'
@@ -27,7 +27,7 @@ jobs:
27
27
  steps:
28
28
  - name: Validate Board Configuration
29
29
  if: ${{ env.PROJECT_TOKEN != '' && env.PROJECT_ID != '{{PROJECT_ID}}' }}
30
- uses: actions/github-script@v7
30
+ uses: actions/github-script@v8
31
31
  with:
32
32
  github-token: ${{ env.PROJECT_TOKEN }}
33
33
  script: |
@@ -37,12 +37,12 @@ jobs:
37
37
 
38
38
  for NUM in $ISSUE_NUMS; do
39
39
  LABELS=$(gh issue view "$NUM" --repo "$REPO" --json labels --jq '[.labels[].name] | join(" ")' 2>/dev/null || echo "")
40
- if echo "$LABELS" | grep -qw "stage:approval" || echo "$LABELS" | grep -qw "spec:approved" || echo "$LABELS" | grep -qw "stage:development"; then
40
+ if echo "$LABELS" | grep -qw "stage:approval" || echo "$LABELS" | grep -qw "spec:approved" || echo "$LABELS" | grep -qw "stage:development" || echo "$LABELS" | grep -qw "stage:testing" || echo "$LABELS" | grep -qw "human-approved"; then
41
41
  echo "βœ… Issue #$NUM approved"
42
42
  else
43
43
  STAGE=$(echo "$LABELS" | tr ' ' '\n' | grep "^stage:" | head -1 || echo "none")
44
44
  echo "❌ Issue #$NUM missing approval (current: $STAGE)"
45
- echo " Required: stage:approval OR spec:approved OR stage:development label on the issue."
45
+ echo " Required: stage:approval OR spec:approved OR stage:development OR stage:testing OR human-approved label on the issue."
46
46
  echo " Emergency bypass: add 'hotfix' label to this PR."
47
47
  exit 1
48
48
  fi