forgedock 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -2
- package/bin/engine/phases.mjs +178 -28
- package/bin/engine/state.mjs +39 -7
- package/bin/engine-cli.mjs +63 -2
- package/bin/engine.mjs +25 -4
- package/bin/forgedock.mjs +235 -4
- package/bin/hooks/pre-tool-use.mjs +26 -12
- package/bin/journey.mjs +103 -11
- package/commands/forge-stats.md +5 -4
- package/commands/issue.md +5 -1
- package/commands/orchestrate/phase-4-execution.md +2 -0
- package/commands/qa-sweep.md +17 -0
- package/commands/replay.md +16 -2
- package/commands/review-pr-agents/spec-cli.md +179 -0
- package/commands/review-pr.md +49 -0
- package/commands/upgrade-deps.md +518 -0
- package/commands/work-on/build/validate.md +61 -1
- package/commands/work-on/build.md +9 -1
- package/commands/work-on/decompose.md +8 -1
- package/docs/AIDER.md +264 -0
- package/docs/AUTOPILOT-CRON.md +130 -0
- package/docs/CONFIG.md +36 -0
- package/docs/CURSOR.md +135 -0
- package/docs/OPENCODE.md +312 -0
- package/docs/demo.gif +0 -0
- package/docs/eval/README.md +4 -4
- package/docs/launch/show-hn.md +3 -3
- package/docs/site/architecture.md +180 -0
- package/docs/site/command-reference.md +17 -0
- package/docs/site/configuration.md +243 -0
- package/docs/site/for-companies.md +4 -4
- package/docs/site/forge-annotation-protocol.md +1 -1
- package/docs/site/github-is-the-memory.md +2 -2
- package/docs/site/how-it-works.md +1 -1
- package/docs/site/public/llms-full.txt +380 -0
- package/docs/site/public/llms.txt +35 -0
- package/docs/spec-graph-schema.md +3 -3
- package/package.json +2 -2
- package/packages/protocol/README.md +75 -0
- package/packages/protocol/src/card.js +114 -0
- package/packages/protocol/src/cli.js +4 -77
- package/packages/protocol/src/emit.js +14 -3
- package/packages/protocol/src/index.js +2 -1
- package/packages/protocol/src/parse.js +17 -8
- package/packages/protocol/src/types.js +1 -1
- package/packages/protocol/src/validate.js +11 -0
- package/scripts/check-command-side-effects.sh +261 -0
- package/scripts/check-spec-markers.sh +273 -0
- package/scripts/classify-lane.sh +29 -7
- package/scripts/forge-run.sh +322 -0
- package/templates/cross-repo-trigger.yml +282 -0
- package/templates/cursor/forge.mdc +148 -0
- package/templates/github-actions/forgedock-autopilot.yml +113 -0
|
@@ -0,0 +1,518 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Autonomous dependency upgrade pipeline — detect outdated dependencies, create upgrade issues, and run /work-on to investigate, build, test, and merge each upgrade
|
|
3
|
+
argument-hint: [--dry-run | --ecosystem npm|pip|cargo | --allow-major | --limit N | --batch]
|
|
4
|
+
---
|
|
5
|
+
<!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
|
|
6
|
+
<!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
|
|
7
|
+
|
|
8
|
+
# /upgrade-deps — Autonomous Dependency Upgrade Pipeline
|
|
9
|
+
|
|
10
|
+
**Input**: $ARGUMENTS
|
|
11
|
+
|
|
12
|
+
You are a dependency upgrade agent. Your job is to detect outdated packages, triage them by semver risk level, create GitHub issues for eligible upgrades, and run them through the full `/work-on` pipeline — so each upgrade gets investigation, compatibility verification, quality gate, review, and merge — rather than a blind version bump PR.
|
|
13
|
+
|
|
14
|
+
**Agent model policy**: Default `model: "sonnet"`. If Sonnet is rate-limited, fall back to `model: "opus"`.
|
|
15
|
+
**NEVER use plan mode (EnterPlanMode).**
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Argument Parsing
|
|
20
|
+
|
|
21
|
+
| Flag | Effect |
|
|
22
|
+
|------|--------|
|
|
23
|
+
| (none) | Detect + triage + report (no issue creation, no fixing) |
|
|
24
|
+
| `--dry-run` | Run all phases but do NOT create issues, PRs, or labels — report only |
|
|
25
|
+
| `--ecosystem npm` | Limit detection to npm only |
|
|
26
|
+
| `--ecosystem pip` | Limit detection to pip only |
|
|
27
|
+
| `--ecosystem cargo` | Limit detection to cargo only |
|
|
28
|
+
| `--allow-major` | Include major upgrades in issue creation (default: flag for human review only) |
|
|
29
|
+
| `--limit N` | Cap issue creation to at most N issues per run (default: 10) |
|
|
30
|
+
| `--batch` | Group all patch upgrades into one batch issue instead of one-per-package |
|
|
31
|
+
| `--fix` | After issue creation, also invoke `/work-on` on each created issue |
|
|
32
|
+
|
|
33
|
+
Parse `$ARGUMENTS`:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
DRY_RUN=false
|
|
37
|
+
ECOSYSTEM_FILTER=""
|
|
38
|
+
ALLOW_MAJOR=false
|
|
39
|
+
ISSUE_LIMIT=10
|
|
40
|
+
BATCH_PATCHES=false
|
|
41
|
+
DO_FIX=false
|
|
42
|
+
NEXT_IS_ECOSYSTEM=false
|
|
43
|
+
NEXT_IS_LIMIT=false
|
|
44
|
+
|
|
45
|
+
for arg in $ARGUMENTS; do
|
|
46
|
+
case "$arg" in
|
|
47
|
+
--dry-run) DRY_RUN=true ;;
|
|
48
|
+
--allow-major) ALLOW_MAJOR=true ;;
|
|
49
|
+
--batch) BATCH_PATCHES=true ;;
|
|
50
|
+
--fix) DO_FIX=true ;;
|
|
51
|
+
--ecosystem) NEXT_IS_ECOSYSTEM=true ;;
|
|
52
|
+
--limit) NEXT_IS_LIMIT=true ;;
|
|
53
|
+
*)
|
|
54
|
+
if [ "$NEXT_IS_ECOSYSTEM" = "true" ]; then
|
|
55
|
+
ECOSYSTEM_FILTER="$arg"
|
|
56
|
+
NEXT_IS_ECOSYSTEM=false
|
|
57
|
+
elif [ "$NEXT_IS_LIMIT" = "true" ]; then
|
|
58
|
+
ISSUE_LIMIT="$arg"
|
|
59
|
+
NEXT_IS_LIMIT=false
|
|
60
|
+
fi
|
|
61
|
+
;;
|
|
62
|
+
esac
|
|
63
|
+
done
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
If `DRY_RUN=true`, prefix all actions with `[DRY RUN]` and skip all `gh issue create`, `gh issue edit`, and `Skill()` invocations — report what would happen instead.
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Phase 0: Config Resolution
|
|
71
|
+
|
|
72
|
+
**Config variables used by this command** (set in `forge.yaml → upgrade_deps`):
|
|
73
|
+
- `{ECOSYSTEMS}` ← `upgrade_deps.ecosystems` — list of ecosystems to check (default: `["npm"]`)
|
|
74
|
+
- `{ALLOW_MAJOR_CONFIG}` ← `upgrade_deps.allow_major` (default: `false`) — whether to create issues for major upgrades
|
|
75
|
+
- `{EXCLUDE_PACKAGES}` ← `upgrade_deps.exclude` — list of package names to skip regardless of semver level
|
|
76
|
+
- `{ISSUE_BATCH_SIZE}` ← `upgrade_deps.issue_batch_size` — how many patch packages to group per batch issue (default: 5)
|
|
77
|
+
|
|
78
|
+
Read `forge.yaml` at the project root before running any phase:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
CONFIG_FILE="${FORGE_CONFIG:-forge.yaml}"
|
|
82
|
+
if [ ! -f "$CONFIG_FILE" ]; then
|
|
83
|
+
echo "ERROR: forge.yaml not found. Run: npx forgedock init && /forgedock-init"
|
|
84
|
+
exit 1
|
|
85
|
+
fi
|
|
86
|
+
|
|
87
|
+
GH_REPO=$(yq '.project.owner + "/" + .project.repo' "$CONFIG_FILE")
|
|
88
|
+
GH_FLAG="-R $GH_REPO"
|
|
89
|
+
REPO_PATH=$(yq '.paths.root' "$CONFIG_FILE")
|
|
90
|
+
STAGING_BRANCH=$(yq '.branches.staging // "staging"' "$CONFIG_FILE")
|
|
91
|
+
|
|
92
|
+
# upgrade_deps section (all optional — fall back to defaults)
|
|
93
|
+
ECOSYSTEMS_CONFIG=$(yq '.upgrade_deps.ecosystems // ["npm"] | join(" ")' "$CONFIG_FILE" 2>/dev/null || echo "npm")
|
|
94
|
+
ALLOW_MAJOR_CONFIG=$(yq '.upgrade_deps.allow_major // false' "$CONFIG_FILE" 2>/dev/null || echo "false")
|
|
95
|
+
EXCLUDE_PACKAGES=$(yq '.upgrade_deps.exclude // [] | join(" ")' "$CONFIG_FILE" 2>/dev/null || echo "")
|
|
96
|
+
ISSUE_BATCH_SIZE=$(yq '.upgrade_deps.issue_batch_size // 5' "$CONFIG_FILE" 2>/dev/null || echo "5")
|
|
97
|
+
|
|
98
|
+
# CLI flags override config values
|
|
99
|
+
[ "$ECOSYSTEM_FILTER" != "" ] && ECOSYSTEMS_CONFIG="$ECOSYSTEM_FILTER"
|
|
100
|
+
[ "$ALLOW_MAJOR" = "true" ] && ALLOW_MAJOR_CONFIG="true"
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Phase 1: Detect Outdated Dependencies
|
|
106
|
+
|
|
107
|
+
Run dependency detection for each configured ecosystem. Skip an ecosystem if its tool is not installed.
|
|
108
|
+
|
|
109
|
+
### 1A: npm
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
if echo "$ECOSYSTEMS_CONFIG" | grep -q "npm"; then
|
|
113
|
+
if command -v npm >/dev/null 2>&1; then
|
|
114
|
+
echo "--- npm ecosystem ---"
|
|
115
|
+
cd "$REPO_PATH"
|
|
116
|
+
NPM_OUTDATED=$(npm outdated --json 2>/dev/null || echo "{}")
|
|
117
|
+
echo "$NPM_OUTDATED" | python3 -c "
|
|
118
|
+
import json, sys
|
|
119
|
+
data = json.load(sys.stdin)
|
|
120
|
+
for pkg, info in data.items():
|
|
121
|
+
current = info.get('current', 'N/A')
|
|
122
|
+
wanted = info.get('wanted', 'N/A')
|
|
123
|
+
latest = info.get('latest', 'N/A')
|
|
124
|
+
print(f'{pkg}|{current}|{wanted}|{latest}')
|
|
125
|
+
" 2>/dev/null || true
|
|
126
|
+
else
|
|
127
|
+
echo "npm not installed — skipping npm ecosystem"
|
|
128
|
+
fi
|
|
129
|
+
fi
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### 1B: pip
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
if echo "$ECOSYSTEMS_CONFIG" | grep -q "pip"; then
|
|
136
|
+
PIP_CMD=""
|
|
137
|
+
command -v pip3 >/dev/null 2>&1 && PIP_CMD="pip3"
|
|
138
|
+
command -v pip >/dev/null 2>&1 && PIP_CMD="${PIP_CMD:-pip}"
|
|
139
|
+
|
|
140
|
+
if [ -n "$PIP_CMD" ]; then
|
|
141
|
+
echo "--- pip ecosystem ---"
|
|
142
|
+
$PIP_CMD list --outdated --format=json 2>/dev/null | python3 -c "
|
|
143
|
+
import json, sys
|
|
144
|
+
data = json.load(sys.stdin)
|
|
145
|
+
for pkg in data:
|
|
146
|
+
print(f'{pkg[\"name\"]}|{pkg[\"version\"]}|{pkg[\"latest_version\"]}')
|
|
147
|
+
" 2>/dev/null || true
|
|
148
|
+
else
|
|
149
|
+
echo "pip/pip3 not installed — skipping pip ecosystem"
|
|
150
|
+
fi
|
|
151
|
+
fi
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### 1C: cargo
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
if echo "$ECOSYSTEMS_CONFIG" | grep -q "cargo"; then
|
|
158
|
+
if command -v cargo >/dev/null 2>&1 && command -v cargo-outdated >/dev/null 2>&1; then
|
|
159
|
+
echo "--- cargo ecosystem ---"
|
|
160
|
+
cd "$REPO_PATH"
|
|
161
|
+
cargo outdated --format json 2>/dev/null | python3 -c "
|
|
162
|
+
import json, sys
|
|
163
|
+
try:
|
|
164
|
+
data = json.load(sys.stdin)
|
|
165
|
+
for dep in data.get('dependencies', []):
|
|
166
|
+
name = dep.get('name', '')
|
|
167
|
+
project = dep.get('project', 'N/A')
|
|
168
|
+
latest = dep.get('latest', 'N/A')
|
|
169
|
+
print(f'{name}|{project}|{latest}')
|
|
170
|
+
except: pass
|
|
171
|
+
" 2>/dev/null || true
|
|
172
|
+
else
|
|
173
|
+
command -v cargo >/dev/null 2>&1 || echo "cargo not installed — skipping cargo ecosystem"
|
|
174
|
+
command -v cargo-outdated >/dev/null 2>&1 || echo "cargo-outdated not installed — run: cargo install cargo-outdated"
|
|
175
|
+
fi
|
|
176
|
+
fi
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### 1D: Aggregate results
|
|
180
|
+
|
|
181
|
+
Collect all detected outdated packages into a structured list:
|
|
182
|
+
|
|
183
|
+
```
|
|
184
|
+
OUTDATED_PACKAGES = [
|
|
185
|
+
{ name, current_version, latest_version, semver_level, ecosystem }
|
|
186
|
+
]
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Compute `semver_level` for each package:
|
|
190
|
+
- **patch**: major and minor match (e.g., 1.2.3 → 1.2.9)
|
|
191
|
+
- **minor**: major matches, minor differs (e.g., 1.2.3 → 1.5.0)
|
|
192
|
+
- **major**: major version differs (e.g., 1.x.x → 2.x.x)
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## Phase 2: Triage
|
|
197
|
+
|
|
198
|
+
**Goal**: Filter, classify, and rank the detected upgrades.
|
|
199
|
+
|
|
200
|
+
### 2A: Apply exclusions
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
# Filter out packages in the EXCLUDE_PACKAGES list
|
|
204
|
+
for pkg in $EXCLUDE_PACKAGES; do
|
|
205
|
+
echo "Skipping excluded package: $pkg"
|
|
206
|
+
# Remove from OUTDATED_PACKAGES
|
|
207
|
+
done
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### 2B: Apply semver gate
|
|
211
|
+
|
|
212
|
+
- **patch** and **minor**: eligible for issue creation (default behavior)
|
|
213
|
+
- **major**: eligible only if `ALLOW_MAJOR_CONFIG=true` or `--allow-major` was passed; otherwise, flag for human review
|
|
214
|
+
|
|
215
|
+
### 2C: Deduplication — skip if open issue already tracks this upgrade
|
|
216
|
+
|
|
217
|
+
Before creating any issue, check for existing open issues that already track the same package upgrade:
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
for pkg in ${ELIGIBLE_PACKAGES}; do
|
|
221
|
+
# Pass $pkg to jq via the environment (env.PKG) instead of splicing it into the
|
|
222
|
+
# jq program text — avoids breaking the outer quoting if $pkg ever contained a
|
|
223
|
+
# single quote or other shell-meaningful character.
|
|
224
|
+
export PKG="$pkg"
|
|
225
|
+
EXISTING=$(gh issue list $GH_FLAG --state open \
|
|
226
|
+
--search "upgrade $pkg" --limit 5 \
|
|
227
|
+
--json number,title \
|
|
228
|
+
--jq '.[] | select(.title | test("upgrade.*" + env.PKG; "i")) | .number' 2>/dev/null | head -1)
|
|
229
|
+
|
|
230
|
+
if [ -n "$EXISTING" ]; then
|
|
231
|
+
echo "Skipping $pkg — open issue #$EXISTING already tracks this upgrade"
|
|
232
|
+
# Add $pkg to SKIPPED list with reason "existing-issue:#$EXISTING"
|
|
233
|
+
fi
|
|
234
|
+
done
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### 2D: Apply limit
|
|
238
|
+
|
|
239
|
+
Cap the final eligible list to `$ISSUE_LIMIT` packages. Prioritize by semver impact:
|
|
240
|
+
1. minor upgrades (higher value)
|
|
241
|
+
2. patch upgrades (lower value, safe to batch)
|
|
242
|
+
|
|
243
|
+
If `BATCH_PATCHES=true`, group all patch-level packages into one batch issue (up to `$ISSUE_BATCH_SIZE` per batch issue).
|
|
244
|
+
|
|
245
|
+
### 2E: Major upgrade report (human review required)
|
|
246
|
+
|
|
247
|
+
For major upgrades when `ALLOW_MAJOR_CONFIG=false`, collect them for the summary report but do NOT create issues. Print:
|
|
248
|
+
|
|
249
|
+
```
|
|
250
|
+
Major upgrades detected (not auto-processed — require human review):
|
|
251
|
+
{package}: {current} → {latest} [major]
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## Phase 3: Issue Creation
|
|
257
|
+
|
|
258
|
+
For each eligible package (or batch), create a GitHub issue:
|
|
259
|
+
|
|
260
|
+
### 3A: Per-package issue (patch or minor, non-batched)
|
|
261
|
+
|
|
262
|
+
```bash
|
|
263
|
+
# DRY_RUN guard — must stay above the create it guards (forge#1609). In dry-run
|
|
264
|
+
# mode print the would-be issue and move on; never create it.
|
|
265
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
266
|
+
echo "[DRY RUN] Would create issue: feat(deps): upgrade {PACKAGE} from {CURRENT} to {LATEST} ({SEMVER_LEVEL})"
|
|
267
|
+
else
|
|
268
|
+
gh issue create $GH_FLAG \
|
|
269
|
+
--title "feat(deps): upgrade {PACKAGE} from {CURRENT} to {LATEST} ({SEMVER_LEVEL})" \
|
|
270
|
+
--label "enhancement" \
|
|
271
|
+
--body "$(cat <<'ISSUE_EOF'
|
|
272
|
+
## Problem
|
|
273
|
+
|
|
274
|
+
{PACKAGE} is outdated: current version {CURRENT}, latest is {LATEST} ({SEMVER_LEVEL} bump).
|
|
275
|
+
|
|
276
|
+
Dependency upgrades are repetitive, well-scoped work. This issue tracks the upgrade through the full ForgeDock pipeline: investigate compatibility, implement the upgrade, run tests, review, and merge.
|
|
277
|
+
|
|
278
|
+
## Affected Files
|
|
279
|
+
|
|
280
|
+
Files that may need changes:
|
|
281
|
+
1. `package.json` / `requirements.txt` / `Cargo.toml` — version constraint update
|
|
282
|
+
2. Lockfile (`package-lock.json` / `poetry.lock` / `Cargo.lock`) — regenerated by install command
|
|
283
|
+
|
|
284
|
+
Additional files will be identified if the new version has breaking changes requiring code updates.
|
|
285
|
+
|
|
286
|
+
## Expected Behavior
|
|
287
|
+
|
|
288
|
+
- Package is updated to {LATEST} (or the highest compatible version within {SEMVER_LEVEL} range)
|
|
289
|
+
- All existing tests pass
|
|
290
|
+
- No breaking changes introduced
|
|
291
|
+
|
|
292
|
+
## Acceptance Criteria
|
|
293
|
+
|
|
294
|
+
- [ ] `{PACKAGE}` version updated from `{CURRENT}` to `{LATEST}` in the manifest
|
|
295
|
+
- [ ] Lockfile regenerated (`npm install` / `pip install` / `cargo update`)
|
|
296
|
+
- [ ] Test suite passes (no regressions)
|
|
297
|
+
- [ ] Breaking changes identified and addressed (especially for minor upgrades)
|
|
298
|
+
- [ ] CHANGELOG or release notes consulted for deprecation notices
|
|
299
|
+
|
|
300
|
+
## Context
|
|
301
|
+
|
|
302
|
+
**Ecosystem**: {ECOSYSTEM}
|
|
303
|
+
**Semver level**: {SEMVER_LEVEL}
|
|
304
|
+
**Detected by**: `/upgrade-deps` cycle on {DATE}
|
|
305
|
+
**Release notes**: https://www.npmjs.com/package/{PACKAGE}/v/{LATEST} (adjust URL for ecosystem)
|
|
306
|
+
|
|
307
|
+
---
|
|
308
|
+
*Created by `/upgrade-deps`. Will be validated before any changes are applied.*
|
|
309
|
+
ISSUE_EOF
|
|
310
|
+
)"
|
|
311
|
+
fi
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### 3B: Batch patch issue (when `--batch` is set)
|
|
315
|
+
|
|
316
|
+
Group multiple patch-level packages into one issue:
|
|
317
|
+
|
|
318
|
+
```bash
|
|
319
|
+
BATCH_PACKAGE_LIST=$(printf '- `%s`: %s → %s\n' {PACKAGE} {CURRENT} {LATEST})
|
|
320
|
+
|
|
321
|
+
# DRY_RUN guard — must stay above the create it guards (forge#1609).
|
|
322
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
323
|
+
echo "[DRY RUN] Would create batch issue: feat(deps): batch patch upgrades — {COUNT} packages ({DATE})"
|
|
324
|
+
echo "[DRY RUN] Packages: $BATCH_PACKAGE_LIST"
|
|
325
|
+
else
|
|
326
|
+
gh issue create $GH_FLAG \
|
|
327
|
+
--title "feat(deps): batch patch upgrades — {COUNT} packages ({DATE})" \
|
|
328
|
+
--label "enhancement" \
|
|
329
|
+
--body "$(cat <<'BATCH_ISSUE_EOF'
|
|
330
|
+
## Problem
|
|
331
|
+
|
|
332
|
+
{COUNT} dependencies have patch-level updates available. Patch upgrades are low-risk (semver guarantees no breaking changes) and are batched here for efficiency.
|
|
333
|
+
|
|
334
|
+
## Packages in This Batch
|
|
335
|
+
|
|
336
|
+
{BATCH_PACKAGE_LIST}
|
|
337
|
+
|
|
338
|
+
## Affected Files
|
|
339
|
+
|
|
340
|
+
1. `package.json` / `requirements.txt` / `Cargo.toml` — version constraint updates
|
|
341
|
+
2. Lockfile — regenerated after all updates
|
|
342
|
+
|
|
343
|
+
## Expected Behavior
|
|
344
|
+
|
|
345
|
+
All listed packages updated to their latest patch version. Full test suite passes.
|
|
346
|
+
|
|
347
|
+
## Acceptance Criteria
|
|
348
|
+
|
|
349
|
+
- [ ] All listed packages updated in the manifest
|
|
350
|
+
- [ ] Lockfile regenerated
|
|
351
|
+
- [ ] Test suite passes with no regressions
|
|
352
|
+
|
|
353
|
+
## Context
|
|
354
|
+
|
|
355
|
+
**Ecosystem**: {ECOSYSTEM}
|
|
356
|
+
**Semver level**: patch (all)
|
|
357
|
+
**Detected by**: `/upgrade-deps` cycle on {DATE}
|
|
358
|
+
|
|
359
|
+
---
|
|
360
|
+
*Batch created by `/upgrade-deps`. Investigate step will verify each package's patch notes.*
|
|
361
|
+
BATCH_ISSUE_EOF
|
|
362
|
+
)"
|
|
363
|
+
fi
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
**DRY_RUN check**: enforced inline in 3A and 3B above — the guard wraps each `gh issue create` rather than being stated after it, so a dry run cannot create issues even if only one section is read in isolation (forge#1609).
|
|
367
|
+
|
|
368
|
+
Store created issue numbers as `CREATED_ISSUES` (empty when `DRY_RUN=true`).
|
|
369
|
+
|
|
370
|
+
---
|
|
371
|
+
|
|
372
|
+
## Phase 4: Fix (requires `--fix` flag)
|
|
373
|
+
|
|
374
|
+
**Skip unless `--fix` was passed.**
|
|
375
|
+
|
|
376
|
+
For each issue in `CREATED_ISSUES`, invoke `/work-on` via the Skill tool:
|
|
377
|
+
|
|
378
|
+
```
|
|
379
|
+
Skill(skill: "work-on", args: "{ISSUE_NUMBER}")
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
Run **sequentially** — each `/work-on` invocation is heavyweight. If one fails, continue to the next and record the outcome.
|
|
383
|
+
|
|
384
|
+
Track outcomes:
|
|
385
|
+
|
|
386
|
+
```
|
|
387
|
+
FIX_RESULTS = [
|
|
388
|
+
{ issue: NUMBER, outcome: "merged" | "invalid" | "failed" | "needs-human", pr: PR_NUMBER | null }
|
|
389
|
+
]
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
**DRY_RUN check**: If `DRY_RUN=true`, print what would be invoked but skip `Skill()` calls.
|
|
393
|
+
|
|
394
|
+
---
|
|
395
|
+
|
|
396
|
+
## Phase 5: Summary Report
|
|
397
|
+
|
|
398
|
+
Print a structured report at the end of every run:
|
|
399
|
+
|
|
400
|
+
```markdown
|
|
401
|
+
## /upgrade-deps Cycle Report — {DATE}
|
|
402
|
+
|
|
403
|
+
### Detection Results
|
|
404
|
+
| Ecosystem | Outdated | Eligible | Skipped (existing issue) | Skipped (excluded) | Major (human review) |
|
|
405
|
+
|-----------|----------|----------|--------------------------|-------------------|----------------------|
|
|
406
|
+
| npm | {N} | {N} | {N} | {N} | {N} |
|
|
407
|
+
| pip | {N} | {N} | {N} | {N} | {N} |
|
|
408
|
+
|
|
409
|
+
### Issues Created
|
|
410
|
+
{list with issue numbers and titles, or "None — use --fix=false dry-run or no --fix flag"}
|
|
411
|
+
|
|
412
|
+
### Major Upgrades (Human Review Required)
|
|
413
|
+
{list: package: current → latest [major], or "None detected"}
|
|
414
|
+
|
|
415
|
+
### Fix Results (only if --fix was passed)
|
|
416
|
+
| Issue | Package | Outcome | PR |
|
|
417
|
+
|-------|---------|---------|-----|
|
|
418
|
+
| #{N} | {pkg} | merged | #{PR} |
|
|
419
|
+
|
|
420
|
+
### Next Steps
|
|
421
|
+
- [ ] Review major upgrades above and create issues manually if appropriate
|
|
422
|
+
- [ ] Set `upgrade_deps.exclude` in `forge.yaml` for packages that should never auto-upgrade
|
|
423
|
+
- [ ] Run `/upgrade-deps --fix` to also execute the pipeline for created issues
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
---
|
|
427
|
+
|
|
428
|
+
## Scheduling via GitHub Actions
|
|
429
|
+
|
|
430
|
+
To run `/upgrade-deps` on a schedule, add a workflow to `.github/workflows/upgrade-deps.yml` in your project:
|
|
431
|
+
|
|
432
|
+
```yaml
|
|
433
|
+
# .github/workflows/upgrade-deps.yml
|
|
434
|
+
# Runs /upgrade-deps weekly — creates GitHub issues for outdated deps.
|
|
435
|
+
# Issues flow through the ForgeDock pipeline: investigate → build → review → merge.
|
|
436
|
+
name: Weekly Dependency Upgrade Check
|
|
437
|
+
|
|
438
|
+
on:
|
|
439
|
+
schedule:
|
|
440
|
+
- cron: '0 9 * * 1' # Every Monday at 09:00 UTC
|
|
441
|
+
workflow_dispatch: # Allow manual trigger
|
|
442
|
+
|
|
443
|
+
jobs:
|
|
444
|
+
upgrade-deps:
|
|
445
|
+
runs-on: ubuntu-latest
|
|
446
|
+
permissions:
|
|
447
|
+
contents: read
|
|
448
|
+
issues: write
|
|
449
|
+
|
|
450
|
+
steps:
|
|
451
|
+
- uses: actions/checkout@v4
|
|
452
|
+
|
|
453
|
+
- name: Set up Claude Code
|
|
454
|
+
run: npm install -g @anthropic-ai/claude-code
|
|
455
|
+
|
|
456
|
+
- name: Run /upgrade-deps
|
|
457
|
+
env:
|
|
458
|
+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
459
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
460
|
+
run: |
|
|
461
|
+
claude -p "/upgrade-deps --limit 5" --allowedTools "Bash,Read,Write,Edit,Glob,Grep"
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
> **Note**: The workflow above creates issues only (no `--fix`). To also invoke `/work-on` from CI, add `--fix` to the claude command. Each `/work-on` run uses Anthropic API credits proportional to issue complexity. Review your budget before enabling `--fix` in scheduled workflows.
|
|
465
|
+
|
|
466
|
+
---
|
|
467
|
+
|
|
468
|
+
## forge.yaml Configuration Reference
|
|
469
|
+
|
|
470
|
+
Add an `upgrade_deps` section to your `forge.yaml` to configure the command:
|
|
471
|
+
|
|
472
|
+
```yaml
|
|
473
|
+
# upgrade_deps (OPTIONAL)
|
|
474
|
+
# Controls /upgrade-deps behavior.
|
|
475
|
+
upgrade_deps:
|
|
476
|
+
# Ecosystems to check (default: ["npm"])
|
|
477
|
+
ecosystems:
|
|
478
|
+
- npm
|
|
479
|
+
- pip
|
|
480
|
+
# - cargo # requires: cargo install cargo-outdated
|
|
481
|
+
|
|
482
|
+
# Whether to create issues for major version upgrades (default: false)
|
|
483
|
+
# Major upgrades carry breaking-change risk — enable only with human review process.
|
|
484
|
+
allow_major: false
|
|
485
|
+
|
|
486
|
+
# Packages to never auto-upgrade (excluded from issue creation and /work-on)
|
|
487
|
+
# Useful for packages with known breaking changes or intentional version pins.
|
|
488
|
+
exclude:
|
|
489
|
+
# - "some-package" # example: pinned intentionally
|
|
490
|
+
|
|
491
|
+
# How many patch packages to group per batch issue when --batch is used (default: 5)
|
|
492
|
+
issue_batch_size: 5
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
---
|
|
496
|
+
|
|
497
|
+
## Safety Rules
|
|
498
|
+
|
|
499
|
+
1. **NEVER bump a major version without `--allow-major`** — major upgrades have breaking-change risk and require human review.
|
|
500
|
+
2. **NEVER bypass the deduplication check** — if an open issue already tracks an upgrade, skip it.
|
|
501
|
+
3. **NEVER create more than `ISSUE_LIMIT` issues per run** — prevents runaway issue flooding.
|
|
502
|
+
4. **NEVER skip `/work-on` investigation** — every upgrade goes through full pipeline (investigate compatibility, build, test, review). A blind version bump PR is not a valid outcome.
|
|
503
|
+
5. **DRY_RUN means NO side effects** — no issues created, no `/work-on` calls. Report only.
|
|
504
|
+
6. **NEVER run cargo-outdated if cargo-outdated is not installed** — detect tool availability first.
|
|
505
|
+
7. **NEVER upgrade excluded packages** — respect the `upgrade_deps.exclude` list in `forge.yaml`.
|
|
506
|
+
|
|
507
|
+
---
|
|
508
|
+
|
|
509
|
+
## Integration with /autopilot
|
|
510
|
+
|
|
511
|
+
`/upgrade-deps` is a peer to `/autopilot` — both create issues and optionally invoke `/work-on`. They are complementary:
|
|
512
|
+
|
|
513
|
+
| Command | Signal source | Scope |
|
|
514
|
+
|---------|--------------|-------|
|
|
515
|
+
| `/autopilot` | Production health, CI failures, backlog | Platform operational health |
|
|
516
|
+
| `/upgrade-deps` | Package registries (npm, pip, cargo) | Dependency freshness |
|
|
517
|
+
|
|
518
|
+
`/autopilot` does NOT detect outdated dependencies. Use `/upgrade-deps` for dependency-specific work, and pair it with `/autopilot` for full-spectrum platform maintenance.
|
|
@@ -254,6 +254,65 @@ fi
|
|
|
254
254
|
|
|
255
255
|
---
|
|
256
256
|
|
|
257
|
+
## Phase V3.6: Browser Signal Check
|
|
258
|
+
|
|
259
|
+
**Skip if**: No TypeScript/TSX files were changed, OR `forge.yaml → services.app_url` is absent or empty.
|
|
260
|
+
|
|
261
|
+
After static proxy checks, run a lightweight live browser check using Playwright MCP tools to surface console errors, failed network requests, and basic performance metrics for any changed UI routes. This check is advisory — findings are surfaced as warnings in the V5 summary but do NOT block the gate unless the browser session is available AND returns ERROR-level console output.
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
cd {WORKTREE_PATH}
|
|
265
|
+
APP_URL=$(yq '.services.app_url // ""' forge.yaml 2>/dev/null || echo '')
|
|
266
|
+
if [ -z "$APP_URL" ]; then
|
|
267
|
+
echo "SKIPPED — services.app_url not configured in forge.yaml (browser signal check requires a running app URL)"
|
|
268
|
+
else
|
|
269
|
+
echo "BROWSER SIGNAL CHECK: navigating $APP_URL"
|
|
270
|
+
fi
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
**When APP_URL is configured**, perform the following using Playwright MCP tools:
|
|
274
|
+
|
|
275
|
+
**Step 1 — Navigate**
|
|
276
|
+
Use `browser_navigate` to load `{APP_URL}`. If the changed files include a specific page route (e.g., `web/src/app/dashboard/page.tsx`), derive the route path and navigate there instead (e.g., `{APP_URL}/dashboard`).
|
|
277
|
+
|
|
278
|
+
**Step 2 — Capture console messages**
|
|
279
|
+
```
|
|
280
|
+
browser_console_messages
|
|
281
|
+
```
|
|
282
|
+
Classify findings:
|
|
283
|
+
- Any message at `error` level → **MEDIUM** finding: `BROWSER-CONSOLE-ERROR | MEDIUM | console | {message}`
|
|
284
|
+
- Any message at `warn` level → **LOW** advisory: `BROWSER-CONSOLE-WARN | LOW | console | {message}`
|
|
285
|
+
- Ignore `info` and `log` levels
|
|
286
|
+
|
|
287
|
+
**Step 3 — Capture network failures**
|
|
288
|
+
```
|
|
289
|
+
browser_network_requests filter="static:false"
|
|
290
|
+
```
|
|
291
|
+
Check for HTTP 4xx and 5xx responses on non-static requests. Exclude known third-party analytics/tracking domains.
|
|
292
|
+
- HTTP 4xx or 5xx response → **HIGH** finding: `BROWSER-NETWORK-FAIL | HIGH | network | {url} returned {status}`
|
|
293
|
+
|
|
294
|
+
**Step 4 — Capture performance metrics (LCP-ish)**
|
|
295
|
+
```
|
|
296
|
+
browser_evaluate function="() => {
|
|
297
|
+
const nav = performance.getEntriesByType('navigation')[0];
|
|
298
|
+
const paint = performance.getEntriesByType('paint');
|
|
299
|
+
const fcp = paint.find(e => e.name === 'first-contentful-paint');
|
|
300
|
+
return {
|
|
301
|
+
domContentLoaded: nav ? Math.round(nav.domContentLoadedEventEnd) : null,
|
|
302
|
+
loadTime: nav ? Math.round(nav.loadEventEnd) : null,
|
|
303
|
+
fcp: fcp ? Math.round(fcp.startTime) : null
|
|
304
|
+
};
|
|
305
|
+
}"
|
|
306
|
+
```
|
|
307
|
+
Classify:
|
|
308
|
+
- `loadTime > 4000` ms → **HIGH** finding: `BROWSER-PERF | HIGH | performance | page load time {loadTime}ms exceeds 4s threshold`
|
|
309
|
+
- `loadTime > 2500` ms → **MEDIUM** finding: `BROWSER-PERF | MEDIUM | performance | page load time {loadTime}ms exceeds 2.5s threshold`
|
|
310
|
+
- `fcp > 1800` ms → **LOW** advisory: `BROWSER-PERF | LOW | performance | FCP {fcp}ms — consider lazy-loading or code splitting`
|
|
311
|
+
|
|
312
|
+
**Advisory scope**: Browser signal findings are included in the V5 summary under "Browser Signals". They do NOT block the gate (GATE_PASSED stays true) unless a BROWSER-NETWORK-FAIL HIGH finding is present on the primary app URL (indicating the app is completely broken for that route). Console ERROR findings are MEDIUM — surfaced for human review, not blocking.
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
257
316
|
## Phase V4: Deployment Completeness Check
|
|
258
317
|
|
|
259
318
|
**Skip if**: No new environment variables were introduced in the changed files.
|
|
@@ -398,9 +457,10 @@ This module runs at **Step 3F.5** — after implement, before commit (3J) and PR
|
|
|
398
457
|
|
|
399
458
|
```
|
|
400
459
|
3F → Implement (by implement.md) — code written, staged (not committed)
|
|
401
|
-
3F.5 → [THIS MODULE] Validate — gate loop, format, proxy, deploy checks
|
|
460
|
+
3F.5 → [THIS MODULE] Validate — gate loop, format, proxy, browser signals, deploy checks
|
|
402
461
|
3G → (covered by Phase V2 above)
|
|
403
462
|
3H → (covered by Phase V3 above)
|
|
463
|
+
3H.5 → (covered by Phase V3.6 above — browser signal check for UI changes)
|
|
404
464
|
3I → (covered by Phase V4 above)
|
|
405
465
|
3J → V5 commit happens here after GATE_PASSED=true (single commit for implementation + any fixes)
|
|
406
466
|
```
|
|
@@ -154,6 +154,13 @@ gh issue edit {NUMBER} {GH_FLAG} \
|
|
|
154
154
|
|
|
155
155
|
Post `<!-- FORGE:CONTRACT -->` comment documenting what will be built and why:
|
|
156
156
|
|
|
157
|
+
**Before posting, read the attribution config**:
|
|
158
|
+
```bash
|
|
159
|
+
SHOW_ATTRIBUTION=$(yq '.branding.show_attribution // "true"' forge.yaml 2>/dev/null || echo "true")
|
|
160
|
+
[ "$SHOW_ATTRIBUTION" = "false" ] && ATTRIBUTION_LINE="" || ATTRIBUTION_LINE="
|
|
161
|
+
> Pipeline powered by [ForgeDock](https://github.com/RapierCraftStudios/ForgeDock)"
|
|
162
|
+
```
|
|
163
|
+
|
|
157
164
|
```bash
|
|
158
165
|
gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:CONTRACT -->
|
|
159
166
|
## Builder Contract
|
|
@@ -180,7 +187,8 @@ gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:CONTRACT -->
|
|
|
180
187
|
|
|
181
188
|
### Out of Scope
|
|
182
189
|
|
|
183
|
-
{OUT_OF_SCOPE_ITEMS}
|
|
190
|
+
{OUT_OF_SCOPE_ITEMS}
|
|
191
|
+
${ATTRIBUTION_LINE}"
|
|
184
192
|
```
|
|
185
193
|
|
|
186
194
|
Contract must be grounded in the investigation report. Every deliverable file must appear in the affected files list from the investigator. Adversarially validate the proposed fix against adjacent system layers before posting.
|
|
@@ -218,6 +218,13 @@ gh issue edit {NUMBER} {GH_FLAG} --body "${CURRENT_BODY}${TRACKER}"
|
|
|
218
218
|
|
|
219
219
|
## Phase D5: Post FORGE:DECOMPOSED Comment
|
|
220
220
|
|
|
221
|
+
**Before posting, read the attribution config**:
|
|
222
|
+
```bash
|
|
223
|
+
SHOW_ATTRIBUTION=$(yq '.branding.show_attribution // "true"' forge.yaml 2>/dev/null || echo "true")
|
|
224
|
+
[ "$SHOW_ATTRIBUTION" = "false" ] && ATTRIBUTION_LINE="" || ATTRIBUTION_LINE="
|
|
225
|
+
> Pipeline powered by [ForgeDock](https://github.com/RapierCraftStudios/ForgeDock)"
|
|
226
|
+
```
|
|
227
|
+
|
|
221
228
|
```bash
|
|
222
229
|
gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:DECOMPOSED -->
|
|
223
230
|
## Decomposition Complete
|
|
@@ -232,7 +239,7 @@ This issue has been broken into sub-issues. Each sub-issue runs through its own
|
|
|
232
239
|
### Decomposition Rationale
|
|
233
240
|
|
|
234
241
|
{brief summary of why this issue was decomposed and the dependency ordering chosen}
|
|
235
|
-
|
|
242
|
+
${ATTRIBUTION_LINE}
|
|
236
243
|
<!-- FORGE:DECOMPOSED:COMPLETE -->"
|
|
237
244
|
```
|
|
238
245
|
|