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.
Files changed (53) hide show
  1. package/README.md +8 -2
  2. package/bin/engine/phases.mjs +178 -28
  3. package/bin/engine/state.mjs +39 -7
  4. package/bin/engine-cli.mjs +63 -2
  5. package/bin/engine.mjs +25 -4
  6. package/bin/forgedock.mjs +235 -4
  7. package/bin/hooks/pre-tool-use.mjs +26 -12
  8. package/bin/journey.mjs +103 -11
  9. package/commands/forge-stats.md +5 -4
  10. package/commands/issue.md +5 -1
  11. package/commands/orchestrate/phase-4-execution.md +2 -0
  12. package/commands/qa-sweep.md +17 -0
  13. package/commands/replay.md +16 -2
  14. package/commands/review-pr-agents/spec-cli.md +179 -0
  15. package/commands/review-pr.md +49 -0
  16. package/commands/upgrade-deps.md +518 -0
  17. package/commands/work-on/build/validate.md +61 -1
  18. package/commands/work-on/build.md +9 -1
  19. package/commands/work-on/decompose.md +8 -1
  20. package/docs/AIDER.md +264 -0
  21. package/docs/AUTOPILOT-CRON.md +130 -0
  22. package/docs/CONFIG.md +36 -0
  23. package/docs/CURSOR.md +135 -0
  24. package/docs/OPENCODE.md +312 -0
  25. package/docs/demo.gif +0 -0
  26. package/docs/eval/README.md +4 -4
  27. package/docs/launch/show-hn.md +3 -3
  28. package/docs/site/architecture.md +180 -0
  29. package/docs/site/command-reference.md +17 -0
  30. package/docs/site/configuration.md +243 -0
  31. package/docs/site/for-companies.md +4 -4
  32. package/docs/site/forge-annotation-protocol.md +1 -1
  33. package/docs/site/github-is-the-memory.md +2 -2
  34. package/docs/site/how-it-works.md +1 -1
  35. package/docs/site/public/llms-full.txt +380 -0
  36. package/docs/site/public/llms.txt +35 -0
  37. package/docs/spec-graph-schema.md +3 -3
  38. package/package.json +2 -2
  39. package/packages/protocol/README.md +75 -0
  40. package/packages/protocol/src/card.js +114 -0
  41. package/packages/protocol/src/cli.js +4 -77
  42. package/packages/protocol/src/emit.js +14 -3
  43. package/packages/protocol/src/index.js +2 -1
  44. package/packages/protocol/src/parse.js +17 -8
  45. package/packages/protocol/src/types.js +1 -1
  46. package/packages/protocol/src/validate.js +11 -0
  47. package/scripts/check-command-side-effects.sh +261 -0
  48. package/scripts/check-spec-markers.sh +273 -0
  49. package/scripts/classify-lane.sh +29 -7
  50. package/scripts/forge-run.sh +322 -0
  51. package/templates/cross-repo-trigger.yml +282 -0
  52. package/templates/cursor/forge.mdc +148 -0
  53. 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