forgedock 1.1.1 → 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 +53 -8
- package/bin/batch-runner.mjs +339 -0
- package/bin/demo.mjs +92 -7
- package/bin/engine/phases.mjs +20 -0
- package/bin/engine/state.mjs +8 -3
- package/bin/engine-cli.mjs +177 -5
- package/bin/forgedock.mjs +409 -26
- 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 +72 -14
- package/bin/registry.mjs +139 -0
- package/bin/report.mjs +447 -0
- package/bin/runner.mjs +68 -19
- package/bin/settings-hook.mjs +235 -1
- 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 +80 -8
- 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 +58 -1
- package/commands/design-bench.md +5 -1
- package/commands/design-render-critique-loop.md +1 -1
- package/commands/diagnose.md +2 -1
- package/commands/explain.md +2 -1
- package/commands/forge-stats.md +121 -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 +23 -2
- 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 -2045
- 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 +248 -10
- 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 +591 -52
- 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 +3 -1
- package/commands/work-on/build/validate.md +26 -7
- package/commands/work-on/build.md +163 -24
- package/commands/work-on/close.md +130 -3
- package/commands/work-on/decompose.md +19 -1
- package/commands/work-on/investigate.md +117 -2
- package/commands/work-on/review.md +24 -4
- package/commands/work-on-monolithic.md +7 -2
- package/commands/work-on.md +202 -4
- package/docs/BRAND.md +14 -7
- package/docs/CI.md +2 -2
- package/docs/CODEX.md +36 -1
- package/docs/CONFIG.md +5 -8
- 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 +43 -4
- package/docs/site/github-is-the-memory.md +113 -0
- package/docs/site/how-it-works.md +3 -13
- package/docs/site/index.md +9 -1
- 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/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 +3 -1
- 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/worktree-lifecycle.sh +3 -3
- package/templates/devdocs/agent/using-forgedock.md +14 -0
- package/docs/site/.vitepress/config.mjs +0 -85
package/commands/forge-stats.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Track command sizes, detect bloat, compare against baselines and thresholds
|
|
3
|
-
argument-hint: [diff | baseline | full]
|
|
3
|
+
argument-hint: [diff | baseline | full | cost]
|
|
4
4
|
install: internal
|
|
5
5
|
---
|
|
6
6
|
<!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
|
|
@@ -8,7 +8,7 @@ install: internal
|
|
|
8
8
|
|
|
9
9
|
# /forge-stats — Prompt Bloat Tracker
|
|
10
10
|
|
|
11
|
-
**Input**: $ARGUMENTS (default: `diff`)
|
|
11
|
+
**Input**: $ARGUMENTS (default: `diff`; modes: `diff`, `baseline`, `full`, `cost`)
|
|
12
12
|
|
|
13
13
|
Tracks the size of every Forge command file over time. Detects bloat by comparing current sizes against the git-tracked baseline and per-command thresholds.
|
|
14
14
|
|
|
@@ -194,3 +194,122 @@ If 3+ commands repeat the same instruction block (e.g., "how to create a worktre
|
|
|
194
194
|
|
|
195
195
|
### 4. Remove motivational padding
|
|
196
196
|
Emojis, ALL CAPS warnings, "YOU MUST", "CRITICAL", "NO EXCEPTIONS" — these are noise. Sonnet follows clear instructions, not emphasis. Replace 5 lines of warnings with 1 line of instruction.
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## Mode: `cost`
|
|
201
|
+
|
|
202
|
+
Report raw cost-per-issue, cost-per-stage, and cost-per-agent-tier by reading `FORGE:DECISION_RECORD` annotations from merged PRs. **Raw numbers only** — no trend dashboards, no automated regression-flagging, no visualization. Respects the open-core boundary defined in `devdocs/project/architecture.md` (trend analytics are a Platform value-add).
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
cd "$FORGE_HOME"
|
|
206
|
+
|
|
207
|
+
CONFIG_FILE="${FORGE_CONFIG:-forge.yaml}"
|
|
208
|
+
GH_REPO=$(yq e '.project.owner + "/" + .project.repo' "$CONFIG_FILE")
|
|
209
|
+
|
|
210
|
+
echo "=== Forge Cost Report — $(date +%Y-%m-%d) ==="
|
|
211
|
+
echo "Repo: $GH_REPO"
|
|
212
|
+
echo ""
|
|
213
|
+
|
|
214
|
+
# Fetch all merged PRs (last 50) and extract their FORGE:DECISION_RECORD JSON blocks
|
|
215
|
+
RECORDS=$(gh pr list -R "$GH_REPO" --state merged --limit 50 --json number \
|
|
216
|
+
--jq '.[].number' 2>/dev/null | while read pr; do
|
|
217
|
+
gh api "repos/$GH_REPO/issues/$pr/comments" \
|
|
218
|
+
--jq '.[] | select(.body | contains("FORGE:DECISION_RECORD")) | .body' 2>/dev/null
|
|
219
|
+
done)
|
|
220
|
+
|
|
221
|
+
if [ -z "$RECORDS" ]; then
|
|
222
|
+
echo "No FORGE:DECISION_RECORD annotations found in the last 50 merged PRs."
|
|
223
|
+
echo "(Older issues predate the cost block — no data to report.)"
|
|
224
|
+
exit 0
|
|
225
|
+
fi
|
|
226
|
+
|
|
227
|
+
# Extract cost blocks via Python (handles missing cost block gracefully)
|
|
228
|
+
echo "$RECORDS" | python3 - <<'PYEOF'
|
|
229
|
+
import sys, re, json
|
|
230
|
+
|
|
231
|
+
records = []
|
|
232
|
+
for block in sys.stdin.read().split("<!-- FORGE:DECISION_RECORD -->"):
|
|
233
|
+
m = re.search(r'```json\s*(\{.*?\})\s*```', block, re.DOTALL)
|
|
234
|
+
if not m:
|
|
235
|
+
continue
|
|
236
|
+
try:
|
|
237
|
+
data = json.loads(m.group(1))
|
|
238
|
+
except json.JSONDecodeError:
|
|
239
|
+
continue
|
|
240
|
+
records.append(data)
|
|
241
|
+
|
|
242
|
+
if not records:
|
|
243
|
+
print("No parseable FORGE:DECISION_RECORD JSON found.")
|
|
244
|
+
sys.exit(0)
|
|
245
|
+
|
|
246
|
+
print(f"Parsed {len(records)} FORGE:DECISION_RECORD annotation(s).\n")
|
|
247
|
+
|
|
248
|
+
has_cost = [r for r in records if r.get("cost")]
|
|
249
|
+
no_cost = [r for r in records if not r.get("cost")]
|
|
250
|
+
|
|
251
|
+
print(f" With cost block : {len(has_cost)}")
|
|
252
|
+
print(f" Without cost block: {len(no_cost)} (older annotations — no data)")
|
|
253
|
+
print()
|
|
254
|
+
|
|
255
|
+
if not has_cost:
|
|
256
|
+
print("No cost data available. Cost blocks are written starting with #1296.")
|
|
257
|
+
sys.exit(0)
|
|
258
|
+
|
|
259
|
+
# Per-issue raw cost
|
|
260
|
+
print("--- Cost Per Issue ---")
|
|
261
|
+
print(f"{'Issue':<10} {'PR':<8} {'Total ($)':<12} {'Stages'}")
|
|
262
|
+
print(f"{'-----':<10} {'--':<8} {'---------':<12} {'------'}")
|
|
263
|
+
for r in sorted(has_cost, key=lambda x: x.get("issue", 0)):
|
|
264
|
+
cost = r.get("cost", {})
|
|
265
|
+
total = cost.get("total_usd", "n/a")
|
|
266
|
+
stages = cost.get("stages", {})
|
|
267
|
+
stage_str = ", ".join(f"{k}: ${v}" for k, v in stages.items()) if stages else "n/a"
|
|
268
|
+
print(f" #{r.get('issue','?'):<8} #{r.get('pr','?'):<6} {str(total):<12} {stage_str}")
|
|
269
|
+
|
|
270
|
+
print()
|
|
271
|
+
|
|
272
|
+
# Per-stage aggregate
|
|
273
|
+
stage_totals = {}
|
|
274
|
+
for r in has_cost:
|
|
275
|
+
for stage, val in (r.get("cost", {}).get("stages") or {}).items():
|
|
276
|
+
try:
|
|
277
|
+
stage_totals[stage] = stage_totals.get(stage, 0.0) + float(val)
|
|
278
|
+
except (TypeError, ValueError):
|
|
279
|
+
pass
|
|
280
|
+
|
|
281
|
+
if stage_totals:
|
|
282
|
+
print("--- Cost Per Stage (aggregate) ---")
|
|
283
|
+
for stage, total in sorted(stage_totals.items()):
|
|
284
|
+
print(f" {stage:<20}: ${total:.4f}")
|
|
285
|
+
print()
|
|
286
|
+
|
|
287
|
+
# Per-agent-tier aggregate
|
|
288
|
+
tier_totals = {}
|
|
289
|
+
for r in has_cost:
|
|
290
|
+
for tier, val in (r.get("cost", {}).get("agent_tiers") or {}).items():
|
|
291
|
+
try:
|
|
292
|
+
tier_totals[tier] = tier_totals.get(tier, 0.0) + float(val)
|
|
293
|
+
except (TypeError, ValueError):
|
|
294
|
+
pass
|
|
295
|
+
|
|
296
|
+
if tier_totals:
|
|
297
|
+
print("--- Cost Per Agent Tier (aggregate) ---")
|
|
298
|
+
for tier, total in sorted(tier_totals.items()):
|
|
299
|
+
print(f" {tier:<20}: ${total:.4f}")
|
|
300
|
+
print()
|
|
301
|
+
|
|
302
|
+
# Summary
|
|
303
|
+
if has_cost:
|
|
304
|
+
totals = [float(r["cost"]["total_usd"]) for r in has_cost if r.get("cost", {}).get("total_usd") not in (None, "unavailable")]
|
|
305
|
+
if totals:
|
|
306
|
+
print(f"Issues with cost data: {len(totals)}")
|
|
307
|
+
print(f" Total cost (window) : ${sum(totals):.4f}")
|
|
308
|
+
print(f" Average per issue : ${sum(totals)/len(totals):.4f}")
|
|
309
|
+
print(f" Min / Max : ${min(totals):.4f} / ${max(totals):.4f}")
|
|
310
|
+
PYEOF
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
**Scope constraint**: This mode reports raw numbers sourced from `FORGE:DECISION_RECORD` annotations only. No new data source is invented. Trend visualization, regression-flagging, and alerting logic are Platform features — do not add them here. If a cost block is absent (older annotations predating #1296), report "no data" rather than erroring.
|
|
314
|
+
|
|
315
|
+
<!-- Added: forge#1297 -->
|
|
@@ -11,7 +11,7 @@ argument-hint: [--preserve | --interactive | --section <name>]
|
|
|
11
11
|
|
|
12
12
|
You complete the `forge.yaml` configuration that `npx forgedock init` started. The CLI generates required sections (project, paths, branches) from auto-detection. Your job is to scan the codebase, query GitHub APIs, and produce a complete `forge.yaml` with every applicable optional section filled — using detected values directly, without asking for confirmation.
|
|
13
13
|
|
|
14
|
-
**Agent model policy**:
|
|
14
|
+
**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.
|
|
15
15
|
**NEVER use plan mode (EnterPlanMode).**
|
|
16
16
|
|
|
17
17
|
---
|
package/commands/geo-audit.md
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: "Run a GEO audit — check AI referral traffic, page compliance, and auto-create improvement issues"
|
|
3
|
+
install: extras
|
|
3
4
|
---
|
|
4
5
|
<!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
|
|
5
6
|
<!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
|
|
@@ -12,6 +13,8 @@ You are the GEO (Generative Engine Optimization) auditor. Pull AI referral data
|
|
|
12
13
|
|
|
13
14
|
**NEVER use plan mode (EnterPlanMode)** — it breaks execution context.
|
|
14
15
|
|
|
16
|
+
<!-- FORGE:SPEC_LOADED — geo-audit.md loaded and active. Agent is bound by this spec. -->
|
|
17
|
+
|
|
15
18
|
---
|
|
16
19
|
|
|
17
20
|
## Phase 0: Load Config
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Coordinate P0 incident response — hotfix validation, timeline reconstruction, and post-incident analysis
|
|
3
3
|
argument-hint: [issue number | "active" | "postmortem {issue}"]
|
|
4
|
+
install: extras
|
|
4
5
|
---
|
|
5
6
|
<!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
|
|
6
7
|
<!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
|
|
@@ -15,7 +16,7 @@ argument-hint: [issue number | "active" | "postmortem {issue}"]
|
|
|
15
16
|
|
|
16
17
|
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.
|
|
17
18
|
|
|
18
|
-
**Agent model policy**:
|
|
19
|
+
**Agent model policy**: `model: "sonnet"` (standard tier). Fallback: `model: "opus"` if rate-limited. User can override with `--model <name>`. Feature gate: pass `effort` in Task/Skill spawns only on Claude Code >= 2.1.154.
|
|
19
20
|
|
|
20
21
|
**NEVER use plan mode (EnterPlanMode)** — it breaks execution context.
|
|
21
22
|
|
package/commands/issue.md
CHANGED
|
@@ -11,7 +11,7 @@ argument-hint: [description of the problem or feature]
|
|
|
11
11
|
|
|
12
12
|
You create GitHub issues with the exact structure the `/work-on` pipeline expects. Every issue you create must give the investigation agent enough context to find the right files on the first pass — no vague descriptions, no missing domains, no ambiguous scope.
|
|
13
13
|
|
|
14
|
-
**Agent model policy**:
|
|
14
|
+
**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.
|
|
15
15
|
**NEVER use plan mode (EnterPlanMode).**
|
|
16
16
|
|
|
17
17
|
---
|
|
@@ -133,8 +133,28 @@ List every file that the fix/feature will need to touch. Be thorough:
|
|
|
133
133
|
|
|
134
134
|
### 2D: Check for duplicates
|
|
135
135
|
|
|
136
|
+
Run the deterministic dedup script first (authoritative check), then fall back to an LLM pass if the script produces no result:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
# Authoritative deterministic check — uses token overlap algorithm (see scripts/issue-dedup.sh)
|
|
140
|
+
DEDUP_RESULT=$(scripts/issue-dedup.sh "{PROPOSED_TITLE}" {GH_FLAG} 2>&1)
|
|
141
|
+
DEDUP_EXIT=$?
|
|
142
|
+
|
|
143
|
+
if [ "$DEDUP_EXIT" -eq 1 ]; then
|
|
144
|
+
echo "Near-duplicate detected: $DEDUP_RESULT"
|
|
145
|
+
echo "Existing issue found — do NOT create a new one."
|
|
146
|
+
# STOP here and report to user (see handling rules below)
|
|
147
|
+
elif [ "$DEDUP_EXIT" -eq 2 ]; then
|
|
148
|
+
echo "Dedup check usage error: $DEDUP_RESULT"
|
|
149
|
+
echo "Do NOT proceed to issue creation — fix the invocation and retry."
|
|
150
|
+
# STOP here — do not fall through to gh issue create
|
|
151
|
+
fi
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
If the script exits 0 (no match found), also run an LLM-side search as a fallback:
|
|
155
|
+
|
|
136
156
|
```bash
|
|
137
|
-
#
|
|
157
|
+
# LLM fallback — broader semantic search for issues the token algorithm may miss
|
|
138
158
|
gh issue list {GH_FLAG} --state open --limit 20 --search "{key_terms}" --json number,title,labels --jq '.[] | "#\(.number) [\(.labels | map(.name) | join(","))] \(.title)"'
|
|
139
159
|
|
|
140
160
|
# Also check recently closed issues (might be a regression)
|
|
@@ -144,6 +164,7 @@ gh issue list {GH_FLAG} --state closed --limit 10 --search "{key_terms}" --json
|
|
|
144
164
|
If a duplicate exists:
|
|
145
165
|
- **Open duplicate found**: Tell the user. Do NOT create the issue. Show the existing issue number.
|
|
146
166
|
- **Closed duplicate found (regression)**: Create the issue but reference the prior issue in the body: "Regression of #{N}."
|
|
167
|
+
- **User wants to override**: Pass `--force` to the dedup script — this is an explicit human override path. Agents MUST NOT pass `--force` without user authorization. <!-- Added: forge#1335 -->
|
|
147
168
|
|
|
148
169
|
### 2E: Check for milestone context
|
|
149
170
|
|
package/commands/milestone.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Create, manage, and ship milestones — the top-level planning layer for feature development
|
|
3
3
|
argument-hint: [create <name> | status | ship <slug> | sync <slug>]
|
|
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,12 @@ Milestones are the top-level planning unit. They group related issues into a shi
|
|
|
13
14
|
|
|
14
15
|
**Hierarchy**: Milestone → Issues → Sub-issues (optional decomposition)
|
|
15
16
|
|
|
16
|
-
**
|
|
17
|
+
**NEVER use plan mode (EnterPlanMode).**
|
|
18
|
+
**NEVER use the Agent tool for issue work** — milestone dispatches issue work via `Skill(skill="work-on", ...)` only. The Agent tool bypasses the Skill pipeline's label state machine, FORGE annotations, and structured review. The Agent tool may be used only for coordination tasks within milestone planning itself (e.g., parallel issue list queries) — never for /work-on sub-dispatch.
|
|
19
|
+
|
|
20
|
+
<!-- FORGE:SPEC_LOADED — milestone.md loaded and active. Agent is bound by this spec. -->
|
|
21
|
+
|
|
22
|
+
**You have access to Task, Skill, and Bash tools** for milestone coordination.
|
|
17
23
|
|
|
18
24
|
## Config Resolution
|
|
19
25
|
|
|
@@ -498,18 +504,29 @@ ISSUE_EOF
|
|
|
498
504
|
fi
|
|
499
505
|
```
|
|
500
506
|
|
|
501
|
-
### Step 3: Create shipping PR
|
|
507
|
+
### Step 3: Create or update shipping PR (merge-train mechanics)
|
|
508
|
+
|
|
509
|
+
<!-- Added: forge#1327 (update-in-place), extended: forge#1332 (merge-train) -->
|
|
510
|
+
|
|
511
|
+
**Merge-train policy**: One staging→main PR stays open per deploy cycle. When a milestone ships to staging, it joins the deploy train as a candidate. The train PR is updated in place when fixes are pushed — it is NEVER closed and recreated. Dead-PR chains are eliminated: fix → push → re-request review on the same PR.
|
|
512
|
+
|
|
513
|
+
**Update-in-place policy**: Before creating a new PR, check whether an open shipping PR already exists for this milestone. If one does, push the updated milestone branch and re-request review on the existing PR — do NOT close and recreate. Closing-and-recreating is pure churn: it burns a new PR number, loses review history, and contributes to a low staging→main pass rate. The correct flow is: fix → push → re-request review on the same PR.
|
|
502
514
|
|
|
503
515
|
```bash
|
|
504
|
-
#
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
516
|
+
# Check for an existing open shipping PR for this milestone
|
|
517
|
+
EXISTING_PR=$(gh pr list {GH_FLAG} --head "milestone/{slug}" --base "{STAGING_BRANCH}" --state open \
|
|
518
|
+
--json number,url --jq '.[0] | "\(.number)|\(.url)"' 2>/dev/null)
|
|
519
|
+
|
|
520
|
+
if [ -n "$EXISTING_PR" ]; then
|
|
521
|
+
PR_NUMBER=$(echo "$EXISTING_PR" | cut -d'|' -f1)
|
|
522
|
+
PR_URL=$(echo "$EXISTING_PR" | cut -d'|' -f2)
|
|
523
|
+
echo "Existing open shipping PR #$PR_NUMBER found — updating in place (not recreating)."
|
|
524
|
+
|
|
525
|
+
# Update PR body to reflect latest diff state after fixes were pushed
|
|
526
|
+
DIFF_STATS=$(git diff origin/{STAGING_BRANCH}...origin/milestone/{slug} --stat)
|
|
527
|
+
COMMIT_LOG=$(git log origin/{STAGING_BRANCH}..origin/milestone/{slug} --oneline)
|
|
528
|
+
gh pr edit "$PR_NUMBER" {GH_FLAG} --body "$(cat <<'PR_EOF'
|
|
529
|
+
<!-- FORGE:TRAIN_CANDIDATE milestone={slug} -->
|
|
513
530
|
## Milestone: {MILESTONE_TITLE}
|
|
514
531
|
|
|
515
532
|
{MILESTONE_DESCRIPTION}
|
|
@@ -529,7 +546,43 @@ gh pr create {GH_FLAG} \
|
|
|
529
546
|
## Commits
|
|
530
547
|
{COMMIT_LOG}
|
|
531
548
|
PR_EOF
|
|
532
|
-
)"
|
|
549
|
+
)"
|
|
550
|
+
|
|
551
|
+
# Re-request review so reviewers are notified of the updated push
|
|
552
|
+
gh pr review "$PR_NUMBER" {GH_FLAG} --request-review 2>/dev/null || true
|
|
553
|
+
echo "PR #$PR_NUMBER updated and review re-requested."
|
|
554
|
+
else
|
|
555
|
+
# No open PR exists — create a new one
|
|
556
|
+
DIFF_STATS=$(git diff origin/{STAGING_BRANCH}...origin/milestone/{slug} --stat)
|
|
557
|
+
COMMIT_LOG=$(git log origin/{STAGING_BRANCH}..origin/milestone/{slug} --oneline)
|
|
558
|
+
|
|
559
|
+
gh pr create {GH_FLAG} \
|
|
560
|
+
--base {STAGING_BRANCH} \
|
|
561
|
+
--head milestone/{slug} \
|
|
562
|
+
--title "Ship: {MILESTONE_TITLE}" \
|
|
563
|
+
--body "$(cat <<'PR_EOF'
|
|
564
|
+
<!-- FORGE:TRAIN_CANDIDATE milestone={slug} -->
|
|
565
|
+
## Milestone: {MILESTONE_TITLE}
|
|
566
|
+
|
|
567
|
+
{MILESTONE_DESCRIPTION}
|
|
568
|
+
|
|
569
|
+
## Issues Included
|
|
570
|
+
{list of all closed issues in this milestone with PR references}
|
|
571
|
+
|
|
572
|
+
## Hunk-Loss Audit
|
|
573
|
+
{If Step 2.5 rebase ran: "✓ Rebase completed — {AT_RISK_COUNT} at-risk staging-branch hunks absorbed into milestone branch before this PR was created. All staging content is preserved."}
|
|
574
|
+
{If Step 2.5 found no at-risk hunks: "✓ Clean — no at-risk staging-branch hunks detected. Safe to merge."}
|
|
575
|
+
|
|
576
|
+
> ⚠ **IMPORTANT: Merge this PR using a MERGE COMMIT, not squash.** Squash-merging a milestone PR can silently drop staging-only hunks in files the milestone touches. Use the "Create a merge commit" option in the GitHub UI, or `gh pr merge {PR_NUMBER} --merge`.
|
|
577
|
+
|
|
578
|
+
## Diff Summary
|
|
579
|
+
{DIFF_STATS}
|
|
580
|
+
|
|
581
|
+
## Commits
|
|
582
|
+
{COMMIT_LOG}
|
|
583
|
+
PR_EOF
|
|
584
|
+
)"
|
|
585
|
+
fi
|
|
533
586
|
```
|
|
534
587
|
|
|
535
588
|
### Step 4: Run staging review
|
|
@@ -540,6 +593,16 @@ Use the `/review-pr` skill to review the shipping PR. This is a comprehensive re
|
|
|
540
593
|
Skill(skill="review-pr", args="{PR_NUMBER}")
|
|
541
594
|
```
|
|
542
595
|
|
|
596
|
+
**If the review returns blocking findings (FINDINGS, not PASSED)**:
|
|
597
|
+
- Do NOT close the PR.
|
|
598
|
+
- Do NOT restart from Step 1.
|
|
599
|
+
- Fix the blockers on `milestone/{slug}`, push the commits, then re-invoke review on the same PR:
|
|
600
|
+
```
|
|
601
|
+
Skill(skill="review-pr", args="{PR_NUMBER}")
|
|
602
|
+
```
|
|
603
|
+
- Step 3 will detect the open PR on the next `/milestone ship {slug}` call and update it in place — the same PR number is preserved, review history is retained, and the reviewer is re-notified.
|
|
604
|
+
- This eliminates the close-and-recreate churn that inflates the failed-PR count and lowers the staging→main pass rate. <!-- Added: forge#1328 -->
|
|
605
|
+
|
|
543
606
|
### Step 5: Report — PR is ready for user to merge
|
|
544
607
|
|
|
545
608
|
```
|
|
@@ -572,6 +635,8 @@ Milestone stays open. Issues remain assigned. Branch milestone/{slug} preserved.
|
|
|
572
635
|
Run /milestone ship {slug} again when the milestone is ready for another shipping attempt.
|
|
573
636
|
```
|
|
574
637
|
|
|
638
|
+
**When the staging review returns blockers (FINDINGS, not PASSED)**: do NOT close the PR. Fix the blockers on `milestone/{slug}`, push the fixes, then re-run `/milestone ship {slug}`. Step 3 will detect the open PR and update it in place — the same PR number is preserved, review history is retained, and the reviewer is re-notified. This eliminates the close-and-recreate churn that inflates the failed-PR count and lowers the staging→main pass rate. <!-- Added: forge#1327 -->
|
|
639
|
+
|
|
575
640
|
**If the PR merged successfully**, run the following:
|
|
576
641
|
|
|
577
642
|
```bash
|
package/commands/optimize.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Analyze this repo and generate per-repo adaptive scripts from learned patterns, git history, and existing configuration
|
|
3
3
|
argument-hint: [--dry-run | --force | --no-registry]
|
|
4
|
+
install: extras
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
# /optimize — Per-Repo Adaptive Script Generation
|
|
@@ -9,7 +10,7 @@ argument-hint: [--dry-run | --force | --no-registry]
|
|
|
9
10
|
|
|
10
11
|
Proactively analyze the current repository and generate `.forgedock/scripts/` entries from `forge.yaml → learned:`, git commit history, and pipeline annotations — without waiting for a correction event. This is the proactive half of the learning loop; reactive capture happens in `/work-on` Phase 1D.
|
|
11
12
|
|
|
12
|
-
**Agent model policy**:
|
|
13
|
+
**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.
|
|
13
14
|
**NEVER use plan mode (EnterPlanMode).**
|
|
14
15
|
|
|
15
16
|
---
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
---
|
|
2
|
+
install: internal
|
|
3
|
+
---
|
|
4
|
+
<!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
|
|
5
|
+
<!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
|
|
6
|
+
|
|
7
|
+
# /orchestrate — Configuration, Hard Rules & Multi-Repo Support
|
|
8
|
+
|
|
9
|
+
Read this file at the start of every `/orchestrate` invocation.
|
|
10
|
+
|
|
11
|
+
## HARD RULES — READ BEFORE ANYTHING ELSE
|
|
12
|
+
|
|
13
|
+
1. **Every agent MUST invoke `/work-on` via the Skill tool.** You do NOT write implementation prompts. You copy the Phase 4A template verbatim and fill in the `{VARIABLES}`. Nothing else. No custom prompts. No "just read and edit" shortcuts. The Skill tool invocation is what triggers labels, investigation comments, structured review, and trajectory tracking. Without it, the agent's work has no paper trail and is worthless.
|
|
14
|
+
|
|
15
|
+
2. **You are a dispatcher, not a builder.** You resolve issues, build the dependency DAG, spawn agents, and report results. You NEVER read code, edit files, or implement fixes yourself.
|
|
16
|
+
|
|
17
|
+
3. **After each agent completes, verify it used `/work-on`.** Check that completed issues have `workflow:*` labels and structured comments. If an agent bypassed the pipeline, report it as a failure.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
You are the top-level orchestrator. Your job is to take a batch of issues, plan the execution order, spawn parallel sub-agents (each running the full `/work-on` pipeline), and report consolidated results.
|
|
22
|
+
|
|
23
|
+
**You have access to ALL tools** — Agent tool (critical), Task tool, Skill tool, Bash, everything. Use the Agent tool aggressively to parallelize work.
|
|
24
|
+
|
|
25
|
+
**Agent model policy**: `model: "haiku"`, `effort: low` (mechanical — dispatch bookkeeping, lane routing, classification). Fallback: `model: "sonnet"` if rate-limited. User can override with `--model <name>`. Feature gate: pass `effort` in Task/Skill spawns only on Claude Code >= 2.1.154.
|
|
26
|
+
**NEVER use plan mode (EnterPlanMode).**
|
|
27
|
+
|
|
28
|
+
<!-- FORGE:SPEC_LOADED — orchestrate.md loaded and active. Agent is bound by HARD RULES above. -->
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Config Resolution
|
|
33
|
+
|
|
34
|
+
Read `forge.yaml` at the project root to resolve all project-specific variables before running any commands:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
# Parse forge.yaml for project context
|
|
38
|
+
CONFIG_FILE="${FORGE_CONFIG:-forge.yaml}"
|
|
39
|
+
GH_REPO=$(yq '.project.owner + "/" + .project.repo' "$CONFIG_FILE")
|
|
40
|
+
GH_FLAG="-R $GH_REPO"
|
|
41
|
+
REPO_PATH=$(yq '.paths.root' "$CONFIG_FILE")
|
|
42
|
+
PROJECT_NAME=$(yq '.project.name' "$CONFIG_FILE")
|
|
43
|
+
STAGING_BRANCH=$(yq '.branches.staging' "$CONFIG_FILE")
|
|
44
|
+
DEFAULT_BRANCH=$(yq '.branches.default' "$CONFIG_FILE")
|
|
45
|
+
# Build satellite repo map from repos.satellites list
|
|
46
|
+
# Each satellite: { prefix, repo, staging_branch }
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
All `{GH_REPO}`, `{GH_FLAG}`, `{REPO_PATH}`, `{PROJECT_NAME}`, `{STAGING_BRANCH}`, and `{DEFAULT_BRANCH}` references below are populated from `forge.yaml`.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Multi-Repo Support
|
|
54
|
+
|
|
55
|
+
This orchestrator supports issues across multiple repositories. See `/work-on` → "Multi-Repo Support" and `forge.yaml → repos` for the full project registry and context variables.
|
|
56
|
+
|
|
57
|
+
### Cross-Repo Issue References
|
|
58
|
+
|
|
59
|
+
Issues can be prefixed with a project shorthand derived from `forge.yaml → repos.satellites`:
|
|
60
|
+
- `#123` or `123` → default repo (`{GH_REPO}`)
|
|
61
|
+
- `{satellite_prefix}:5` → satellite repo (e.g., `mcp:5`, `n8n:12`) — prefixes and repos come from `forge.yaml → repos.satellites`
|
|
62
|
+
- `all-repos` → Scan all configured repos for open issues (combine and prioritize)
|
|
63
|
+
|
|
64
|
+
When spawning sub-agents, pass the project prefix so `/work-on` resolves to the correct repo.
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
---
|
|
2
|
+
install: internal
|
|
3
|
+
---
|
|
4
|
+
<!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
|
|
5
|
+
<!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
|
|
6
|
+
|
|
7
|
+
# /orchestrate — Phase 1: Resolve the Issue Set
|
|
8
|
+
|
|
9
|
+
## Phase 1: Resolve the Issue Set
|
|
10
|
+
|
|
11
|
+
Parse `$ARGUMENTS` to determine which issues to work on:
|
|
12
|
+
|
|
13
|
+
### Input Patterns
|
|
14
|
+
|
|
15
|
+
| Input | Resolution |
|
|
16
|
+
|-------|------------|
|
|
17
|
+
| `milestone <slug>` | All open issues assigned to that GitHub milestone (default repo) |
|
|
18
|
+
| `#1 #2 #3` or `1 2 3` | Specific issue numbers, optionally repo-prefixed (e.g., `#123 mcp:5 n8n:12`) |
|
|
19
|
+
| `next <N>` | Top N priority open issues (priority:P0 first, then priority:P1, etc.) |
|
|
20
|
+
| `next <N> all-repos` | Top N across ALL ecosystem repos |
|
|
21
|
+
| `fast-lane` or `fast` | All open fast-lane issues (no milestone, bugs/fixes) |
|
|
22
|
+
| `priority:P0` or `priority:P1` | All open issues with that priority label |
|
|
23
|
+
| `mcp:fast` or `n8n:next 3` | Repo-scoped queries |
|
|
24
|
+
| `<slug>` (no keyword) | Try milestone first, then fall back to label search |
|
|
25
|
+
|
|
26
|
+
### Fetch the issues
|
|
27
|
+
|
|
28
|
+
For each resolved repo, use the appropriate `-R` flag:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# Default repo (no flag needed if using GH_FLAG="" for the default):
|
|
32
|
+
gh issue list {GH_FLAG} --milestone "{TITLE}" --state open --limit 500 --json number,title,labels,milestone --jq '.[] | {number, title, labels: [.labels[].name], milestone: .milestone.title}'
|
|
33
|
+
|
|
34
|
+
# Satellite repos — always include -R flag with the satellite repo from forge.yaml:
|
|
35
|
+
# For each satellite in forge.yaml → repos.satellites:
|
|
36
|
+
gh issue list -R {SATELLITE_REPO} --milestone "{TITLE}" --state open --limit 500 --json number,title,labels,milestone
|
|
37
|
+
|
|
38
|
+
# For specific numbers with repo prefix:
|
|
39
|
+
gh issue view {NUMBER} -R {SATELLITE_REPO} --json number,title,labels,state,milestone,body
|
|
40
|
+
|
|
41
|
+
# For "all-repos" — fetch from all configured repos and combine:
|
|
42
|
+
gh issue list {GH_FLAG} --state open --limit {N} --json number,title,labels,milestone
|
|
43
|
+
# For each satellite in forge.yaml → repos.satellites:
|
|
44
|
+
gh issue list -R {SATELLITE_REPO} --state open --limit 500 --json number,title,labels,milestone
|
|
45
|
+
# Combine, sort by priority, take top N
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**Count validation**: After fetching issues, log the count returned. If a milestone query returns exactly 30 issues, warn the user: "WARNING: Exactly 30 issues returned — gh CLI default may have truncated results. Re-running with --limit 500 is recommended." (The --limit 500 above prevents this for standard runs, but verify if the count seems suspiciously low relative to milestone expectations.)
|
|
49
|
+
|
|
50
|
+
**Tag each issue with its project prefix** in your internal tracking so sub-agents receive the correct repo context.
|
|
51
|
+
|
|
52
|
+
### Filter out ineligible issues
|
|
53
|
+
|
|
54
|
+
Remove issues that should NOT be worked on:
|
|
55
|
+
- Already closed or has `workflow:merged` label
|
|
56
|
+
- Has `workflow:invalid` label
|
|
57
|
+
- Has `needs-human` label (requires manual action)
|
|
58
|
+
- Has `workflow:decomposed` label (parent tracker — its sub-issues should be picked up instead)
|
|
59
|
+
- Has `workflow:building` or `workflow:in-review` label (already in progress)
|
|
60
|
+
- Is an epic (`epic` label) — these are planning containers, not buildable
|
|
61
|
+
|
|
62
|
+
If a `workflow:decomposed` issue is found, automatically expand it to its open sub-issues instead.
|
|
63
|
+
|
|
64
|
+
### P3 Review-Finding Batching (deterministic grouping rule)
|
|
65
|
+
|
|
66
|
+
<!-- Added: forge#1333 -->
|
|
67
|
+
|
|
68
|
+
Before finalizing the issue set, apply the P3 batching rule to reduce full-pipeline overhead on low-severity review findings.
|
|
69
|
+
|
|
70
|
+
**Trigger conditions** (BOTH must be true to batch):
|
|
71
|
+
1. The issue has labels `review-finding` + `priority:P3`
|
|
72
|
+
2. The issue has `<!-- FORGE:BATCHABLE -->` in its body
|
|
73
|
+
|
|
74
|
+
**Safety exclusions — NEVER batch** (override all trigger conditions):
|
|
75
|
+
- Issue body contains the word "security" or "billing" anywhere in the title or `## Problem` section
|
|
76
|
+
- Issue has a `security` label
|
|
77
|
+
- Issue has a `billing` label
|
|
78
|
+
|
|
79
|
+
**Grouping algorithm:**
|
|
80
|
+
```bash
|
|
81
|
+
# Fetch all open batchable P3 issues
|
|
82
|
+
BATCHABLE_P3=$(gh issue list {GH_FLAG} \
|
|
83
|
+
--state open \
|
|
84
|
+
--label "review-finding,priority:P3" \
|
|
85
|
+
--limit 500 \
|
|
86
|
+
--json number,title,body,labels \
|
|
87
|
+
--jq '.[] | select(.body | test("<!-- FORGE:BATCHABLE -->"))
|
|
88
|
+
| select((.title | test("security|billing"; "i")) | not)
|
|
89
|
+
| select(.body | test("## Problem[\\s\\S]{0,500}(security|billing)"; "i") | not)
|
|
90
|
+
| select(([.labels[].name] | any(. == "security" or . == "billing")) | not)')
|
|
91
|
+
|
|
92
|
+
# Group by domain (file-cluster derived from the affected file path in each issue body)
|
|
93
|
+
# Domain is the top-level directory of the first affected file listed under "## Affected Files"
|
|
94
|
+
# e.g., "services/api/auth/login.py" → domain "services/api/auth"
|
|
95
|
+
# "web/src/app/billing/" → EXCLUDED (billing path)
|
|
96
|
+
# "commands/review-pr.md" → domain "commands"
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**Batch creation rule**: When 5+ batchable P3 issues share the same domain, OR when the oldest batchable P3 in a domain exceeds 72 hours, create a single batch issue grouping those issues:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
BATCH_ISSUE_NUM=$(gh issue create {GH_FLAG} \
|
|
103
|
+
--title "fix(batch): P3 review findings — {DOMAIN} domain (batch #{BATCH_N})" \
|
|
104
|
+
--label "review-finding,priority:P3,batch" \
|
|
105
|
+
--body "$(cat <<'BATCH_EOF'
|
|
106
|
+
## Problem
|
|
107
|
+
|
|
108
|
+
Batch of P3 review findings in the **{DOMAIN}** domain, grouped to reduce per-finding pipeline overhead.
|
|
109
|
+
|
|
110
|
+
## Member Findings
|
|
111
|
+
|
|
112
|
+
<!-- FORGE:BATCH_MEMBERS -->
|
|
113
|
+
{for each member issue: "- [ ] #{NUM}: {TITLE}"}
|
|
114
|
+
<!-- /FORGE:BATCH_MEMBERS -->
|
|
115
|
+
|
|
116
|
+
## Acceptance Criteria
|
|
117
|
+
|
|
118
|
+
- [ ] All member findings addressed or closed as false-positive
|
|
119
|
+
- [ ] Member issues auto-closed with reference to this batch PR on merge
|
|
120
|
+
- [ ] No security or billing paths touched (validated before batching)
|
|
121
|
+
|
|
122
|
+
## Context
|
|
123
|
+
|
|
124
|
+
**Batch policy**: 5+ open P3 findings in the same domain, or oldest > 72h.
|
|
125
|
+
**Member issues**: #{N1}, #{N2}, #{N3}, ...
|
|
126
|
+
|
|
127
|
+
<!-- FORGE:BATCHABLE -->
|
|
128
|
+
BATCH_EOF
|
|
129
|
+
)")
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
**Replace member issues with the batch issue** in the resolved issue set. Member issues are NOT individually dispatched to `/work-on` — the batch issue is the single pipeline unit.
|
|
133
|
+
|
|
134
|
+
**Important limits**:
|
|
135
|
+
- Max 10 members per batch issue — if more than 10 batchable P3s exist in a domain, create multiple batch issues of ≤ 10 each
|
|
136
|
+
- P1 and P2 issues are NEVER batched — they keep the standard one-issue-one-PR path
|
|
137
|
+
- Batch issues themselves are never nested inside other batch issues
|
|
138
|
+
|
|
139
|
+
If fewer than 5 batchable P3 findings exist in any domain AND none exceed 72h, skip batch creation entirely — individual P3s run through the standard pipeline.
|
|
140
|
+
|
|
141
|
+
### CRITICAL: No duplicate detection at orchestrator level
|
|
142
|
+
|
|
143
|
+
**The orchestrator NEVER closes, merges, or deduplicates issues.** Even if two issues look like duplicates (similar titles, same error message, same symptoms), they may target different code paths, different ORM objects, or different failure modes. Only a `/work-on` investigation agent — after reading the actual code — can determine whether an issue is truly a duplicate.
|
|
144
|
+
|
|
145
|
+
**What the orchestrator must NOT do:**
|
|
146
|
+
- Close an issue as "duplicate" based on title/symptom similarity
|
|
147
|
+
- Skip an issue because another issue "covers it"
|
|
148
|
+
- Merge two issues into one agent
|
|
149
|
+
- Make ANY judgment about issue validity
|
|
150
|
+
|
|
151
|
+
**What the orchestrator SHOULD do:**
|
|
152
|
+
- If two issues look related, add a note in the agent prompt: "Note: #{OTHER} has similar symptoms — investigate whether this is the same root cause or a different code path"
|
|
153
|
+
- Let both agents run independently — if one finds it's truly a duplicate, `/work-on`'s investigation phase will close it as invalid with evidence
|
|
154
|
+
- Flag potential overlap in the DAG plan for the user's awareness, but NEVER act on it
|
|
155
|
+
|
|
156
|
+
**Why**: Surface-level similarity hides critical differences. #3842 (api_key.id lazy load) and #4039 (user.id lazy load) had identical error messages but targeted completely different ORM objects. Closing #4039 as a "duplicate" would have left a customer-impacting P0 bug unfixed.
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|