forgedock 1.0.22 → 1.1.2
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/README.md +255 -211
- package/bin/batch-runner.mjs +339 -0
- package/bin/cinema.mjs +320 -0
- package/bin/demo.mjs +92 -7
- package/bin/engine/phases.mjs +168 -0
- package/bin/engine/projector.mjs +32 -0
- package/bin/engine/reconcile.mjs +11 -0
- package/bin/engine/runlog.mjs +79 -0
- package/bin/engine/state.mjs +32 -0
- package/bin/engine-cli.mjs +222 -0
- package/bin/engine.mjs +138 -0
- package/bin/forgedock.mjs +896 -1540
- package/bin/hooks/interactive-engine.mjs +509 -0
- package/bin/hooks/pre-tool-use.mjs +498 -0
- package/bin/hooks/subagent-stop-enforce.mjs +197 -0
- package/bin/journey.mjs +841 -0
- package/bin/registry.mjs +139 -0
- package/bin/report.mjs +447 -0
- package/bin/runner.mjs +136 -20
- package/bin/settings-hook.mjs +346 -0
- package/bin/tui.mjs +6 -3
- package/commands/adopt.md +4 -1
- package/commands/analytics.md +4 -1
- package/commands/audit-agents.md +110 -27
- package/commands/audit.md +20 -1
- package/commands/autopilot.md +84 -12
- package/commands/changelog.md +2 -1
- package/commands/ci-audit.md +2 -1
- package/commands/cleanup.md +161 -1
- package/commands/compat-audit.md +2 -1
- package/commands/deploy-info.md +63 -4
- package/commands/design-bench.md +6 -1
- package/commands/design-render-critique-loop.md +2 -1
- package/commands/design.md +1 -0
- package/commands/diagnose.md +2 -1
- package/commands/explain.md +2 -1
- package/commands/forge-stats.md +122 -2
- package/commands/forgedock-init.md +1 -1
- package/commands/geo-audit.md +3 -0
- package/commands/incident-response.md +2 -1
- package/commands/issue.md +24 -3
- package/commands/milestone.md +77 -12
- package/commands/optimize.md +2 -1
- package/commands/orchestrate/config.md +67 -0
- package/commands/orchestrate/phase-1-resolve.md +159 -0
- package/commands/orchestrate/phase-2-triage.md +126 -0
- package/commands/orchestrate/phase-2.5-synthesis.md +156 -0
- package/commands/orchestrate/phase-3-dependency.md +591 -0
- package/commands/orchestrate/phase-4-execution.md +962 -0
- package/commands/orchestrate/phase-5-cleanup.md +47 -0
- package/commands/orchestrate/phase-6-report.md +198 -0
- package/commands/orchestrate/safety.md +62 -0
- package/commands/orchestrate.md +25 -2027
- package/commands/pipeline-health/config.md +34 -0
- package/commands/pipeline-health/phase-1-context.md +180 -0
- package/commands/pipeline-health/phase-2-metrics.md +1729 -0
- package/commands/pipeline-health/phase-3-analyze.md +204 -0
- package/commands/pipeline-health/phase-4-proposals.md +117 -0
- package/commands/pipeline-health/phase-5-report.md +284 -0
- package/commands/pipeline-health/phase-6-summary.md +52 -0
- package/commands/pipeline-health.md +25 -2401
- package/commands/pipeline-resume.md +24 -1
- package/commands/pipeline-status.md +65 -0
- package/commands/qa-sweep.md +4 -1
- package/commands/quality-gate.md +326 -42
- package/commands/replay.md +2 -1
- package/commands/review-pr-agents/api.md +135 -0
- package/commands/review-pr-agents/auth.md +137 -0
- package/commands/review-pr-agents/billing.md +101 -0
- package/commands/review-pr-agents/concurrency.md +145 -0
- package/commands/review-pr-agents/database.md +103 -0
- package/commands/review-pr-agents/frontend.md +89 -0
- package/commands/review-pr-agents/infra.md +385 -0
- package/commands/review-pr-agents/protocols.md +185 -0
- package/commands/review-pr-agents/scraper.md +185 -0
- package/commands/review-pr-agents/security.md +406 -0
- package/commands/review-pr-agents.md +45 -1722
- package/commands/review-pr-staging.md +196 -40
- package/commands/review-pr.md +612 -56
- package/commands/rollback.md +2 -1
- package/commands/scope.md +2 -1
- package/commands/security-audit.md +2 -1
- package/commands/signal-planner.md +623 -0
- package/commands/sync-ecosystem.md +1 -0
- package/commands/test-gate.md +17 -1
- package/commands/validate.md +2 -1
- package/commands/work-on/build/architect.md +28 -0
- package/commands/work-on/build/context.md +2 -0
- package/commands/work-on/build/implement.md +19 -5
- package/commands/work-on/build/validate.md +62 -6
- package/commands/work-on/build.md +187 -30
- package/commands/work-on/close.md +132 -5
- package/commands/work-on/decompose.md +19 -1
- package/commands/work-on/investigate.md +117 -2
- package/commands/work-on/review.md +48 -4
- package/commands/work-on-monolithic.md +8 -2
- package/commands/work-on.md +247 -7
- package/docs/BRAND.md +16 -5
- package/docs/CI.md +2 -2
- package/docs/CODEX.md +36 -1
- package/docs/CONFIG.md +44 -42
- package/docs/FORGE-PROTOCOL.md +15 -1064
- package/docs/anthropic-devrel-outreach.md +2 -2
- package/docs/articles/command-decomposition-token-savings.md +214 -0
- package/docs/articles/model-release-playbook.md +131 -0
- package/docs/articles/per-repo-adaptive-scripts-token-savings.md +27 -25
- package/docs/claude-breakpoints.json +75 -5
- package/docs/design/abc-benchmark.md +1 -1
- package/docs/design/design-spec-schema.md +1 -1
- package/docs/design/divergent-generation.md +1 -1
- package/docs/design/eval-gate-runbook.md +178 -0
- package/docs/design/show-hn-draft.md +26 -1
- package/docs/design/token-cost-baseline-1298.md +59 -0
- package/docs/eval/README.md +117 -0
- package/docs/eval/corpus/seed-v1.json +6 -0
- package/docs/eval/published/claude-sonnet-4-6-2026-07-04.json +33 -0
- package/docs/site/annotations-explained.md +24 -0
- package/docs/site/command-learning-path.md +51 -14
- package/docs/site/command-reference.md +339 -113
- package/docs/site/for-companies.md +95 -0
- package/docs/site/forge-annotation-protocol.md +12 -256
- package/docs/site/getting-started.md +58 -12
- package/docs/site/github-is-the-memory.md +113 -0
- package/docs/site/how-it-works.md +3 -13
- package/docs/site/index.md +10 -2
- package/docs/spec/eval-run-result.md +147 -0
- package/docs/spec/forge-protocol-v1.md +441 -18
- package/docs/spec/label-state-machine.md +55 -0
- package/docs/spec/review-protocol.md +158 -0
- package/docs/spec-graph-schema.md +28 -0
- package/docs/superpowers/plans/2026-07-03-cinematic-onboarding-tui.md +2339 -0
- package/docs/superpowers/plans/2026-07-03-durable-execution-engine.md +1223 -0
- package/docs/superpowers/specs/2026-07-03-cinematic-onboarding-tui-design.md +157 -0
- package/docs/superpowers/specs/2026-07-03-durable-execution-engine-design.md +194 -0
- package/docs/superpowers/specs/2026-07-04-economic-self-governance-design.md +181 -0
- package/docs/superpowers/specs/2026-07-04-provenance-trail-design.md +189 -0
- package/examples/forgedock-demo/README.md +41 -22
- package/examples/forgedock-demo/issues/06-bug-tags-type-validation.md +43 -0
- package/examples/forgedock-demo/issues/07-bug-negative-offset-pagination.md +42 -0
- package/examples/forgedock-demo/issues/08-bug-patch-mass-assignment.md +49 -0
- package/examples/forgedock-demo/issues/09-bug-count-injection.md +47 -0
- package/examples/forgedock-demo/issues/10-feature-sort-param.md +39 -0
- package/examples/forgedock-demo/issues/11-feature-patch-tags.md +37 -0
- package/examples/forgedock-demo/issues/12-feature-bulk-create.md +42 -0
- package/examples/forgedock-demo/issues/13-feature-archive-notes.md +46 -0
- package/examples/forgedock-demo/issues/14-refactor-extract-title-validation.md +41 -0
- package/examples/forgedock-demo/issues/15-refactor-protected-route-flag.md +43 -0
- package/examples/forgedock-demo/issues/16-refactor-extract-store.md +45 -0
- package/examples/forgedock-demo/issues/17-perf-tags-dedup-set.md +35 -0
- package/examples/forgedock-demo/issues/18-perf-remove-redundant-sort.md +37 -0
- package/examples/forgedock-demo/issues/19-docs-api-reference-full.md +53 -0
- package/examples/forgedock-demo/issues/20-docs-intentional-flaw-convention.md +46 -0
- package/examples/forgedock-demo/scripts/smoke.js +18 -1
- package/examples/forgedock-demo/src/db.js +36 -11
- package/examples/forgedock-demo/src/routes/notes.js +106 -9
- package/examples/forgedock-demo/src/server.js +4 -1
- package/package.json +12 -4
- package/packages/protocol/LICENSE +21 -0
- package/packages/protocol/src/cli.js +135 -0
- package/packages/protocol/src/emit.js +146 -0
- package/packages/protocol/src/index.js +43 -0
- package/packages/protocol/src/parse.js +229 -0
- package/packages/protocol/src/types.js +171 -0
- package/packages/protocol/src/validate.js +122 -0
- package/scripts/bench-topology-cost.mjs +372 -0
- package/scripts/check-command-docs-drift.sh +198 -0
- package/scripts/check-protocol-restatements.sh +176 -0
- package/scripts/check-registry/README.md +72 -0
- package/scripts/check-registry/manifest.json +6 -0
- package/scripts/code-index.sh +454 -0
- package/scripts/conformance-check.mjs +168 -0
- package/scripts/doctor-pipeline-state.sh +484 -0
- package/scripts/eval-gate-baseline.json +17 -0
- package/scripts/eval-gate-scorecard.mjs +242 -0
- package/scripts/eval-scorecard-aggregator.mjs +252 -0
- package/scripts/flaky-quarantine.sh +241 -0
- package/scripts/forge-annotation.sh +567 -0
- package/scripts/issue-dedup.sh +236 -0
- package/scripts/validate-annotation-node.mjs +233 -0
- package/scripts/verify-env-vars.sh +18 -2
- package/scripts/verify-host-headers.sh +4 -4
- package/scripts/verify-route-registration.sh +2 -2
- package/scripts/worktree-lifecycle.sh +202 -0
- package/templates/devdocs/agent/using-forgedock.md +14 -0
package/commands/audit-agents.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Audit agent outputs from an orchestration run — timeline analysis, stall detection, active vs idle time breakdown
|
|
3
3
|
argument-hint: [session-id | latest | <agent-id>]
|
|
4
|
+
install: extras
|
|
4
5
|
---
|
|
5
6
|
<!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
|
|
6
7
|
<!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
|
|
@@ -48,20 +49,49 @@ fi
|
|
|
48
49
|
|
|
49
50
|
### Step 1A: Find the session
|
|
50
51
|
|
|
51
|
-
Agent outputs live in
|
|
52
|
+
Agent outputs live in `~/.claude/projects/{munged-project-path}/subagents/agent-{agentId}.jsonl`.
|
|
53
|
+
The project path munging rule: both `/` and `.` are replaced with `-`
|
|
54
|
+
(e.g. `/home/user/projects/myproject` → `-home-user-projects-myproject`).
|
|
52
55
|
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
56
|
+
```python
|
|
57
|
+
import os, sys
|
|
58
|
+
from pathlib import Path
|
|
59
|
+
|
|
60
|
+
def _session_project_dir(project_path):
|
|
61
|
+
"""Convert a Claude Code project path to its ~/.claude/projects/ directory name.
|
|
62
|
+
Rule: both '/' and '.' are replaced with '-'.
|
|
63
|
+
Matches pipeline-health.md _session_project_dir() exactly.
|
|
64
|
+
"""
|
|
65
|
+
import re
|
|
66
|
+
return re.sub(r'[/.]', '-', project_path)
|
|
67
|
+
|
|
68
|
+
CLAUDE_PROJECTS_DIR = Path.home() / ".claude" / "projects"
|
|
69
|
+
|
|
70
|
+
# Discover all subagent JSONL files under ~/.claude/projects/
|
|
71
|
+
# Each subagent file: ~/.claude/projects/{project}/{sessionId}/subagents/agent-{agentId}.jsonl
|
|
72
|
+
subagent_files = []
|
|
73
|
+
if CLAUDE_PROJECTS_DIR.exists():
|
|
74
|
+
for jsonl_path in sorted(CLAUDE_PROJECTS_DIR.rglob("subagents/agent-*.jsonl"),
|
|
75
|
+
key=lambda p: p.stat().st_mtime, reverse=True):
|
|
76
|
+
# Only process files with actual content (> 10 lines / non-empty)
|
|
77
|
+
try:
|
|
78
|
+
size = jsonl_path.stat().st_size
|
|
79
|
+
if size < 200: # 200 bytes ≈ fewer than 10 minimal JSONL lines
|
|
80
|
+
continue
|
|
81
|
+
# Count lines portably without wc -l
|
|
82
|
+
with open(jsonl_path, 'rb') as fh:
|
|
83
|
+
line_count = sum(1 for _ in fh)
|
|
84
|
+
if line_count > 10:
|
|
85
|
+
subagent_files.append(jsonl_path)
|
|
86
|
+
except OSError:
|
|
87
|
+
continue
|
|
88
|
+
|
|
89
|
+
if not subagent_files:
|
|
90
|
+
print("SKIPPED — transcripts unreadable on this platform")
|
|
91
|
+
print(" ~/.claude/projects/ not found or contains no subagent JSONL files.")
|
|
92
|
+
sys.exit(0)
|
|
93
|
+
|
|
94
|
+
print(f"Found {len(subagent_files)} subagent JSONL file(s) under {CLAUDE_PROJECTS_DIR}")
|
|
65
95
|
```
|
|
66
96
|
|
|
67
97
|
**Input resolution:**
|
|
@@ -72,20 +102,47 @@ done | sort -rn | head -5
|
|
|
72
102
|
|
|
73
103
|
### Step 1B: Collect agent JSONL files
|
|
74
104
|
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
#
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
105
|
+
```python
|
|
106
|
+
import os
|
|
107
|
+
from pathlib import Path
|
|
108
|
+
|
|
109
|
+
# Filter subagent_files by $ARGUMENTS (session UUID, agent ID, project fragment, or "latest")
|
|
110
|
+
ARGUMENT = "$ARGUMENTS".strip()
|
|
111
|
+
|
|
112
|
+
if not ARGUMENT or ARGUMENT.lower() == "latest":
|
|
113
|
+
# Use the most recent session — take the most recently modified subagent directory
|
|
114
|
+
# Group by parent session dir (grandparent of the subagent file)
|
|
115
|
+
by_session = {}
|
|
116
|
+
for p in subagent_files:
|
|
117
|
+
session_dir = p.parent.parent # .../projects/{project}/{sessionId}/
|
|
118
|
+
mtime = session_dir.stat().st_mtime if session_dir.exists() else 0
|
|
119
|
+
if session_dir not in by_session or mtime > by_session[session_dir][0]:
|
|
120
|
+
by_session[session_dir] = (mtime, [])
|
|
121
|
+
by_session[session_dir][1].append(p)
|
|
122
|
+
if by_session:
|
|
123
|
+
latest_session_dir = max(by_session, key=lambda d: by_session[d][0])
|
|
124
|
+
agent_files = by_session[latest_session_dir][1]
|
|
125
|
+
else:
|
|
126
|
+
agent_files = []
|
|
127
|
+
else:
|
|
128
|
+
# Filter by session UUID, agent ID, or project path fragment
|
|
129
|
+
agent_files = [
|
|
130
|
+
p for p in subagent_files
|
|
131
|
+
if ARGUMENT in str(p)
|
|
132
|
+
]
|
|
133
|
+
|
|
134
|
+
if not agent_files:
|
|
135
|
+
print(f"SKIPPED — no matching agent JSONL files found for argument: {ARGUMENT!r}")
|
|
136
|
+
import sys; sys.exit(0)
|
|
137
|
+
|
|
138
|
+
print(f"Processing {len(agent_files)} agent file(s)")
|
|
139
|
+
for p in agent_files:
|
|
140
|
+
agent_id = p.stem.replace("agent-", "", 1)
|
|
141
|
+
print(f" {agent_id} {p}")
|
|
86
142
|
```
|
|
87
143
|
|
|
88
144
|
Only process files with `> 10` lines (smaller files are helper/polling agents, not work-on agents).
|
|
145
|
+
Line count is computed in Python from the actual JSONL bytes — not from a symlink stub.
|
|
89
146
|
|
|
90
147
|
---
|
|
91
148
|
|
|
@@ -189,17 +246,43 @@ def parse_agent(filepath, agent_id):
|
|
|
189
246
|
max_replays = max(ts_counts.values()) if ts_counts else 1
|
|
190
247
|
resume_cycles = max_replays - 1 # first occurrence is original, rest are replays
|
|
191
248
|
|
|
192
|
-
# Build phase timeline from unique skill invocations
|
|
249
|
+
# Build phase timeline from unique skill invocations.
|
|
250
|
+
# Stall detection: a gap is a TRUE STALL only when it is >120s AND contains zero
|
|
251
|
+
# tool_use events (i.e. ends in end_turn with no tool calls in the window).
|
|
252
|
+
# Gaps that contain any tool_use block (Bash, Edit, Read, Grep, …) are ACTIVE work —
|
|
253
|
+
# the agent was building; only Skill() calls were sparse.
|
|
254
|
+
#
|
|
255
|
+
# To detect tool activity between consecutive Skill() calls we collect all
|
|
256
|
+
# tool_use timestamps from the raw lines and check whether any fall in the gap.
|
|
257
|
+
all_tool_use_ts = []
|
|
258
|
+
for raw_line in lines:
|
|
259
|
+
raw = json.loads(raw_line)
|
|
260
|
+
raw_msg = raw.get('message', {})
|
|
261
|
+
raw_content = raw_msg.get('content', [])
|
|
262
|
+
raw_ts_str = raw.get('timestamp', '')
|
|
263
|
+
if raw_ts_str and isinstance(raw_content, list):
|
|
264
|
+
for c in raw_content:
|
|
265
|
+
if c.get('type') == 'tool_use':
|
|
266
|
+
try:
|
|
267
|
+
all_tool_use_ts.append(
|
|
268
|
+
datetime.fromisoformat(raw_ts_str.replace('Z', '+00:00'))
|
|
269
|
+
)
|
|
270
|
+
except ValueError:
|
|
271
|
+
pass
|
|
272
|
+
|
|
193
273
|
phases = []
|
|
194
274
|
prev_ts = first_ts
|
|
195
275
|
for si in skill_invocations:
|
|
196
276
|
gap_sec = (si['ts'] - prev_ts).total_seconds()
|
|
277
|
+
# Any tool_use event inside the gap window means the agent was active
|
|
278
|
+
has_tool_use_in_gap = any(prev_ts < t <= si['ts'] for t in all_tool_use_ts)
|
|
279
|
+
is_stall = gap_sec > 120 and not has_tool_use_in_gap
|
|
197
280
|
phases.append({
|
|
198
281
|
'ts': si['ts'],
|
|
199
282
|
'skill': si['skill'],
|
|
200
283
|
'args': si['args'],
|
|
201
284
|
'gap_from_prev_sec': gap_sec,
|
|
202
|
-
'is_stall':
|
|
285
|
+
'is_stall': is_stall, # True only when >2 min AND zero tool calls in gap
|
|
203
286
|
})
|
|
204
287
|
prev_ts = si['ts']
|
|
205
288
|
|
|
@@ -433,6 +516,6 @@ This enables tracking whether orchestrator/prompt changes actually improved thro
|
|
|
433
516
|
## Notes
|
|
434
517
|
|
|
435
518
|
- **File format**: Agent outputs are JSONL (one JSON object per line). Each line has `type` (user/assistant), `timestamp`, `message` (with `content` array and optional `stop_reason`).
|
|
436
|
-
- **
|
|
519
|
+
- **Subagent files**: Located at `~/.claude/projects/{munged-project-path}/{sessionId}/subagents/agent-{agentId}.jsonl`. The munging rule: both `/` and `.` in the project path are replaced with `-` (e.g. `/home/user/projects/myapp` → `-home-user-projects-myapp`). There are no platform-specific temp paths or symlinks involved.
|
|
437
520
|
- **Size filtering**: Only analyze files > 10 lines. Small files are helper/polling agents spawned by the orchestrator for status checks.
|
|
438
521
|
- **Resume detection**: When an agent is resumed, the full conversation is replayed. Duplicate `(timestamp, skill_name)` pairs indicate replay cycles. `max(duplicates) - 1 = resume_cycles`.
|
package/commands/audit.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Trace a production issue or pipeline failure end-to-end through GitHub artifacts, then file a detailed improvement issue to the Forge repo
|
|
3
3
|
argument-hint: <description of what went wrong> [--issue N] [--pr N] [--repo prefix]
|
|
4
|
+
install: extras
|
|
4
5
|
---
|
|
5
6
|
<!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
|
|
6
7
|
<!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
|
|
@@ -13,7 +14,7 @@ You are Forge's post-mortem auditor. When something reaches production that shou
|
|
|
13
14
|
|
|
14
15
|
**This command ALWAYS files issues to `{FORGE_REPO}`.** The target project is read-only — you only read its GitHub artifacts as evidence.
|
|
15
16
|
|
|
16
|
-
**Agent model policy**:
|
|
17
|
+
**Agent model policy**: `model: "sonnet"` (standard tier). Fallback: `model: "opus"` if rate-limited. Feature gate: pass `effort` in Task/Skill spawns only on Claude Code >= 2.1.154.
|
|
17
18
|
|
|
18
19
|
**NEVER use plan mode (EnterPlanMode).**
|
|
19
20
|
|
|
@@ -286,6 +287,24 @@ Create a detailed, actionable issue in the **Forge repository**.
|
|
|
286
287
|
|
|
287
288
|
### 4B: Create the issue
|
|
288
289
|
|
|
290
|
+
**Before creating, run the deterministic dedup check:** <!-- Added: forge#1335 -->
|
|
291
|
+
|
|
292
|
+
```bash
|
|
293
|
+
AUDIT_TITLE="fix(pipeline): {one-line description of what the pipeline missed}"
|
|
294
|
+
DEDUP_RESULT=$(scripts/issue-dedup.sh "$AUDIT_TITLE" -R {FORGE_REPO} 2>&1)
|
|
295
|
+
DEDUP_EXIT=$?
|
|
296
|
+
if [ "$DEDUP_EXIT" -eq 1 ]; then
|
|
297
|
+
echo "DEDUP: Audit finding near-duplicate — $DEDUP_RESULT"
|
|
298
|
+
echo "Skipping creation. Comment on the existing issue with new evidence instead."
|
|
299
|
+
# Do NOT call gh issue create for this audit finding
|
|
300
|
+
elif [ "$DEDUP_EXIT" -eq 2 ]; then
|
|
301
|
+
echo "DEDUP: Usage error — $DEDUP_RESULT"
|
|
302
|
+
# Do NOT call gh issue create for this audit finding — fix the invocation and retry
|
|
303
|
+
fi
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
Only call `gh issue create` when the dedup script exits 0:
|
|
307
|
+
|
|
289
308
|
```bash
|
|
290
309
|
gh issue create -R {FORGE_REPO} \
|
|
291
310
|
--title "fix(pipeline): {one-line description of what the pipeline missed}" \
|
package/commands/autopilot.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Autonomous platform improvement cycle — recon, triage, fix, report. Runs recon+triage by default; pass --fix to also pick up and fix top issues. Human gates all deploys.
|
|
3
|
-
argument-hint: [--fix | --recon-only | --fix --limit 5 | --dry-run]
|
|
3
|
+
argument-hint: [--fix | --recon-only | --fix --limit 5 | --dry-run | --fix --yes]
|
|
4
|
+
install: extras
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
# /autopilot — Recursive Platform Improvement Cycle
|
|
@@ -10,7 +11,7 @@ argument-hint: [--fix | --recon-only | --fix --limit 5 | --dry-run]
|
|
|
10
11
|
**Config variables used by this command** (set in `forge.yaml`):
|
|
11
12
|
- `{CREDENTIALS_FILE}` ← `paths.credentials.file` (optional) — path to credentials YAML for analytics APIs
|
|
12
13
|
- `{SERVER_SSH}` ← `services.server_ssh` (optional) — SSH target for production server health checks (e.g., `ubuntu@1.2.3.4`)
|
|
13
|
-
- `{
|
|
14
|
+
- `{OPS_INBOX_PATH}` ← `services.ops_inbox_path` (optional) — path on production server to a directory of open work-item files (e.g. `/srv/ops/inbox`)
|
|
14
15
|
- `{BILLING_ENABLED}` ← `billing.enabled` (optional, default `false`) — set to `true` to enable Stripe data collection in the Analytics Snapshot agent
|
|
15
16
|
|
|
16
17
|
Resolve `{BILLING_ENABLED}` from `forge.yaml` before executing any phase:
|
|
@@ -19,13 +20,18 @@ Resolve `{BILLING_ENABLED}` from `forge.yaml` before executing any phase:
|
|
|
19
20
|
BILLING_ENABLED=$(yq '.billing.enabled // false' "$(git rev-parse --show-toplevel)/forge.yaml" 2>/dev/null || echo 'false')
|
|
20
21
|
```
|
|
21
22
|
|
|
23
|
+
**NEVER use plan mode (EnterPlanMode).**
|
|
24
|
+
**NEVER use the Agent tool** — autopilot dispatches work via `Skill(skill="work-on", ...)` only. The Agent tool bypasses the Skill pipeline's label state machine, investigation comments, and structured review — leaving no audit trail.
|
|
25
|
+
|
|
26
|
+
<!-- FORGE:SPEC_LOADED — autopilot.md loaded and active. Agent is bound by this spec. -->
|
|
27
|
+
|
|
22
28
|
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.
|
|
23
29
|
|
|
24
30
|
**This is designed to run repeatedly.** Each cycle builds on the last — issues created in cycle N get fixed in cycle N+1. The platform compounds improvements over time.
|
|
25
31
|
|
|
26
32
|
**NEVER use plan mode (EnterPlanMode)** — it breaks execution context.
|
|
27
33
|
|
|
28
|
-
**Agent model policy**:
|
|
34
|
+
**Agent model policy**: `model: "sonnet"` (standard tier). Fallback: `model: "opus"` if rate-limited. User can override with `--model <name>`. Pass model explicitly in every `Agent`/`Task` tool call. Feature gate: pass `effort` in Task/Skill spawns only on Claude Code >= 2.1.154.
|
|
29
35
|
|
|
30
36
|
---
|
|
31
37
|
|
|
@@ -38,6 +44,7 @@ You are an autonomous improvement engine for this project. Your job is to **find
|
|
|
38
44
|
| `--fix --limit N` | Fix at most N issues (default: 3) |
|
|
39
45
|
| `--recon-only` | Phase 1 only (data collection, no issue creation) |
|
|
40
46
|
| `--dry-run` | Run everything but don't create issues or PRs — just report what would happen |
|
|
47
|
+
| `--fix --yes` | Auto-approve low-risk fixes (P3, single-file, non-sensitive domain); P0/P1, multi-file, and sensitive-domain fixes still require confirmation |
|
|
41
48
|
|
|
42
49
|
Parse `$ARGUMENTS` and set these variables:
|
|
43
50
|
```
|
|
@@ -45,6 +52,7 @@ MODE = "full" | "recon-only" | "fix" # derived from flags
|
|
|
45
52
|
DO_FIX = true if --fix present
|
|
46
53
|
FIX_LIMIT = N from --limit N, default 3
|
|
47
54
|
DRY_RUN = true if --dry-run present
|
|
55
|
+
AUTO_YES = true if --yes present
|
|
48
56
|
```
|
|
49
57
|
|
|
50
58
|
---
|
|
@@ -112,7 +120,7 @@ Spawn these as **background agents** (all `run_in_background=true`, all `model="
|
|
|
112
120
|
**Agent: Production Health**
|
|
113
121
|
```
|
|
114
122
|
Check production system health:
|
|
115
|
-
1. SSH to check for open
|
|
123
|
+
1. SSH to check for open ops-inbox items: if {SERVER_SSH} is configured, run: ssh {SERVER_SSH} "ls {OPS_INBOX_PATH}/*-open-* 2>/dev/null" — skip this step if SERVER_SSH or OPS_INBOX_PATH is not set in forge.yaml
|
|
116
124
|
2. If any exist, cat each one and summarize
|
|
117
125
|
3. Check container health via MCP: get_production_status, run_production_health_check
|
|
118
126
|
4. Check recent error logs: get_production_logs for api and worker (last 100 lines), grep for ERROR/CRITICAL
|
|
@@ -197,7 +205,7 @@ For each finding from RECON_DATA, assign:
|
|
|
197
205
|
|
|
198
206
|
| Signal | Priority | Type |
|
|
199
207
|
|--------|----------|------|
|
|
200
|
-
|
|
|
208
|
+
| Open ops-inbox item | P1 | bug |
|
|
201
209
|
| Container unhealthy | P0 | bug |
|
|
202
210
|
| Error spike (>5x normal) | P1 | bug |
|
|
203
211
|
| Tier degradation (>20% drop) | P2 | bug |
|
|
@@ -286,7 +294,7 @@ Print a structured report:
|
|
|
286
294
|
|
|
287
295
|
### Production Health
|
|
288
296
|
- Containers: {all healthy / N unhealthy}
|
|
289
|
-
- Open
|
|
297
|
+
- Open ops-inbox items: {count}
|
|
290
298
|
- Error patterns: {summary or "none"}
|
|
291
299
|
|
|
292
300
|
### Scraping Performance
|
|
@@ -356,22 +364,47 @@ gh issue list --state open --label "bug" --limit 50 --json number,title,labels,c
|
|
|
356
364
|
|
|
357
365
|
**MANDATORY CHECKPOINT — do NOT proceed without user confirmation.**
|
|
358
366
|
|
|
367
|
+
**Exception**: when `AUTO_YES = true`, classify each fix target by risk tier before prompting. Low-risk fixes are auto-approved; high-risk fixes still require confirmation.
|
|
368
|
+
|
|
369
|
+
**Risk classification** (evaluate per-issue at this step):
|
|
370
|
+
|
|
371
|
+
```
|
|
372
|
+
LOW_RISK = all of the following are true:
|
|
373
|
+
- Priority is P2 or P3 (not P0 or P1)
|
|
374
|
+
- Affects only a single file (from issue body or recon finding)
|
|
375
|
+
- No sensitive-domain labels: auth, billing, database, security, payments, gdpr
|
|
376
|
+
|
|
377
|
+
HIGH_RISK = any of the following:
|
|
378
|
+
- Priority P0 or P1
|
|
379
|
+
- Multi-file change (2+ files)
|
|
380
|
+
- Has a sensitive-domain label (auth, billing, database, security, payments, gdpr)
|
|
381
|
+
- Issue has `needs-human` label
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
When `AUTO_YES = true`:
|
|
385
|
+
- **LOW_RISK issues**: auto-approve, log `[AUTO-APPROVED: low-risk]`, proceed to 4B.5/4C without waiting.
|
|
386
|
+
- **HIGH_RISK issues**: present the checkpoint below and wait for user response. If user skips, remove from fix targets and continue with remaining low-risk issues.
|
|
387
|
+
|
|
388
|
+
When `AUTO_YES = false` (default): present the full checkpoint for ALL fix targets and wait.
|
|
389
|
+
|
|
359
390
|
```markdown
|
|
360
391
|
## Autopilot Fix Plan
|
|
361
392
|
|
|
362
393
|
I'll run `/work-on` for these {N} issues:
|
|
363
394
|
|
|
364
|
-
| # | Title | Priority | Estimated Scope |
|
|
365
|
-
|
|
366
|
-
| {number} | {title} | {priority} | {small/medium/large} |
|
|
395
|
+
| # | Title | Priority | Estimated Scope | Risk Tier |
|
|
396
|
+
|---|-------|----------|-----------------|-----------|
|
|
397
|
+
| {number} | {title} | {priority} | {small/medium/large} | LOW / HIGH |
|
|
367
398
|
|
|
368
399
|
Each issue goes through: investigate → validate → build → review → merge to staging.
|
|
369
400
|
Nothing merges to `main` — you deploy when ready.
|
|
370
401
|
|
|
402
|
+
{If AUTO_YES and any HIGH_RISK: "Note: {N_low} low-risk issues were auto-approved. The {N_high} high-risk issue(s) above require your confirmation."}
|
|
403
|
+
|
|
371
404
|
**Proceed?** (yes / adjust / skip)
|
|
372
405
|
```
|
|
373
406
|
|
|
374
|
-
Wait for user response. If they adjust, re-select. If they skip, end the cycle.
|
|
407
|
+
Wait for user response on high-risk issues. If they adjust, re-select. If they skip all, end the cycle.
|
|
375
408
|
|
|
376
409
|
### 4B.5: Spec-Edit Impact Analysis (MANDATORY before any spec-touching fix) <!-- Added: forge#870 -->
|
|
377
410
|
|
|
@@ -484,6 +517,44 @@ FIX_RESULTS.push({
|
|
|
484
517
|
When ready to deploy, merge `staging` → `main` via GitHub.
|
|
485
518
|
```
|
|
486
519
|
|
|
520
|
+
### 4E: Deploy Train Check <!-- Added: forge#1332 -->
|
|
521
|
+
|
|
522
|
+
After fixes are merged to staging, check the deploy train state. The deploy train is the single rolling staging→main PR — one per deploy cycle. Do NOT open a new staging→main PR if one is already open.
|
|
523
|
+
|
|
524
|
+
```bash
|
|
525
|
+
# Check for an open staging→main deploy PR (the current train)
|
|
526
|
+
TRAIN_PR=$(gh pr list {GH_FLAG} \
|
|
527
|
+
--head "{STAGING_BRANCH}" \
|
|
528
|
+
--base "{DEFAULT_BRANCH}" \
|
|
529
|
+
--state open \
|
|
530
|
+
--json number,url \
|
|
531
|
+
--jq '.[0] // empty' 2>/dev/null)
|
|
532
|
+
|
|
533
|
+
TRAIN_PR_NUMBER=$(echo "$TRAIN_PR" | jq -r '.number // empty')
|
|
534
|
+
|
|
535
|
+
if [ -n "$TRAIN_PR_NUMBER" ]; then
|
|
536
|
+
# Train exists — check if it is held by open findings
|
|
537
|
+
HELD_BY_COUNT=$(gh issue list {GH_FLAG} \
|
|
538
|
+
--label "review-finding" \
|
|
539
|
+
--state open \
|
|
540
|
+
--search "PR #${TRAIN_PR_NUMBER}" \
|
|
541
|
+
--json number --jq '. | length' 2>/dev/null || echo 0)
|
|
542
|
+
|
|
543
|
+
if [ "$HELD_BY_COUNT" -gt 0 ]; then
|
|
544
|
+
echo "Deploy train PR #${TRAIN_PR_NUMBER} is HELD by ${HELD_BY_COUNT} open finding(s)."
|
|
545
|
+
echo "The fixes merged in this autopilot cycle may resolve some of them."
|
|
546
|
+
echo "Re-run /review-pr-staging ${TRAIN_PR_NUMBER} to re-evaluate the train gate."
|
|
547
|
+
else
|
|
548
|
+
echo "Deploy train PR #${TRAIN_PR_NUMBER} is CLEAR — no open findings. Ready to merge when you authorize."
|
|
549
|
+
fi
|
|
550
|
+
else
|
|
551
|
+
echo "No open staging→main PR found. The merged fixes are queued in staging."
|
|
552
|
+
echo "Run /review-pr-staging to open a new train PR when ready."
|
|
553
|
+
fi
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
**Deploy policy**: Autopilot NEVER merges staging→main. It merges issue fixes TO staging only. The train PR is a human-gated merge — the user decides when to ship.
|
|
557
|
+
|
|
487
558
|
---
|
|
488
559
|
|
|
489
560
|
## Safety Rules
|
|
@@ -497,7 +568,7 @@ When ready to deploy, merge `staging` → `main` via GitHub.
|
|
|
497
568
|
7. **If a fix fails, move on** — don't retry. Log it and let the next cycle or human handle it.
|
|
498
569
|
8. **DRY_RUN means NO side effects** — no issues created, no PRs, no label changes. Report only.
|
|
499
570
|
9. **P0 overrides everything** — if P0 exists, skip normal recon and fix P0 immediately.
|
|
500
|
-
10. **Always gate fixes on user approval** — Phase 4B checkpoint is non-negotiable.
|
|
571
|
+
10. **Always gate high-risk fixes on user approval** — Phase 4B checkpoint is non-negotiable for P0/P1, multi-file, or sensitive-domain changes. `--yes` only auto-approves LOW_RISK tier (P2/P3, single-file, non-sensitive).
|
|
501
572
|
|
|
502
573
|
---
|
|
503
574
|
|
|
@@ -525,4 +596,5 @@ The platform gets better every cycle. Issues that aren't picked up age and rise
|
|
|
525
596
|
- **Runtime**: Recon-only ~3-5 min. Full cycle with fixes ~15-45 min depending on issue count.
|
|
526
597
|
- **Token budget**: Recon is cheap (mostly MCP + API calls). Fixes are expensive (full /work-on per issue).
|
|
527
598
|
- **Idempotent**: Safe to run multiple times — dedup prevents duplicate issues, /work-on resumes from checkpoints.
|
|
528
|
-
- **Pairs with /loop**: Run `/loop 4h /autopilot` for continuous improvement,
|
|
599
|
+
- **Pairs with /loop**: Run `/loop 4h /autopilot` for continuous improvement, `/loop 4h /autopilot --fix --limit 2` for autonomous fixing with a human checkpoint every cycle, or `/loop 4h /autopilot --fix --yes` for fully unattended low-risk fixes (high-risk issues still surface for approval).
|
|
600
|
+
- **Event-driven complement**: `/autopilot` is a periodic cycle. When a specific production signal fires (metric regression, incident, GEO gap), use `/signal-planner` instead — it converts the signal into a dependency-ordered issue DAG, executes via `/orchestrate`, and verifies the originating signal is resolved after the work merges. Use `/autopilot` for scheduled health sweeps; use `/signal-planner` for targeted signal-driven response.
|
package/commands/changelog.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Auto-generate release notes from merged PRs and FORGE:TRAJECTORY annotations — grouped by conventional commit type
|
|
3
3
|
argument-hint: [v1.0.0..v1.1.0 | since:YYYY-MM-DD | last-N]
|
|
4
|
+
install: extras
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
# /changelog — Release Notes Generator
|
|
@@ -9,7 +10,7 @@ argument-hint: [v1.0.0..v1.1.0 | since:YYYY-MM-DD | last-N]
|
|
|
9
10
|
|
|
10
11
|
Generate a structured markdown changelog from merged PRs and their FORGE:TRAJECTORY annotations. Groups entries by conventional commit type (feat, fix, refactor, docs, chore) and enriches each entry with task type and investigation verdict from the pipeline's annotation layer.
|
|
11
12
|
|
|
12
|
-
**Agent model policy**:
|
|
13
|
+
**Agent model policy**: `model: "haiku"`, `effort: low` (mechanical tier — annotation reading, changelog generation). Fallback: `model: "sonnet"` if rate-limited. Feature gate: pass `effort` only on Claude Code >= 2.1.154.
|
|
13
14
|
|
|
14
15
|
**NEVER use plan mode (EnterPlanMode)** — it breaks execution context.
|
|
15
16
|
|
package/commands/ci-audit.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Stack-aware CI gap detection — audits a project's GitHub Actions workflows against its declared tech stack and files issues for missing config validation checks
|
|
3
3
|
argument-hint: [--repo <prefix>] [--dry-run]
|
|
4
|
+
install: extras
|
|
4
5
|
---
|
|
5
6
|
<!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
|
|
6
7
|
<!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
|
|
@@ -29,7 +30,7 @@ This command is designed to be run:
|
|
|
29
30
|
|
|
30
31
|
This is NOT a PR review — it does not approve or block. It creates issues for each CI gap found.
|
|
31
32
|
|
|
32
|
-
**Agent model policy**:
|
|
33
|
+
**Agent model policy**: `model: "sonnet"` (standard tier). Fallback: `model: "opus"` if rate-limited. Feature gate: pass `effort` in Task/Skill spawns only on Claude Code >= 2.1.154.
|
|
33
34
|
**NEVER use plan mode (EnterPlanMode).**
|
|
34
35
|
|
|
35
36
|
---
|
package/commands/cleanup.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Sweep closed issues for stale labels, missing workflow state, and Project board gaps — plus prune worktrees, branches, and milestones
|
|
3
3
|
argument-hint: [labels | branches | milestones | board | orphans | all]
|
|
4
|
+
install: extras
|
|
4
5
|
---
|
|
5
6
|
<!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
|
|
6
7
|
<!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
|
|
@@ -44,11 +45,12 @@ All `{GH_REPO}`, `{GH_FLAG}`, `{REPO_PATH}`, `{STAGING_BRANCH}`, `{PROJECT_BOARD
|
|
|
44
45
|
|
|
45
46
|
| Input | Action |
|
|
46
47
|
|-------|--------|
|
|
47
|
-
| `labels` or empty | Fix stale/missing workflow labels on closed issues |
|
|
48
|
+
| `labels` or empty | Fix stale/missing workflow labels on closed issues; detect OPEN issues stuck in intermediate states (Phase 1D) |
|
|
48
49
|
| `orphans` | Close open issues whose PRs are already merged |
|
|
49
50
|
| `branches` | Prune worktrees and remote branches for merged PRs |
|
|
50
51
|
| `milestones` | Report milestones with 0 open issues (advisory — never closes) |
|
|
51
52
|
| `board` | Sync closed issues to Project board with correct terminal state |
|
|
53
|
+
| `batch-p3` | Sweep: fold stale unbatched P3 review findings into domain-grouped batch issues |
|
|
52
54
|
| `all` | All of the above, in order |
|
|
53
55
|
|
|
54
56
|
---
|
|
@@ -105,6 +107,46 @@ gh issue list {GH_FLAG} --state closed --limit 200 --json number,title,labels \
|
|
|
105
107
|
|
|
106
108
|
These were closed outside the pipeline. Report count but don't fix (not necessarily wrong).
|
|
107
109
|
|
|
110
|
+
### 1D: Detect stale OPEN issues in intermediate workflow states
|
|
111
|
+
|
|
112
|
+
Open issues stuck in `workflow:building`, `workflow:in-review`, or `workflow:investigating` beyond a configurable threshold indicate a stalled agent. These are invisible to Phase 1A–1C (which only query closed issues).
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
# Configurable threshold — override by setting STALE_THRESHOLD_HOURS before invoking /cleanup
|
|
116
|
+
STALE_THRESHOLD_HOURS=${STALE_THRESHOLD_HOURS:-48}
|
|
117
|
+
|
|
118
|
+
# Compute cutoff datetime — GNU date (Linux) with BSD date (macOS) fallback
|
|
119
|
+
CUTOFF=$(date -d "${STALE_THRESHOLD_HOURS} hours ago" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null \
|
|
120
|
+
|| date -v-${STALE_THRESHOLD_HOURS}H +%Y-%m-%dT%H:%M:%SZ 2>/dev/null \
|
|
121
|
+
|| echo "")
|
|
122
|
+
|
|
123
|
+
echo "=== Stale-Open Intermediate Issues (threshold: ${STALE_THRESHOLD_HOURS}h) ==="
|
|
124
|
+
|
|
125
|
+
STALE_OPEN_COUNT=0
|
|
126
|
+
|
|
127
|
+
for LABEL in "workflow:building" "workflow:in-review" "workflow:investigating"; do
|
|
128
|
+
ISSUES=$(gh issue list {GH_FLAG} \
|
|
129
|
+
--state open \
|
|
130
|
+
--label "$LABEL" \
|
|
131
|
+
--limit 100 \
|
|
132
|
+
--json number,title,updatedAt,labels \
|
|
133
|
+
--jq --arg threshold "$CUTOFF" --arg label "$LABEL" \
|
|
134
|
+
'[.[] | select($threshold == "" or .updatedAt < $threshold)] |
|
|
135
|
+
.[] | "#\(.number) — \(.title[:60]) [label: \($label)] [last update: \(.updatedAt[:10])] → resume: /work-on \(.number)"' \
|
|
136
|
+
2>/dev/null || echo "")
|
|
137
|
+
|
|
138
|
+
if [ -n "$ISSUES" ]; then
|
|
139
|
+
echo "$ISSUES"
|
|
140
|
+
COUNT=$(echo "$ISSUES" | grep -c '^#' 2>/dev/null || echo 0)
|
|
141
|
+
STALE_OPEN_COUNT=$((STALE_OPEN_COUNT + COUNT))
|
|
142
|
+
fi
|
|
143
|
+
done
|
|
144
|
+
|
|
145
|
+
echo "Total stale-open intermediate issues: $STALE_OPEN_COUNT"
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
These are **report-only** — no automatic action is taken. Use `/work-on <n>` (idempotent) to resume a stalled issue. Priority issues (P1/P2) should be actioned first.
|
|
149
|
+
|
|
108
150
|
---
|
|
109
151
|
|
|
110
152
|
## Phase 2: Orphaned Issues (open issues with merged PRs)
|
|
@@ -278,6 +320,111 @@ Do NOT call `gh api ... -X PATCH -f state=closed` on any milestone. This phase i
|
|
|
278
320
|
|
|
279
321
|
---
|
|
280
322
|
|
|
323
|
+
## Phase 4B: P3 Batch Sweep (if `batch-p3` or `all`)
|
|
324
|
+
|
|
325
|
+
<!-- Added: forge#1333 -->
|
|
326
|
+
|
|
327
|
+
**Purpose**: Fold stale unbatched P3 review findings into domain-grouped batch issues. Reduces pipeline overhead from individual per-finding full-pipeline runs.
|
|
328
|
+
|
|
329
|
+
**Skip if**: Input is not `batch-p3` and not `all`.
|
|
330
|
+
|
|
331
|
+
### Step 4B.1: Fetch open batchable P3 findings
|
|
332
|
+
|
|
333
|
+
```bash
|
|
334
|
+
# Find all open P3 review findings that carry the FORGE:BATCHABLE annotation
|
|
335
|
+
# and are not already members of a batch (no "batch" label)
|
|
336
|
+
UNBATCHED_P3=$(gh issue list {GH_FLAG} \
|
|
337
|
+
--state open \
|
|
338
|
+
--label "review-finding,priority:P3" \
|
|
339
|
+
--limit 500 \
|
|
340
|
+
--json number,title,body,labels,createdAt \
|
|
341
|
+
--jq '.[] | select(.body | test("<!-- FORGE:BATCHABLE -->"))
|
|
342
|
+
| select(([.labels[].name] | any(. == "batch")) | not)
|
|
343
|
+
| select(([.labels[].name] | any(. == "security" or . == "billing")) | not)
|
|
344
|
+
| select((.title | test("security|billing|payment|stripe"; "i")) | not)')
|
|
345
|
+
|
|
346
|
+
echo "Unbatched batchable P3 findings: $(echo "$UNBATCHED_P3" | jq -s 'length')"
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
### Step 4B.2: Group by domain
|
|
350
|
+
|
|
351
|
+
For each finding, extract the domain from the first affected file path under `## Affected Files`:
|
|
352
|
+
|
|
353
|
+
```bash
|
|
354
|
+
# Domain = top-level directory of the first affected file
|
|
355
|
+
# e.g., "services/api/auth/login.py" → "services/api/auth"
|
|
356
|
+
# "commands/review-pr.md" → "commands"
|
|
357
|
+
# Exclude findings where the domain contains "billing" or "security"
|
|
358
|
+
|
|
359
|
+
# Group findings by domain, track oldest creation timestamp per group
|
|
360
|
+
declare -A DOMAIN_ISSUES
|
|
361
|
+
declare -A DOMAIN_OLDEST
|
|
362
|
+
|
|
363
|
+
echo "$UNBATCHED_P3" | jq -c '.[]' | while read -r issue; do
|
|
364
|
+
NUM=$(echo "$issue" | jq -r '.number')
|
|
365
|
+
CREATED=$(echo "$issue" | jq -r '.createdAt')
|
|
366
|
+
BODY=$(echo "$issue" | jq -r '.body')
|
|
367
|
+
|
|
368
|
+
# Extract first affected file path from body
|
|
369
|
+
FIRST_FILE=$(echo "$BODY" | sed -n '/^## Affected Files/,/^## /p' | grep -oP '`[^`]+`' | head -1 | tr -d '`')
|
|
370
|
+
if [ -z "$FIRST_FILE" ]; then
|
|
371
|
+
FIRST_FILE=$(echo "$BODY" | grep -oP '(?<=\`)[^`]+\.(?:py|ts|tsx|js|go|rs|java|sh|md)(?=\`)' | head -1 || echo "unknown")
|
|
372
|
+
fi
|
|
373
|
+
DOMAIN=$(echo "$FIRST_FILE" | cut -d/ -f1,2,3 | sed 's|/[^/]*$||')
|
|
374
|
+
[ -z "$DOMAIN" ] && DOMAIN="general"
|
|
375
|
+
|
|
376
|
+
echo "${NUM}:${DOMAIN}:${CREATED}"
|
|
377
|
+
done
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
### Step 4B.3: Apply batching threshold
|
|
381
|
+
|
|
382
|
+
For each domain group:
|
|
383
|
+
- **Trigger**: 5+ unbatched batchable P3 findings in the domain, **OR** the oldest finding in the domain is > 72 hours old
|
|
384
|
+
- **No trigger**: fewer than 5 findings AND none older than 72 hours → skip (no batch created)
|
|
385
|
+
|
|
386
|
+
```bash
|
|
387
|
+
NOW_EPOCH=$(date +%s)
|
|
388
|
+
HOURS_72=$((72 * 3600))
|
|
389
|
+
|
|
390
|
+
# For each domain group that meets the threshold, create a batch issue
|
|
391
|
+
# (max 10 members per batch — if more, create multiple batches of ≤ 10 each)
|
|
392
|
+
|
|
393
|
+
# Exclude domains with "security" or "billing" in the domain path
|
|
394
|
+
echo "$DOMAIN_ISSUES" | while IFS=: read -r domain issue_list; do
|
|
395
|
+
echo "$domain" | grep -qiE 'security|billing' && continue
|
|
396
|
+
|
|
397
|
+
COUNT=$(echo "$issue_list" | tr ',' '\n' | wc -l)
|
|
398
|
+
OLDEST_EPOCH=$(echo "$DOMAIN_OLDEST[$domain]" | date -d "$..." +%s 2>/dev/null || echo "$NOW_EPOCH")
|
|
399
|
+
AGE_SECS=$((NOW_EPOCH - OLDEST_EPOCH))
|
|
400
|
+
|
|
401
|
+
if [ "$COUNT" -ge 5 ] || [ "$AGE_SECS" -ge "$HOURS_72" ]; then
|
|
402
|
+
echo "Batching $COUNT P3 findings in domain: ${domain}"
|
|
403
|
+
# Create batch issues (in chunks of ≤ 10)
|
|
404
|
+
# See orchestrate.md Phase 1 for the batch creation template
|
|
405
|
+
fi
|
|
406
|
+
done
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
**Batch creation** uses the same template as `orchestrate.md Phase 1 → P3 Review-Finding Batching`. After creating the batch issue, add the `batch` label to each member issue to prevent re-batching on the next sweep.
|
|
410
|
+
|
|
411
|
+
### Step 4B.4: Report
|
|
412
|
+
|
|
413
|
+
```
|
|
414
|
+
## P3 Batch Sweep
|
|
415
|
+
|
|
416
|
+
| Domain | Unbatched P3s | Threshold Met | Action |
|
|
417
|
+
|--------|--------------|---------------|--------|
|
|
418
|
+
| {domain} | {N} | ≥5 issues | Created batch #{NUM} ({M} members) |
|
|
419
|
+
| {domain} | {N} | Oldest > 72h | Created batch #{NUM} ({M} members) |
|
|
420
|
+
| {domain} | {N} | Below threshold | No action |
|
|
421
|
+
|
|
422
|
+
Total batch issues created: {N}
|
|
423
|
+
Total P3 findings batched: {N}
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
---
|
|
427
|
+
|
|
281
428
|
## Phase 5: Sync Project Board (if `board` or `all`)
|
|
282
429
|
|
|
283
430
|
**Skip if `project_board` is not configured** — check resolved vars before proceeding:
|
|
@@ -320,6 +467,13 @@ fi
|
|
|
320
467
|
| workflow:investigating → cleaned | {N} |
|
|
321
468
|
| needs-validation removed | {N} |
|
|
322
469
|
|
|
470
|
+
### Stale-Open Intermediate Issues (Phase 1D)
|
|
471
|
+
| Issue | Label | Last Update | Action |
|
|
472
|
+
|-------|-------|-------------|--------|
|
|
473
|
+
| #{N} | workflow:X | YYYY-MM-DD | `/work-on {N}` to resume |
|
|
474
|
+
|
|
475
|
+
*Total stale-open: {N} (threshold: {STALE_THRESHOLD_HOURS}h)*
|
|
476
|
+
|
|
323
477
|
### Orphaned Issues Closed
|
|
324
478
|
| Issue | Merged PR | Action |
|
|
325
479
|
|-------|-----------|--------|
|
|
@@ -345,4 +499,10 @@ fi
|
|
|
345
499
|
|
|
346
500
|
### Still Missing Workflow Label
|
|
347
501
|
{N} closed issues have no workflow label (closed outside pipeline — no action needed)
|
|
502
|
+
|
|
503
|
+
### P3 Batch Sweep Results
|
|
504
|
+
| Domain | Unbatched P3s | Action |
|
|
505
|
+
|--------|--------------|--------|
|
|
506
|
+
| {domain} | {N} | Created batch #{NUM} with {M} members |
|
|
507
|
+
| {domain} | {N} | Below threshold — no action (< 5 findings, none > 72h) |
|
|
348
508
|
```
|