forgedock 1.1.1 → 1.1.3
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 +24 -2
- 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 +70 -20
- 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 +518 -361
- 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/deploy-pr.md +485 -0
- 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/fix-ci.md +577 -0
- 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/recover-orphans.md +576 -0
- 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 +16 -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 +120 -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 +386 -111
- 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 +254 -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
|
@@ -0,0 +1,576 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Scan for pipeline-orphaned issues stuck in intermediate workflow states and recover them — diagnose each orphan's actual GitHub state, apply recovery actions, clean up worktrees
|
|
3
|
+
argument-hint: [--dry-run | --since <hours> | --issue <number>]
|
|
4
|
+
---
|
|
5
|
+
<!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
|
|
6
|
+
<!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
|
|
7
|
+
|
|
8
|
+
# /recover-orphans — Pipeline Orphan Recovery
|
|
9
|
+
|
|
10
|
+
**Input**: $ARGUMENTS
|
|
11
|
+
|
|
12
|
+
Scan ALL open issues with intermediate workflow labels for orphaned state — issues where the agent died mid-pipeline (context expired, rate-limited, crashed) and no active agent is continuing. Diagnose each orphan's actual GitHub state and apply the appropriate recovery action.
|
|
13
|
+
|
|
14
|
+
**Agent model policy**: `model: "sonnet"`. Fallback: `model: "opus"` if rate-limited.
|
|
15
|
+
**NEVER use plan mode (EnterPlanMode).**
|
|
16
|
+
**NEVER use the Agent tool** — recover-orphans re-enters the pipeline via `Skill(skill="work-on", ...)` and `Skill(skill="review-pr", ...)` only.
|
|
17
|
+
|
|
18
|
+
<!-- FORGE:SPEC_LOADED — recover-orphans.md loaded and active. Agent is bound by this spec. -->
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Config Resolution
|
|
23
|
+
|
|
24
|
+
Read `forge.yaml` at the project root before running any commands:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
CONFIG_FILE="${FORGE_CONFIG:-forge.yaml}"
|
|
28
|
+
GH_REPO=$(yq '.project.owner + "/" + .project.repo' "$CONFIG_FILE")
|
|
29
|
+
GH_FLAG="-R $GH_REPO"
|
|
30
|
+
REPO_PATH=$(yq '.paths.root' "$CONFIG_FILE")
|
|
31
|
+
WORKTREE_BASE=$(yq '.paths.worktree_base' "$CONFIG_FILE")
|
|
32
|
+
STAGING_BRANCH=$(yq '.branches.staging' "$CONFIG_FILE")
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
If `forge.yaml` is missing: stop and tell the user to run `npx forgedock init` to generate it.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Argument Parsing
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
DRY_RUN=false
|
|
43
|
+
SINCE_HOURS=""
|
|
44
|
+
TARGET_ISSUE=""
|
|
45
|
+
|
|
46
|
+
# Parse flags
|
|
47
|
+
for arg in $ARGUMENTS; do
|
|
48
|
+
case "$arg" in
|
|
49
|
+
--dry-run) DRY_RUN=true ;;
|
|
50
|
+
--since) : ;; # value follows
|
|
51
|
+
--issue) : ;; # value follows
|
|
52
|
+
esac
|
|
53
|
+
done
|
|
54
|
+
|
|
55
|
+
# Parse --since <hours> and --issue <number> (value follows flag)
|
|
56
|
+
PREV=""
|
|
57
|
+
for arg in $ARGUMENTS; do
|
|
58
|
+
if [ "$PREV" = "--since" ]; then
|
|
59
|
+
SINCE_HOURS="$arg"
|
|
60
|
+
elif [ "$PREV" = "--issue" ]; then
|
|
61
|
+
TARGET_ISSUE="$arg"
|
|
62
|
+
fi
|
|
63
|
+
PREV="$arg"
|
|
64
|
+
done
|
|
65
|
+
|
|
66
|
+
echo "=== /recover-orphans: $(date -u +%Y-%m-%dT%H:%M:%SZ) ==="
|
|
67
|
+
echo "DRY_RUN=$DRY_RUN | SINCE_HOURS=${SINCE_HOURS:-all} | TARGET_ISSUE=${TARGET_ISSUE:-all}"
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
| Flag | Effect |
|
|
71
|
+
|------|--------|
|
|
72
|
+
| (none) | Scan all intermediate-state open issues |
|
|
73
|
+
| `--dry-run` | Report-only — print what would be done, no mutations |
|
|
74
|
+
| `--since <hours>` | Only scan issues not updated in the last N hours (default: all) |
|
|
75
|
+
| `--issue <number>` | Recover a single specific issue |
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Phase 1: Find Orphans
|
|
80
|
+
|
|
81
|
+
**Note**: `gh issue list --label` uses AND semantics when multiple labels are given. To find issues with ANY intermediate workflow label, query each label separately and merge results.
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
# Compute stale cutoff if --since was provided
|
|
85
|
+
CUTOFF=""
|
|
86
|
+
if [ -n "$SINCE_HOURS" ]; then
|
|
87
|
+
CUTOFF=$(python3 -c "
|
|
88
|
+
from datetime import datetime, timedelta, timezone
|
|
89
|
+
h = int('$SINCE_HOURS')
|
|
90
|
+
print((datetime.now(timezone.utc) - timedelta(hours=h)).strftime('%Y-%m-%dT%H:%M:%SZ'))
|
|
91
|
+
" 2>/dev/null || echo "")
|
|
92
|
+
fi
|
|
93
|
+
|
|
94
|
+
if [ -n "$TARGET_ISSUE" ]; then
|
|
95
|
+
# Single-issue mode
|
|
96
|
+
ORPHAN_LIST="$TARGET_ISSUE"
|
|
97
|
+
echo "Single-issue mode: targeting #$TARGET_ISSUE"
|
|
98
|
+
else
|
|
99
|
+
# Fleet scan: query each workflow label separately, merge, deduplicate
|
|
100
|
+
ORPHAN_JSON=$(
|
|
101
|
+
for LABEL in "workflow:investigating" "workflow:ready-to-build" "workflow:building" "workflow:in-review"; do
|
|
102
|
+
gh issue list ${GH_FLAG} \
|
|
103
|
+
--state open \
|
|
104
|
+
--label "$LABEL" \
|
|
105
|
+
--limit 100 \
|
|
106
|
+
--json number,title,labels,updatedAt
|
|
107
|
+
done | jq -s '
|
|
108
|
+
flatten |
|
|
109
|
+
unique_by(.number) |
|
|
110
|
+
sort_by(.updatedAt) | reverse |
|
|
111
|
+
.[]
|
|
112
|
+
')
|
|
113
|
+
|
|
114
|
+
# Apply --since filter if specified
|
|
115
|
+
if [ -n "$CUTOFF" ]; then
|
|
116
|
+
ORPHAN_JSON=$(echo "$ORPHAN_JSON" | jq -s --arg cutoff "$CUTOFF" '
|
|
117
|
+
[.[] | select(.updatedAt < $cutoff)] | .[]
|
|
118
|
+
')
|
|
119
|
+
fi
|
|
120
|
+
|
|
121
|
+
ORPHAN_LIST=$(echo "$ORPHAN_JSON" | jq -r '.number' | sort -un)
|
|
122
|
+
fi
|
|
123
|
+
|
|
124
|
+
ORPHAN_COUNT=$(echo "$ORPHAN_LIST" | grep -c '[0-9]' 2>/dev/null || echo 0)
|
|
125
|
+
echo "Orphan candidates found: $ORPHAN_COUNT"
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
If `ORPHAN_COUNT` is 0: print `No orphaned issues found — pipeline is clean.` and STOP.
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## Phase 2: Diagnose Each Orphan
|
|
133
|
+
|
|
134
|
+
For each issue number in `ORPHAN_LIST`, run the full diagnostic and determine the recovery action.
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
# Diagnosis result arrays (accumulated for Phase 3 and Phase 5 report)
|
|
138
|
+
declare -A DIAG_ACTION # issue_num -> action name
|
|
139
|
+
declare -A DIAG_REASON # issue_num -> human-readable reason
|
|
140
|
+
declare -A DIAG_PR_NUM # issue_num -> associated PR number (if any)
|
|
141
|
+
declare -A DIAG_BRANCH # issue_num -> associated branch (if any)
|
|
142
|
+
|
|
143
|
+
for NUM in $ORPHAN_LIST; do
|
|
144
|
+
echo ""
|
|
145
|
+
echo "--- Diagnosing #$NUM ---"
|
|
146
|
+
|
|
147
|
+
# Read current issue state
|
|
148
|
+
ISSUE=$(gh issue view "$NUM" ${GH_FLAG} \
|
|
149
|
+
--json number,title,labels,state,updatedAt,body 2>/dev/null)
|
|
150
|
+
if [ -z "$ISSUE" ]; then
|
|
151
|
+
echo "#$NUM: Could not fetch — skipping"
|
|
152
|
+
continue
|
|
153
|
+
fi
|
|
154
|
+
|
|
155
|
+
ISSUE_STATE=$(echo "$ISSUE" | jq -r '.state')
|
|
156
|
+
ISSUE_TITLE=$(echo "$ISSUE" | jq -r '.title')
|
|
157
|
+
ISSUE_LABELS=$(echo "$ISSUE" | jq -r '[.labels[].name] | join(", ")')
|
|
158
|
+
WORKFLOW_LABEL=$(echo "$ISSUE" | jq -r '[.labels[].name | select(startswith("workflow:"))] | first // "none"')
|
|
159
|
+
|
|
160
|
+
echo "#$NUM: $ISSUE_TITLE"
|
|
161
|
+
echo " State: $ISSUE_STATE | Labels: $ISSUE_LABELS"
|
|
162
|
+
|
|
163
|
+
# Skip if already in terminal state (race condition: label changed since query)
|
|
164
|
+
if [ "$ISSUE_STATE" = "CLOSED" ]; then
|
|
165
|
+
DIAG_ACTION[$NUM]="skip"
|
|
166
|
+
DIAG_REASON[$NUM]="Issue already closed"
|
|
167
|
+
continue
|
|
168
|
+
fi
|
|
169
|
+
if echo "$ISSUE_LABELS" | grep -qE "workflow:merged|workflow:invalid"; then
|
|
170
|
+
DIAG_ACTION[$NUM]="skip"
|
|
171
|
+
DIAG_REASON[$NUM]="Already in terminal state: $ISSUE_LABELS"
|
|
172
|
+
continue
|
|
173
|
+
fi
|
|
174
|
+
|
|
175
|
+
# Check for merged PR referencing this issue
|
|
176
|
+
MERGED_PR=$(gh pr list ${GH_FLAG} \
|
|
177
|
+
--state merged \
|
|
178
|
+
--search "Closes #$NUM" \
|
|
179
|
+
--json number \
|
|
180
|
+
--jq '.[0].number' 2>/dev/null)
|
|
181
|
+
|
|
182
|
+
if [ -z "$MERGED_PR" ]; then
|
|
183
|
+
# Also check body/title for issue reference pattern
|
|
184
|
+
MERGED_PR=$(gh pr list ${GH_FLAG} \
|
|
185
|
+
--state merged \
|
|
186
|
+
--search "#$NUM" \
|
|
187
|
+
--limit 20 \
|
|
188
|
+
--json number,body \
|
|
189
|
+
--jq ".[] | select(.body | test(\"Closes #${NUM}|closes #${NUM}|Fix #${NUM}|fix #${NUM}\")) | .number" \
|
|
190
|
+
2>/dev/null | head -1)
|
|
191
|
+
fi
|
|
192
|
+
|
|
193
|
+
if [ -n "$MERGED_PR" ]; then
|
|
194
|
+
DIAG_ACTION[$NUM]="label-cleanup"
|
|
195
|
+
DIAG_REASON[$NUM]="PR #$MERGED_PR already merged — update labels and close issue"
|
|
196
|
+
DIAG_PR_NUM[$NUM]="$MERGED_PR"
|
|
197
|
+
echo " Diagnosis: LABEL-CLEANUP (PR #$MERGED_PR already merged)"
|
|
198
|
+
continue
|
|
199
|
+
fi
|
|
200
|
+
|
|
201
|
+
# Check for an open PR on any branch associated with this issue
|
|
202
|
+
# Strategy: look for branches with the issue number in the name
|
|
203
|
+
ISSUE_BRANCH=$(git ls-remote --heads origin 2>/dev/null | grep "/$NUM" | sed 's|.*refs/heads/||' | head -1)
|
|
204
|
+
if [ -z "$ISSUE_BRANCH" ]; then
|
|
205
|
+
# Also check recent PR list for a PR referencing this issue
|
|
206
|
+
OPEN_PR_JSON=$(gh pr list ${GH_FLAG} \
|
|
207
|
+
--state open \
|
|
208
|
+
--search "#$NUM" \
|
|
209
|
+
--limit 20 \
|
|
210
|
+
--json number,headRefName,reviewDecision,statusCheckRollup \
|
|
211
|
+
2>/dev/null)
|
|
212
|
+
OPEN_PR_NUM=$(echo "$OPEN_PR_JSON" | jq -r ".[] | select(.body | test(\"#${NUM}\")) | .number" 2>/dev/null | head -1)
|
|
213
|
+
else
|
|
214
|
+
OPEN_PR_JSON=$(gh pr list ${GH_FLAG} \
|
|
215
|
+
--state open \
|
|
216
|
+
--head "$ISSUE_BRANCH" \
|
|
217
|
+
--json number,headRefName,reviewDecision,statusCheckRollup \
|
|
218
|
+
2>/dev/null)
|
|
219
|
+
OPEN_PR_NUM=$(echo "$OPEN_PR_JSON" | jq -r '.[0].number' 2>/dev/null)
|
|
220
|
+
fi
|
|
221
|
+
|
|
222
|
+
# If open PR found, diagnose PR state
|
|
223
|
+
if [ -n "$OPEN_PR_NUM" ]; then
|
|
224
|
+
DIAG_BRANCH[$NUM]="${ISSUE_BRANCH:-unknown}"
|
|
225
|
+
DIAG_PR_NUM[$NUM]="$OPEN_PR_NUM"
|
|
226
|
+
|
|
227
|
+
# Read PR details
|
|
228
|
+
PR_DETAIL=$(gh pr view "$OPEN_PR_NUM" ${GH_FLAG} \
|
|
229
|
+
--json number,headRefName,reviewDecision,statusCheckRollup,state 2>/dev/null)
|
|
230
|
+
PR_REVIEW=$(echo "$PR_DETAIL" | jq -r '.reviewDecision // "REVIEW_REQUIRED"')
|
|
231
|
+
PR_CI=$(echo "$PR_DETAIL" | jq -r '
|
|
232
|
+
if (.statusCheckRollup == null or (.statusCheckRollup | length) == 0) then "UNKNOWN"
|
|
233
|
+
elif ([.statusCheckRollup[] | select(.conclusion == "FAILURE" or .conclusion == "ERROR")] | length) > 0 then "FAILED"
|
|
234
|
+
elif ([.statusCheckRollup[] | select(.status == "IN_PROGRESS" or .status == "QUEUED" or .status == "PENDING")] | length) > 0 then "PENDING"
|
|
235
|
+
elif ([.statusCheckRollup[] | select(.conclusion == "SUCCESS")] | length) > 0 then "SUCCESS"
|
|
236
|
+
else "UNKNOWN"
|
|
237
|
+
end')
|
|
238
|
+
BRANCH_NAME=$(echo "$PR_DETAIL" | jq -r '.headRefName // ""')
|
|
239
|
+
DIAG_BRANCH[$NUM]="$BRANCH_NAME"
|
|
240
|
+
|
|
241
|
+
echo " PR #$OPEN_PR_NUM found — reviewDecision=$PR_REVIEW | CI=$PR_CI"
|
|
242
|
+
|
|
243
|
+
if [ "$PR_REVIEW" = "APPROVED" ] && [ "$PR_CI" = "SUCCESS" ]; then
|
|
244
|
+
DIAG_ACTION[$NUM]="merge-pr"
|
|
245
|
+
DIAG_REASON[$NUM]="PR #$OPEN_PR_NUM approved + CI green — merge and close"
|
|
246
|
+
echo " Diagnosis: MERGE-PR (approved + CI green)"
|
|
247
|
+
elif [ "$PR_CI" = "FAILED" ]; then
|
|
248
|
+
DIAG_ACTION[$NUM]="escalate-ci"
|
|
249
|
+
DIAG_REASON[$NUM]="PR #$OPEN_PR_NUM has CI failures — manual intervention needed"
|
|
250
|
+
echo " Diagnosis: ESCALATE-CI (CI failed, needs /fix-ci or manual fix)"
|
|
251
|
+
elif [ "$PR_REVIEW" = "CHANGES_REQUESTED" ]; then
|
|
252
|
+
DIAG_ACTION[$NUM]="escalate-changes"
|
|
253
|
+
DIAG_REASON[$NUM]="PR #$OPEN_PR_NUM has requested changes — needs human review of findings"
|
|
254
|
+
echo " Diagnosis: ESCALATE-CHANGES (reviewer requested changes)"
|
|
255
|
+
else
|
|
256
|
+
DIAG_ACTION[$NUM]="review-pr"
|
|
257
|
+
DIAG_REASON[$NUM]="PR #$OPEN_PR_NUM exists but not reviewed — invoke /review-pr"
|
|
258
|
+
echo " Diagnosis: REVIEW-PR (open PR awaiting review)"
|
|
259
|
+
fi
|
|
260
|
+
continue
|
|
261
|
+
fi
|
|
262
|
+
|
|
263
|
+
# No open or merged PR — check if a branch exists with commits
|
|
264
|
+
BRANCH_EXISTS=false
|
|
265
|
+
BRANCH_NAME=""
|
|
266
|
+
if [ -n "$ISSUE_BRANCH" ]; then
|
|
267
|
+
BRANCH_EXISTS=true
|
|
268
|
+
BRANCH_NAME="$ISSUE_BRANCH"
|
|
269
|
+
else
|
|
270
|
+
# Try common naming patterns
|
|
271
|
+
for PATTERN in "fix/.*${NUM}" "feat/.*${NUM}" "refactor/.*${NUM}"; do
|
|
272
|
+
FOUND=$(git ls-remote --heads origin 2>/dev/null | grep -oE "fix/[^ ]*${NUM}[^ ]*|feat/[^ ]*${NUM}[^ ]*|refactor/[^ ]*${NUM}[^ ]*" | head -1)
|
|
273
|
+
if [ -n "$FOUND" ]; then
|
|
274
|
+
BRANCH_EXISTS=true
|
|
275
|
+
BRANCH_NAME="$FOUND"
|
|
276
|
+
break
|
|
277
|
+
fi
|
|
278
|
+
done
|
|
279
|
+
fi
|
|
280
|
+
|
|
281
|
+
if [ "$BRANCH_EXISTS" = "true" ] && [ -n "$BRANCH_NAME" ]; then
|
|
282
|
+
DIAG_BRANCH[$NUM]="$BRANCH_NAME"
|
|
283
|
+
# Check if branch has commits beyond the base
|
|
284
|
+
BRANCH_COMMITS=$(git log "origin/${STAGING_BRANCH}..origin/${BRANCH_NAME}" --oneline 2>/dev/null | wc -l | tr -d ' ')
|
|
285
|
+
if [ "${BRANCH_COMMITS:-0}" -gt 0 ]; then
|
|
286
|
+
DIAG_ACTION[$NUM]="create-pr"
|
|
287
|
+
DIAG_REASON[$NUM]="Branch $BRANCH_NAME has $BRANCH_COMMITS commits but no PR — resume /work-on to create PR"
|
|
288
|
+
echo " Diagnosis: CREATE-PR (branch has commits, no PR)"
|
|
289
|
+
else
|
|
290
|
+
DIAG_ACTION[$NUM]="reset-labels"
|
|
291
|
+
DIAG_REASON[$NUM]="Branch $BRANCH_NAME exists but has no commits beyond base — reset labels to unworked"
|
|
292
|
+
echo " Diagnosis: RESET-LABELS (empty branch)"
|
|
293
|
+
fi
|
|
294
|
+
else
|
|
295
|
+
# No branch, no PR — pure label orphan
|
|
296
|
+
DIAG_ACTION[$NUM]="reset-labels"
|
|
297
|
+
DIAG_REASON[$NUM]="No branch or PR found — reset workflow labels so issue is unworked"
|
|
298
|
+
echo " Diagnosis: RESET-LABELS (no branch, no PR)"
|
|
299
|
+
fi
|
|
300
|
+
|
|
301
|
+
done
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
## Phase 3: Apply Recovery
|
|
307
|
+
|
|
308
|
+
For each diagnosed issue, apply the recovery action. All mutating actions are skipped when `DRY_RUN=true`.
|
|
309
|
+
|
|
310
|
+
```bash
|
|
311
|
+
RECOVERY_RESULTS=""
|
|
312
|
+
|
|
313
|
+
for NUM in $ORPHAN_LIST; do
|
|
314
|
+
ACTION="${DIAG_ACTION[$NUM]:-skip}"
|
|
315
|
+
REASON="${DIAG_REASON[$NUM]:-unknown}"
|
|
316
|
+
PR_NUM="${DIAG_PR_NUM[$NUM]:-}"
|
|
317
|
+
BRANCH="${DIAG_BRANCH[$NUM]:-}"
|
|
318
|
+
|
|
319
|
+
echo ""
|
|
320
|
+
echo "--- Recovering #$NUM (action: $ACTION) ---"
|
|
321
|
+
echo " Reason: $REASON"
|
|
322
|
+
|
|
323
|
+
case "$ACTION" in
|
|
324
|
+
|
|
325
|
+
skip)
|
|
326
|
+
echo " Skipped: $REASON"
|
|
327
|
+
RECOVERY_RESULTS="${RECOVERY_RESULTS}| #${NUM} | skip | ${REASON} |\n"
|
|
328
|
+
;;
|
|
329
|
+
|
|
330
|
+
label-cleanup)
|
|
331
|
+
# PR already merged — update labels and close issue
|
|
332
|
+
echo " Applying label-cleanup: marking workflow:merged and closing issue"
|
|
333
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
334
|
+
echo " [DRY-RUN] Would: gh issue edit $NUM --add-label workflow:merged --remove-label intermediate"
|
|
335
|
+
echo " [DRY-RUN] Would: gh issue close $NUM with merged comment"
|
|
336
|
+
else
|
|
337
|
+
gh issue edit "$NUM" ${GH_FLAG} \
|
|
338
|
+
--add-label "workflow:merged" \
|
|
339
|
+
--remove-label "workflow:investigating,workflow:ready-to-build,workflow:building,workflow:in-review,workflow:invalid,workflow:decomposed" \
|
|
340
|
+
2>/dev/null || true
|
|
341
|
+
gh issue close "$NUM" ${GH_FLAG} \
|
|
342
|
+
--comment "Closed by /recover-orphans: PR #${PR_NUM} was already merged. Labels corrected." \
|
|
343
|
+
2>/dev/null || true
|
|
344
|
+
echo " Done: #$NUM closed with workflow:merged"
|
|
345
|
+
fi
|
|
346
|
+
RECOVERY_RESULTS="${RECOVERY_RESULTS}| #${NUM} | label-cleanup | PR #${PR_NUM} merged — closed issue |\n"
|
|
347
|
+
;;
|
|
348
|
+
|
|
349
|
+
merge-pr)
|
|
350
|
+
# Open PR is approved + CI green — merge it
|
|
351
|
+
echo " Applying merge-pr: merging PR #$PR_NUM"
|
|
352
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
353
|
+
echo " [DRY-RUN] Would: gh pr merge $PR_NUM --merge --auto"
|
|
354
|
+
else
|
|
355
|
+
MERGE_RESULT=$(gh pr merge "$PR_NUM" ${GH_FLAG} --merge --auto 2>&1)
|
|
356
|
+
MERGE_EXIT=$?
|
|
357
|
+
echo " Merge result (exit $MERGE_EXIT): $MERGE_RESULT"
|
|
358
|
+
if [ $MERGE_EXIT -eq 0 ]; then
|
|
359
|
+
# Close the issue explicitly (Closes # only auto-closes on default branch)
|
|
360
|
+
gh issue close "$NUM" ${GH_FLAG} \
|
|
361
|
+
--comment "Closed by /recover-orphans: PR #${PR_NUM} merged." \
|
|
362
|
+
2>/dev/null || true
|
|
363
|
+
gh issue edit "$NUM" ${GH_FLAG} \
|
|
364
|
+
--add-label "workflow:merged" \
|
|
365
|
+
--remove-label "workflow:investigating,workflow:ready-to-build,workflow:building,workflow:in-review,workflow:invalid,workflow:decomposed" \
|
|
366
|
+
2>/dev/null || true
|
|
367
|
+
fi
|
|
368
|
+
fi
|
|
369
|
+
RECOVERY_RESULTS="${RECOVERY_RESULTS}| #${NUM} | merge-pr | PR #${PR_NUM} merged |\n"
|
|
370
|
+
;;
|
|
371
|
+
|
|
372
|
+
review-pr)
|
|
373
|
+
# Open PR awaiting review — invoke /review-pr
|
|
374
|
+
echo " Applying review-pr: invoking /review-pr on PR #$PR_NUM"
|
|
375
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
376
|
+
echo " [DRY-RUN] Would: Skill(skill='review-pr', args='$PR_NUM --auto-merge --issue $NUM --gh-flag $GH_FLAG')"
|
|
377
|
+
else
|
|
378
|
+
Skill(skill="review-pr", args="${PR_NUM} --auto-merge --issue ${NUM} --gh-flag ${GH_FLAG}")
|
|
379
|
+
# After review: update label
|
|
380
|
+
gh issue edit "$NUM" ${GH_FLAG} --add-label "workflow:in-review" \
|
|
381
|
+
--remove-label "workflow:building" 2>/dev/null || true
|
|
382
|
+
fi
|
|
383
|
+
RECOVERY_RESULTS="${RECOVERY_RESULTS}| #${NUM} | review-pr | PR #${PR_NUM} submitted for review |\n"
|
|
384
|
+
;;
|
|
385
|
+
|
|
386
|
+
create-pr)
|
|
387
|
+
# Branch has commits but no PR — resume /work-on to create PR
|
|
388
|
+
echo " Applying create-pr: resuming /work-on to advance from build to PR creation"
|
|
389
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
390
|
+
echo " [DRY-RUN] Would: Skill(skill='work-on', args='$NUM')"
|
|
391
|
+
else
|
|
392
|
+
Skill(skill="work-on", args="${NUM}")
|
|
393
|
+
fi
|
|
394
|
+
RECOVERY_RESULTS="${RECOVERY_RESULTS}| #${NUM} | create-pr | Resumed /work-on — branch $BRANCH has commits, no PR |\n"
|
|
395
|
+
;;
|
|
396
|
+
|
|
397
|
+
reset-labels)
|
|
398
|
+
# No branch, no PR, or empty branch — reset to unworked state
|
|
399
|
+
echo " Applying reset-labels: removing intermediate workflow labels"
|
|
400
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
401
|
+
echo " [DRY-RUN] Would: remove workflow:investigating, workflow:ready-to-build, workflow:building, workflow:in-review"
|
|
402
|
+
else
|
|
403
|
+
gh issue edit "$NUM" ${GH_FLAG} \
|
|
404
|
+
--remove-label "workflow:investigating,workflow:ready-to-build,workflow:building,workflow:in-review" \
|
|
405
|
+
2>/dev/null || true
|
|
406
|
+
gh issue comment "$NUM" ${GH_FLAG} \
|
|
407
|
+
--body "<!-- FORGE:ORPHAN_RECOVERED -->
|
|
408
|
+
## Orphan Recovery Applied
|
|
409
|
+
|
|
410
|
+
**Action**: Label reset — no branch or PR found (or branch had no commits).
|
|
411
|
+
**Recovered at**: $(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
412
|
+
**Recovered by**: /recover-orphans
|
|
413
|
+
|
|
414
|
+
Issue returned to unworked state. Run \`/work-on ${NUM}\` to restart the pipeline." \
|
|
415
|
+
2>/dev/null || true
|
|
416
|
+
echo " Done: #$NUM labels reset to unworked"
|
|
417
|
+
fi
|
|
418
|
+
RECOVERY_RESULTS="${RECOVERY_RESULTS}| #${NUM} | reset-labels | No progress found — labels cleared |\n"
|
|
419
|
+
;;
|
|
420
|
+
|
|
421
|
+
escalate-ci)
|
|
422
|
+
# CI failed on open PR — add needs-human
|
|
423
|
+
echo " Escalating: PR #$PR_NUM has CI failures — adding needs-human label"
|
|
424
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
425
|
+
echo " [DRY-RUN] Would: gh issue edit $NUM --add-label needs-human; gh issue comment with CI failure info"
|
|
426
|
+
else
|
|
427
|
+
gh issue edit "$NUM" ${GH_FLAG} --add-label "needs-human" 2>/dev/null || true
|
|
428
|
+
gh issue comment "$NUM" ${GH_FLAG} \
|
|
429
|
+
--body "<!-- FORGE:ORPHAN_ESCALATED -->
|
|
430
|
+
## Orphan Recovery: Escalated (CI Failure)
|
|
431
|
+
|
|
432
|
+
**PR**: #${PR_NUM}
|
|
433
|
+
**Reason**: CI checks failed on the open PR. Automated recovery cannot proceed past a CI failure.
|
|
434
|
+
**Escalated at**: $(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
435
|
+
|
|
436
|
+
**Next steps**:
|
|
437
|
+
1. Review CI failures: \`gh pr view ${PR_NUM} --web\`
|
|
438
|
+
2. Fix failing checks, then run \`/work-on ${NUM}\` to resume
|
|
439
|
+
3. Or run \`/fix-ci ${NUM}\` if that command is available" \
|
|
440
|
+
2>/dev/null || true
|
|
441
|
+
fi
|
|
442
|
+
RECOVERY_RESULTS="${RECOVERY_RESULTS}| #${NUM} | escalate-ci | PR #${PR_NUM} CI failed — needs-human added |\n"
|
|
443
|
+
;;
|
|
444
|
+
|
|
445
|
+
escalate-changes)
|
|
446
|
+
# Reviewer requested changes — add needs-human
|
|
447
|
+
echo " Escalating: PR #$PR_NUM has requested changes — needs human review"
|
|
448
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
449
|
+
echo " [DRY-RUN] Would: gh issue edit $NUM --add-label needs-human"
|
|
450
|
+
else
|
|
451
|
+
gh issue edit "$NUM" ${GH_FLAG} --add-label "needs-human" 2>/dev/null || true
|
|
452
|
+
gh issue comment "$NUM" ${GH_FLAG} \
|
|
453
|
+
--body "<!-- FORGE:ORPHAN_ESCALATED -->
|
|
454
|
+
## Orphan Recovery: Escalated (Changes Requested)
|
|
455
|
+
|
|
456
|
+
**PR**: #${PR_NUM}
|
|
457
|
+
**Reason**: A reviewer has requested changes on PR #${PR_NUM}. Human review of the requested changes is required.
|
|
458
|
+
**Escalated at**: $(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
459
|
+
|
|
460
|
+
**Next steps**:
|
|
461
|
+
1. Review requested changes: \`gh pr view ${PR_NUM} --web\`
|
|
462
|
+
2. Address the feedback, then remove \`needs-human\` label to allow pipeline to continue" \
|
|
463
|
+
2>/dev/null || true
|
|
464
|
+
fi
|
|
465
|
+
RECOVERY_RESULTS="${RECOVERY_RESULTS}| #${NUM} | escalate-changes | PR #${PR_NUM} reviewer requested changes |\n"
|
|
466
|
+
;;
|
|
467
|
+
|
|
468
|
+
esac
|
|
469
|
+
done
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
---
|
|
473
|
+
|
|
474
|
+
## Phase 4: Worktree Cleanup
|
|
475
|
+
|
|
476
|
+
Prune orphaned worktrees — those with no corresponding open PR. Follows the same safe pattern as `/cleanup branches`.
|
|
477
|
+
|
|
478
|
+
```bash
|
|
479
|
+
echo ""
|
|
480
|
+
echo "=== Phase 4: Worktree Cleanup ==="
|
|
481
|
+
|
|
482
|
+
if [ -z "$WORKTREE_BASE" ] || [ ! -d "$WORKTREE_BASE" ]; then
|
|
483
|
+
echo "WORKTREE_BASE not configured or directory not found — skipping worktree cleanup"
|
|
484
|
+
else
|
|
485
|
+
cd "$REPO_PATH" 2>/dev/null || true
|
|
486
|
+
|
|
487
|
+
WORKTREE_REMOVED=0
|
|
488
|
+
WORKTREE_KEPT=0
|
|
489
|
+
|
|
490
|
+
while IFS= read -r WT_PATH; do
|
|
491
|
+
# Skip the main worktree (repo root)
|
|
492
|
+
[ "$WT_PATH" = "$REPO_PATH" ] && continue
|
|
493
|
+
# Only manage worktrees under WORKTREE_BASE
|
|
494
|
+
case "$WT_PATH" in
|
|
495
|
+
"$WORKTREE_BASE"*) : ;;
|
|
496
|
+
*) continue ;;
|
|
497
|
+
esac
|
|
498
|
+
|
|
499
|
+
BRANCH=$(git -C "$WT_PATH" branch --show-current 2>/dev/null || echo "")
|
|
500
|
+
if [ -z "$BRANCH" ]; then
|
|
501
|
+
echo " SKIP: $WT_PATH — detached HEAD, leaving as-is"
|
|
502
|
+
WORKTREE_KEPT=$((WORKTREE_KEPT + 1))
|
|
503
|
+
continue
|
|
504
|
+
fi
|
|
505
|
+
|
|
506
|
+
# Check if branch has an open PR
|
|
507
|
+
OPEN_PR_COUNT=$(gh pr list ${GH_FLAG} --head "$BRANCH" --state open --json number --jq 'length' 2>/dev/null || echo "0")
|
|
508
|
+
|
|
509
|
+
# Check if branch has a merged PR
|
|
510
|
+
MERGED_PR=$(gh pr list ${GH_FLAG} --head "$BRANCH" --state merged --json number --jq '.[0].number' 2>/dev/null || echo "")
|
|
511
|
+
|
|
512
|
+
if [ "${OPEN_PR_COUNT:-0}" -gt 0 ]; then
|
|
513
|
+
echo " KEEP: $WT_PATH (branch: $BRANCH) — has open PR"
|
|
514
|
+
WORKTREE_KEPT=$((WORKTREE_KEPT + 1))
|
|
515
|
+
elif [ -n "$MERGED_PR" ]; then
|
|
516
|
+
echo " STALE: $WT_PATH (branch: $BRANCH) — PR #$MERGED_PR merged"
|
|
517
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
518
|
+
echo " [DRY-RUN] Would: git worktree remove $WT_PATH --force"
|
|
519
|
+
echo " [DRY-RUN] Would: git branch -D $BRANCH"
|
|
520
|
+
else
|
|
521
|
+
git worktree remove "$WT_PATH" --force 2>/dev/null || true
|
|
522
|
+
git branch -D "$BRANCH" 2>/dev/null || true
|
|
523
|
+
echo " Removed worktree: $WT_PATH (PR #$MERGED_PR was merged)"
|
|
524
|
+
WORKTREE_REMOVED=$((WORKTREE_REMOVED + 1))
|
|
525
|
+
fi
|
|
526
|
+
else
|
|
527
|
+
echo " UNKNOWN: $WT_PATH (branch: $BRANCH) — no PR found, leaving as-is"
|
|
528
|
+
WORKTREE_KEPT=$((WORKTREE_KEPT + 1))
|
|
529
|
+
fi
|
|
530
|
+
done < <(git worktree list --porcelain 2>/dev/null | grep "^worktree " | sed 's/^worktree //')
|
|
531
|
+
|
|
532
|
+
echo "Worktree cleanup: removed=$WORKTREE_REMOVED kept=$WORKTREE_KEPT"
|
|
533
|
+
fi
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
---
|
|
537
|
+
|
|
538
|
+
## Phase 5: Report
|
|
539
|
+
|
|
540
|
+
Print a structured recovery report.
|
|
541
|
+
|
|
542
|
+
```bash
|
|
543
|
+
echo ""
|
|
544
|
+
echo "========================================"
|
|
545
|
+
echo " /recover-orphans — Recovery Report"
|
|
546
|
+
echo "========================================"
|
|
547
|
+
echo ""
|
|
548
|
+
echo "Scan timestamp: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
549
|
+
echo "Dry-run: $DRY_RUN"
|
|
550
|
+
echo "Orphans scanned: $ORPHAN_COUNT"
|
|
551
|
+
echo ""
|
|
552
|
+
echo "## Recovery Actions"
|
|
553
|
+
echo ""
|
|
554
|
+
echo "| Issue | Action | Outcome |"
|
|
555
|
+
echo "|-------|--------|---------|"
|
|
556
|
+
printf '%b' "$RECOVERY_RESULTS"
|
|
557
|
+
echo ""
|
|
558
|
+
echo "## Worktrees"
|
|
559
|
+
echo "See Phase 4 output above for worktree-specific actions."
|
|
560
|
+
echo ""
|
|
561
|
+
echo "## Next Steps"
|
|
562
|
+
echo ""
|
|
563
|
+
echo "For issues with action 'reset-labels':"
|
|
564
|
+
echo " Run /work-on <issue-number> to restart the pipeline from the beginning."
|
|
565
|
+
echo ""
|
|
566
|
+
echo "For issues with action 'escalate-ci' or 'escalate-changes':"
|
|
567
|
+
echo " Address the manual intervention required, then remove the needs-human label."
|
|
568
|
+
echo ""
|
|
569
|
+
echo "For issues with action 'create-pr':"
|
|
570
|
+
echo " /work-on has been re-invoked — monitor the issue for workflow:in-review label."
|
|
571
|
+
echo ""
|
|
572
|
+
|
|
573
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
574
|
+
echo "DRY-RUN MODE: No changes were made. Remove --dry-run to apply recovery actions."
|
|
575
|
+
fi
|
|
576
|
+
```
|
package/commands/replay.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Read-only playback of a completed pipeline run — shows FORGE annotations phase by phase with timestamps
|
|
3
3
|
argument-hint: <issue-number>
|
|
4
|
+
install: extras
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
# /replay — Pipeline Run Playback
|
|
@@ -9,7 +10,7 @@ argument-hint: <issue-number>
|
|
|
9
10
|
|
|
10
11
|
Replay a completed pipeline run. Fetches all FORGE annotations posted during the pipeline and displays them chronologically — phase headings, timestamps, and key structured fields. Read-only: nothing is written, no labels changed.
|
|
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
|
|
|
14
15
|
---
|
|
15
16
|
|