@entelligentsia/forgecli 0.6.6 → 0.7.6

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 (183) hide show
  1. package/CHANGELOG.md +88 -0
  2. package/README.md +28 -1
  3. package/dist/bin/forge.js +20 -0
  4. package/dist/bin/forge.js.map +1 -1
  5. package/dist/extensions/forgecli/approve.d.ts +24 -0
  6. package/dist/extensions/forgecli/approve.js +202 -0
  7. package/dist/extensions/forgecli/approve.js.map +1 -0
  8. package/dist/extensions/forgecli/audience-gate.d.ts +4 -0
  9. package/dist/extensions/forgecli/audience-gate.js +8 -5
  10. package/dist/extensions/forgecli/audience-gate.js.map +1 -1
  11. package/dist/extensions/forgecli/collate.d.ts +24 -0
  12. package/dist/extensions/forgecli/collate.js +199 -0
  13. package/dist/extensions/forgecli/collate.js.map +1 -0
  14. package/dist/extensions/forgecli/commit.d.ts +24 -0
  15. package/dist/extensions/forgecli/commit.js +202 -0
  16. package/dist/extensions/forgecli/commit.js.map +1 -0
  17. package/dist/extensions/forgecli/fix-bug.d.ts +75 -0
  18. package/dist/extensions/forgecli/fix-bug.js +1133 -0
  19. package/dist/extensions/forgecli/fix-bug.js.map +1 -0
  20. package/dist/extensions/forgecli/forge-commands.js +7 -0
  21. package/dist/extensions/forgecli/forge-commands.js.map +1 -1
  22. package/dist/extensions/forgecli/forge-init.js +16 -8
  23. package/dist/extensions/forgecli/forge-init.js.map +1 -1
  24. package/dist/extensions/forgecli/forge-subagent.d.ts +29 -0
  25. package/dist/extensions/forgecli/forge-subagent.js +14 -1
  26. package/dist/extensions/forgecli/forge-subagent.js.map +1 -1
  27. package/dist/extensions/forgecli/hook-dispatcher.d.ts +53 -1
  28. package/dist/extensions/forgecli/hook-dispatcher.js +47 -1
  29. package/dist/extensions/forgecli/hook-dispatcher.js.map +1 -1
  30. package/dist/extensions/forgecli/hooks/post-init-hook.d.ts +15 -0
  31. package/dist/extensions/forgecli/hooks/post-init-hook.js +127 -0
  32. package/dist/extensions/forgecli/hooks/post-init-hook.js.map +1 -0
  33. package/dist/extensions/forgecli/hooks/post-sprint-hook.d.ts +37 -0
  34. package/dist/extensions/forgecli/hooks/post-sprint-hook.js +166 -0
  35. package/dist/extensions/forgecli/hooks/post-sprint-hook.js.map +1 -0
  36. package/dist/extensions/forgecli/index.js +47 -0
  37. package/dist/extensions/forgecli/index.js.map +1 -1
  38. package/dist/extensions/forgecli/review-code.d.ts +24 -0
  39. package/dist/extensions/forgecli/review-code.js +202 -0
  40. package/dist/extensions/forgecli/review-code.js.map +1 -0
  41. package/dist/extensions/forgecli/review-plan.d.ts +24 -0
  42. package/dist/extensions/forgecli/review-plan.js +203 -0
  43. package/dist/extensions/forgecli/review-plan.js.map +1 -0
  44. package/dist/extensions/forgecli/run-sprint.d.ts +18 -0
  45. package/dist/extensions/forgecli/run-sprint.js +33 -1
  46. package/dist/extensions/forgecli/run-sprint.js.map +1 -1
  47. package/dist/extensions/forgecli/run-task.d.ts +21 -2
  48. package/dist/extensions/forgecli/run-task.js +33 -9
  49. package/dist/extensions/forgecli/run-task.js.map +1 -1
  50. package/dist/extensions/forgecli/session-registry.d.ts +10 -0
  51. package/dist/extensions/forgecli/session-registry.js +9 -0
  52. package/dist/extensions/forgecli/session-registry.js.map +1 -1
  53. package/dist/extensions/forgecli/validate.d.ts +24 -0
  54. package/dist/extensions/forgecli/validate.js +202 -0
  55. package/dist/extensions/forgecli/validate.js.map +1 -0
  56. package/dist/extensions/forgecli/wf-engine/engine.d.ts +23 -0
  57. package/dist/extensions/forgecli/wf-engine/engine.js +384 -0
  58. package/dist/extensions/forgecli/wf-engine/engine.js.map +1 -0
  59. package/dist/extensions/forgecli/wf-engine/event-parser.d.ts +6 -0
  60. package/dist/extensions/forgecli/wf-engine/event-parser.js +29 -0
  61. package/dist/extensions/forgecli/wf-engine/event-parser.js.map +1 -0
  62. package/dist/extensions/forgecli/wf-engine/id-gen.d.ts +6 -0
  63. package/dist/extensions/forgecli/wf-engine/id-gen.js +17 -0
  64. package/dist/extensions/forgecli/wf-engine/id-gen.js.map +1 -0
  65. package/dist/extensions/forgecli/wf-engine/loader.d.ts +2 -0
  66. package/dist/extensions/forgecli/wf-engine/loader.js +100 -0
  67. package/dist/extensions/forgecli/wf-engine/loader.js.map +1 -0
  68. package/dist/extensions/forgecli/wf-engine/predicate.d.ts +7 -0
  69. package/dist/extensions/forgecli/wf-engine/predicate.js +36 -0
  70. package/dist/extensions/forgecli/wf-engine/predicate.js.map +1 -0
  71. package/dist/extensions/forgecli/wf-engine/prompt-compiler.d.ts +15 -0
  72. package/dist/extensions/forgecli/wf-engine/prompt-compiler.js +23 -0
  73. package/dist/extensions/forgecli/wf-engine/prompt-compiler.js.map +1 -0
  74. package/dist/extensions/forgecli/wf-engine/register.d.ts +9 -0
  75. package/dist/extensions/forgecli/wf-engine/register.js +59 -0
  76. package/dist/extensions/forgecli/wf-engine/register.js.map +1 -0
  77. package/dist/extensions/forgecli/wf-engine/remit-check.d.ts +6 -0
  78. package/dist/extensions/forgecli/wf-engine/remit-check.js +42 -0
  79. package/dist/extensions/forgecli/wf-engine/remit-check.js.map +1 -0
  80. package/dist/extensions/forgecli/wf-engine/state-store.d.ts +13 -0
  81. package/dist/extensions/forgecli/wf-engine/state-store.js +43 -0
  82. package/dist/extensions/forgecli/wf-engine/state-store.js.map +1 -0
  83. package/dist/extensions/forgecli/wf-engine/types.d.ts +66 -0
  84. package/dist/extensions/forgecli/wf-engine/types.js +2 -0
  85. package/dist/extensions/forgecli/wf-engine/types.js.map +1 -0
  86. package/dist/extensions/forgecli/wf-engine/worker.d.ts +11 -0
  87. package/dist/extensions/forgecli/wf-engine/worker.js +50 -0
  88. package/dist/extensions/forgecli/wf-engine/worker.js.map +1 -0
  89. package/dist/forge-payload/.base-pack/workflows/_fragments/context-injection.md +10 -4
  90. package/dist/forge-payload/.base-pack/workflows/fix_bug.md +12 -0
  91. package/dist/forge-payload/.schemas/bug.schema.json +4 -2
  92. package/dist/forge-payload/.schemas/event.schema.json +22 -3
  93. package/dist/forge-payload/commands/add-pipeline.md +342 -0
  94. package/dist/forge-payload/commands/add-task.md +269 -0
  95. package/dist/forge-payload/commands/ask.md +43 -0
  96. package/dist/forge-payload/commands/calibrate.md +356 -0
  97. package/dist/forge-payload/commands/config.md +202 -0
  98. package/dist/forge-payload/commands/enhance.md +38 -0
  99. package/dist/forge-payload/commands/health.md +225 -0
  100. package/dist/forge-payload/commands/init.md +165 -0
  101. package/dist/forge-payload/commands/materialize.md +119 -0
  102. package/dist/forge-payload/commands/migrate.md +160 -0
  103. package/dist/forge-payload/commands/quiz-agent.md +38 -0
  104. package/dist/forge-payload/commands/regenerate.md +673 -0
  105. package/dist/forge-payload/commands/remove.md +174 -0
  106. package/dist/forge-payload/commands/report-bug.md +191 -0
  107. package/dist/forge-payload/commands/store-query.md +73 -0
  108. package/dist/forge-payload/commands/store-repair.md +187 -0
  109. package/dist/forge-payload/commands/update-tools.md +56 -0
  110. package/dist/forge-payload/commands/update.md +1376 -0
  111. package/dist/forge-payload/tools/preflight-gate.cjs +2 -1
  112. package/dist/forge-payload/tools/read-verdict.cjs +41 -8
  113. package/dist/forge-payload/tools/store-cli.cjs +4 -3
  114. package/node_modules/argparse/CHANGELOG.md +216 -0
  115. package/node_modules/argparse/LICENSE +254 -0
  116. package/node_modules/argparse/README.md +84 -0
  117. package/node_modules/argparse/argparse.js +3707 -0
  118. package/node_modules/argparse/lib/sub.js +67 -0
  119. package/node_modules/argparse/lib/textwrap.js +440 -0
  120. package/node_modules/argparse/package.json +31 -0
  121. package/node_modules/cliui/CHANGELOG.md +121 -0
  122. package/node_modules/color-convert/CHANGELOG.md +54 -0
  123. package/node_modules/esprima/ChangeLog +235 -0
  124. package/node_modules/js-yaml/LICENSE +21 -0
  125. package/node_modules/js-yaml/README.md +247 -0
  126. package/node_modules/js-yaml/bin/js-yaml.js +126 -0
  127. package/node_modules/js-yaml/dist/js-yaml.js +3880 -0
  128. package/node_modules/js-yaml/dist/js-yaml.min.js +2 -0
  129. package/node_modules/js-yaml/dist/js-yaml.mjs +3856 -0
  130. package/node_modules/js-yaml/index.js +47 -0
  131. package/node_modules/js-yaml/lib/common.js +59 -0
  132. package/node_modules/js-yaml/lib/dumper.js +965 -0
  133. package/node_modules/js-yaml/lib/exception.js +55 -0
  134. package/node_modules/js-yaml/lib/loader.js +1733 -0
  135. package/node_modules/js-yaml/lib/schema/core.js +11 -0
  136. package/node_modules/js-yaml/lib/schema/default.js +22 -0
  137. package/node_modules/js-yaml/lib/schema/failsafe.js +17 -0
  138. package/node_modules/js-yaml/lib/schema/json.js +19 -0
  139. package/node_modules/js-yaml/lib/schema.js +121 -0
  140. package/node_modules/js-yaml/lib/snippet.js +101 -0
  141. package/node_modules/js-yaml/lib/type/binary.js +125 -0
  142. package/node_modules/js-yaml/lib/type/bool.js +35 -0
  143. package/node_modules/js-yaml/lib/type/float.js +97 -0
  144. package/node_modules/js-yaml/lib/type/int.js +156 -0
  145. package/node_modules/js-yaml/lib/type/map.js +8 -0
  146. package/node_modules/js-yaml/lib/type/merge.js +12 -0
  147. package/node_modules/js-yaml/lib/type/null.js +35 -0
  148. package/node_modules/js-yaml/lib/type/omap.js +44 -0
  149. package/node_modules/js-yaml/lib/type/pairs.js +53 -0
  150. package/node_modules/js-yaml/lib/type/seq.js +8 -0
  151. package/node_modules/js-yaml/lib/type/set.js +29 -0
  152. package/node_modules/js-yaml/lib/type/str.js +8 -0
  153. package/node_modules/js-yaml/lib/type/timestamp.js +88 -0
  154. package/node_modules/js-yaml/lib/type.js +66 -0
  155. package/node_modules/js-yaml/package.json +66 -0
  156. package/node_modules/mz/HISTORY.md +66 -0
  157. package/node_modules/proper-lockfile/CHANGELOG.md +108 -0
  158. package/node_modules/source-map/CHANGELOG.md +301 -0
  159. package/node_modules/thenify/History.md +11 -0
  160. package/node_modules/thenify-all/History.md +11 -0
  161. package/node_modules/y18n/CHANGELOG.md +100 -0
  162. package/node_modules/yargs/CHANGELOG.md +88 -0
  163. package/node_modules/yargs-parser/CHANGELOG.md +263 -0
  164. package/package.json +6 -2
  165. package/workflows/lead-qualifier/prompts/digest.md +44 -0
  166. package/workflows/lead-qualifier/prompts/draft-outreach.md +44 -0
  167. package/workflows/lead-qualifier/prompts/enrich.md +52 -0
  168. package/workflows/lead-qualifier/prompts/intake.md +48 -0
  169. package/workflows/lead-qualifier/prompts/mark-cold.md +38 -0
  170. package/workflows/lead-qualifier/prompts/score.md +45 -0
  171. package/workflows/lead-qualifier/workflow.yaml +95 -0
  172. package/workflows/research-brief/prompts/brief-synthesize.md +43 -0
  173. package/workflows/research-brief/prompts/intake.md +51 -0
  174. package/workflows/research-brief/prompts/source-critique.md +38 -0
  175. package/workflows/research-brief/prompts/source-score.md +38 -0
  176. package/workflows/research-brief/prompts/source-summarize.md +54 -0
  177. package/workflows/research-brief/workflow.yaml +66 -0
  178. package/dist/extensions/forgecli/session-monitor-widget.d.ts +0 -37
  179. package/dist/extensions/forgecli/session-monitor-widget.js +0 -320
  180. package/dist/extensions/forgecli/session-monitor-widget.js.map +0 -1
  181. package/dist/extensions/forgecli/session-monitor.d.ts +0 -2
  182. package/dist/extensions/forgecli/session-monitor.js +0 -135
  183. package/dist/extensions/forgecli/session-monitor.js.map +0 -1
@@ -0,0 +1,1376 @@
1
+ ---
2
+ name: update
3
+ description: Check for Forge updates, review changes, install, and apply migrations — all in one command
4
+ ---
5
+
6
+ # /forge:update
7
+
8
+ Single entry point for updating Forge. Checks GitHub for new versions, shows
9
+ what changed, guides you through the install, and applies migrations to this
10
+ project's generated artifacts.
11
+
12
+ ## Locate plugin root
13
+
14
+ ```
15
+ FORGE_ROOT: !`echo "${CLAUDE_PLUGIN_ROOT}"`
16
+ ```
17
+
18
+ Detect install mode:
19
+
20
+ ```
21
+ IS_CANARY = FORGE_ROOT does not contain "/.claude/plugins/"
22
+ ```
23
+
24
+ - **Managed install** (`IS_CANARY = false`): plugin lives under the Claude Code
25
+ plugins directory (either `/.claude/plugins/cache/` or
26
+ `/.claude/plugins/marketplaces/`). Updated via the plugin manager.
27
+ - **Canary / source install** (`IS_CANARY = true`): FORGE_ROOT is outside the
28
+ Claude Code plugins directory — a local source path (e.g. `/home/user/src/forge/forge`).
29
+ The source is already at the correct version — there is nothing to install.
30
+ Only migration steps apply.
31
+
32
+ ---
33
+
34
+ ## Model-alias auto-suppression pre-check
35
+
36
+ **Reusable sub-procedure — invoked from Steps 2A, 2B, and 4 after
37
+ aggregating `manual` items.**
38
+
39
+ When a migration chain includes the 0.6.13→0.7.0 step (or any step whose
40
+ `manual` list contains an item about custom model overrides), this pre-check
41
+ determines whether that manual item is a false positive for the current
42
+ project and removes it if so.
43
+
44
+ ### Procedure
45
+
46
+ After the aggregation step has produced the `manual` list (and `breaking`
47
+ flag), but **before** displaying the breaking-change block or prompting for
48
+ confirmation:
49
+
50
+ 1. **Identify model-override manual items.** Scan the aggregated `manual`
51
+ list for any item whose text contains the substring
52
+ `custom 'model' overrides in config.pipelines`. If none found, skip this
53
+ entire sub-procedure — nothing to suppress.
54
+
55
+ 2. **Read `.forge/config.json`.** If the file does not exist, or if it
56
+ contains no `pipelines` key, or if `pipelines` is empty — there are no
57
+ custom model overrides. Remove the matching manual item(s) and jump to
58
+ step 4 below.
59
+
60
+ 3. **Scan pipeline phases.** For every pipeline in `config.pipelines`, for
61
+ every phase that has a `model` field, classify the value:
62
+ - **Standard Forge aliases:** `sonnet`, `opus`, `haiku`
63
+ - **Non-standard:** anything else (e.g. raw model IDs like
64
+ `claude-3-opus`, `claude-sonnet-4-6`, or unknown aliases)
65
+
66
+ If **all** `model` values across all pipelines are standard aliases or
67
+ absent (no `model` field on the phase), the model-override manual item is
68
+ a false positive. Remove it from `manual`.
69
+
70
+ If **any** non-standard `model` value is found, the manual item is
71
+ legitimate — keep it in `manual` and do not suppress the confirmation.
72
+
73
+ 4. **Re-evaluate `breaking` flag.** After removing model-override items, if
74
+ `manual` is now empty, set `breaking = false` for the current step's
75
+ display/confirmation logic. A breaking-change section with zero manual
76
+ items must not be shown.
77
+
78
+ ### Result
79
+
80
+ The `manual` list and `breaking` flag are updated in-place. The calling step
81
+ then renders its summary and confirmation prompts using the filtered values —
82
+ no further changes to the step logic are needed.
83
+
84
+ ---
85
+
86
+ ## Progress Output Format
87
+
88
+ Open the run with the ember hero, then a subtitle:
89
+
90
+ ```sh
91
+ node "$FORGE_ROOT/tools/banners.cjs" ember
92
+ node "$FORGE_ROOT/tools/banners.cjs" --subtitle "Updating Forge — checking remote, applying migrations, verifying state"
93
+ ```
94
+
95
+ At the start of each step, emit a step header via `banners.cjs --phase`:
96
+
97
+ ```sh
98
+ node "$FORGE_ROOT/tools/banners.cjs" --phase {N} 7 "{Step Name}" {bannerKey} \
99
+ "$(node "$FORGE_ROOT/tools/manage-config.cjs" get mode 2>/dev/null || echo full)"
100
+ ```
101
+
102
+ Step ↔ banner key map:
103
+
104
+ | Step | Name | Banner key |
105
+ |------|------|------------|
106
+ | 1 | Check for updates | `north` |
107
+ | 2A | Plugin update available | `rift` |
108
+ | 2B | Apply project migrations | `drift` |
109
+ | 3 | Verify installation | `lumen` |
110
+ | 4 | Apply migrations | `forge` |
111
+ | 5 | Pipeline audit | `oracle` |
112
+ | 6 | Record state | `drift` |
113
+ | 7 | Tomoshibi | `lumen` |
114
+
115
+ The `--phase` helper replaces the older `━━━ Step N/7 — <name> ━━━` emits
116
+ — do not emit a second em-dash banner after the helper. `banners.cjs`
117
+ strips ANSI in `NO_COLOR` / non-tty / `--plain` contexts.
118
+
119
+ ---
120
+
121
+ ## Resume Detection (FR-002)
122
+
123
+ Before Step 1, check whether a previous update left the project in a Pending
124
+ state. Read `.forge/update-check-cache.json` and check the `updateStatus` field.
125
+
126
+ If `updateStatus === "pending"`:
127
+
128
+ 1. Print the pending state:
129
+ ```
130
+ △ Previous update is incomplete — pending migration(s): {pendingMigrations}
131
+ Reason: {pendingReason}
132
+
133
+ Run /forge:migrate to complete the pending migration, then re-run
134
+ /forge:update to verify.
135
+ ```
136
+
137
+ 2. Do NOT proceed to Step 1 (remote version check) or Steps 2A, 2B, 3 — these
138
+ were completed in the previous run. The project already has the correct
139
+ plugin version installed; only the migration chain remains.
140
+
141
+ 3. Exit. The user runs `/forge:migrate` to complete, then re-runs `/forge:update`.
142
+
143
+ If `updateStatus !== "pending"` (or field absent): proceed normally with Step 1.
144
+
145
+ ---
146
+
147
+ ## Step 1 — Check for updates
148
+
149
+ ```sh
150
+ node "$FORGE_ROOT/tools/banners.cjs" --phase 1 7 "Check for updates" north \
151
+ "$(node "$FORGE_ROOT/tools/manage-config.cjs" get mode 2>/dev/null || echo full)"
152
+ ```
153
+
154
+ Read `$FORGE_ROOT/.claude-plugin/plugin.json`. Extract `"version"` → `LOCAL_VERSION`.
155
+
156
+ Determine the distribution from `FORGE_ROOT` path — the cache path encodes the
157
+ marketplace name and is more reliable than reading fields from `plugin.json`:
158
+
159
+ | FORGE_ROOT contains | Distribution |
160
+ |---------------------|-------------|
161
+ | `/cache/skillforge/forge/` | `forge@skillforge` |
162
+ | `/marketplaces/skillforge/forge/` | `forge@skillforge` |
163
+ | anything else | `forge@forge` / canary |
164
+
165
+ For **both** distributions, resolve `UPDATE_URL` and `MIGRATIONS_URL` from the
166
+ installed `plugin.json` — each distribution branch ships its own correct URLs:
167
+
168
+ ```
169
+ UPDATE_URL = plugin.json → updateUrl, fallback: https://raw.githubusercontent.com/Entelligentsia/forge/main/forge/.claude-plugin/plugin.json
170
+ MIGRATIONS_URL = plugin.json → migrationsUrl, fallback: https://raw.githubusercontent.com/Entelligentsia/forge/main/forge/migrations.json
171
+ ```
172
+
173
+ **Do NOT hardcode per-distribution URLs** — the installed `plugin.json` is the
174
+ authoritative source. Hardcoding breaks when distribution hosting moves.
175
+
176
+ Set `UPDATE_URL`, `MIGRATIONS_URL`, and `DISTRIBUTION` accordingly before fetching.
177
+
178
+ Also read `distribution` from `.forge/update-check-cache.json` → `PRIOR_DISTRIBUTION`.
179
+ If the file doesn't exist or the field is absent, set `PRIOR_DISTRIBUTION = DISTRIBUTION`.
180
+
181
+ Fetch the **remote** plugin manifest to get the latest available version.
182
+ Use the WebFetch tool (preferred) or `curl` via Bash:
183
+
184
+ ```
185
+ URL: {UPDATE_URL}
186
+ ```
187
+
188
+ Parse the response JSON and extract the `version` field → `REMOTE_VERSION`.
189
+
190
+ If the fetch fails (network error, timeout), warn the user:
191
+ > Could not reach GitHub to check for updates. Proceeding with local version only.
192
+
193
+ Then skip to **Step 3** (apply pending migrations if any).
194
+
195
+ ### Determine update status
196
+
197
+ Read the migration baseline from the project-scoped update-check cache:
198
+
199
+ ```
200
+ CACHE_FILE = .forge/update-check-cache.json
201
+ ```
202
+
203
+ This file is project-scoped — each project maintains its own migration state.
204
+ Read it if it exists.
205
+
206
+ **Baseline derivation formula (evaluated in order, stop at first defined value):**
207
+
208
+ ```
209
+ baseline = migratedFrom ?? localVersion ?? LOCAL_VERSION
210
+ ```
211
+
212
+ - `migratedFrom` — written by Step 4 of the last successful update run. It represents
213
+ "the version from which the last successful migration chain was applied to this project"
214
+ (i.e. the baseline of the prior migration, not the version the project is currently at).
215
+ This field was introduced in v0.30.0; older cache files may not have it.
216
+ - `localVersion` — the version recorded when the cache was last written. Used when
217
+ `migratedFrom` is absent (cache file predates v0.30.0 or was written before the first
218
+ migration ran).
219
+ - `LOCAL_VERSION` — the installed plugin version. Last resort when the cache file does not
220
+ exist at all (fresh project, or cache deleted).
221
+
222
+ > **Semantics note:** `migratedFrom` may be *lower* than `localVersion` — it is the
223
+ > starting point of the last migration chain, not the end point. Example: a project that
224
+ > migrated from 0.28.0 to 0.32.0 has `migratedFrom: "0.28.0"` and `localVersion: "0.32.0"`.
225
+ > The next update run uses `0.28.0` as the baseline to walk forward from. This is correct —
226
+ > any migrations between 0.28.0 and 0.32.0 that were applied should be idempotent, and
227
+ > any new steps after 0.32.0 will be picked up.
228
+
229
+ > **Legacy fallback:** If `.forge/update-check-cache.json` does not exist but a
230
+ > plugin-level cache does (`${CLAUDE_PLUGIN_DATA}/forge-plugin-data/update-check-cache.json`
231
+ > or `/tmp/forge-plugin-data/update-check-cache.json`), read `migratedFrom` from
232
+ > there as a one-time migration. Step 6 will write the project-scoped file going forward.
233
+
234
+ The user can also pass `--from <version>` as an argument to set the baseline
235
+ explicitly — this overrides any cached value.
236
+
237
+ ### Baseline derivation — worked examples
238
+
239
+ - **Scenario A:** Cache contains `{ "migratedFrom": "0.28.0", "localVersion": "0.32.0" }` → baseline is `0.28.0` (`migratedFrom` takes precedence).
240
+ - **Scenario B:** Cache contains `{ "localVersion": "0.29.0" }` (no `migratedFrom` — pre-v0.30.0 cache) → baseline is `0.29.0`.
241
+ - **Scenario C:** Cache file absent → baseline is `LOCAL_VERSION` (the currently installed version).
242
+
243
+ Now evaluate — **stop at the first matching row and follow only that row's action**:
244
+
245
+ | # | Condition | Action |
246
+ |---|-----------|--------|
247
+ | 1 | `REMOTE_VERSION` == `LOCAL_VERSION` and `LOCAL_VERSION` == baseline | Print "Forge {LOCAL_VERSION} — up to date. No pending migrations." and **exit**. |
248
+ | 2 | `REMOTE_VERSION` == `LOCAL_VERSION` and `LOCAL_VERSION` != baseline | Jump to **Step 2B** (project migration — no install needed). |
249
+ | 3 | `IS_CANARY` is true | Jump to **Step 2B** (canary — no install needed). |
250
+ | 4 | `LOCAL_VERSION` > `REMOTE_VERSION` | Print "Local version ({LOCAL_VERSION}) is ahead of the release channel ({REMOTE_VERSION}). No install needed — applying any pending project migrations." then jump to **Step 2B**. |
251
+ | 5 | `REMOTE_VERSION` != `LOCAL_VERSION` | Proceed to **Step 2A** (plugin update available). |
252
+
253
+ > **Row 4 worked example:** If `LOCAL_VERSION` is `0.35.0` and `REMOTE_VERSION` is
254
+ > `0.32.0` (user built from source or is on a forward canary), row 4 triggers — project
255
+ > migrations from baseline forward are applied, but no install prompt is shown. This
256
+ > handles cases where `IS_CANARY` was not detected (e.g. a managed install whose
257
+ > `plugin.json` version was manually bumped ahead of the release branch).
258
+
259
+ **Do NOT show an install prompt for rows 1, 2, 3, or 4. Install prompts only appear in Step 2A.**
260
+
261
+ ---
262
+
263
+ ## Step 2A — Plugin update available
264
+
265
+ ```sh
266
+ node "$FORGE_ROOT/tools/banners.cjs" --phase 2 7 "Plugin update available" rift \
267
+ "$(node "$FORGE_ROOT/tools/manage-config.cjs" get mode 2>/dev/null || echo full)"
268
+ ```
269
+
270
+ > **Only reached when `REMOTE_VERSION` != `LOCAL_VERSION` (row 4 above).**
271
+
272
+ Fetch the **remote** migrations manifest from GitHub:
273
+
274
+ ```
275
+ URL: {MIGRATIONS_URL}
276
+ ```
277
+
278
+ Parse the response JSON. Walk the migration chain from `LOCAL_VERSION` forward
279
+ to `REMOTE_VERSION`. Aggregate across all steps:
280
+ - Union of all `regenerate` targets, applying the dominance rule: for each
281
+ category (`workflows`, `knowledge-base`, `commands`, `tools`), if any step lists a
282
+ bare category name (e.g. `"workflows"`), that category is flagged for full
283
+ rebuild. If all steps for a category use sub-targets (e.g.
284
+ `"workflows:plan_task"`), collect the union of those sub-targets
285
+ (deduplicated, order preserved).
286
+ - Concatenated `notes` (one line per step)
287
+ - `breaking: true` if any step is breaking
288
+ - Union of all `manual` items
289
+
290
+ **Run the Model-alias auto-suppression pre-check** (see section above)
291
+ on the aggregated `manual` list and `breaking` flag before displaying
292
+ the summary below.
293
+
294
+ Present the update summary:
295
+
296
+ ```
297
+ ## Forge Update Available
298
+
299
+ {LOCAL_VERSION} → {REMOTE_VERSION}
300
+
301
+ ### What's new
302
+ {for each step in path:}
303
+ • {version}: {notes}
304
+
305
+ ### After install, Forge will regenerate
306
+ {for each category in aggregated result:}
307
+ {if full rebuild:}
308
+ • {category}: (full rebuild)
309
+ {else:}
310
+ • {category}: {sub-target1}, {sub-target2}, ...
311
+
312
+ {if breaking:}
313
+ ### △ Breaking changes — manual steps required
314
+ {for each item in manual:}
315
+ • {item}
316
+
317
+ ### How to proceed
318
+ [1] Install now — I'll guide you through it
319
+ [2] Skip for now
320
+ ```
321
+
322
+ If no migration path can be constructed, show available notes and recommend
323
+ `/forge:regenerate workflows`.
324
+
325
+ Ask the user to choose. If they choose **[2]**, exit.
326
+
327
+ If they choose **[1]**, proceed to **Guided install** below.
328
+
329
+ ### Guided install
330
+
331
+ **If `IS_CANARY` is true** (safety net — should have been caught by row 3):
332
+
333
+ Print:
334
+ ```
335
+ Canary install detected — FORGE_ROOT is a local source directory, not the
336
+ plugin cache. There is nothing to install via the plugin manager.
337
+
338
+ Your source is already at {LOCAL_VERSION}. Proceeding directly to migrations.
339
+ ```
340
+
341
+ Jump to **Step 4**.
342
+
343
+ **If `IS_CANARY` is false (marketplace install):**
344
+
345
+ Print:
346
+ ```
347
+ To install the update:
348
+
349
+ 1. Run /plugin to open the plugin manager
350
+ 2. Find Forge in your installed plugins and update it
351
+
352
+ Tell me when the install is done.
353
+ ```
354
+
355
+ Wait for the user to confirm the install completed.
356
+
357
+ ---
358
+
359
+ ## Step 2B — Project migration pending (plugin already current)
360
+
361
+ ```sh
362
+ node "$FORGE_ROOT/tools/banners.cjs" --phase 2 7 "Apply project migrations" drift \
363
+ "$(node "$FORGE_ROOT/tools/manage-config.cjs" get mode 2>/dev/null || echo full)"
364
+ ```
365
+
366
+ > **Only reached from rows 2 or 3 — the plugin is already at the right version.**
367
+ > **Do NOT show an install prompt here. There is nothing to install.**
368
+
369
+ Read `$FORGE_ROOT/migrations.json` (local).
370
+
371
+ Walk the migration chain from `baseline` forward to `LOCAL_VERSION`. Aggregate:
372
+ - Union of all `regenerate` targets, applying the dominance rule: for each
373
+ category (`workflows`, `knowledge-base`, `commands`, `tools`), if any step lists a
374
+ bare category name (e.g. `"workflows"`), that category is flagged for full
375
+ rebuild. If all steps for a category use sub-targets (e.g.
376
+ `"workflows:plan_task"`), collect the union of those sub-targets
377
+ (deduplicated, order preserved).
378
+ - Concatenated `notes`
379
+ - `breaking: true` if any step is breaking
380
+ - Union of all `manual` items
381
+
382
+ **Run the Model-alias auto-suppression pre-check** (see section above)
383
+ on the aggregated `manual` list and `breaking` flag before printing
384
+ the summary below.
385
+
386
+ Print:
387
+
388
+ ```
389
+ ## Forge {LOCAL_VERSION} — Plugin up to date
390
+
391
+ Your project was last migrated at {baseline}. The following changes need
392
+ to be applied to this project's generated files:
393
+
394
+ ### Changes since {baseline}
395
+ {for each step in path:}
396
+ • {version}: {notes}
397
+
398
+ ### Will regenerate
399
+ {for each category in aggregated result:}
400
+ {if full rebuild:}
401
+ • {category}: (full rebuild)
402
+ {else:}
403
+ • {category}: {sub-target1}, {sub-target2}, ...
404
+
405
+ {if breaking:}
406
+ ### △ Breaking changes — complete these steps first:
407
+ {for each item in manual:}
408
+ • {item}
409
+
410
+ Apply migrations now? [Y/n]
411
+ ```
412
+
413
+ If the user declines, exit without changes.
414
+ If `breaking: true`, confirm they have completed the manual steps first.
415
+
416
+ Then jump to **Step 4** to execute the regeneration.
417
+
418
+ ---
419
+
420
+ ## Step 3 — Verify installation
421
+
422
+ ```sh
423
+ node "$FORGE_ROOT/tools/banners.cjs" --phase 3 7 "Verify installation" lumen \
424
+ "$(node "$FORGE_ROOT/tools/manage-config.cjs" get mode 2>/dev/null || echo full)"
425
+ ```
426
+
427
+ After the user confirms the install:
428
+
429
+ Re-read the local plugin version:
430
+ ```
431
+ $FORGE_ROOT/.claude-plugin/plugin.json → extract "version" → NEW_LOCAL_VERSION
432
+ ```
433
+
434
+ | Condition | Action |
435
+ |-----------|--------|
436
+ | `NEW_LOCAL_VERSION` == `REMOTE_VERSION` | Print "〇 Forge {NEW_LOCAL_VERSION} installed successfully." and proceed to **Step 4**. |
437
+ | `NEW_LOCAL_VERSION` == `LOCAL_VERSION` (unchanged) | Warn: "The plugin version hasn't changed ({LOCAL_VERSION}). The install may not have completed. Would you like to try again or continue anyway?" If user wants to continue, proceed to **Step 4** using available version. |
438
+ | `NEW_LOCAL_VERSION` is different but not `REMOTE_VERSION` | Print "Installed Forge {NEW_LOCAL_VERSION} (expected {REMOTE_VERSION}). Proceeding with {NEW_LOCAL_VERSION}." and continue to **Step 4**. |
439
+
440
+ Update `LOCAL_VERSION` to `NEW_LOCAL_VERSION` for subsequent steps.
441
+
442
+ ### Re-derive FORGE_ROOT
443
+
444
+ After verifying the new version, re-derive `FORGE_ROOT`. A managed plugin
445
+ install changes the cache path (e.g. `…/cache/forge/forge/0.9.6/` →
446
+ `…/cache/forge/forge/0.9.9/`), so the `FORGE_ROOT` captured at the top of
447
+ this command is stale.
448
+
449
+ ```sh
450
+ FORGE_ROOT: !`echo "${CLAUDE_PLUGIN_ROOT}"`
451
+ ```
452
+
453
+ If the re-derived `FORGE_ROOT` differs from the original value, print:
454
+ > 〇 FORGE_ROOT updated: {old} → {new}
455
+
456
+ If `IS_CANARY` is true, `FORGE_ROOT` never changes (it is a local source
457
+ path) — skip the re-derivation and keep the original value.
458
+
459
+ ---
460
+
461
+ ## Step 4 — Apply migrations
462
+
463
+ ```sh
464
+ node "$FORGE_ROOT/tools/banners.cjs" --phase 4 7 "Apply migrations" forge \
465
+ "$(node "$FORGE_ROOT/tools/manage-config.cjs" get mode 2>/dev/null || echo full)"
466
+ ```
467
+
468
+ > **Sequencing note:** `paths.forgeRoot` is written at the very start of Step 4,
469
+ > before any migration targets or regeneration commands execute. This ensures all
470
+ > subsequent tool invocations in Step 4 (including `build-init-context.cjs` called
471
+ > by regeneration sub-steps) use the current, correct plugin path.
472
+
473
+ **Refresh `paths.forgeRoot` before applying migrations:**
474
+
475
+ ```sh
476
+ node "$FORGE_ROOT/tools/manage-config.cjs" set paths.forgeRoot "$FORGE_ROOT"
477
+ ```
478
+
479
+ **Write `paths.forgeRef` (FR-010):** Also write the installed plugin version
480
+ as `paths.forgeRef` to config. This makes the config portable across machines —
481
+ `forgeRef` is a version string rather than an absolute path:
482
+
483
+ ```sh
484
+ LOCAL_VERSION=$(node -e "console.log(require('$FORGE_ROOT/.claude-plugin/plugin.json').version)")
485
+ node "$FORGE_ROOT/tools/manage-config.cjs" set paths.forgeRef "$LOCAL_VERSION"
486
+ ```
487
+
488
+ Determine the baseline version:
489
+ - Use `migratedFrom` from `CACHE_FILE` (set in Step 1)
490
+ - Or the `--from <version>` argument if provided
491
+ - Or the pre-install `LOCAL_VERSION` (before Step 3 updated it)
492
+
493
+ If `LOCAL_VERSION` equals baseline, there are no migrations to apply — skip
494
+ to **Step 5**.
495
+
496
+ Read `$FORGE_ROOT/migrations.json` (local — now updated after install).
497
+
498
+ Before walking the migration chain, check for a cross-distribution downgrade:
499
+ if `PRIOR_DISTRIBUTION` ≠ `DISTRIBUTION` and baseline appears higher than
500
+ `LOCAL_VERSION` (e.g. baseline is `1.1.0` and `LOCAL_VERSION` is `1.0.5`):
501
+
502
+ > The migration baseline ({baseline}) was set on **{PRIOR_DISTRIBUTION}** and is
503
+ > higher than the current plugin version ({LOCAL_VERSION} on {DISTRIBUTION}).
504
+ > The {PRIOR_DISTRIBUTION} migration chain does not exist in {DISTRIBUTION}'s
505
+ > migrations.json — walking it would fail.
506
+ >
507
+ > Reset migration baseline to {LOCAL_VERSION} and regenerate workflows to match
508
+ > the current installed version? This is safe — it re-generates files from the
509
+ > installed plugin, discarding the unreachable canary state. [Y/n]
510
+
511
+ If yes: set `baseline = LOCAL_VERSION`. If baseline now equals `LOCAL_VERSION`,
512
+ there are no migrations to apply — skip directly to **Step 6**.
513
+
514
+ If no: exit without changes.
515
+
516
+ Walk the migration chain from baseline forward to `LOCAL_VERSION`:
517
+ - Each entry key is a `from` version; its `version` field is the `to` version.
518
+ - Collect the ordered list of migration steps that bridge baseline → current.
519
+ - If no path exists, warn:
520
+ > No migration path found from {baseline} to {LOCAL_VERSION}. Running
521
+ > `/forge:regenerate workflows` is recommended.
522
+ Then exit.
523
+
524
+ Aggregate across all steps in the path, applying the dominance rule:
525
+ - For each category (`workflows`, `knowledge-base`, `commands`, `tools`):
526
+ - If ANY step has a bare entry for this category → full rebuild for that category.
527
+ - Otherwise → union of all sub-targets across all steps (deduplicated, order preserved).
528
+ - Concatenated `notes` (one line per step)
529
+ - `breaking: true` if any step is breaking
530
+ - Union of all `manual` items
531
+
532
+ **Run the Model-alias auto-suppression pre-check** (see section above)
533
+ on the aggregated `manual` list and `breaking` flag before displaying
534
+ the confirmation prompt below.
535
+
536
+ ### Regeneration order
537
+
538
+ Execute regeneration targets in this order:
539
+
540
+ | Order | Target | Can run after | Special handling |
541
+ |-------|--------|---------------|-----------------|
542
+ | 0 | `hooks` | — (N/A) | **No-op for project copies.** Hooks ship with the plugin — they are already updated at the time of install. Emit: `〇 hooks — updated via plugin install (no project copy to regenerate)` |
543
+ | 1 | `tools` | — (independent) | — |
544
+ | 2 | `workflows` | — (independent) | — |
545
+ | 3 | `templates` | — (independent) | — |
546
+ | 4 | `personas` | — (independent) | — |
547
+ | 5 | `commands` | Must run after `workflows` | — |
548
+ | 6 | `knowledge-base` sub-targets | — (independent) | — |
549
+ | 7 | `schemas` | — (independent) | **Delegate to `/forge:update-tools`.** Emit: `Delegating schemas regeneration to /forge:update-tools…` then read and follow `$FORGE_ROOT/commands/update-tools.md`. |
550
+
551
+ > **Known special targets — note for migration authors:** `hooks` and `schemas` are
552
+ > special-cased here. Future `migrations.json` entries should only use recognised
553
+ > target names; using unknown bare-category targets will produce a warning and be
554
+ > skipped. The recognised targets are: `hooks`, `tools`, `workflows`, `templates`,
555
+ > `personas`, `commands`, `knowledge-base`, `skills`, `schemas`.
556
+
557
+ `commands` depends on `workflows` because command wrappers reference workflow
558
+ filenames. All other targets are independent and could run in parallel, but
559
+ are executed sequentially here to keep the output readable.
560
+
561
+ Only execute targets that appear in the aggregated result — skip absent ones.
562
+
563
+ ### Subagent probe (FR-009)
564
+
565
+ Before offering the regeneration confirmation prompt, probe whether subagent
566
+ dispatch will succeed in this session.
567
+
568
+ **Probe step:** Attempt to invoke a lightweight read-only command via the Skill
569
+ tool: call `skill: "forge:health"` with no arguments. This is a read-only
570
+ operation that checks knowledge base currency — it cannot modify any state and
571
+ is safe to invoke as a probe.
572
+
573
+ **Success determination:** If the Skill tool call returns without error (exit 0
574
+ or a normal response), the probe succeeds. Note that subagent dispatch is
575
+ available.
576
+
577
+ **Failure determination:** If the Skill tool call is unavailable (tool not
578
+ found), returns a permission error, or returns any unexpected error, the probe
579
+ fails. Note that subagent dispatch is not available in this session.
580
+
581
+ If the probe fails:
582
+ - Warn explicitly: "Subagent dispatch is not available in this session.
583
+ Regeneration will run inline (in the current session)."
584
+ - If the A/B/C regeneration choice is offered (Step 2A), mark option A
585
+ (regenerate now) as "will fall back to inline execution" or remove it
586
+ entirely if option B (defer to fresh session) is safer.
587
+
588
+ **Mid-orchestration failure handling:** If a regeneration sub-step dispatches
589
+ via Agent tool and the agent fails mid-execution:
590
+ - Do NOT silently fall back to inline execution.
591
+ - Re-prompt the user with the failure details and three explicit options:
592
+ - **(a) Retry:** Re-attempt the failed step using the Agent tool.
593
+ - **(b) Defer:** Skip remaining steps. Mark the update as Pending with a
594
+ reason noting the subagent failure. The user can complete remaining steps
595
+ in a fresh session.
596
+ - **(c) Skip:** Skip the failed step (with warning that some generated
597
+ artifacts may be missing), continue with remaining steps inline.
598
+
599
+ **Success report flagging:** In the Step 6 summary, include a line:
600
+ `Subagent isolation: {used | bypassed (inline)}` to make it clear which mode
601
+ was used.
602
+
603
+ ### Confirm and regenerate
604
+
605
+ Print a migration summary:
606
+
607
+ ```
608
+ ## Applying Migrations: {baseline} → {LOCAL_VERSION}
609
+
610
+ Changes:
611
+ {notes from each step, one per line}
612
+
613
+ Regeneration targets:
614
+ {for each category in aggregated result:}
615
+ {if full rebuild:}
616
+ • {category}: (full rebuild)
617
+ {else:}
618
+ • {category}: {sub-target1}, {sub-target2}, ...
619
+
620
+ {if breaking:}
621
+ △ Breaking changes — complete these manual steps first:
622
+ {manual items}
623
+
624
+ Proceed? [Y/n]
625
+ ```
626
+
627
+ If the user declines, exit without modifying anything.
628
+ If `breaking: true`, require the user to confirm they have completed the manual
629
+ steps before proceeding.
630
+
631
+ For each category in the aggregated result, invoke `/forge:regenerate` by
632
+ reading and following `$FORGE_ROOT/commands/regenerate.md`:
633
+ - If flagged for full rebuild: invoke `/forge:regenerate <category>`
634
+ - If sub-targets collected: invoke `/forge:regenerate <category> <sub-target>`
635
+ for each sub-target in order
636
+
637
+ **Category-to-command mapping:** most categories are handled by
638
+ `/forge:regenerate`, but the `tools` category is special. When `tools`
639
+ appears in the aggregated result, invoke `/forge:update-tools` instead of
640
+ `/forge:regenerate tools`. The update-tools command copies JSON schemas from
641
+ `$FORGE_ROOT/schemas/` to `.forge/schemas/` and validates the store. It does
642
+ not use the regeneration framework — tools ship with the plugin and are invoked
643
+ directly via `$FORGE_ROOT/tools/`.
644
+
645
+ Run non-knowledge-base targets first (workflows, templates, commands, tools),
646
+ then knowledge-base sub-targets if present.
647
+
648
+ **Sub-target filename resolution:** a sub-target like `architect_sprint_plan`
649
+ maps to the file `.forge/workflows/architect_sprint_plan.md` — append `.md`
650
+ to the sub-target name as written. Do NOT strip any prefix or suffix.
651
+
652
+ ### Post-migration structure check
653
+
654
+ After all regeneration targets complete, run:
655
+ ```sh
656
+ node "$FORGE_ROOT/tools/check-structure.cjs" --path .
657
+ ```
658
+
659
+ If exit 0 (all present):
660
+ > 〇 All expected generated files are present.
661
+
662
+ If exit 1 (gaps remain):
663
+ > △ Structure check: N file(s) still missing after migration:
664
+ > (list missing files)
665
+ >
666
+ > This may indicate a failed regeneration step. Re-run `/forge:regenerate <namespace>`
667
+ > for each affected namespace, or `/forge:regenerate` to rebuild all targets.
668
+ > Note: skills entries require an explicit `/forge:regenerate skills` — they are not
669
+ > included in the default regenerate run.
670
+
671
+ Do NOT block migration success on gaps — surface them as a warning only. The user
672
+ is already informed of failed regeneration steps by the Iron Laws above; this check
673
+ is an additional safety net.
674
+
675
+ ### Refresh calibrationBaseline
676
+
677
+ After the structure check, if at least one regeneration target was applied (the
678
+ aggregated `regenerate` list was non-empty), refresh `calibrationBaseline` in
679
+ `.forge/config.json`. This keeps the calibration baseline in sync with the newly
680
+ materialized artifacts so `/forge:calibrate` does not report false drift.
681
+
682
+ Skip this step if no regeneration targets were applied (e.g. all targets were absent
683
+ from the migration path, or Step 4 was entered with `baseline == LOCAL_VERSION`).
684
+
685
+ 1. Read `$FORGE_ROOT/.claude-plugin/plugin.json` → `version`.
686
+ 2. Resolve `KB_PATH`:
687
+ ```sh
688
+ KB_PATH=$(node "$FORGE_ROOT/tools/manage-config.cjs" get paths.engineering 2>/dev/null || echo "engineering")
689
+ ```
690
+ 3. Hash `{KB_PATH}/MASTER_INDEX.md` (strip blank lines + `<!--` lines, SHA-256):
691
+ ```sh
692
+ node -e "const crypto=require('crypto'),fs=require('fs'); const lines=fs.readFileSync('${KB_PATH}/MASTER_INDEX.md','utf8').split('\n').filter(l=>l.trim()&&!l.trim().startsWith('<!--')); console.log(crypto.createHash('sha256').update(lines.join('\n')).digest('hex'))"
693
+ ```
694
+ 4. List done sprint IDs from `.forge/store/sprints/` using union-merge
695
+ (FR-003: cumulative provenance — `sprintsCovered` must never shrink):
696
+ ```sh
697
+ node -e "const fs=require('fs'),p='.forge/store/sprints'; try{const files=fs.readdirSync(p).filter(f=>f.endsWith('.json')); const done=files.map(f=>JSON.parse(fs.readFileSync(p+'/'+f,'utf8'))).filter(s=>['done','retrospective-done'].includes(s.status)).map(s=>s.sprintId); console.log(JSON.stringify(done));}catch(e){console.log('[]')}"
698
+ ```
699
+ 5. Today's ISO date: `date -u +"%Y-%m-%d"`
700
+ 6. **Union-merge `sprintsCovered`** (FR-003): read the existing
701
+ `calibrationBaseline.sprintsCovered` from config, compute the union with
702
+ the current done sprint IDs:
703
+ ```sh
704
+ node -e "
705
+ const fs = require('fs');
706
+ const cfg = JSON.parse(fs.readFileSync('.forge/config.json','utf8'));
707
+ const existing = new Set(cfg.calibrationBaseline ? cfg.calibrationBaseline.sprintsCovered || [] : []);
708
+ const current = new Set(<computed done sprint IDs from step 4>);
709
+ const merged = [...new Set([...existing, ...current])];
710
+ const cfg2 = JSON.parse(fs.readFileSync('.forge/config.json','utf8'));
711
+ cfg2.calibrationBaseline = {
712
+ lastCalibrated: '<date>',
713
+ version: '<ver>',
714
+ masterIndexHash: '<hash>',
715
+ sprintsCovered: merged
716
+ };
717
+ fs.writeFileSync('.forge/config.json', JSON.stringify(cfg2, null, 2) + '\n');
718
+ "
719
+ ```
720
+ If the existing `sprintsCovered` array was shrunk in a prior version (i.e. the
721
+ existing array is shorter than a heuristic would suggest), perform a **backfill
722
+ scan** before the union-merge. Broaden the scan to include sprints with these
723
+ terminal and historical status values: `done`, `retrospective-done`, `completed`,
724
+ `partially-completed`, and `abandoned`. These statuses cover: (a) the current
725
+ canonical terminal statuses (`done`, `retrospective-done`); (b) `completed`,
726
+ which was the pre-v0.40 status for what became `retrospective-done`;
727
+ (c) `partially-completed` and `abandoned`, which represent sprints whose outcomes
728
+ may still have been included in a prior calibration baseline. Present the
729
+ recovered array for user confirmation before writing.
730
+
731
+ Emit: `〇 calibrationBaseline refreshed — version <ver>, hash <first-8-chars-of-hash>...`
732
+
733
+ ### Post-migration pack rebuild
734
+
735
+ After the calibrationBaseline refresh, if the aggregated regeneration targets
736
+ include any of `workflows`, `personas`, or `skills` (bare category or sub-target),
737
+ rebuild the persona and context packs so they reflect the newly regenerated artifacts.
738
+
739
+ If no `workflows`, `personas`, or `skills` targets were applied (e.g. only `tools`,
740
+ `schemas`, `commands`, or `knowledge-base` were in the migration path), skip this
741
+ step entirely — these categories do not affect the packs.
742
+
743
+ **1. Rebuild persona pack:**
744
+
745
+ ```sh
746
+ node "$FORGE_ROOT/tools/build-persona-pack.cjs" --out .forge/cache/persona-pack.json
747
+ ```
748
+
749
+ - Exit 0: emit `〇 persona pack refreshed`
750
+ - Exit 1: surface error, warn user (non-fatal — pack will refresh on next full regenerate)
751
+
752
+ **2. Rebuild context pack:**
753
+
754
+ ```sh
755
+ ENGINEERING=$(node "$FORGE_ROOT/tools/manage-config.cjs" get paths.engineering 2>/dev/null || echo engineering)
756
+ node "$FORGE_ROOT/tools/build-context-pack.cjs" \
757
+ --arch-dir "$ENGINEERING/architecture" \
758
+ --out-md .forge/cache/context-pack.md \
759
+ --out-json .forge/cache/context-pack.json
760
+ ```
761
+
762
+ - Exit 0: emit `〇 context pack refreshed`
763
+ - Exit 1: warn (non-fatal — architecture directory may not yet exist for new projects)
764
+
765
+ Full Step 4 post-migration sequence: regenerate → structure check → calibrationBaseline refresh → pack rebuild.
766
+
767
+ ### Migration completion gate (FR-002)
768
+
769
+ After the post-migration pack rebuild, evaluate the migration chain against the
770
+ ADR-S14-01 decision criteria to determine the update outcome:
771
+
772
+ **Low-risk classification** (all of these must be true for the step):
773
+ - `breaking: false` or `breaking` field absent
774
+ - No `manual` items (or all `manual` items resolved by model-alias auto-suppression)
775
+ - No net-new `.forge/` files requiring user-provided data
776
+ - No changes to `calibrationBaseline` semantics
777
+
778
+ Concretely, a low-risk step is one where the only actions are: schema-only
779
+ changes (`/forge:update-tools` for schemas), file-copy from base-pack
780
+ (templates, workflows, personas), or config-key additions with deterministic
781
+ defaults.
782
+
783
+ **User-affecting classification** (any of these triggers Pending for the
784
+ entire chain):
785
+ - `breaking: true`
786
+ - `manual` items present after auto-suppression
787
+ - Net-new `.forge/` files requiring user-provided data (e.g.
788
+ `project-context.json`)
789
+ - Changes to `calibrationBaseline` semantics (e.g. `sprintsCovered` reduction)
790
+
791
+ #### Auto-invoke deterministic path (ADR-S14-01 D6)
792
+
793
+ If all migration steps are low-risk, auto-invoke the deterministic migration
794
+ path inline within Step 4, after regeneration targets and `check-structure.cjs`
795
+ pass:
796
+
797
+ 1. Check whether the migration chain includes any net-new `.forge/` files not
798
+ covered by the `regenerate` targets. If none, skip to the normal
799
+ post-migration sequence (calibrationBaseline refresh, pack rebuild) — the
800
+ auto-invoke is a no-op.
801
+
802
+ 2. If net-new files exist and the migration is low-risk, execute the
803
+ deterministic migration path:
804
+ - **Structural migration (Step 0 of `migrate.md`):** run
805
+ `check-structure.cjs` to detect whether `structure-versions.json` is
806
+ absent (indicating a pre-T05 install). If absent, read and follow
807
+ `$FORGE_ROOT/meta/workflows/meta-migrate.md` Phase 0 through Phase 1
808
+ (extraction) and Phase 2 (confirmation), then Phase 3 (write) — but ONLY
809
+ the deterministic file-copy operations (archive, substitute, register).
810
+ **Do NOT execute the interactive interview (Phase 1 extraction that
811
+ requires user input, or Phase 2 confirmation).** The auto-invoke path
812
+ only performs operations that can be completed without any user prompts.
813
+ - **Deterministic file copies from base-pack:** if
814
+ `structure-versions.json` already exists (post-T05 install), the
815
+ deterministic path is simply running any remaining `/forge:regenerate`
816
+ targets that were not already applied in the main Step 4 regeneration
817
+ sequence.
818
+
819
+ 3. If the deterministic migration path completes successfully, proceed to the
820
+ normal Step 4 post-migration sequence (calibrationBaseline refresh, pack
821
+ rebuild).
822
+
823
+ 4. **Fallback to Pending:** If the deterministic migration path fails at any
824
+ point (e.g. `check-structure.cjs` reports missing files after auto-invoke,
825
+ a tool exits non-zero, or an unexpected requirement for user input is
826
+ detected), immediately fall back to the Pending state:
827
+ - Do NOT bump `calibrationBaseline.version`.
828
+ - Write `updateStatus: "pending"`, `pendingReason: "Auto-invoke failed:
829
+ {error description}"`, and `pendingMigrations: [list]` to
830
+ `.forge/update-check-cache.json`.
831
+ - Print: "Update Pending — auto-invoke failed. Run `/forge:migrate` to
832
+ complete" with the failure details.
833
+ - Exit without proceeding to Steps 5-7.
834
+
835
+ **Key invariant (from ADR-S14-01 D6):** The auto-invoke never prompts the user
836
+ for migration decisions. If any migration step needs user input, the entire
837
+ chain is classified as user-affecting and enters Pending.
838
+
839
+ #### User-affecting path — Pending state
840
+
841
+ If any step is user-affecting (and the chain was NOT auto-invoked), enter the
842
+ **Pending** state:
843
+ - Do NOT bump `calibrationBaseline.version`.
844
+ - Write `updateStatus: "pending"`, `pendingReason`, and `pendingMigrations` to
845
+ `.forge/update-check-cache.json`.
846
+ - Print: "Update Pending — run `/forge:migrate` to complete" with the list of
847
+ pending migrations and next steps.
848
+ - Exit without proceeding to Steps 5-7.
849
+
850
+ #### Update Complete path
851
+
852
+ On the "Update Complete" path (all migrations applied,
853
+ `check-structure.cjs` passes): set `updateStatus: "complete"`,
854
+ `pendingReason: null`, `pendingMigrations: []` in the cache file.
855
+
856
+ ### Iron Laws for Step 4
857
+
858
+ - YOU MUST NOT call `generation-manifest.cjs record` directly for migration targets.
859
+ The regenerate command records hashes after writing — calling record on a file that
860
+ has not been regenerated yet produces a stale hash and silently corrupts the manifest.
861
+ - YOU MUST NOT inline the regeneration. Read `$FORGE_ROOT/commands/regenerate.md`
862
+ and follow it as the authoritative procedure for every target.
863
+ - YOU MUST NOT declare success if any regeneration step errors. Surface the error
864
+ to the user and stop. Do not continue to Step 5 with a failed regeneration.
865
+
866
+ ---
867
+
868
+ ## Step 5 — Pipeline and configuration audit
869
+
870
+ ```sh
871
+ node "$FORGE_ROOT/tools/banners.cjs" --phase 5 7 "Pipeline audit" oracle \
872
+ "$(node "$FORGE_ROOT/tools/manage-config.cjs" get mode 2>/dev/null || echo full)"
873
+ ```
874
+
875
+ Runs on every update. Collects all findings first, then presents a single
876
+ consolidated prompt. **Nothing is written without the user saying yes.**
877
+
878
+ ### 5a — Locate tools
879
+
880
+ ```
881
+ FORGE_ROOT: !`echo "${CLAUDE_PLUGIN_ROOT}"`
882
+ ```
883
+
884
+ All tools are invoked directly from the plugin:
885
+ - `node "$FORGE_ROOT/tools/manage-config.cjs"`
886
+ - `node "$FORGE_ROOT/tools/generation-manifest.cjs"`
887
+
888
+ If `.forge/config.json` does not exist, skip this step and proceed to **Step 6**.
889
+
890
+ ### 5b-collect — Run all sub-checks silently
891
+
892
+ Run each sub-check without prompting. Accumulate findings into an `AUDIT_ITEMS`
893
+ list. Each item has:
894
+
895
+ | Field | Description |
896
+ |-------|-------------|
897
+ | `type` | `delete-command`, `delete-workflow`, `update-pipeline-cmd`, `legacy-model-field`, `add-workflow-field`, `add-persona-symbol`, `add-paths-key`, `missing-command-file`, `add-gitignore-entry` |
898
+ | `label` | Human-readable one-line description |
899
+ | `action` | What will be done if approved |
900
+ | `path` | File or pipeline affected |
901
+ | `modified` | `true` if user edits detected (from manifest check); `false` otherwise |
902
+ | `required` | `true` for items requiring confirmation; `false` for auto-applied or advisory items |
903
+
904
+ **Item classification:**
905
+
906
+ | Type | `required` | `modified` can be true? | Notes |
907
+ |------|-----------|------------------------|-------|
908
+ | `delete-command` | true | yes (△) | Retired command file |
909
+ | `delete-workflow` | true | yes (△) | Retired workflow file |
910
+ | `update-pipeline-cmd` | true | no | Retired command name in pipeline config |
911
+ | `legacy-model-field` | false | no | Auto-migrated by regeneration |
912
+ | `add-workflow-field` | true | no | Missing workflow field in pipeline phase |
913
+ | `add-persona-symbol` | false | no | Optional decoration |
914
+ | `add-paths-key` | true | no | Missing config key |
915
+ | `missing-command-file` | false | no | Advisory only |
916
+ | `add-gitignore-entry` | true | no | Transient store path not gitignored |
917
+
918
+ Items where `modified: true` must be flagged with `△` in the label.
919
+
920
+ **Sub-checks to run silently** (logic unchanged from previous Step 5, just no
921
+ prompts — accumulate items instead):
922
+
923
+ **5b-pre — Retired generated files.** Check both `.claude/commands/` and
924
+ `.forge/workflows/` for retired filenames.
925
+
926
+ Old command files (exact match only):
927
+ - `engineer.md` → retired in favour of `plan.md`
928
+ - `supervisor.md` → retired in favour of `review-plan.md` / `review-code.md`
929
+
930
+ Do NOT match partial names, prefixes, or variants — those are custom commands.
931
+
932
+ For each found command file, check manifest status:
933
+ ```sh
934
+ node "$FORGE_ROOT/tools/generation-manifest.cjs" check .claude/commands/{old-name}.md
935
+ ```
936
+
937
+ - Pristine/untracked → add item: `type: delete-command`, `modified: false`,
938
+ `path: .claude/commands/{old-name}.md`,
939
+ `label: ".claude/commands/{old-name}.md — retired name, safe to remove"`,
940
+ `action: "Delete old file"`
941
+ - Modified (exit 1) → add item: `type: delete-command`, `modified: true`,
942
+ `path: .claude/commands/{old-name}.md`,
943
+ `label: "△ .claude/commands/{old-name}.md — retired name, user edits detected. Merge into {new-name}.md before deleting"`,
944
+ `action: "Delete old file after merge"`
945
+ - Manifest tool absent → add item: `type: delete-command`, `modified: false`,
946
+ `path: .claude/commands/{old-name}.md`,
947
+ `label: ".claude/commands/{old-name}.md — retired name, cannot verify modifications"`,
948
+ `action: "Delete old file"`
949
+
950
+ Old workflow files:
951
+
952
+ ```
953
+ engineer_plan_task.md → renamed to plan_task.md
954
+ engineer_implement_plan.md → renamed to implement_plan.md
955
+ engineer_commit_task.md → renamed to commit_task.md
956
+ engineer_update_plan.md → renamed to update_plan.md
957
+ engineer_update_implementation.md → renamed to update_implementation.md
958
+ engineer_fix_bug.md → renamed to fix_bug.md
959
+ supervisor_review_plan.md → renamed to review_plan.md
960
+ supervisor_review_implementation.md → renamed to review_code.md
961
+ ```
962
+
963
+ For each found workflow file, check manifest status:
964
+ ```sh
965
+ node "$FORGE_ROOT/tools/generation-manifest.cjs" check .forge/workflows/{old-name}.md
966
+ ```
967
+
968
+ - Pristine/untracked → add item: `type: delete-workflow`, `modified: false`,
969
+ `path: .forge/workflows/{old-name}.md`,
970
+ `label: ".forge/workflows/{old-name}.md — retired workflow, safe to remove"`,
971
+ `action: "Delete old workflow"`
972
+ - Modified (exit 1) → add item: `type: delete-workflow`, `modified: true`,
973
+ `path: .forge/workflows/{old-name}.md`,
974
+ `label: "△ .forge/workflows/{old-name}.md — retired workflow, user edits detected"`,
975
+ `action: "Delete old workflow after merge"`
976
+ - Manifest tool absent → add item: `type: delete-workflow`, `modified: false`,
977
+ `path: .forge/workflows/{old-name}.md`,
978
+ `label: ".forge/workflows/{old-name}.md — retired workflow, cannot verify modifications"`,
979
+ `action: "Delete old workflow"`
980
+
981
+ **5b-portability — Legacy model fields in workflows.** Scan all `.md` files
982
+ in `.forge/workflows/` for the pattern `^model:\s+.+$` (multiline).
983
+
984
+ For each file containing legacy `model:` fields, add item:
985
+ `type: legacy-model-field`, `required: false`,
986
+ `path: .forge/workflows/{filename}`,
987
+ `label: ".forge/workflows/{filename} — legacy model: field detected"`,
988
+ `action: "Will be auto-migrated by /forge:regenerate workflows"`
989
+
990
+ **5b-rename — Retired command names in pipeline config.** Scan every configured
991
+ pipeline for phases that use retired built-in command names.
992
+
993
+ Retired command name map:
994
+
995
+ | Retired command | Role | Replacement |
996
+ |----------------|------|-------------|
997
+ | `engineer` | `plan` | `plan` |
998
+ | `supervisor` | `review-plan` | `review-plan` |
999
+ | `supervisor` | `review-code` | `review-code` |
1000
+
1001
+ Read all pipelines:
1002
+ ```sh
1003
+ node "$FORGE_ROOT/tools/manage-config.cjs" list-pipelines 2>/dev/null
1004
+ ```
1005
+
1006
+ For each pipeline, read its full phase list:
1007
+ ```sh
1008
+ node "$FORGE_ROOT/tools/manage-config.cjs" pipeline get {NAME}
1009
+ ```
1010
+
1011
+ For each phase with a retired command, add item:
1012
+ `type: update-pipeline-cmd`, `required: true`,
1013
+ `path: pipeline "{name}" phase {N}`,
1014
+ `label: "pipeline \"{name}\" phase {N} — command \"{cmd}\" is retired"`,
1015
+ `action: "Rename command to \"{replacement}\""`
1016
+
1017
+ Do not match custom commands like `supervisor-code` or `engineer-security`.
1018
+
1019
+ **5c — Missing `paths.customCommands` key.**
1020
+
1021
+ ```sh
1022
+ node "$FORGE_ROOT/tools/manage-config.cjs" get paths.customCommands 2>/dev/null
1023
+ ```
1024
+
1025
+ If the key is missing from config, add item:
1026
+ `type: add-paths-key`, `required: true`,
1027
+ `path: .forge/config.json`,
1028
+ `label: "New config field available: paths.customCommands"`,
1029
+ `action: "Add paths.customCommands with default \"engineering/commands\""`
1030
+
1031
+ **5d/5e — Custom phase workflow field audit.** For each pipeline, identify
1032
+ phases whose `command` is not in the built-in list
1033
+ (`plan`, `review-plan`, `implement`, `review-code`, `validate`, `approve`, `commit`).
1034
+
1035
+ For each custom phase where `workflow` field is NOT set, check both locations:
1036
+ ```sh
1037
+ ls engineering/commands/{cmd}.md 2>/dev/null && echo "found:engineering"
1038
+ ls .claude/commands/{cmd}.md 2>/dev/null && echo "found:claude"
1039
+ ```
1040
+
1041
+ - Found in exactly one location → add item:
1042
+ `type: add-workflow-field`, `required: true`,
1043
+ `path: pipeline "{name}" phase {N}`,
1044
+ `label: "pipeline \"{name}\" phase {N} (role: {role}) — no workflow field, file found at {path}"`,
1045
+ `action: "Add workflow field: \"{found_path}\""`
1046
+
1047
+ - Found in both locations → add item:
1048
+ `type: add-workflow-field`, `required: true`,
1049
+ `path: pipeline "{name}" phase {N}`,
1050
+ `label: "pipeline \"{name}\" phase {N} (role: {role}) — no workflow field, file found in two locations"`,
1051
+ `action: "Add workflow field (user chooses which path)"`
1052
+
1053
+ - Not found → add item:
1054
+ `type: missing-command-file`, `required: false`,
1055
+ `path: command "{cmd}"`,
1056
+ `label: "pipeline \"{name}\" phase {N} — command file missing for \"{cmd}\""`,
1057
+ `action: "Run /forge:add-pipeline to create it, or create manually"`
1058
+
1059
+ **5g — Transient store gitignore audit.** `.forge/store/events/` accumulates
1060
+ one JSON per agent phase per task or bug — these are transient logs that
1061
+ should not be committed. Detect projects where the path is not yet ignored.
1062
+
1063
+ 1. Check whether the project root has a `.gitignore`:
1064
+ ```sh
1065
+ ls .gitignore 2>/dev/null
1066
+ ```
1067
+ If absent, skip this sub-check entirely (no items added — the project is
1068
+ not under git or has no gitignore convention; do not auto-create).
1069
+
1070
+ 2. Read `.gitignore` and search for any of these match strings on a
1071
+ non-comment, non-blank line (literal substring match):
1072
+ - `.forge/store/events/`
1073
+ - `.forge/store/events`
1074
+ - `.forge/store/`
1075
+ - `.forge/`
1076
+
1077
+ If any match → already ignored, no item added.
1078
+
1079
+ 3. If no match → add item:
1080
+ `type: add-gitignore-entry`, `required: true`,
1081
+ `path: .gitignore`,
1082
+ `label: ".gitignore — .forge/store/events/ is not ignored (transient agent event logs)"`,
1083
+ `action: "Append .forge/store/events/ to .gitignore"`
1084
+
1085
+ This sub-check runs whether or not pipelines are configured — it depends
1086
+ only on `.gitignore` and the project's `.forge/` layout (always present
1087
+ post-init).
1088
+
1089
+ **5f — Persona decoration.** For each command file found during 5d/5e that
1090
+ has a `## Persona` section but no symbol line (first non-blank line after the
1091
+ heading does not start with one of `🌱 🌿 ⛰️ 🌊 🍂 🍃`), add item:
1092
+
1093
+ Check manifest status:
1094
+ ```sh
1095
+ node "$FORGE_ROOT/tools/generation-manifest.cjs" check {filepath}
1096
+ ```
1097
+
1098
+ - Unmodified → `modified: false`
1099
+ - Modified (exit 1) → `modified: true`, flag with `△` in label
1100
+
1101
+ `type: add-persona-symbol`, `required: false`,
1102
+ `path: {filepath}`,
1103
+ `label: "{filepath} — Persona section, no symbol line (optional)"`,
1104
+ `action: "Add: {symbol} **{Project} {name}** — {announcement}"`
1105
+
1106
+ Symbol is derived from the phase role:
1107
+ - `plan` / `implement` / `commit` → 🌱
1108
+ - `review-plan` / `review-code` → 🌿
1109
+ - `approve` → ⛰️
1110
+ - any other → 🌿
1111
+
1112
+ **Pipeline gate behavior:** All sub-checks always run. Pipeline-dependent
1113
+ sub-checks (5b-rename, 5c, 5d, 5e, 5f) naturally produce zero items when
1114
+ no pipelines are configured. Project-wide sub-checks (5b-pre, 5b-portability,
1115
+ 5g) run regardless of pipeline state. If `.forge/config.json` does not exist,
1116
+ Step 5 is skipped entirely (handled by 5a above).
1117
+
1118
+ ### 5b-present — Present consolidated prompt
1119
+
1120
+ If `AUDIT_ITEMS` is empty:
1121
+ > 〇 Pipeline audit complete — nothing to update.
1122
+
1123
+ Skip to **Step 6**.
1124
+
1125
+ If `AUDIT_ITEMS` has entries, separate them into required and optional groups
1126
+ and emit:
1127
+
1128
+ ```
1129
+ ## Step 5 — Audit (N items)
1130
+
1131
+ [1] △ .claude/commands/supervisor.md — retired name, user edits detected.
1132
+ Merge into review-plan.md before deleting. Delete old file?
1133
+ [2] 〇 .forge/workflows/architect_sprint_plan.md — legacy model: field detected.
1134
+ Will be auto-migrated by /forge:regenerate workflows. No action needed.
1135
+ [3] 〇 pipeline "main" phase 3 — no workflow field.
1136
+ Add: "workflow": "engineering/commands/qa.md"
1137
+ [4] 〇 pipeline "main" phase 4 — command file missing.
1138
+ No file found for command "security-check". Create via /forge:add-pipeline.
1139
+ [5] 〇 engineering/commands/qa.md — Persona section, no symbol line. (optional)
1140
+ Add: 🌿 **QA Engineer** — I validate implementations...
1141
+
1142
+ Apply required? [Y] Apply all (including optional)? [a] Review individually [r] Skip [n]
1143
+ ```
1144
+
1145
+ **Ordering within the list:**
1146
+
1147
+ 1. Deletion items (`delete-command`, `delete-workflow`) first — highest urgency
1148
+ 2. Pipeline config updates (`update-pipeline-cmd`, `add-paths-key`) second
1149
+ 3. Workflow field additions (`add-workflow-field`) third
1150
+ 4. Gitignore entries (`add-gitignore-entry`) fourth — repo hygiene
1151
+ 5. Missing file warnings (`missing-command-file`) fifth — always advisory
1152
+ 6. Legacy model field items (`legacy-model-field`) sixth — auto-applied
1153
+ 7. Optional decoration items (`add-persona-symbol`) last — marked `(optional)`
1154
+
1155
+ **Behavior for each choice:**
1156
+
1157
+ ---
1158
+
1159
+ **[Y] — Apply required items:**
1160
+
1161
+ For each item where `required: true`:
1162
+ - If `modified: true` (△): prompt individually for that specific item before
1163
+ acting. Use the original prompt text from the corresponding sub-check
1164
+ (5b-pre for deletions, 5b-rename for command renames, 5c for paths key,
1165
+ 5e for workflow fields).
1166
+ - If `modified: false`: apply automatically, emit ` 〇 applied: <label>`
1167
+
1168
+ For `legacy-model-field` items (`required: false`): acknowledge automatically,
1169
+ emit ` 〇 acknowledged: <label>`
1170
+
1171
+ For `add-persona-symbol` items: skip, emit
1172
+ ` ── skipped: <label> (optional)`
1173
+
1174
+ For `missing-command-file` items: emit advisory reminder at the end.
1175
+
1176
+ **`add-gitignore-entry` apply rule:** append the following two lines to
1177
+ `.gitignore` (preserve existing trailing newline; do not introduce a
1178
+ duplicate if either line is already present after a re-read):
1179
+
1180
+ ```
1181
+ # Forge — transient agent event logs (one file per phase, do not commit)
1182
+ .forge/store/events/
1183
+ ```
1184
+
1185
+ Touch nothing else in `.gitignore`. Emit `〇 applied: appended .forge/store/events/ to .gitignore`.
1186
+
1187
+ After processing, if any optional items were skipped:
1188
+ ```
1189
+ ── N optional decoration item(s) skipped (re-run with [a] to include, or [r] for individual review)
1190
+ ```
1191
+
1192
+ ---
1193
+
1194
+ **[a] — Apply all including optional:**
1195
+
1196
+ Same as [Y] for all required items, plus apply `add-persona-symbol` items.
1197
+ Each decoration is applied by prepending the symbol line immediately after the
1198
+ `## Persona` heading in the target file. Touch **nothing else** in the file
1199
+ — not punctuation, not spacing, not other lines.
1200
+
1201
+ For `modified: true` files targeted for decoration, show the diff before applying:
1202
+ ```
1203
+ △ {filepath} has been manually modified — your edits will be preserved.
1204
+ The decoration only adds one line after ## Persona.
1205
+ ```
1206
+
1207
+ ---
1208
+
1209
+ **[r] — Review individually:**
1210
+
1211
+ Fall back to the original per-item behavior. Walk through each item in order
1212
+ using the existing prompts from the corresponding sub-checks:
1213
+
1214
+ - `delete-command` / `delete-workflow` items → follow 5b-pre prompts
1215
+ - `legacy-model-field` items → follow 5b-portability acknowledgment prompt
1216
+ - `update-pipeline-cmd` items → follow 5b-rename diff preview and confirmation
1217
+ - `add-paths-key` items → follow 5c prompt
1218
+ - `add-workflow-field` items → follow 5e audit (Case A, B, or C)
1219
+ - `missing-command-file` items → follow 5e Case C prompt
1220
+ - `add-gitignore-entry` items → confirm append, then write the entry per 5g
1221
+ - `add-persona-symbol` items → follow 5f persona decoration prompt
1222
+
1223
+ This preserves backward compatibility exactly — each item type uses the same
1224
+ prompt text and confirmation flow as the original sub-checks.
1225
+
1226
+ ---
1227
+
1228
+ **[n] — Skip all:**
1229
+
1230
+ Emit summary of skipped items:
1231
+ ```
1232
+ ── N item(s) skipped:
1233
+ ── [1] <label>
1234
+ ── [2] <label>
1235
+ ...
1236
+ ```
1237
+
1238
+ For any `legacy-model-field` items in the skipped list, add:
1239
+ ```
1240
+ △ Some workflows may not resolve models correctly until regenerated.
1241
+ ```
1242
+
1243
+ Proceed to **Step 6**.
1244
+
1245
+ ---
1246
+
1247
+ ### Key behavioral invariants
1248
+
1249
+ - `△` items (user-modified files) must never be deleted without explicit confirmation
1250
+ even in bulk-apply mode — if `[Y]` or `[a]` is chosen and an item is `modified: true`,
1251
+ prompt for that specific item before acting
1252
+ - `missing-command-file` items are always advisory — never blocked, always result
1253
+ in a reminder at the end
1254
+ - `legacy-model-field` items are auto-acknowledged in `[Y]` and `[a]` modes (no
1255
+ individual prompt), but shown in the list for transparency
1256
+ - `add-persona-symbol` items are excluded from `[Y]` bulk-apply but included in `[a]`
1257
+ - The individual review mode `[r]` must behave identically to the original
1258
+ per-item prompts from 5b-pre, 5b-portability, 5b-rename, 5c, 5e, and 5f
1259
+ - If `.forge/config.json` does not exist, Step 5 is skipped entirely
1260
+ - If `AUDIT_ITEMS` is empty (no findings at all), print the "nothing to update"
1261
+ message and proceed to Step 6
1262
+
1263
+ ---
1264
+
1265
+ ## Step 6 — Record state and summarise
1266
+
1267
+ ```sh
1268
+ node "$FORGE_ROOT/tools/banners.cjs" --phase 6 7 "Record state" drift \
1269
+ "$(node "$FORGE_ROOT/tools/manage-config.cjs" get mode 2>/dev/null || echo full)"
1270
+ ```
1271
+
1272
+ > **Note:** `paths.forgeRoot` and `paths.forgeRef` were already written at the start
1273
+ > of Step 4. Step 6 does not repeat those writes — it records migration state only.
1274
+
1275
+ **Write `.forge/update-check-cache.json`** to record the completed migration.
1276
+ Read the existing file if present, update `migratedFrom`, `localVersion`,
1277
+ `distribution`, `forgeRoot`, `forgeRef`, `updateStatus`, `pendingReason`, and
1278
+ `pendingMigrations`, then write it back. Use the Write or Edit tool — do not run
1279
+ a shell command for this step. The `.forge/` directory always exists at this
1280
+ point (it was checked earlier), so no `mkdir -p` is needed.
1281
+
1282
+ If the file does not exist, create it with:
1283
+ ```json
1284
+ {
1285
+ "migratedFrom": "<LOCAL_VERSION>",
1286
+ "localVersion": "<LOCAL_VERSION>",
1287
+ "distribution": "<DISTRIBUTION>",
1288
+ "forgeRoot": "<FORGE_ROOT>",
1289
+ "forgeRef": "<LOCAL_VERSION>",
1290
+ "updateStatus": "complete",
1291
+ "pendingReason": null,
1292
+ "pendingMigrations": []
1293
+ }
1294
+ ```
1295
+
1296
+ On the Update Complete path, `updateStatus` is `"complete"`, `pendingReason` is
1297
+ `null`, and `pendingMigrations` is `[]`. On the Pending path (Step 4), the
1298
+ fields are set differently — see the migration completion gate section.
1299
+
1300
+ Print the final summary:
1301
+
1302
+ ```
1303
+ ## 〇 Forge {LOCAL_VERSION} — Update Complete
1304
+
1305
+ {if install happened:}
1306
+ Plugin updated: {old version} → {LOCAL_VERSION}
1307
+ {end if}
1308
+
1309
+ {if migrations applied:}
1310
+ Migrations applied: {baseline} → {LOCAL_VERSION}
1311
+ Regenerated: {list of targets}
1312
+ {end if}
1313
+
1314
+ {if custom command audit ran:}
1315
+ Pipeline audit: {N} phase(s) reviewed
1316
+ {if workflow fields added:} 〇 workflow fields added: {list of pipeline/phase} {end if}
1317
+ {if files missing:} △ command files still needed: {list} {end if}
1318
+ {end if}
1319
+
1320
+ ── Next steps:
1321
+ • Run /forge:health to verify knowledge base currency
1322
+ • Generated workflows and tools are ready to use
1323
+ {if files missing:}• Run /forge:add-pipeline to create missing command file(s){end if}
1324
+
1325
+ Subagent isolation: {used | bypassed (inline)}
1326
+ ```
1327
+
1328
+ **Fast-mode promotion hint.** After printing the summary, read
1329
+ `.forge/config.json` `mode`:
1330
+
1331
+ ```sh
1332
+ CURRENT_MODE=$(node "$FORGE_ROOT/tools/manage-config.cjs" get mode 2>/dev/null || echo "unset")
1333
+ ```
1334
+
1335
+ If `CURRENT_MODE == "fast"`, also emit:
1336
+
1337
+ ```
1338
+ 〇 Migration applied in fast mode. Materialized artifacts refreshed; stubs will pick up changes on first use.
1339
+ 〇 To fully promote: /forge:config mode full
1340
+ ```
1341
+
1342
+ This is informational only — no behavioural change. Do not block on it.
1343
+
1344
+ ---
1345
+
1346
+ ## Step 7 — Link KB to Agent Instruction Files
1347
+
1348
+ ```sh
1349
+ node "$FORGE_ROOT/tools/banners.cjs" --phase 7 7 "Tomoshibi" lumen \
1350
+ "$(node "$FORGE_ROOT/tools/manage-config.cjs" get mode 2>/dev/null || echo full)"
1351
+ ```
1352
+
1353
+ Invoke Tomoshibi to ensure every coding-agent instruction file in the project
1354
+ has up-to-date links to the Forge knowledge base and generated workflow entry points.
1355
+
1356
+ Use the Skill tool:
1357
+ skill: "forge:refresh-kb-links"
1358
+
1359
+ ---
1360
+
1361
+ ## Arguments
1362
+
1363
+ $ARGUMENTS
1364
+
1365
+ | Argument | Purpose |
1366
+ |----------|---------|
1367
+ | `--from <version>` | Override the migration baseline (useful when cache is missing or user jumped versions) |
1368
+ | `--skip-check` | Skip the remote version check — only apply pending migrations from cache |
1369
+
1370
+ ---
1371
+
1372
+ ## On error
1373
+
1374
+ If any step above fails unexpectedly, describe what went wrong and ask:
1375
+
1376
+ > "This looks like a Forge bug. Would you like to file a report to help improve it? Run `/forge:report-bug` — I'll pre-fill the report from this conversation."