forgedock 1.0.5 → 1.0.7

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 (42) hide show
  1. package/README.md +7 -1
  2. package/bin/forgedock.mjs +496 -364
  3. package/bin/labels.json +152 -0
  4. package/commands/audit-agents.md +4 -2
  5. package/commands/autopilot.md +19 -11
  6. package/commands/cleanup.md +27 -12
  7. package/commands/deploy-info.md +36 -17
  8. package/commands/incident-response.md +15 -10
  9. package/commands/issue.md +8 -6
  10. package/commands/milestone.md +97 -48
  11. package/commands/orchestrate.md +109 -65
  12. package/commands/pipeline-health.md +8 -1
  13. package/commands/qa-sweep.md +14 -3
  14. package/commands/review-pr-agents.md +1 -1
  15. package/commands/review-pr-staging.md +129 -23
  16. package/commands/review-pr.md +237 -90
  17. package/commands/rollback.md +26 -7
  18. package/commands/security-audit.md +27 -25
  19. package/commands/sync-ecosystem.md +2 -2
  20. package/commands/work-on/build/architect.md +154 -2
  21. package/commands/work-on/build/context.md +131 -3
  22. package/commands/work-on/build/validate.md +32 -13
  23. package/commands/work-on/decompose.md +2 -0
  24. package/commands/work-on-monolithic.md +9 -5
  25. package/commands/work-on.md +67 -23
  26. package/docs/CONFIG.md +117 -0
  27. package/package.json +2 -1
  28. package/scripts/verify-route-registration.sh +36 -15
  29. package/scripts/verify-sops-chain.sh +18 -0
  30. package/templates/devdocs/README.md +96 -0
  31. package/templates/devdocs/agent/using-forgedock.md +245 -0
  32. package/templates/devdocs/agent/using-github.md +325 -0
  33. package/templates/devdocs/project/architecture.md +122 -0
  34. package/templates/devdocs/project/conventions.md +158 -0
  35. package/templates/devdocs/project/custom-instructions.md +73 -0
  36. package/templates/devdocs/project/glossary.md +84 -0
  37. package/templates/devdocs/project/stack.md +80 -0
  38. package/templates/devdocs/templates/commit-convention.md +147 -0
  39. package/templates/devdocs/templates/issue.md +141 -0
  40. package/templates/devdocs/templates/pull-request.md +115 -0
  41. package/templates/devdocs/templates/review-finding.md +147 -0
  42. package/bin/github-app-manifest.json +0 -24
@@ -0,0 +1,152 @@
1
+ [
2
+ {
3
+ "name": "priority:P0",
4
+ "color": "B60205",
5
+ "description": "Critical priority — production broken, data loss, security breach. Managed by ForgeDock."
6
+ },
7
+ {
8
+ "name": "priority:P1",
9
+ "color": "D93F0B",
10
+ "description": "High priority — major feature broken, no workaround. Managed by ForgeDock."
11
+ },
12
+ {
13
+ "name": "priority:P2",
14
+ "color": "FBCA04",
15
+ "description": "Medium priority — impaired functionality, workaround exists. Managed by ForgeDock."
16
+ },
17
+ {
18
+ "name": "priority:P3",
19
+ "color": "C2E0C6",
20
+ "description": "Low priority — minor issue or polish. Managed by ForgeDock."
21
+ },
22
+ {
23
+ "name": "workflow:investigating",
24
+ "color": "1D76DB",
25
+ "description": "Pipeline: investigation phase in progress. Managed by ForgeDock."
26
+ },
27
+ {
28
+ "name": "workflow:ready-to-build",
29
+ "color": "0075CA",
30
+ "description": "Pipeline: investigation complete, ready for build. Managed by ForgeDock."
31
+ },
32
+ {
33
+ "name": "workflow:building",
34
+ "color": "0052CC",
35
+ "description": "Pipeline: implementation in progress. Managed by ForgeDock."
36
+ },
37
+ {
38
+ "name": "workflow:in-review",
39
+ "color": "5319E7",
40
+ "description": "Pipeline: PR created, under review. Managed by ForgeDock."
41
+ },
42
+ {
43
+ "name": "workflow:merged",
44
+ "color": "0E8A16",
45
+ "description": "Pipeline: PR merged, issue closed. Managed by ForgeDock."
46
+ },
47
+ {
48
+ "name": "workflow:decomposed",
49
+ "color": "BFD4F2",
50
+ "description": "Pipeline: decomposed into sub-issues. Managed by ForgeDock."
51
+ },
52
+ {
53
+ "name": "workflow:invalid",
54
+ "color": "CCCCCC",
55
+ "description": "Pipeline: issue closed as invalid after investigation. Managed by ForgeDock."
56
+ },
57
+ {
58
+ "name": "needs-human",
59
+ "color": "E4E669",
60
+ "description": "Pipeline stalled — requires human intervention. Managed by ForgeDock."
61
+ },
62
+ {
63
+ "name": "review-finding",
64
+ "color": "D93F0B",
65
+ "description": "Defect or improvement found during automated PR review. Managed by ForgeDock."
66
+ },
67
+ {
68
+ "name": "needs-validation",
69
+ "color": "FBCA04",
70
+ "description": "Review finding awaiting human validation. Managed by ForgeDock."
71
+ },
72
+ {
73
+ "name": "validated",
74
+ "color": "0E8A16",
75
+ "description": "Review finding confirmed as a real issue. Managed by ForgeDock."
76
+ },
77
+ {
78
+ "name": "false-positive",
79
+ "color": "CCCCCC",
80
+ "description": "Review finding dismissed as a false positive. Managed by ForgeDock."
81
+ },
82
+ {
83
+ "name": "staging-review",
84
+ "color": "1D76DB",
85
+ "description": "Finding from a staging branch review before deploy to main. Managed by ForgeDock."
86
+ },
87
+ {
88
+ "name": "audit-finding",
89
+ "color": "D93F0B",
90
+ "description": "Security or compliance defect found during audit. Managed by ForgeDock."
91
+ },
92
+ {
93
+ "name": "orchestration-metrics",
94
+ "color": "5319E7",
95
+ "description": "Running log of persisted audit-agents efficiency summaries. Managed by ForgeDock."
96
+ },
97
+ {
98
+ "name": "health-report",
99
+ "color": "1D76DB",
100
+ "description": "Pipeline health report issue. Managed by ForgeDock."
101
+ },
102
+ {
103
+ "name": "bug",
104
+ "color": "D73A4A",
105
+ "description": "Something isn't working. Managed by ForgeDock."
106
+ },
107
+ {
108
+ "name": "enhancement",
109
+ "color": "A2EEEF",
110
+ "description": "New feature or request. Managed by ForgeDock."
111
+ },
112
+ {
113
+ "name": "feature",
114
+ "color": "0075CA",
115
+ "description": "New feature implementation. Managed by ForgeDock."
116
+ },
117
+ {
118
+ "name": "refactor",
119
+ "color": "FEF2C0",
120
+ "description": "Code restructuring without behavior change. Managed by ForgeDock."
121
+ },
122
+ {
123
+ "name": "dead-code",
124
+ "color": "E4E669",
125
+ "description": "Unused code to be removed. Managed by ForgeDock."
126
+ },
127
+ {
128
+ "name": "improvement",
129
+ "color": "C5DEF5",
130
+ "description": "Non-bug improvement to existing functionality. Managed by ForgeDock."
131
+ },
132
+ {
133
+ "name": "documentation",
134
+ "color": "0075CA",
135
+ "description": "Documentation addition or update. Managed by ForgeDock."
136
+ },
137
+ {
138
+ "name": "qa",
139
+ "color": "E99695",
140
+ "description": "Quality assurance or testing issue. Managed by ForgeDock."
141
+ },
142
+ {
143
+ "name": "security",
144
+ "color": "B60205",
145
+ "description": "Security vulnerability or hardening. Managed by ForgeDock."
146
+ },
147
+ {
148
+ "name": "performance",
149
+ "color": "F9D0C4",
150
+ "description": "Performance optimization or regression. Managed by ForgeDock."
151
+ }
152
+ ]
@@ -343,9 +343,11 @@ This step posts a structured `<!-- FORGE:AUDIT-AGENTS -->` summary comment to th
343
343
  **Step 4E.1 — Locate or create the tracking issue**:
344
344
 
345
345
  ```bash
346
- # Ensure the label exists before using it (gh issue create fails with GraphQL error if label is absent)
346
+ # Ensure the label exists before using it (gh issue create fails with GraphQL error if label is absent).
347
+ # Color and description match the canonical ForgeDock label manifest (bin/labels.json).
348
+ # Run `npx forgedock labels setup` to bootstrap all managed labels at once.
347
349
  gh label create "orchestration-metrics" -R {FORGE_REPO} \
348
- --color "5319E7" --description "Running log of persisted audit-agents efficiency summaries" \
350
+ --color "5319E7" --description "Running log of persisted audit-agents efficiency summaries. Managed by ForgeDock." \
349
351
  --force 2>/dev/null || true
350
352
 
351
353
  TRACKING_ISSUE=$(gh issue list -R {FORGE_REPO} \
@@ -11,6 +11,7 @@ argument-hint: [--fix | --recon-only | --fix --limit 5 | --dry-run]
11
11
  - `{CREDENTIALS_FILE}` ← `paths.credentials.file` (optional) — path to credentials YAML for analytics APIs
12
12
  - `{SERVER_SSH}` ← `services.server_ssh` (optional) — SSH target for production server health checks (e.g., `ubuntu@1.2.3.4`)
13
13
  - `{EMEMO_PATH}` ← `services.ememo_path` (optional) — path on production server to open eMemo files
14
+ - `{BILLING_ENABLED}` ← `billing.enabled` (optional, default `false`) — set to `true` to enable Stripe data collection in the Analytics Snapshot agent
14
15
 
15
16
  You are an autonomous improvement engine for this project. Your job is to **find what's wrong, create trackable issues, and optionally fix the highest-impact ones** — all in a single cycle. Every cycle leaves the platform measurably better than before.
16
17
 
@@ -51,25 +52,31 @@ DRY_RUN = true if --dry-run present
51
52
  ```bash
52
53
  echo "=== Autopilot Cycle: $(date -u +%Y-%m-%dT%H:%M:%SZ) ==="
53
54
 
55
+ # Portable ISO-8601 date arithmetic using python3 (works on Windows, macOS, Linux).
56
+ # Replaces GNU-only `date -u -d 'N days ago'` which fails on Windows and macOS.
57
+ DATE_3D_AGO=$(python3 -c "from datetime import datetime, timedelta, timezone; print((datetime.now(timezone.utc) - timedelta(days=3)).strftime('%Y-%m-%dT%H:%M:%SZ'))")
58
+ DATE_7D_AGO=$(python3 -c "from datetime import datetime, timedelta, timezone; print((datetime.now(timezone.utc) - timedelta(days=7)).strftime('%Y-%m-%dT%H:%M:%SZ'))")
59
+ DATE_1D_AGO=$(python3 -c "from datetime import datetime, timedelta, timezone; print((datetime.now(timezone.utc) - timedelta(days=1)).strftime('%Y-%m-%dT%H:%M:%SZ'))")
60
+
54
61
  # Recent closed issues (last 3 days) — what was fixed recently?
55
62
  gh issue list --state closed --json number,title,labels,closedAt \
56
- --jq '[.[] | select(.closedAt > "'$(date -u -d '3 days ago' +%Y-%m-%dT%H:%M:%SZ)'")] | length' 2>/dev/null || echo "0"
63
+ --jq '[.[] | select(.closedAt > "'"$DATE_3D_AGO"'")] | length' 2>/dev/null || echo "0"
57
64
 
58
65
  # Open issue count by priority
59
- gh issue list --state open --label P0 --json number --jq 'length'
60
- gh issue list --state open --label P1 --json number --jq 'length'
61
- gh issue list --state open --label P2 --json number --jq 'length'
66
+ gh issue list --state open --label "priority:P0" --json number --jq 'length'
67
+ gh issue list --state open --label "priority:P1" --json number --jq 'length'
68
+ gh issue list --state open --label "priority:P2" --json number --jq 'length'
62
69
 
63
70
  # Stale issues (open, no workflow label, older than 7 days)
64
71
  gh issue list --state open --limit 200 --json number,title,labels,createdAt \
65
72
  --jq '[.[] | select(
66
73
  (.labels | map(.name) | any(startswith("workflow:")) | not) and
67
- (.createdAt < "'$(date -u -d '7 days ago' +%Y-%m-%dT%H:%M:%SZ)'")
74
+ (.createdAt < "'"$DATE_7D_AGO"'")
68
75
  )] | length'
69
76
 
70
77
  # Failed CI runs in last 24h
71
78
  gh run list --limit 30 --json conclusion,createdAt \
72
- --jq '[.[] | select(.conclusion == "failure" and .createdAt > "'$(date -u -d '1 day ago' +%Y-%m-%dT%H:%M:%SZ)'")] | length'
79
+ --jq '[.[] | select(.conclusion == "failure" and .createdAt > "'"$DATE_1D_AGO"'")] | length'
73
80
  ```
74
81
 
75
82
  Store these as `BASELINE` metrics for the cycle report.
@@ -77,7 +84,7 @@ Store these as `BASELINE` metrics for the cycle report.
77
84
  ### 0B: Check for open P0s
78
85
 
79
86
  ```bash
80
- gh issue list --state open --label P0 --json number,title --jq '.[] | "#\(.number) \(.title)"'
87
+ gh issue list --state open --label "priority:P0" --json number,title --jq '.[] | "#\(.number) \(.title)"'
81
88
  ```
82
89
 
83
90
  If any P0 issues exist:
@@ -140,8 +147,9 @@ Analyze GitHub issue backlog:
140
147
  Quick analytics pulse — just the key metrics, not a full audit:
141
148
  1. Read credentials from {CREDENTIALS_FILE} (set via paths.credentials.file in forge.yaml)
142
149
  2. GSC: mcp__gsc__search_analytics for last 7 days — total clicks, impressions, avg position
143
- 3. Stripe: mcp__stripe__retrieve_balance — current balance
144
- 4. Return: clicks trend (up/down), any revenue, notable changes
150
+ 3. Stripe (only if {BILLING_ENABLED} is true): mcp__stripe__retrieve_balance — current balance.
151
+ Skip this step entirely if {BILLING_ENABLED} is false — do NOT call any Stripe MCP tools.
152
+ 4. Return: clicks trend (up/down), revenue (or "N/A — billing.enabled: false" if skipped), notable changes
145
153
  ```
146
154
 
147
155
  ### 1B: Collect results
@@ -291,7 +299,7 @@ Print a structured report:
291
299
 
292
300
  ### Analytics Pulse
293
301
  - Clicks (7d): {N} ({trend})
294
- - Revenue: ${N}
302
+ - Revenue: ${N} (or "N/A — billing.enabled: false" if Stripe step was skipped)
295
303
 
296
304
  ### Actions Taken
297
305
  - Issues created: {count} ({list with numbers})
@@ -334,7 +342,7 @@ Pick the top `FIX_LIMIT` issues by this priority:
334
342
  ```bash
335
343
  # Get candidates
336
344
  gh issue list --state open --label "bug" --limit 50 --json number,title,labels,createdAt \
337
- --jq 'sort_by(.labels | map(select(.name | startswith("P"))) | .[0].name) | .[:{FIX_LIMIT}] | .[] | "#\(.number) \(.title)"'
345
+ --jq 'sort_by(.labels | map(select(.name | startswith("priority:P"))) | .[0].name) | .[:{FIX_LIMIT}] | .[] | "#\(.number) \(.title)"'
338
346
  ```
339
347
 
340
348
  ### 4B: Present fix plan to user
@@ -25,11 +25,16 @@ GH_FLAG="-R $GH_REPO"
25
25
  REPO_PATH=$(yq '.paths.root' "$CONFIG_FILE")
26
26
  STAGING_BRANCH=$(yq '.branches.staging' "$CONFIG_FILE")
27
27
  PROJECT_BOARD_OWNER=$(yq '.project_board.owner // .project.owner' "$CONFIG_FILE")
28
- PROJECT_NUMBER=$(yq '.project_board.project_number // "1"' "$CONFIG_FILE")
29
- PROJECT_ID=$(yq '.project_board.project_id' "$CONFIG_FILE")
28
+ PROJECT_NUMBER=$(yq '.project_board.project_number // empty' "$CONFIG_FILE" 2>/dev/null || echo "")
29
+ PROJECT_ID=$(yq '.project_board.project_id // empty' "$CONFIG_FILE" 2>/dev/null || echo "")
30
+ # Project board field and option IDs — empty string when project_board section is absent
31
+ STATUS_FIELD_ID=$(yq '.project_board.field_ids.status // ""' "$CONFIG_FILE" 2>/dev/null || echo "")
32
+ WORKFLOW_FIELD_ID=$(yq '.project_board.field_ids.workflow // ""' "$CONFIG_FILE" 2>/dev/null || echo "")
33
+ STATUS_DONE_OPTION_ID=$(yq '.project_board.option_ids.status.done // ""' "$CONFIG_FILE" 2>/dev/null || echo "")
34
+ WORKFLOW_MERGED_OPTION_ID=$(yq '.project_board.option_ids.workflow.merged // ""' "$CONFIG_FILE" 2>/dev/null || echo "")
30
35
  ```
31
36
 
32
- All `{GH_REPO}`, `{GH_FLAG}`, `{REPO_PATH}`, `{STAGING_BRANCH}`, `{PROJECT_BOARD_OWNER}`, `{PROJECT_NUMBER}`, and `{PROJECT_ID}` references below are populated from `forge.yaml`.
37
+ All `{GH_REPO}`, `{GH_FLAG}`, `{REPO_PATH}`, `{STAGING_BRANCH}`, `{PROJECT_BOARD_OWNER}`, `{PROJECT_NUMBER}`, `{PROJECT_ID}`, `{STATUS_FIELD_ID}`, `{WORKFLOW_FIELD_ID}`, `{STATUS_DONE_OPTION_ID}`, and `{WORKFLOW_MERGED_OPTION_ID}` references below are populated from `forge.yaml`.
33
38
 
34
39
  ---
35
40
 
@@ -222,19 +227,29 @@ fi
222
227
 
223
228
  ## Phase 5: Sync Project Board (if `board` or `all`)
224
229
 
225
- For closed issues that are on the Project board but have stale fields:
230
+ **Skip if `project_board` is not configured** — check resolved vars before proceeding:
226
231
 
227
232
  ```bash
228
- gh project item-list {PROJECT_NUMBER} --owner {PROJECT_BOARD_OWNER} --format json --limit 200
229
- ```
233
+ if [ -z "$PROJECT_BOARD_OWNER" ] || [ -z "$PROJECT_ID" ] || [ -z "$PROJECT_NUMBER" ]; then
234
+ echo "INFO: project_board not configured in forge.yaml — skipping board sync"
235
+ else
236
+ # Board sync: mark closed issues as Done/Merged on the project board
230
237
 
231
- For each item where `content.state == "CLOSED"` but `status != "Done"` or `workflow` is not a terminal state:
238
+ # List all board items
239
+ ITEMS=$(gh project item-list "$PROJECT_NUMBER" --owner "$PROJECT_BOARD_OWNER" --format json --limit 200)
232
240
 
233
- ```bash
234
- gh project item-edit --project-id {PROJECT_ID} --id "$ITEM_ID" \
235
- --field-id {PROJECT_BOARD_STATUS_FIELD_ID} --single-select-option-id {PROJECT_BOARD_STATUS_DONE_ID} 2>/dev/null || true # Status=Done
236
- gh project item-edit --project-id {PROJECT_ID} --id "$ITEM_ID" \
237
- --field-id {PROJECT_BOARD_WORKFLOW_FIELD_ID} --single-select-option-id {PROJECT_BOARD_WORKFLOW_MERGED_ID} 2>/dev/null || true # Workflow=Merged
241
+ # For each item where content.state == "CLOSED" but status != "Done" or workflow is not terminal:
242
+ echo "$ITEMS" | jq -r '.items[] | select(.content.state == "CLOSED") | .id' | while read -r ITEM_ID; do
243
+ if [ -n "$STATUS_FIELD_ID" ] && [ -n "$STATUS_DONE_OPTION_ID" ]; then
244
+ gh project item-edit --project-id "$PROJECT_ID" --id "$ITEM_ID" \
245
+ --field-id "$STATUS_FIELD_ID" --single-select-option-id "$STATUS_DONE_OPTION_ID" 2>/dev/null || true # Status=Done
246
+ fi
247
+ if [ -n "$WORKFLOW_FIELD_ID" ] && [ -n "$WORKFLOW_MERGED_OPTION_ID" ]; then
248
+ gh project item-edit --project-id "$PROJECT_ID" --id "$ITEM_ID" \
249
+ --field-id "$WORKFLOW_FIELD_ID" --single-select-option-id "$WORKFLOW_MERGED_OPTION_ID" 2>/dev/null || true # Workflow=Merged
250
+ fi
251
+ done
252
+ fi
238
253
  ```
239
254
 
240
255
  ---
@@ -10,6 +10,8 @@ argument-hint: [staging | milestone/{slug} | compare {branch}]
10
10
  **Config variables used by this command** (set in `forge.yaml`):
11
11
  - `{REPO_PATH}` ← `paths.root` — project repository root
12
12
  - `{HEALTH_ENDPOINT}` ← `services.health_endpoint` (optional) — URL used in post-deploy health check
13
+ - `{DEPLOY_WORKFLOW}` ← `deploy.workflow` (optional) — GitHub Actions workflow filename to trigger a deploy (e.g., `hotfix-deploy.yml`). When absent or empty, workflow-trigger steps are omitted.
14
+ - `{DEPLOY_SECRETS_BACKEND}` ← `deploy.secrets_backend` (optional) — secrets delivery method (`sops`, `aws-sm`, `vault`, `ci-env`, `none`). Controls whether secret-chain checks run.
13
15
 
14
16
  You are the pipeline's deploy awareness layer. Before the user merges staging → main (triggering CI/CD deployment), this command shows exactly what's going out: which PRs, which issues, what changed, and what risks exist.
15
17
 
@@ -153,13 +155,20 @@ done
153
155
  # Full file diff summary
154
156
  git diff --stat origin/$TARGET..origin/$SOURCE
155
157
 
158
+ # Read layout paths from forge.yaml, fall back to AlterLab defaults
159
+ LAYOUT_API=$(yq '.review.layout.api // "services/api"' $PROJECT_ROOT/forge.yaml 2>/dev/null || echo "services/api")
160
+ LAYOUT_WORKER=$(yq '.review.layout.worker // "services/worker"' $PROJECT_ROOT/forge.yaml 2>/dev/null || echo "services/worker")
161
+ LAYOUT_PAGES=$(yq '.review.layout.pages // "web/src/app"' $PROJECT_ROOT/forge.yaml 2>/dev/null || echo "web/src/app")
162
+ # Derive web root from pages root (strip trailing /src/app or similar suffix if present)
163
+ LAYOUT_WEB=$(echo "$LAYOUT_PAGES" | sed 's|/src/app$||; s|/src$||')
164
+
156
165
  # Categorize by service
157
166
  echo "=== By Service ==="
158
167
  git diff --name-only origin/$TARGET..origin/$SOURCE | sort | while read f; do
159
168
  case "$f" in
160
- services/api/*) echo "API: $f" ;;
161
- services/worker/*) echo "WORKER: $f" ;;
162
- web/*) echo "WEB: $f" ;;
169
+ ${LAYOUT_API}/*) echo "API: $f" ;;
170
+ ${LAYOUT_WORKER}/*) echo "WORKER: $f" ;;
171
+ ${LAYOUT_WEB}/*) echo "WEB: $f" ;;
163
172
  shared/*) echo "SHARED: $f" ;;
164
173
  infra/*) echo "INFRA: $f" ;;
165
174
  *) echo "OTHER: $f" ;;
@@ -170,8 +179,11 @@ done | sort
170
179
  ### Step 2E: Migration check
171
180
 
172
181
  ```bash
182
+ # Read migrations path from forge.yaml, fall back to infra/migrations
183
+ LAYOUT_MIGRATIONS=$(yq '.review.layout.migrations // "infra/migrations"' $PROJECT_ROOT/forge.yaml 2>/dev/null || echo "infra/migrations")
184
+
173
185
  # Are there new migrations?
174
- MIGRATIONS=$(git diff --name-only origin/$TARGET..origin/$SOURCE | grep "^infra/migrations/" | sort)
186
+ MIGRATIONS=$(git diff --name-only origin/$TARGET..origin/$SOURCE | grep "^${LAYOUT_MIGRATIONS}/" | sort)
175
187
  if [ -n "$MIGRATIONS" ]; then
176
188
  echo "⚠️ DATABASE MIGRATIONS INCLUDED:"
177
189
  echo "$MIGRATIONS"
@@ -195,7 +207,7 @@ Analyze the deploy payload and flag risks:
195
207
  |--------|-----------|--------|
196
208
  | Database migrations present | MEDIUM | Verify migrations are reversible |
197
209
  | `shared/` changes | LOW | Restart-only deploy possible (no rebuild) |
198
- | `.env.example` changes | HIGH | Verify SOPS + decrypt-secrets has new vars |
210
+ | `.env.example` changes | HIGH | Verify secrets backend has new vars (see `deploy.secrets_backend`) |
199
211
  | `docker-compose.prod.yml` changes | HIGH | Review infrastructure changes carefully |
200
212
  | `infra/traefik/` changes | HIGH | Routing changes — test before full deploy |
201
213
  | 10+ PRs in one deploy | MEDIUM | Consider deploying in smaller batches |
@@ -205,17 +217,22 @@ Analyze the deploy payload and flag risks:
205
217
  ### Automated Checks
206
218
 
207
219
  ```bash
208
- # Check for new env vars that might not be in SOPS
220
+ # Check for new env vars that might not be in the configured secrets backend
209
221
  NEW_ENV=$(git diff origin/$TARGET..origin/$SOURCE -- '.env.example' | grep "^+" | grep -v "^+++" | grep -v "^#")
210
222
  if [ -n "$NEW_ENV" ]; then
211
- echo "⚠️ NEW ENVIRONMENT VARIABLES — verify they exist in SOPS:"
223
+ echo "⚠️ NEW ENVIRONMENT VARIABLES — verify they exist in your secrets backend ({deploy.secrets_backend}):"
212
224
  echo "$NEW_ENV"
213
- # Cross-check with decrypt-secrets.sh
214
- for var in $(echo "$NEW_ENV" | grep -oP '^\+\K[A-Z_]+'); do
215
- if ! grep -q "$var" scripts/decrypt-secrets.sh 2>/dev/null; then
216
- echo " ❌ $var NOT in decrypt-secrets.sh ENV_MAPPING"
217
- fi
218
- done
225
+ # Cross-check with SOPS chain (only when deploy.secrets_backend == "sops")
226
+ # If your project uses a different backend, skip this block and verify manually.
227
+ if [ "{deploy.secrets_backend}" = "sops" ]; then
228
+ for var in $(echo "$NEW_ENV" | grep -oP '^\+\K[A-Z_]+'); do
229
+ if ! grep -q "$var" scripts/decrypt-secrets.sh 2>/dev/null; then
230
+ echo " ❌ $var NOT in decrypt-secrets.sh ENV_MAPPING"
231
+ fi
232
+ done
233
+ else
234
+ echo " ℹ️ SOPS chain check skipped — deploy.secrets_backend is '{deploy.secrets_backend}'. Verify new vars in your configured backend."
235
+ fi
219
236
  fi
220
237
  ```
221
238
 
@@ -232,7 +249,7 @@ Generate a checklist based on what's in the deploy:
232
249
  - [ ] All PRs in this deploy have been reviewed
233
250
  - [ ] No open `needs-human` issues blocking deploy
234
251
  - [ ] {If migrations} Database backup verified
235
- - [ ] {If new env vars} SOPS secrets updated and decrypt-secrets.sh has mapping
252
+ - [ ] {If new env vars} Secrets backend ({deploy.secrets_backend}) updated with new vars; verify via your configured secrets chain
236
253
  - [ ] {If traefik changes} Routing tested locally
237
254
 
238
255
  ### Deploy Command
@@ -240,8 +257,9 @@ Generate a checklist based on what's in the deploy:
240
257
  # Standard deploy (merge staging → main via GitHub web UI)
241
258
  # CI/CD auto-triggers on push to main
242
259
 
243
- # Or manual trigger for specific services:
244
- gh workflow run hotfix-deploy.yml --ref main -f services={affected_services} -f reason="Deploy: {summary}"
260
+ # Or manual trigger for specific services (requires deploy.workflow set in forge.yaml):
261
+ gh workflow run {deploy.workflow} --ref main -f {deploy.workflow_inputs.services}={affected_services} -f {deploy.workflow_inputs.reason}="Deploy: {summary}"
262
+ # If deploy.workflow is not configured, deploy via the GitHub web UI or your CI/CD provider.
245
263
  ```
246
264
 
247
265
  ### Post-Deploy
@@ -292,5 +310,6 @@ gh workflow run hotfix-deploy.yml --ref main -f services={affected_services} -f
292
310
  ---
293
311
 
294
312
  **Ready to deploy?** Merge staging → main via GitHub web UI, or run:
295
- `gh workflow run hotfix-deploy.yml --ref staging -f services={services} -f reason="Batch deploy"`
313
+ `gh workflow run {deploy.workflow} --ref staging -f {deploy.workflow_inputs.services}={services} -f {deploy.workflow_inputs.reason}="Batch deploy"`
314
+ *(requires `deploy.workflow` set in `forge.yaml`; omit this line if not configured)*
296
315
  ```
@@ -9,6 +9,7 @@ argument-hint: [issue number | "active" | "postmortem {issue}"]
9
9
 
10
10
  **Config variables used by this command** (set in `forge.yaml`):
11
11
  - `{REPO_PATH}` ← `paths.root` — project repository root
12
+ - `{DEPLOY_WORKFLOW}` ← `deploy.workflow` (optional) — GitHub Actions workflow filename for hotfix deploys (e.g., `hotfix-deploy.yml`). When absent or empty, workflow-trigger and workflow-monitor steps are omitted.
12
13
 
13
14
  You are the pipeline's incident response coordinator. When production goes down or a critical bug surfaces, this command orchestrates the response: validates the hotfix before deploy, reconstructs the incident timeline, and produces a post-incident analysis.
14
15
 
@@ -39,7 +40,7 @@ You are the pipeline's incident response coordinator. When production goes down
39
40
  gh issue view {NUMBER} --json number,title,body,labels,state,comments,assignees,createdAt
40
41
 
41
42
  # If "active" — find most recent open P0
42
- gh issue list --state open --label "P0" --limit 5 --json number,title,createdAt,labels \
43
+ gh issue list --state open --label "priority:P0" --limit 5 --json number,title,createdAt,labels \
43
44
  --jq 'sort_by(.createdAt) | reverse | .[0]'
44
45
  ```
45
46
 
@@ -55,8 +56,9 @@ Parse the issue to understand:
55
56
  gh pr list --state merged --base main --json number,title,mergedAt \
56
57
  --jq '[.[] | select(.mergedAt > (now - 86400 | todate))] | .[] | "\(.number) | \(.title) | \(.mergedAt)"'
57
58
 
58
- # Check recent hotfix deploys
59
- gh run list --workflow=hotfix-deploy.yml --limit 5 --json databaseId,status,conclusion,createdAt,displayTitle
59
+ # Check recent hotfix deploys (requires deploy.workflow set in forge.yaml)
60
+ # Skip this check if deploy.workflow is not configured.
61
+ gh run list --workflow={deploy.workflow} --limit 5 --json databaseId,status,conclusion,createdAt,displayTitle
60
62
  ```
61
63
 
62
64
  #### Step 1C: Post incident acknowledgment
@@ -119,9 +121,10 @@ Your job:
119
121
  - Does it remove functionality other code depends on?
120
122
  - Are there syntax errors?
121
123
  - Does it change DB schema without a migration?
122
- 4. Run compile checks:
123
- - Python: python -m py_compile on changed .py files
124
- - TypeScript: npx tsc --noEmit (if web/ files changed)
124
+ 4. Run compile checks (read forge.yaml → verification.commands for tool commands):
125
+ - Python: always run python -m py_compile on changed .py files (no config needed)
126
+ - Python format/lint: run verification.commands.python.format and .lint if configured; log "SKIPPED — not configured in verification.commands" if absent
127
+ - TypeScript: run verification.commands.typescript.typecheck if configured; log "SKIPPED — not configured in verification.commands" if absent
125
128
  5. Check if the fix is scoped tightly (minimal changes for the incident)
126
129
 
127
130
  Output:
@@ -143,10 +146,11 @@ gh issue comment {NUMBER} --body "$(cat <<'EOF'
143
146
  **Confidence**: {level}
144
147
 
145
148
  {If safe}:
146
- **Ready for deploy.** Run:
149
+ **Ready for deploy.** Run (requires `deploy.workflow` set in `forge.yaml`):
147
150
  ```
148
- gh workflow run hotfix-deploy.yml --ref {FIX_BRANCH} -f services={affected_services} -f reason="P0: {TITLE}"
151
+ gh workflow run {deploy.workflow} --ref {FIX_BRANCH} -f {deploy.workflow_inputs.services}={affected_services} -f {deploy.workflow_inputs.reason}="P0: {TITLE}"
149
152
  ```
153
+ *(If `deploy.workflow` is not configured, merge the fix branch via the GitHub web UI or your CI/CD provider.)*
150
154
 
151
155
  {If not safe}:
152
156
  **Concerns found:**
@@ -164,8 +168,9 @@ EOF
164
168
  #### Step 3A: Watch the deploy workflow
165
169
 
166
170
  ```bash
167
- # Find the most recent hotfix-deploy run
168
- RUN_ID=$(gh run list --workflow=hotfix-deploy.yml --limit 1 --json databaseId --jq '.[0].databaseId')
171
+ # Find the most recent deploy run (requires deploy.workflow set in forge.yaml)
172
+ # Skip if deploy.workflow is not configured.
173
+ RUN_ID=$(gh run list --workflow={deploy.workflow} --limit 1 --json databaseId --jq '.[0].databaseId')
169
174
 
170
175
  # Check status
171
176
  gh run view $RUN_ID --json status,conclusion,jobs
package/commands/issue.md CHANGED
@@ -28,7 +28,7 @@ The user's input (`$ARGUMENTS`) can be:
28
28
  |-----------|---------|
29
29
  | Free-text description | "the billing page crashes when credits hit zero" |
30
30
  | URL to error/log | "https://sentry.io/..." or a pasted stack trace |
31
- | Reference to code | "the scraper queue leaks memory in unified_consumer.py" |
31
+ | Reference to code | "the background worker leaks memory in task_processor.py" |
32
32
  | Feature request | "add a dark mode toggle to the dashboard settings page" |
33
33
  | Multi-repo prefix | "mcp: add list_schemas tool" or "n8n: fix credential refresh" |
34
34
  | Audit/investigation | "investigate why deploy times doubled this week" |
@@ -161,8 +161,8 @@ Format: `{prefix}: {concise description}`
161
161
  Rules:
162
162
  - **Max 80 characters**
163
163
  - Use conventional commit prefixes: `fix:`, `feat:`, `refactor:`, `investigate:`, `docs:`
164
- - Be specific — "fix: billing page crash" is bad, "fix: credit_service division by zero when user credits reach 0" is good
165
- - Include the component/domain if it helps: `fix(scraper): queue leak in unified_consumer heartbeat loop`
164
+ - Be specific — "fix: billing page crash" is bad, "fix: payment_processor.charge() raises ZeroDivisionError when amount == 0" is good
165
+ - Include the component/domain if it helps: `fix(worker): queue leak in task_processor heartbeat loop`
166
166
  - Never use vague words: "improve", "update", "change", "handle" without specifics
167
167
 
168
168
  ### 3B: Compose the body
@@ -258,6 +258,8 @@ Files/areas to examine:
258
258
 
259
259
  ### 3D: Pipeline Issue Template (machine-callable reference)
260
260
 
261
+ > **Canonical Standard**: This template is the single source of truth for all automated issue creation in the ForgeDock pipeline. `milestone.md`, `orchestrate.md`, and `work-on/decompose.md` MUST use this template structure when creating issues programmatically. A shared convention is only useful if all create-paths enforce it — divergent inline templates in each command file cause acceptance-criteria drift and coverage gaps. See `work-on/decompose.md` Phase D0 for the investigation-gate pattern that enforces this standard at decomposition time. <!-- Added: forge#293 -->
262
+
261
263
  When pipeline agents create issues programmatically (not via user input), use this canonical template. All automated `gh issue create` calls across Forge commands MUST include these mandatory sections. Domain-specific sections are additive — preserve them, but wrap them with this structure.
262
264
 
263
265
  ```markdown
@@ -488,10 +490,10 @@ Create all {N} issues? (yes / adjust #N / cancel)
488
490
  | Bad Issue | Why It Fails | Good Version |
489
491
  |-----------|-------------|-------------|
490
492
  | "Fix docker mount" | Which compose file? Which mount? | "fix: `.gemini` volume mount in docker-compose.prod.yml missing rw flag" |
491
- | "Update billing" | What about billing? What's broken? | "fix: credit_service.deduct() raises DivisionByZero when user.credits == 0" |
492
- | "Investigate performance" | Investigate what? Where? | "investigate: API p95 latency doubled since last deploy — trace hot paths in scraping routers" |
493
+ | "Update billing" | What about billing? What's broken? | "fix: payment_processor.charge() raises ZeroDivisionError when amount == 0" |
494
+ | "Investigate performance" | Investigate what? Where? | "investigate: API p95 latency doubled since last deploy — trace hot paths in job processing routers" |
493
495
  | No affected files listed | Investigator guesses wrong files | List every file with what needs to change |
494
- | "Handle edge case" | Which edge case? In what function? | "fix: unified_consumer.process_job() silently drops jobs when queue.length > MAX_BATCH" |
496
+ | "Handle edge case" | Which edge case? In what function? | "fix: task_processor.process_job() silently drops jobs when queue.length > MAX_BATCH" |
495
497
  | Missing override files | Fix is incomplete, deploy fails | Include docker-compose.prod.yml, .env.example, etc. |
496
498
  | P0 for a typo fix | Wastes priority signaling | Use P3 for cosmetic issues |
497
499