create-agentic-pdlc 1.2.1 → 2.0.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/.github/assets/agentic-pdlc-flow.svg +255 -0
- package/.github/workflows/agent-trigger.yml +40 -1
- package/.github/workflows/ci.yml +3 -0
- package/.github/workflows/pdlc-health-check.yml +123 -0
- package/.github/workflows/project-automation.yml +17 -40
- package/AGENTS.md +3 -1
- package/README.md +12 -8
- package/SETUP.md +41 -2
- package/adapters/claude-code/skill.md +17 -2
- package/docs/flow.md +160 -0
- package/docs/pdlc.md +1 -1
- package/docs/spikes/04-upstream-flow-gap-analysis.md +39 -0
- package/package.json +1 -1
- package/templates/.github/CODEOWNERS +5 -0
- package/templates/.github/workflows/agent-trigger.yml +40 -1
- package/templates/.github/workflows/ci.yml +3 -0
- package/templates/.github/workflows/pdlc-health-check.yml +123 -0
- package/templates/.github/workflows/project-automation.yml +17 -40
- package/templates/.github/workflows/upstream-gate.yml +54 -0
- package/templates/AGENTS.md +28 -1
- package/templates/docs/pdlc.md +1 -1
package/SETUP.md
CHANGED
|
@@ -41,7 +41,7 @@ Note down the `PROJECT_ID` and the `STATUS_FIELD_ID` for future steps.
|
|
|
41
41
|
```bash
|
|
42
42
|
REPO="owner/repo"
|
|
43
43
|
gh label create "spec:approved" --repo $REPO --color "0e8a16" --description "Spec approved — agent can implement"
|
|
44
|
-
gh label create "pr:review"
|
|
44
|
+
gh label create "pr:in-review" --repo $REPO --color "e4e669" --description "PR awaiting code review"
|
|
45
45
|
gh label create "pr:approved" --repo $REPO --color "0e8a16" --description "PR approved, ready for merge"
|
|
46
46
|
gh label create "architecture-violation" --repo $REPO --color "d93f0b" --description "Invariant violation detected by CI"
|
|
47
47
|
```
|
|
@@ -122,13 +122,52 @@ For each known idea or feature, create an issue using the convention:
|
|
|
122
122
|
gh issue create \
|
|
123
123
|
--repo owner/repo \
|
|
124
124
|
--title "👤 US: [short description]" \
|
|
125
|
-
--body "
|
|
125
|
+
--body "**As** [user],
|
|
126
|
+
**I want** [action],
|
|
127
|
+
**so that** [benefit].
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
Initial idea issue. Spec to be detailed."
|
|
126
132
|
```
|
|
127
133
|
|
|
128
134
|
Move the issues to the `Idea` column on the board matching your setup.
|
|
129
135
|
|
|
130
136
|
---
|
|
131
137
|
|
|
138
|
+
## (Optional) Architecture Violation Integration
|
|
139
|
+
|
|
140
|
+
If your project uses an automated architecture auditing tool (e.g., a CI job that creates issues with an `architecture-violation` label), the Setup Mode can automatically add a GitHub Actions job to `project-automation.yml` that moves these issues to the `Idea` column.
|
|
141
|
+
|
|
142
|
+
If you skipped this during setup but want to add it later, you can append the following job to `.github/workflows/project-automation.yml` under the `jobs` section:
|
|
143
|
+
|
|
144
|
+
```yaml
|
|
145
|
+
move-violation-to-board:
|
|
146
|
+
name: architecture-violation → 💡 Idea
|
|
147
|
+
if: github.event_name == 'issues' && github.event.label.name == 'architecture-violation'
|
|
148
|
+
runs-on: ubuntu-latest
|
|
149
|
+
steps:
|
|
150
|
+
- uses: actions/github-script@v7
|
|
151
|
+
with:
|
|
152
|
+
github-token: ${{ secrets.PROJECT_TOKEN }}
|
|
153
|
+
script: |
|
|
154
|
+
const issueNodeId = context.payload.issue.node_id;
|
|
155
|
+
const addResult = await github.graphql(`
|
|
156
|
+
mutation($p: ID!, $c: ID!) {
|
|
157
|
+
addProjectV2ItemById(input: {projectId: $p, contentId: $c}) { item { id } }
|
|
158
|
+
}`, { p: process.env.PROJECT_ID, c: issueNodeId });
|
|
159
|
+
const itemId = addResult.addProjectV2ItemById.item.id;
|
|
160
|
+
await github.graphql(`
|
|
161
|
+
mutation($p: ID!, $i: ID!, $f: ID!, $v: ProjectV2FieldValue!) {
|
|
162
|
+
updateProjectV2ItemFieldValue(input: {projectId: $p, itemId: $i, fieldId: $f, value: $v}) {
|
|
163
|
+
projectV2Item { id }
|
|
164
|
+
}
|
|
165
|
+
}`, { p: process.env.PROJECT_ID, i: itemId, f: process.env.STATUS_FIELD_ID,
|
|
166
|
+
v: { singleSelectOptionId: process.env.STATUS_IDEA } });
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
132
171
|
## Final Verification Checklist
|
|
133
172
|
|
|
134
173
|
- [ ] Board has 10 columns fully configured
|
|
@@ -13,19 +13,22 @@ If the user invokes you in a new project, you must first check if the PDLC artif
|
|
|
13
13
|
Specifically, check for:
|
|
14
14
|
- `AGENTS.md`
|
|
15
15
|
- `docs/pdlc.md`
|
|
16
|
+
- `.github/CODEOWNERS`
|
|
16
17
|
- `.github/workflows/project-automation.yml`
|
|
17
18
|
- `.github/workflows/agent-trigger.yml`
|
|
19
|
+
- `.github/workflows/pdlc-health-check.yml`
|
|
18
20
|
|
|
19
21
|
If any of these files are missing, you are in **Setup Mode**. Do not proceed with feature requests until setup is complete.
|
|
20
22
|
1. Acknowledge that the framework is not yet set up.
|
|
21
23
|
2. Interactively ask the user for required values **one group at a time**:
|
|
22
|
-
- **Project basics:** Project Name, Description, Technical Stack (Structure).
|
|
24
|
+
- **Project basics:** Project Name, Description, Technical Stack (Structure), and GitHub Username (for CODEOWNERS security).
|
|
23
25
|
- **Commands:** Test command, Lint command, Build command.
|
|
24
26
|
- **Invariants:** Critical business rules agents must never violate (e.g. Human-in-the-loop).
|
|
25
27
|
- **Board IDs:** PROJECT_ID, STATUS_FIELD_ID, column option IDs (provide standard PDLC options: Idea, Exploration, Brainstorming, Detail Solution, Approval, Development, Testing, Code Review / PR, Merge, Production). Allow user to answer "skip", which means you leave the placeholders intact.
|
|
28
|
+
- **Architecture Violation:** Ask "Does your project use an automated architecture auditing tool (e.g., a CI job that creates issues with an `architecture-violation` label)?". If yes, replace `{{OPTIONAL_ARCHITECTURE_VIOLATION_JOB}}` in `project-automation.yml` with the job definition (available in the framework documentation). If no, ask if they would like help implementing one, reminding them that it significantly improves their agentic development process. If they decline, remove the placeholder.
|
|
26
29
|
- **Implementation agent handle:** e.g., `@google-labs-jules`, or "none".
|
|
27
30
|
3. Generate and write the missing files replacing the `{{SCREAMING_SNAKE_CASE}}` placeholders using the templates logic you know (usually they reside in standard Agentic PDLC templates).
|
|
28
|
-
4. Offer to run the `gh` commands for labels (`spec:approved`, `pr:review`, `pr:approved`, `architecture-violation`).
|
|
31
|
+
4. Offer to run the `gh` commands for labels (`spec:approved`, `pr:in-review`, `pr:approved`, `architecture-violation`).
|
|
29
32
|
5. Commit everything with the message: `chore: setup agentic-pdlc framework`.
|
|
30
33
|
6. Conclude Setup Mode.
|
|
31
34
|
|
|
@@ -44,6 +47,18 @@ When asked to work on a feature, you will:
|
|
|
44
47
|
|
|
45
48
|
### 2. Creating the Spec
|
|
46
49
|
Once approved, you will detail the solution directly into the GitHub Issue body. Focus on precise Acceptance Criteria.
|
|
50
|
+
**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:
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
**As** [user],
|
|
54
|
+
**I want** [action],
|
|
55
|
+
**so that** [benefit].
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Acceptance Criteria
|
|
60
|
+
...
|
|
61
|
+
```
|
|
47
62
|
|
|
48
63
|
### 3. Handoff
|
|
49
64
|
Do not write code for downstream features! Your goal is to refine the Spec, so the human Tech Lead can label the issue `spec:approved`. This label triggers the downstream agent via `agent-trigger.yml`.
|
package/docs/flow.md
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# PDLC Flow: From Idea to Production
|
|
2
|
+
|
|
3
|
+
This document describes the full lifecycle of a card on the agentic-pdlc board — who acts at each stage, what triggers each transition, which labels are added or removed, and where human gates are required.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Roles
|
|
8
|
+
|
|
9
|
+
| Role | Responsibility |
|
|
10
|
+
|------|---------------|
|
|
11
|
+
| **PM** (human) | Selects issues for the sprint, approves brainstorm (Gate 1), approves spec (Gate 2) |
|
|
12
|
+
| **TL / Reviewer** (human) | Co-approves spec for technical decisions (Gate 2), reviews and approves the PR (Gate 3) |
|
|
13
|
+
| **Claude** | Exploration, brainstorming, spec writing — acts on PM instruction via chat or via upstream label |
|
|
14
|
+
| **Implementation Agent** | Implementation, running tests, opening the PR |
|
|
15
|
+
| **Workflow** | All label swaps and card movements — the source of truth for board state |
|
|
16
|
+
|
|
17
|
+
> **Principle:** Every board transition must be triggered by a GitHub event (label, PR, review) processed by a workflow. Agents must never be responsible for moving cards.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Step-by-Step Flow
|
|
22
|
+
|
|
23
|
+
### 💡 Idea
|
|
24
|
+
Issue exists in the backlog with no `upstream:` label.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
### 🔍 Exploration
|
|
29
|
+
|
|
30
|
+
**Trigger (two equivalent paths):**
|
|
31
|
+
- PM tells Claude directly in chat → Claude adds `upstream:exploration` to the issue, OR
|
|
32
|
+
- PM adds `upstream:exploration` directly to the issue
|
|
33
|
+
|
|
34
|
+
**Workflow:** `project-automation.yml` detects `upstream:exploration` → moves card to Exploration.
|
|
35
|
+
|
|
36
|
+
**Who works:** Claude reads relevant code and context.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
### 🧠 Brainstorming
|
|
41
|
+
|
|
42
|
+
**Trigger:** Claude, after exploration.
|
|
43
|
+
|
|
44
|
+
**Actions:**
|
|
45
|
+
- Claude posts a comment on the issue with findings and 2–3 proposed approaches
|
|
46
|
+
- Claude swaps `upstream:exploration` → `upstream:brainstorming`
|
|
47
|
+
|
|
48
|
+
**Workflow:** `project-automation.yml` moves card to Brainstorming.
|
|
49
|
+
|
|
50
|
+
**⏸ Human gate (Gate 1 — PM):** PM reads the brainstorming comment and selects an approach. This can be done:
|
|
51
|
+
- By commenting on the issue (e.g., "Option A", "Go with B", "approved", "lgtm")
|
|
52
|
+
- By telling Claude in chat
|
|
53
|
+
|
|
54
|
+
**Note on implicit approval:** If Claude presented multiple options, selecting one (e.g., "Option A") counts as implicit approval. The `upstream-gate.yml` detects option-selection patterns in addition to explicit approval words.
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
### 📐 Detail Solution ← Gate 1
|
|
59
|
+
|
|
60
|
+
**Trigger:** `upstream-gate.yml` detects PM approval comment on a `upstream:brainstorming` issue → swaps `upstream:brainstorming` → `upstream:detailing` via `PROJECT_TOKEN`.
|
|
61
|
+
|
|
62
|
+
**Who works:** Claude rewrites the issue body with:
|
|
63
|
+
1. User story (`As… I want… So that…`)
|
|
64
|
+
2. Acceptance Criteria (AC1, AC2, …)
|
|
65
|
+
3. Files to modify
|
|
66
|
+
|
|
67
|
+
**After spec is written:** Claude swaps `upstream:detailing` → `upstream:approval`.
|
|
68
|
+
|
|
69
|
+
**Workflow:** `project-automation.yml` moves card to Approval.
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
### ✅ Approval
|
|
74
|
+
|
|
75
|
+
**⏸ Human gate (Gate 2 — PM + TL):** Both PM (business decisions) and TL (technical decisions) review the spec. When both are satisfied, PM adds label `spec:approved`.
|
|
76
|
+
|
|
77
|
+
**Workflow:** `agent-trigger.yml` detects `spec:approved` →
|
|
78
|
+
1. Removes `upstream:approval`
|
|
79
|
+
2. Adds `upstream:development`
|
|
80
|
+
3. Adds specific agent label (e.g., `jules`)
|
|
81
|
+
4. Posts a structured comment with implementation instructions for the Agent
|
|
82
|
+
|
|
83
|
+
**Workflow:** `project-automation.yml` moves card to Development.
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
### ⚙️ Development
|
|
88
|
+
|
|
89
|
+
**Who works:** Implementation Agent implements the spec strictly within the Acceptance Criteria.
|
|
90
|
+
|
|
91
|
+
**When done:** Agent adds `upstream:testing` before running tests.
|
|
92
|
+
|
|
93
|
+
**Workflow:** `project-automation.yml` moves card to Testing.
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
### 🧪 Testing
|
|
98
|
+
|
|
99
|
+
**Who works:** Agent runs the test suite (`vitest run` + `typecheck`).
|
|
100
|
+
|
|
101
|
+
**When tests pass:** Agent opens a PR with `Closes #N` in the body.
|
|
102
|
+
|
|
103
|
+
**Workflow:** `project-automation.yml` detects PR opened → moves linked issue to Code Review, adds `pr:in-review` label to the PR.
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
### 👁 Code Review ← Gate 3
|
|
108
|
+
|
|
109
|
+
**⏸ Human gate (Gate 3 — TL / PM):** Reviewer reads the PR, verifies ACs, checks for regressions.
|
|
110
|
+
|
|
111
|
+
**Trigger:** Reviewer approves the PR via GitHub's native review interface (no label needed).
|
|
112
|
+
|
|
113
|
+
**Workflow:** `project-automation.yml` detects `pull_request_review: approved` → moves card to Production upon merge. Adds `pr:approved` label to the PR.
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
### 🚀 Production
|
|
118
|
+
|
|
119
|
+
**Who acts:** PM or TL merges the PR.
|
|
120
|
+
|
|
121
|
+
**Workflow:** `project-automation.yml` detects PR merged → moves card to Production.
|
|
122
|
+
|
|
123
|
+
> **Note:** There is no separate "Merge" column. Review and merge happen in tight sequence in practice, and a transient column adds no visibility value.
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## Label Reference
|
|
128
|
+
|
|
129
|
+
| Label | Added by | Removed by |
|
|
130
|
+
|-------|----------|------------|
|
|
131
|
+
| `upstream:exploration` | PM (human) or Claude | Claude |
|
|
132
|
+
| `upstream:brainstorming` | Claude | `upstream-gate.yml` |
|
|
133
|
+
| `upstream:detailing` | `upstream-gate.yml` | Claude |
|
|
134
|
+
| `upstream:approval` | Claude | `agent-trigger.yml` |
|
|
135
|
+
| `upstream:development` | `agent-trigger.yml` | Impl. Agent |
|
|
136
|
+
| `upstream:testing` | Impl. Agent | `project-automation.yml` (on PR open) |
|
|
137
|
+
| `spec:approved` | PM (human) | — |
|
|
138
|
+
| `agent_label` (e.g. `jules`, `sweep`) | `agent-trigger.yml` | — |
|
|
139
|
+
| `pr:in-review` | `project-automation.yml` | `project-automation.yml` |
|
|
140
|
+
| `pr:approved` | `project-automation.yml` | — |
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## Human Gates Summary
|
|
145
|
+
|
|
146
|
+
| Gate | Who | Trigger | What they decide |
|
|
147
|
+
|------|-----|---------|-----------------|
|
|
148
|
+
| **Gate 1** | PM | Comment on brainstorming issue | Which approach to pursue |
|
|
149
|
+
| **Gate 2** | PM + TL | Add `spec:approved` label | Whether the spec is correct and complete |
|
|
150
|
+
| **Gate 3** | TL / PM | GitHub PR review approval | Whether the implementation meets the ACs |
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Known Risk: Autonomous Agents and Informal Comments
|
|
155
|
+
|
|
156
|
+
Implementation agents often monitor issue comments and can act on informal instructions (e.g., "@agent fix this") — bypassing Gates 1 and 2. This behavior is controlled by the agent's platform and cannot be prevented via GitHub Actions.
|
|
157
|
+
|
|
158
|
+
**Mitigation:** The effective code quality gate is Gate 3 (PR review + mandatory CI). Never comment directly on issues to instruct the agent outside the `spec:approved` flow.
|
|
159
|
+
|
|
160
|
+
See [issue #11](https://github.com/rafaeltcosta86/agentic-pdlc/issues/11) for full context.
|
package/docs/pdlc.md
CHANGED
|
@@ -72,7 +72,7 @@ REPO = {{REPO_OWNER}}/{{REPO_NAME}}
|
|
|
72
72
|
| `stage:detailing` | Issue | Blue | Technical spec is being written |
|
|
73
73
|
| `stage:development` | Issue | Orange | Agent is implementing the spec |
|
|
74
74
|
| `spec:approved` | Issue | Green | Gate 2 — agent is cleared to implement |
|
|
75
|
-
| `pr:review` | PR | Yellow | Awaiting code review |
|
|
75
|
+
| `pr:in-review` | PR | Yellow | Awaiting code review |
|
|
76
76
|
| `pr:approved` | PR | Green | Code review approved |
|
|
77
77
|
|
|
78
78
|
## Approval Gates
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Spike: Upstream Flow Gap Analysis
|
|
2
|
+
|
|
3
|
+
This document analyzes the three gaps identified in the upstream flow of the Agentic PDLC framework and proposes solutions to ensure future projects receive these behaviors out of the box.
|
|
4
|
+
|
|
5
|
+
## 1. Sprint start → cards move to Exploration automatically
|
|
6
|
+
|
|
7
|
+
**Analysis:**
|
|
8
|
+
Currently, when a PM selects issues for a sprint and tells the upstream agent to begin, the cards do not move automatically unless the agent explicitly runs a `gh issue edit` command.
|
|
9
|
+
- `AGENTS.md` does not instruct the upstream agent to add `stage:exploration` (or equivalent) as its first action upon starting.
|
|
10
|
+
- `project-automation.yml` does respond to the label `stage:exploration` and moves the card to the Exploration column.
|
|
11
|
+
- The label naming convention (`stage:exploration`, `stage:brainstorming`, `stage:detailing`) is established in the workflow but not explicitly documented as a mandatory first step for the agent.
|
|
12
|
+
|
|
13
|
+
**Solution (Gap):**
|
|
14
|
+
- **Fix:** Update `AGENTS.md` and `adapters/claude-code/skill.md` to explicitly instruct the agent: *"When beginning work on a new issue, your first action must be to apply the `stage:exploration` label using the GitHub CLI."*
|
|
15
|
+
|
|
16
|
+
## 2. Brainstorm gate — agent observes comments for PM approval
|
|
17
|
+
|
|
18
|
+
**Analysis:**
|
|
19
|
+
After the agent posts an exploration/brainstorming comment on the issue, it needs PM feedback (approval or requested changes) to proceed to Solution Detail.
|
|
20
|
+
- There is no mechanism (webhook or workflow) for the upstream agent to automatically detect PM approval in an issue comment.
|
|
21
|
+
- In current practice, the PM must manually notify the agent in the chat (e.g., "The brainstorm for #123 is approved").
|
|
22
|
+
- This is a known limitation of current agent interfaces (which require explicit chat prompts to resume execution) and is not documented in the framework.
|
|
23
|
+
|
|
24
|
+
**Solution (Gap):**
|
|
25
|
+
- **Fix:** Acknowledge this limitation in the framework documentation. Update `SETUP.md` and/or a new `docs/workflow-guide.md` to establish the pattern: *"To pass Gate 1 (Brainstorming), the PM must review the issue comments and explicitly tell the agent in the chat to proceed to detailing for that specific issue."*
|
|
26
|
+
|
|
27
|
+
## 3. spec:approved → implementation agent picks up the card
|
|
28
|
+
|
|
29
|
+
**Analysis:**
|
|
30
|
+
Cards in the "Approval" column wait for the PM to add the `spec:approved` label to signal the handoff to the implementation agent.
|
|
31
|
+
- `agent-trigger.yml` correctly fires on the `spec:approved` label and comments on the issue to notify the implementation agent (e.g., Jules).
|
|
32
|
+
- However, when `spec:approved` is added, `project-automation.yml` currently tries to move the card to `STATUS_APPROVAL` — which is the column it is already in.
|
|
33
|
+
- The intent is for the card to move to "Development" when the handoff occurs.
|
|
34
|
+
|
|
35
|
+
**Solution (Gap):**
|
|
36
|
+
- **Fix:** Update `project-automation.yml`. In the `move-card-on-label` job, change the target status for `spec:approved` from `STATUS_APPROVAL` to `STATUS_DEVELOPMENT`.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
*This analysis addresses the questions raised in Issue #4.*
|
package/package.json
CHANGED
|
@@ -12,8 +12,41 @@ jobs:
|
|
|
12
12
|
# Runs only when spec:approved is added
|
|
13
13
|
if: github.event.label.name == 'spec:approved'
|
|
14
14
|
runs-on: ubuntu-latest
|
|
15
|
+
permissions:
|
|
16
|
+
issues: write
|
|
17
|
+
pull-requests: write
|
|
18
|
+
contents: read
|
|
19
|
+
env:
|
|
20
|
+
PROJECT_TOKEN: ${{ secrets.PROJECT_TOKEN }}
|
|
15
21
|
steps:
|
|
16
|
-
- name:
|
|
22
|
+
- name: Update Labels
|
|
23
|
+
if: ${{ env.PROJECT_TOKEN != '' }}
|
|
24
|
+
uses: actions/github-script@v7
|
|
25
|
+
with:
|
|
26
|
+
github-token: ${{ env.PROJECT_TOKEN }}
|
|
27
|
+
script: |
|
|
28
|
+
const { owner, repo } = context.repo;
|
|
29
|
+
const issue_number = context.payload.issue.number;
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
await github.rest.issues.removeLabel({
|
|
33
|
+
owner,
|
|
34
|
+
repo,
|
|
35
|
+
issue_number,
|
|
36
|
+
name: 'upstream:approval'
|
|
37
|
+
});
|
|
38
|
+
} catch (error) {
|
|
39
|
+
console.log('Label upstream:approval not found or could not be removed');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
await github.rest.issues.addLabels({
|
|
43
|
+
owner,
|
|
44
|
+
repo,
|
|
45
|
+
issue_number,
|
|
46
|
+
labels: ['upstream:development', '{{IMPLEMENTATION_AGENT_LABEL}}', 'agent:working']
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
- name: Comment on issue to trigger agent and prevent race conditions
|
|
17
50
|
uses: actions/github-script@v7
|
|
18
51
|
with:
|
|
19
52
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
@@ -22,6 +55,8 @@ jobs:
|
|
|
22
55
|
const issueTitle = context.payload.issue.title;
|
|
23
56
|
|
|
24
57
|
const body = [
|
|
58
|
+
`🤖 **Agentic PDLC Orchestrator:** I have dispatched the implementation agent. Please wait for the Pull Request and avoid making concurrent commits on this task to prevent race conditions.`,
|
|
59
|
+
'',
|
|
25
60
|
`{{AGENT_HANDLE}} The spec for this issue has been approved. Please implement it exactly as described in the body above.`,
|
|
26
61
|
'',
|
|
27
62
|
'**Mandatory steps before you begin:**',
|
|
@@ -48,6 +83,10 @@ jobs:
|
|
|
48
83
|
# Runs when architecture-violation is added (Sentinel flow)
|
|
49
84
|
if: github.event.label.name == 'architecture-violation'
|
|
50
85
|
runs-on: ubuntu-latest
|
|
86
|
+
permissions:
|
|
87
|
+
issues: write
|
|
88
|
+
pull-requests: write
|
|
89
|
+
contents: read
|
|
51
90
|
steps:
|
|
52
91
|
- name: Comment on issue to trigger agent
|
|
53
92
|
uses: actions/github-script@v7
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
name: PDLC Health Check (Drift Detection)
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
schedule:
|
|
6
|
+
- cron: '0 8 * * 1' # Every Monday at 8am
|
|
7
|
+
|
|
8
|
+
env:
|
|
9
|
+
PROJECT_ID: "{{PROJECT_ID}}"
|
|
10
|
+
STATUS_FIELD_ID: "{{STATUS_FIELD_ID}}"
|
|
11
|
+
STATUS_EXPLORATION: "{{ID_EXPLORATION}}"
|
|
12
|
+
STATUS_BRAINSTORMING: "{{ID_BRAINSTORMING}}"
|
|
13
|
+
STATUS_DETAILING: "{{ID_DETAILING}}"
|
|
14
|
+
STATUS_APPROVAL: "{{ID_APPROVAL}}"
|
|
15
|
+
STATUS_DEVELOPMENT: "{{ID_DEVELOPMENT}}"
|
|
16
|
+
STATUS_TESTING: "{{ID_TESTING}}"
|
|
17
|
+
STATUS_CODE_REVIEW_PR: "{{ID_CODE_REVIEW_PR}}"
|
|
18
|
+
STATUS_PRODUCTION: "{{ID_PRODUCTION}}"
|
|
19
|
+
|
|
20
|
+
jobs:
|
|
21
|
+
check-drift:
|
|
22
|
+
name: Detect Project Board Drift
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
env:
|
|
25
|
+
PROJECT_TOKEN: ${{ secrets.PROJECT_TOKEN }}
|
|
26
|
+
permissions:
|
|
27
|
+
issues: write
|
|
28
|
+
steps:
|
|
29
|
+
- name: Validate Board Configuration
|
|
30
|
+
if: ${{ env.PROJECT_TOKEN != '' && env.PROJECT_ID != '{{PROJECT_ID}}' }}
|
|
31
|
+
uses: actions/github-script@v7
|
|
32
|
+
with:
|
|
33
|
+
github-token: ${{ env.PROJECT_TOKEN }}
|
|
34
|
+
script: |
|
|
35
|
+
const projectId = process.env.PROJECT_ID;
|
|
36
|
+
const statusFieldId = process.env.STATUS_FIELD_ID;
|
|
37
|
+
const envVars = {
|
|
38
|
+
'STATUS_EXPLORATION': process.env.STATUS_EXPLORATION,
|
|
39
|
+
'STATUS_BRAINSTORMING': process.env.STATUS_BRAINSTORMING,
|
|
40
|
+
'STATUS_DETAILING': process.env.STATUS_DETAILING,
|
|
41
|
+
'STATUS_APPROVAL': process.env.STATUS_APPROVAL,
|
|
42
|
+
'STATUS_DEVELOPMENT': process.env.STATUS_DEVELOPMENT,
|
|
43
|
+
'STATUS_TESTING': process.env.STATUS_TESTING,
|
|
44
|
+
'STATUS_CODE_REVIEW_PR': process.env.STATUS_CODE_REVIEW_PR,
|
|
45
|
+
'STATUS_PRODUCTION': process.env.STATUS_PRODUCTION
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const query = `
|
|
49
|
+
query($projectId: ID!) {
|
|
50
|
+
node(id: $projectId) {
|
|
51
|
+
... on ProjectV2 {
|
|
52
|
+
title
|
|
53
|
+
fields(first: 20) {
|
|
54
|
+
nodes {
|
|
55
|
+
... on ProjectV2SingleSelectField {
|
|
56
|
+
id
|
|
57
|
+
name
|
|
58
|
+
options {
|
|
59
|
+
id
|
|
60
|
+
name
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
`;
|
|
69
|
+
|
|
70
|
+
let result;
|
|
71
|
+
try {
|
|
72
|
+
result = await github.graphql(query, { projectId });
|
|
73
|
+
} catch (error) {
|
|
74
|
+
console.log("❌ Error fetching project. Verify your PROJECT_ID.");
|
|
75
|
+
console.log(error);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const project = result.node;
|
|
80
|
+
if (!project) {
|
|
81
|
+
console.log("❌ Project not found.");
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const statusField = project.fields.nodes.find(f => f.id === statusFieldId);
|
|
86
|
+
if (!statusField) {
|
|
87
|
+
console.log("❌ Status field not found.");
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const validOptions = statusField.options;
|
|
92
|
+
const validOptionIds = validOptions.map(o => o.id);
|
|
93
|
+
|
|
94
|
+
let hasDrift = false;
|
|
95
|
+
let missingVars = [];
|
|
96
|
+
|
|
97
|
+
for (const [varName, id] of Object.entries(envVars)) {
|
|
98
|
+
if (id && !id.startsWith('{{') && !validOptionIds.includes(id)) {
|
|
99
|
+
hasDrift = true;
|
|
100
|
+
missingVars.push(varName);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (hasDrift) {
|
|
105
|
+
console.log("🚨 Drift detected! The following mapped columns no longer exist: " + missingVars.join(", "));
|
|
106
|
+
|
|
107
|
+
let table = "| Column Name | New ID |\n|---|---|\n";
|
|
108
|
+
validOptions.forEach(opt => {
|
|
109
|
+
table += `| ${opt.name} | \`${opt.id}\` |\n`;
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const body = `🚨 **Agentic PDLC Drift Detected**\n\nThe following columns mapped in your \`.github/workflows/project-automation.yml\` no longer exist in your project board:\n\n**${missingVars.join(", ")}**\n\n### How to fix it:\nHere is the list of current columns in your board with their valid IDs. Please update the \`env\` block in your \`.github/workflows/project-automation.yml\` and \`.github/workflows/pdlc-health-check.yml\`.\n\n${table}`;
|
|
113
|
+
|
|
114
|
+
await github.rest.issues.create({
|
|
115
|
+
owner: context.repo.owner,
|
|
116
|
+
repo: context.repo.repo,
|
|
117
|
+
title: '🚨 Agentic PDLC Drift Detected in Project Board',
|
|
118
|
+
body: body,
|
|
119
|
+
labels: ['bug']
|
|
120
|
+
});
|
|
121
|
+
} else {
|
|
122
|
+
console.log("✅ No drift detected. Board configuration is healthy.");
|
|
123
|
+
}
|
|
@@ -16,9 +16,9 @@ env:
|
|
|
16
16
|
STATUS_DETAILING: "{{ID_DETAILING}}"
|
|
17
17
|
STATUS_APPROVAL: "{{ID_APPROVAL}}"
|
|
18
18
|
STATUS_DEVELOPMENT: "{{ID_DEVELOPMENT}}"
|
|
19
|
+
STATUS_TESTING: "{{ID_TESTING}}"
|
|
19
20
|
STATUS_CODE_REVIEW_PR: "{{ID_CODE_REVIEW_PR}}"
|
|
20
|
-
|
|
21
|
-
STATUS_PRODUCAO: "{{ID_PRODUCAO}}"
|
|
21
|
+
STATUS_PRODUCTION: "{{ID_PRODUCTION}}"
|
|
22
22
|
|
|
23
23
|
jobs:
|
|
24
24
|
# Issue Labeled → Move Upstream
|
|
@@ -39,21 +39,24 @@ jobs:
|
|
|
39
39
|
let targetStatusId = null;
|
|
40
40
|
let stageName = null;
|
|
41
41
|
|
|
42
|
-
if (labelName === '
|
|
42
|
+
if (labelName === 'upstream:exploration') {
|
|
43
43
|
targetStatusId = process.env.STATUS_EXPLORATION;
|
|
44
44
|
stageName = 'Exploration';
|
|
45
|
-
} else if (labelName === '
|
|
45
|
+
} else if (labelName === 'upstream:brainstorming') {
|
|
46
46
|
targetStatusId = process.env.STATUS_BRAINSTORMING;
|
|
47
47
|
stageName = 'Brainstorming';
|
|
48
|
-
} else if (labelName === '
|
|
48
|
+
} else if (labelName === 'upstream:detailing') {
|
|
49
49
|
targetStatusId = process.env.STATUS_DETAILING;
|
|
50
50
|
stageName = 'Detailing';
|
|
51
|
-
} else if (labelName === '
|
|
51
|
+
} else if (labelName === 'upstream:approval') {
|
|
52
52
|
targetStatusId = process.env.STATUS_APPROVAL;
|
|
53
53
|
stageName = 'Approval';
|
|
54
|
-
} else if (labelName === '
|
|
54
|
+
} else if (labelName === 'upstream:development') {
|
|
55
55
|
targetStatusId = process.env.STATUS_DEVELOPMENT;
|
|
56
56
|
stageName = 'Development';
|
|
57
|
+
} else if (labelName === 'upstream:testing') {
|
|
58
|
+
targetStatusId = process.env.STATUS_TESTING;
|
|
59
|
+
stageName = 'Testing';
|
|
57
60
|
}
|
|
58
61
|
|
|
59
62
|
if (!targetStatusId) {
|
|
@@ -83,6 +86,7 @@ jobs:
|
|
|
83
86
|
await moveItem(node_id, targetStatusId);
|
|
84
87
|
console.log(`Issue #${number} moved to ${stageName}`);
|
|
85
88
|
|
|
89
|
+
{{OPTIONAL_ARCHITECTURE_VIOLATION_JOB}}
|
|
86
90
|
|
|
87
91
|
# PR Opened → Move linked issue to Code Review / PR
|
|
88
92
|
move-card-on-pr-open:
|
|
@@ -136,17 +140,17 @@ jobs:
|
|
|
136
140
|
console.log(`PR #${prNumber} → Code Review / PR (no linked issue)`);
|
|
137
141
|
}
|
|
138
142
|
|
|
139
|
-
await github.rest.issues.addLabels({ owner, repo, issue_number: prNumber, labels: ['pr:review'] }).catch(() => {});
|
|
143
|
+
await github.rest.issues.addLabels({ owner, repo, issue_number: prNumber, labels: ['pr:in-review'] }).catch(() => {});
|
|
140
144
|
|
|
141
|
-
# Review Approved →
|
|
145
|
+
# Review Approved → Add Label
|
|
142
146
|
move-card-on-review-approved:
|
|
143
|
-
name: Approved PR →
|
|
147
|
+
name: Approved PR → Add Label
|
|
144
148
|
if: github.event_name == 'pull_request_review' && github.event.review.state == 'approved'
|
|
145
149
|
runs-on: ubuntu-latest
|
|
146
150
|
env:
|
|
147
151
|
PROJECT_TOKEN: ${{ secrets.PROJECT_TOKEN }}
|
|
148
152
|
steps:
|
|
149
|
-
- name:
|
|
153
|
+
- name: Swap PR labels
|
|
150
154
|
if: ${{ env.PROJECT_TOKEN != '' }}
|
|
151
155
|
uses: actions/github-script@v7
|
|
152
156
|
with:
|
|
@@ -154,34 +158,7 @@ jobs:
|
|
|
154
158
|
script: |
|
|
155
159
|
const prNumber = context.payload.pull_request.number;
|
|
156
160
|
const { owner, repo } = context.repo;
|
|
157
|
-
|
|
158
|
-
const body = pr.body ?? '';
|
|
159
|
-
const linkedIssues = [...body.matchAll(/(?:Closes?|Fixes?|Resolves?)\s+#(\d+)/gi)].map(m => parseInt(m[1]));
|
|
160
|
-
|
|
161
|
-
const moveItem = async (nodeId) => {
|
|
162
|
-
const { addProjectV2ItemById: { item } } = await github.graphql(`
|
|
163
|
-
mutation($p: ID!, $c: ID!) {
|
|
164
|
-
addProjectV2ItemById(input: {projectId: $p, contentId: $c}) { item { id } }
|
|
165
|
-
}`, { p: process.env.PROJECT_ID, c: nodeId });
|
|
166
|
-
await github.graphql(`
|
|
167
|
-
mutation($p: ID!, $i: ID!, $f: ID!, $v: ProjectV2FieldValue!) {
|
|
168
|
-
updateProjectV2ItemFieldValue(input: {projectId: $p, itemId: $i, fieldId: $f, value: $v}) {
|
|
169
|
-
projectV2Item { id }
|
|
170
|
-
}
|
|
171
|
-
}`, { p: process.env.PROJECT_ID, i: item.id, f: process.env.STATUS_FIELD_ID,
|
|
172
|
-
v: { singleSelectOptionId: process.env.STATUS_MERGE } });
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
if (linkedIssues.length > 0) {
|
|
176
|
-
for (const n of linkedIssues) {
|
|
177
|
-
const { data: issue } = await github.rest.issues.get({ owner, repo, issue_number: n });
|
|
178
|
-
await moveItem(issue.node_id);
|
|
179
|
-
}
|
|
180
|
-
} else {
|
|
181
|
-
await moveItem(pr.node_id);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
try { await github.rest.issues.removeLabel({ owner, repo, issue_number: prNumber, name: 'pr:review' }); } catch {}
|
|
161
|
+
try { await github.rest.issues.removeLabel({ owner, repo, issue_number: prNumber, name: 'pr:in-review' }); } catch {}
|
|
185
162
|
await github.rest.issues.addLabels({ owner, repo, issue_number: prNumber, labels: ['pr:approved'] }).catch(() => {});
|
|
186
163
|
|
|
187
164
|
# PR Merged → Production
|
|
@@ -215,7 +192,7 @@ jobs:
|
|
|
215
192
|
projectV2Item { id }
|
|
216
193
|
}
|
|
217
194
|
}`, { p: process.env.PROJECT_ID, i: item.id, f: process.env.STATUS_FIELD_ID,
|
|
218
|
-
v: { singleSelectOptionId: process.env.
|
|
195
|
+
v: { singleSelectOptionId: process.env.STATUS_PRODUCTION } });
|
|
219
196
|
};
|
|
220
197
|
|
|
221
198
|
if (linkedIssues.length > 0) {
|