@lumenflow/cli 5.5.0 → 5.7.14

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 (229) hide show
  1. package/README.md +41 -40
  2. package/dist/db-journal-recover.js +400 -0
  3. package/dist/db-journal-recover.js.map +1 -0
  4. package/dist/docs-sync.js +8 -3
  5. package/dist/docs-sync.js.map +1 -1
  6. package/dist/doctor.js +11 -0
  7. package/dist/doctor.js.map +1 -1
  8. package/dist/gate-defaults.js +37 -0
  9. package/dist/gate-defaults.js.map +1 -1
  10. package/dist/gates/monolithic-file-contention-guard.js +167 -0
  11. package/dist/gates/monolithic-file-contention-guard.js.map +1 -0
  12. package/dist/gates/prod-migration-drift.js +207 -0
  13. package/dist/gates/prod-migration-drift.js.map +1 -0
  14. package/dist/gates/test-over-deletion-guard.js +280 -0
  15. package/dist/gates/test-over-deletion-guard.js.map +1 -0
  16. package/dist/gates-runners.js +44 -3
  17. package/dist/gates-runners.js.map +1 -1
  18. package/dist/gates.js +3 -2
  19. package/dist/gates.js.map +1 -1
  20. package/dist/hooks/config-resolver.js +16 -1
  21. package/dist/hooks/config-resolver.js.map +1 -1
  22. package/dist/hooks/dirty-guard.js +43 -2
  23. package/dist/hooks/dirty-guard.js.map +1 -1
  24. package/dist/hooks/git-status-parser.js +22 -8
  25. package/dist/hooks/git-status-parser.js.map +1 -1
  26. package/dist/init-templates.js +241 -0
  27. package/dist/init-templates.js.map +1 -1
  28. package/dist/init.js +122 -16
  29. package/dist/init.js.map +1 -1
  30. package/dist/lumenflow-setup.js +144 -0
  31. package/dist/lumenflow-setup.js.map +1 -0
  32. package/dist/lumenflow-upgrade.js +43 -1
  33. package/dist/lumenflow-upgrade.js.map +1 -1
  34. package/dist/mem-create.js +10 -1
  35. package/dist/mem-create.js.map +1 -1
  36. package/dist/mem-signal.js +21 -4
  37. package/dist/mem-signal.js.map +1 -1
  38. package/dist/orchestrate-initiative.js +28 -3
  39. package/dist/orchestrate-initiative.js.map +1 -1
  40. package/dist/public-manifest.js +17 -7
  41. package/dist/public-manifest.js.map +1 -1
  42. package/dist/release.js +53 -18
  43. package/dist/release.js.map +1 -1
  44. package/dist/wu-done-gates.js +13 -9
  45. package/dist/wu-done-gates.js.map +1 -1
  46. package/dist/wu-done.js +14 -2
  47. package/dist/wu-done.js.map +1 -1
  48. package/dist/wu-edit-operations.js +74 -0
  49. package/dist/wu-edit-operations.js.map +1 -1
  50. package/dist/wu-edit-validators.js +58 -0
  51. package/dist/wu-edit-validators.js.map +1 -1
  52. package/dist/wu-edit.js +106 -4
  53. package/dist/wu-edit.js.map +1 -1
  54. package/dist/wu-prep.js +57 -9
  55. package/dist/wu-prep.js.map +1 -1
  56. package/dist/wu-recover.js +6 -0
  57. package/dist/wu-recover.js.map +1 -1
  58. package/dist/wu-release.js +120 -2
  59. package/dist/wu-release.js.map +1 -1
  60. package/dist/wu-sizing-validation.js +47 -17
  61. package/dist/wu-sizing-validation.js.map +1 -1
  62. package/dist/wu-status.js +33 -0
  63. package/dist/wu-status.js.map +1 -1
  64. package/package.json +13 -12
  65. package/packs/agent-runtime/package.json +1 -1
  66. package/packs/sidekick/package.json +1 -1
  67. package/packs/software-delivery/package.json +1 -1
  68. package/templates/core/AGENTS.md.template +67 -3
  69. package/templates/core/LUMENFLOW.md.template +196 -47
  70. package/dist/distribution-preflight.js +0 -230
  71. package/dist/distribution-preflight.js.map +0 -1
  72. package/packs/agent-runtime/agent-heartbeat.ts +0 -163
  73. package/packs/agent-runtime/auto-session-integration.ts +0 -888
  74. package/packs/agent-runtime/capability-factory.ts +0 -104
  75. package/packs/agent-runtime/constants.ts +0 -21
  76. package/packs/agent-runtime/delegation-registry-schema.ts +0 -220
  77. package/packs/agent-runtime/delegation-registry-store.ts +0 -269
  78. package/packs/agent-runtime/delegation-tree.ts +0 -328
  79. package/packs/agent-runtime/index.ts +0 -20
  80. package/packs/agent-runtime/manifest.ts +0 -348
  81. package/packs/agent-runtime/memory-coordination-contract.ts +0 -86
  82. package/packs/agent-runtime/orchestration.ts +0 -2027
  83. package/packs/agent-runtime/pack-registration.ts +0 -110
  84. package/packs/agent-runtime/policy-factory.ts +0 -165
  85. package/packs/agent-runtime/remote-controls/index.ts +0 -7
  86. package/packs/agent-runtime/remote-controls/operations.ts +0 -405
  87. package/packs/agent-runtime/remote-controls/port.ts +0 -48
  88. package/packs/agent-runtime/remote-controls/state-store.ts +0 -258
  89. package/packs/agent-runtime/remote-controls/types.ts +0 -105
  90. package/packs/agent-runtime/session-schema.ts +0 -467
  91. package/packs/agent-runtime/tool-impl/agent-turn-tools.ts +0 -793
  92. package/packs/agent-runtime/tool-impl/index.ts +0 -6
  93. package/packs/agent-runtime/tool-impl/provider-adapters.ts +0 -1245
  94. package/packs/agent-runtime/tool-impl/remote-controls.mock.ts +0 -256
  95. package/packs/agent-runtime/tool-impl/remote-controls.ts +0 -273
  96. package/packs/agent-runtime/tools/index.ts +0 -4
  97. package/packs/agent-runtime/tools/types.ts +0 -47
  98. package/packs/agent-runtime/turn-lifecycle-events.ts +0 -590
  99. package/packs/agent-runtime/types.ts +0 -128
  100. package/packs/agent-runtime/vitest.config.ts +0 -11
  101. package/packs/sidekick/channel-ingress.ts +0 -137
  102. package/packs/sidekick/constants.ts +0 -10
  103. package/packs/sidekick/index.ts +0 -8
  104. package/packs/sidekick/manifest-schema.ts +0 -49
  105. package/packs/sidekick/manifest.ts +0 -512
  106. package/packs/sidekick/pack-registration.ts +0 -110
  107. package/packs/sidekick/policy-factory.ts +0 -38
  108. package/packs/sidekick/sidekick-events.ts +0 -694
  109. package/packs/sidekick/src/adapters/cloud-queue.ts +0 -101
  110. package/packs/sidekick/src/adapters/control-plane-bridge.adapter.ts +0 -386
  111. package/packs/sidekick/src/adapters/filesystem-bridge.adapter.ts +0 -228
  112. package/packs/sidekick/src/domain/channel.types.ts +0 -64
  113. package/packs/sidekick/src/ports/channel-bridge.port.ts +0 -92
  114. package/packs/sidekick/src/routines/commit.ts +0 -74
  115. package/packs/sidekick/tool-impl/channel-tools.ts +0 -577
  116. package/packs/sidekick/tool-impl/channel-transports.ts +0 -75
  117. package/packs/sidekick/tool-impl/index.ts +0 -29
  118. package/packs/sidekick/tool-impl/memory-tools.ts +0 -290
  119. package/packs/sidekick/tool-impl/routine-commit.ts +0 -102
  120. package/packs/sidekick/tool-impl/routine-tools.ts +0 -440
  121. package/packs/sidekick/tool-impl/runtime-context.ts +0 -28
  122. package/packs/sidekick/tool-impl/shared.ts +0 -125
  123. package/packs/sidekick/tool-impl/storage.ts +0 -325
  124. package/packs/sidekick/tool-impl/system-tools.ts +0 -160
  125. package/packs/sidekick/tool-impl/task-tools.ts +0 -506
  126. package/packs/sidekick/tools/channel-tools.ts +0 -53
  127. package/packs/sidekick/tools/index.ts +0 -9
  128. package/packs/sidekick/tools/memory-tools.ts +0 -53
  129. package/packs/sidekick/tools/routine-tools.ts +0 -53
  130. package/packs/sidekick/tools/system-tools.ts +0 -47
  131. package/packs/sidekick/tools/task-tools.ts +0 -61
  132. package/packs/sidekick/tools/types.ts +0 -57
  133. package/packs/sidekick/vitest.config.ts +0 -11
  134. package/packs/software-delivery/constants.ts +0 -10
  135. package/packs/software-delivery/extensions.ts +0 -140
  136. package/packs/software-delivery/gate-policies.ts +0 -134
  137. package/packs/software-delivery/index.ts +0 -8
  138. package/packs/software-delivery/manifest-schema.ts +0 -268
  139. package/packs/software-delivery/manifest.ts +0 -657
  140. package/packs/software-delivery/pack-registration.ts +0 -113
  141. package/packs/software-delivery/src/commands/index.ts +0 -5
  142. package/packs/software-delivery/src/config/delivery-review-contract.ts +0 -256
  143. package/packs/software-delivery/src/config/env-accessors.ts +0 -66
  144. package/packs/software-delivery/src/config/index.ts +0 -8
  145. package/packs/software-delivery/src/config/normalize-config-keys.ts +0 -9
  146. package/packs/software-delivery/src/config/schemas/lumenflow-config-schema-types.ts +0 -460
  147. package/packs/software-delivery/src/config/workspace-reader.ts +0 -375
  148. package/packs/software-delivery/src/constants/backlog-patterns.ts +0 -31
  149. package/packs/software-delivery/src/constants/client-ids.ts +0 -19
  150. package/packs/software-delivery/src/constants/config-contract.ts +0 -7
  151. package/packs/software-delivery/src/constants/docs-layout-presets.ts +0 -50
  152. package/packs/software-delivery/src/constants/duration-constants.ts +0 -20
  153. package/packs/software-delivery/src/constants/gate-constants.ts +0 -32
  154. package/packs/software-delivery/src/constants/index.ts +0 -29
  155. package/packs/software-delivery/src/constants/lock-constants.ts +0 -35
  156. package/packs/software-delivery/src/constants/object-guards.ts +0 -12
  157. package/packs/software-delivery/src/constants/section-headings.ts +0 -107
  158. package/packs/software-delivery/src/constants/wu-cli-constants.ts +0 -500
  159. package/packs/software-delivery/src/constants/wu-domain-constants.ts +0 -466
  160. package/packs/software-delivery/src/constants/wu-git-constants.ts +0 -7
  161. package/packs/software-delivery/src/constants/wu-id-format.ts +0 -327
  162. package/packs/software-delivery/src/constants/wu-paths-constants.ts +0 -384
  163. package/packs/software-delivery/src/constants/wu-statuses.ts +0 -287
  164. package/packs/software-delivery/src/constants/wu-type-helpers.ts +0 -67
  165. package/packs/software-delivery/src/constants/wu-ui-constants.ts +0 -267
  166. package/packs/software-delivery/src/constants/wu-validation-constants.ts +0 -73
  167. package/packs/software-delivery/src/domain/index.ts +0 -5
  168. package/packs/software-delivery/src/domain/orchestration.constants.ts +0 -166
  169. package/packs/software-delivery/src/domain/orchestration.schemas.ts +0 -238
  170. package/packs/software-delivery/src/domain/orchestration.types.ts +0 -176
  171. package/packs/software-delivery/src/methodology/incremental-test.ts +0 -122
  172. package/packs/software-delivery/src/methodology/index.ts +0 -6
  173. package/packs/software-delivery/src/methodology/manual-test-validator.ts +0 -292
  174. package/packs/software-delivery/src/policy/coverage-gate.ts +0 -270
  175. package/packs/software-delivery/src/policy/gates-agent-mode.ts +0 -223
  176. package/packs/software-delivery/src/policy/gates-config-internal.ts +0 -121
  177. package/packs/software-delivery/src/policy/gates-config.ts +0 -300
  178. package/packs/software-delivery/src/policy/gates-coverage.ts +0 -356
  179. package/packs/software-delivery/src/policy/gates-presets.ts +0 -134
  180. package/packs/software-delivery/src/policy/gates-schemas.ts +0 -173
  181. package/packs/software-delivery/src/policy/index.ts +0 -22
  182. package/packs/software-delivery/src/policy/package-manager-resolver.ts +0 -319
  183. package/packs/software-delivery/src/policy/resolve-policy.ts +0 -601
  184. package/packs/software-delivery/src/ports/config.ports.ts +0 -90
  185. package/packs/software-delivery/src/ports/dashboard-renderer.port.ts +0 -125
  186. package/packs/software-delivery/src/ports/index.ts +0 -10
  187. package/packs/software-delivery/src/ports/sync-validator.ports.ts +0 -59
  188. package/packs/software-delivery/src/ports/wu-helpers.ports.ts +0 -168
  189. package/packs/software-delivery/src/ports/wu-state.ports.ts +0 -241
  190. package/packs/software-delivery/src/primitives/index.ts +0 -5
  191. package/packs/software-delivery/src/runtime/index.ts +0 -6
  192. package/packs/software-delivery/src/runtime/work-classifier.ts +0 -561
  193. package/packs/software-delivery/src/sandbox/index.ts +0 -10
  194. package/packs/software-delivery/src/sandbox/sandbox-allowlist.ts +0 -118
  195. package/packs/software-delivery/src/sandbox/sandbox-backend-linux.ts +0 -88
  196. package/packs/software-delivery/src/sandbox/sandbox-backend-macos.ts +0 -154
  197. package/packs/software-delivery/src/sandbox/sandbox-backend-windows.ts +0 -47
  198. package/packs/software-delivery/src/sandbox/sandbox-profile.ts +0 -153
  199. package/packs/software-delivery/src/schemas/index.ts +0 -5
  200. package/packs/software-delivery/src/state/date-utils.ts +0 -158
  201. package/packs/software-delivery/src/state/index.ts +0 -15
  202. package/packs/software-delivery/src/state/state-machine.ts +0 -119
  203. package/packs/software-delivery/src/state/wu-doc-types.ts +0 -51
  204. package/packs/software-delivery/src/state/wu-paths.ts +0 -381
  205. package/packs/software-delivery/src/state/wu-schema.ts +0 -1139
  206. package/packs/software-delivery/src/state/wu-state-schema.ts +0 -255
  207. package/packs/software-delivery/src/state/wu-yaml.ts +0 -338
  208. package/packs/software-delivery/tool-impl/agent-tools.ts +0 -263
  209. package/packs/software-delivery/tool-impl/delegation-tools.ts +0 -66
  210. package/packs/software-delivery/tool-impl/flow-metrics-tools.ts +0 -219
  211. package/packs/software-delivery/tool-impl/git-runner.ts +0 -113
  212. package/packs/software-delivery/tool-impl/git-tools.ts +0 -316
  213. package/packs/software-delivery/tool-impl/index.ts +0 -15
  214. package/packs/software-delivery/tool-impl/initiative-orchestration-tools.ts +0 -720
  215. package/packs/software-delivery/tool-impl/lane-lock.ts +0 -246
  216. package/packs/software-delivery/tool-impl/memory-tools.ts +0 -470
  217. package/packs/software-delivery/tool-impl/pending-runtime-tools.ts +0 -21
  218. package/packs/software-delivery/tool-impl/runtime-cli-adapter.ts +0 -329
  219. package/packs/software-delivery/tool-impl/runtime-native-tools.ts +0 -687
  220. package/packs/software-delivery/tool-impl/worker-loader.ts +0 -52
  221. package/packs/software-delivery/tool-impl/worktree-tools.ts +0 -46
  222. package/packs/software-delivery/tool-impl/wu-lifecycle-tools.ts +0 -807
  223. package/packs/software-delivery/tools/delegation-tools.ts +0 -23
  224. package/packs/software-delivery/tools/git-tools.ts +0 -55
  225. package/packs/software-delivery/tools/index.ts +0 -8
  226. package/packs/software-delivery/tools/lane-lock-tool.ts +0 -37
  227. package/packs/software-delivery/tools/types.ts +0 -71
  228. package/packs/software-delivery/tools/worktree-tools.ts +0 -49
  229. package/packs/software-delivery/vitest.config.ts +0 -11
@@ -2,7 +2,7 @@
2
2
 
3
3
  # Universal Agent Instructions
4
4
 
5
- **Last updated:** {{DATE}}
5
+ **Last updated:** {{DATE}} (5.6.0 — INIT-069 vocabulary: `--estimated-files`, `--interrupt-class`, `test-over-deletion`, `monolithic-file-contention`, `prod-migration-drift`, `db:journal-recover`, file-overlap & lane-fit preflight)
6
6
 
7
7
  > **Works with any AI coding assistant.** This file provides instructions that work regardless of which AI tool you're using -- Claude Code, Cursor, Windsurf, Cline, Codex, Aider, or any other. Just read this file and follow the workflow.
8
8
 
@@ -81,10 +81,22 @@ Capture findings in shared memory so other agents (and other vendors) can see th
81
81
 
82
82
  ```bash
83
83
  pnpm mem:create --type discovery --wu WU-XXXX --tags <csv> --priority P? --title '<prose>'
84
+ # Pre-Phase Audits (WU-2940): omit --wu to capture findings before any WU exists.
85
+ pnpm mem:create --type discovery --tags pre-phase-audit --title 'orchestrator finding'
84
86
  pnpm mem:signal '<short status>' --wu WU-XXXX
87
+ # WU-2940: --interrupt-class is canonical; --interrupt remains a back-compat alias.
88
+ pnpm mem:signal 'urgent: build break on main' --wu WU-XXXX --interrupt-class urgent
85
89
  pnpm mem:checkpoint --wu WU-XXXX --note '<progress>' --next-steps '<what is next>'
86
90
  ```
87
91
 
92
+ For mid-flight scope correction (WU-2940), `pnpm wu:edit` accepts sizing flags
93
+ (`--estimated-files`, `--estimated-tool-calls`, `--sizing-strategy`,
94
+ `--sizing-exception-type`, `--sizing-exception-reason`) and allows
95
+ non-destructive edits (`--description`, `--notes`, sizing) on in-progress WUs
96
+ **from the main checkout** without the wu:release/wu:recover dance. Structural
97
+ edits (`--lane`, `--code-paths` replace, `--priority`, `--type`,
98
+ `--initiative`) still require the dance.
99
+
88
100
  **Classification rule:** if the fact would help _another_ agent vendor working this repo, it belongs in shared memory (`mem:create`), not your vendor's personal memory folder. Examples of shared: bug discoveries, architecture decisions, CI/gates gotchas, initiative direction, cross-WU scope flags. Examples of vendor-personal: behavioural feedback, user preferences, tool-invocation mechanics.
89
101
 
90
102
  **Shell-quoting footgun:** always **single-quote** the `--title` value. Bash expands `$var`, `$0`, backticks, and `!hist` inside double-quoted strings, silently corrupting prose containing prices (`$49`), positional refs, or exclamation marks. See [Memory section in LUMENFLOW.md](LUMENFLOW.md#memory-shared-vs-vendor-personal) for the full mem:\* contract.
@@ -137,6 +149,17 @@ pnpm mem:triage --archive mem-XXXX --reason '<why>' # drop non-actio
137
149
 
138
150
  `wu:done` does not auto-triage. Unreviewed discoveries rot — always check the list before completing.
139
151
 
152
+ ### Database Workflow Primitives (WU-2945)
153
+
154
+ ```bash
155
+ pnpm db:journal-recover # detect drizzle journal collisions (dry-run by default)
156
+ pnpm db:journal-recover --confirm # apply resequence + write audit log under .lumenflow/db-recovery/
157
+ pnpm lumenflow:setup-prereqs # show external prereqs (e.g. Supabase CLI) — runs at end of pnpm setup
158
+ ```
159
+
160
+ - `db:journal-recover` is idempotent — re-running on a healthy chain is a no-op.
161
+ - If your project uses Supabase, install the Supabase CLI before `pnpm db:reset`.
162
+
140
163
  ## Quick Start (Cloud / Branch-PR)
141
164
 
142
165
  Cloud agents (Codex, Claude web, CI bots) that cannot use local worktrees use the **branch-pr** mode. This is a first-class lifecycle, not a workaround.
@@ -186,6 +209,13 @@ The completion workflow is a two-step process. Run `wu:prep` from the worktree (
186
209
 
187
210
  For detailed troubleshooting, see [troubleshooting-wu-done.md]({{DOCS_ONBOARDING_PATH}}/troubleshooting-wu-done.md).
188
211
 
212
+ **Plan-time preflight (WU-2942):** `pnpm orchestrate:initiative` emits two extra sections after the wave breakdown:
213
+
214
+ - _File-overlap preflight_ — WU pairs sharing a `code_paths` entry inside the same wave (silently serial; resolve before claim).
215
+ - _Lane-fit suggestions_ — WUs stacked on a WIP=1 lane with concrete re-lane recommendations from `wu:infer-lane`.
216
+
217
+ Read these BEFORE claiming a wave. Default mode is advisory; configure via `software_delivery.orchestrate.preflight: 'advisory' | 'strict' | 'off'` or pass `--strict-preflight` to make warnings blocking for one run. See [LUMENFLOW.md "Plan-time preflight"](LUMENFLOW.md#plan-time-preflight-wu-2942).
218
+
189
219
  ---
190
220
 
191
221
  ## Core Principles
@@ -196,8 +226,10 @@ For detailed troubleshooting, see [troubleshooting-wu-done.md]({{DOCS_ONBOARDING
196
226
  (`--skip-gate <name>`, repeatable) is the preferred bypass surface as of
197
227
  WU-2925; legacy `--skip-gates` is deprecated for one minor cycle. Only gates
198
228
  marked `skippable: true` (allow-list: `migration-verify`, `lane-health`,
199
- `tdd-diff-evidence`, plus the WU-2927 opt-in local-prep gates `build`,
200
- `integration-test`, `e2e-smoke`) or overridden via
229
+ `tdd-diff-evidence`, the WU-2927 opt-in local-prep gates `build`,
230
+ `integration-test`, `e2e-smoke`, the WU-2943 advisory heuristic gates
231
+ `test-over-deletion`, `monolithic-file-contention`, and the WU-2944
232
+ opt-in `prod-migration-drift` gate) or overridden via
201
233
  `software_delivery.gates.overrides.<gate_id>.skippable=true` may be skipped.
202
234
  Both flags require `--reason` and `--fix-wu`.
203
235
 
@@ -231,6 +263,31 @@ For detailed troubleshooting, see [troubleshooting-wu-done.md]({{DOCS_ONBOARDING
231
263
  See [LUMENFLOW.md gates section](LUMENFLOW.md#extended-local-prep-profile-wu-2927)
232
264
  for the schema and latency budget.
233
265
 
266
+ **Heuristic guard gates (WU-2943).** Two advisory gates run on every
267
+ `wu:prep`, both `skippable: true` (allow-listed) with
268
+ `prereq_strategy: 'auto-skip'`:
269
+ - `test-over-deletion` — flags deleted `.test.ts(x)` whose `describe()` /
270
+ `describe.each()` strings reference symbols outside the current WU's
271
+ `code_paths` (scope-creep deletion).
272
+ - `monolithic-file-contention` — flags files appearing in 2+ active WUs'
273
+ `code_paths` (statuses `in_progress` + `ready`) when the current WU is
274
+ one of the claimants. Output names the file, the contending WUs, and
275
+ suggests splitting the module to enable parallelism.
276
+
277
+ Both auto-skip cleanly when the heuristic does not fire. Per-repo override
278
+ to BLOCK on trigger:
279
+ `pnpm config:set software_delivery.gates.overrides.<gate>.skippable=false`.
280
+
281
+ **Prod-migration-drift gate (WU-2944).** Optional CI-scheduled gate that
282
+ detects when the production database is N+ migrations behind the canonical
283
+ `migrations/` directory on main. Auto-skips `not-applicable` until the
284
+ repo opts in by setting
285
+ `software_delivery.gates.overrides.prod-migration-drift.connection_string_env_var`
286
+ (e.g. `PROD_DATABASE_URL`). Requires the configured env var to be set and
287
+ the prod DB to be reachable; otherwise auto-skips under
288
+ `prereq_strategy: 'auto-skip'`. Threshold is configurable
289
+ (`overrides.prod-migration-drift.threshold`, default 5).
290
+
234
291
  4. **Never Bypass Hooks**: No `--no-verify`
235
292
  5. **Vendor-Agnostic Dirty-Main Guard**: `wu:prep` and `wu:done` hard-block when main has non-allowlisted dirty files during worktree WUs (including MCP/tool-originated writes). `branch-pr` mode is exempt.
236
293
  6. **Docs Parity For Behavior Changes**: If you change LumenFlow behavior or workflow guidance, update internal docs, Starlight docs, or both as appropriate. Generated reference docs do not cover narrative behavior changes by themselves.
@@ -276,6 +333,13 @@ LumenFlow enforces safety at the repository level via git wrappers and hooks. Fo
276
333
 
277
334
  **Key rule:** Always use `wu:` commands for worktree and branch management -- never raw `git worktree` or `git branch -D` commands. Use `wu:recover` for state inconsistencies, `wu:release` for abandoned WUs, and `wu:prune` for stale worktrees.
278
335
 
336
+ **WU-2941:** `wu:release` resets both the YAML status and the lane branch
337
+ (default: delete branch locally + on `origin`). Pass `--keep-branch` to retain
338
+ the branch for diagnostic recovery, or set
339
+ `software_delivery.wu_release.branch_action: 'keep'` in `workspace.yaml` for
340
+ air-gapped/offline repos. `wu:status` surfaces residual main↔worktree drift
341
+ inline with a `Run pnpm wu:recover --id WU-X --action reset` hint.
342
+
279
343
  ---
280
344
 
281
345
  ## Vendor-Specific Overlays
@@ -1,6 +1,6 @@
1
1
  # LumenFlow Workflow Guide
2
2
 
3
- **Last updated:** {{DATE}}
3
+ **Last updated:** {{DATE}} (5.6.0 — INIT-069: workflow ergonomics, smart guard gates, db primitives, prod drift CI gate)
4
4
 
5
5
  LumenFlow is a vendor-agnostic workflow framework for AI-native software development.
6
6
 
@@ -283,7 +283,8 @@ runtime metadata. When it is absent, receipt state falls back to the ephemeral
283
283
 
284
284
  A2A signal semantics are structured, not prose-only. `mem:signal` supports
285
285
  `--thread`, `--reply-to`, `--intent INFO|PROPOSE|COUNTER|AGREE|REJECT`,
286
- `--interrupt advisory|priority|urgent|soon|immediate`, and `--requires-ack`.
286
+ `--interrupt-class advisory|priority|urgent|soon|immediate` (canonical name;
287
+ `--interrupt` remains as a back-compat alias — WU-2940), and `--requires-ack`.
287
288
  Directed root signals default to `interrupt_class: priority` and get a generated
288
289
  `thread_id`; broadcasts default to `interrupt_class: advisory`. Replies inherit
289
290
  the parent thread unless `--thread` is explicit. When a recipient posts
@@ -543,28 +544,75 @@ For the full worktree lifecycle (parallel execution, bootstrap, isolation guaran
543
544
 
544
545
  ### WU Lifecycle
545
546
 
546
- | Command | Description |
547
- | ----------------------- | ----------------------------------------------------------------------- |
548
- | `pnpm wu:create` | Create new WU spec (ID auto-generated) |
549
- | `pnpm wu:claim` | Claim WU, update canonical state, create worktree (or `--cloud`) |
550
- | `pnpm wu:sandbox` | Run command through hardened WU sandbox backend |
551
- | `pnpm wu:prep` | Run gates in worktree, prep for wu:done |
552
- | `pnpm wu:done` | Complete WU (merge, stamp, cleanup) |
553
- | `pnpm wu:edit` | Edit WU spec fields |
554
- | `pnpm wu:block` | Block WU (transitions to blocked, frees lane) |
555
- | `pnpm wu:unblock` | Unblock WU (transitions to in_progress) |
556
- | `pnpm wu:release` | Release orphaned WU (in_progress to ready for reclaim) |
557
- | `pnpm wu:status` | Show WU status, location, and valid commands |
558
- | `pnpm wu:brief` | **MANDATORY after wu:claim.** Generate handoff prompt + record evidence |
559
- | `pnpm wu:delegate` | Generate prompt + record lineage + brief hash attestation |
560
- | `pnpm wu:validate` | Validate WU spec completeness and schema |
561
- | `pnpm wu:recover` | Analyze and fix WU state inconsistencies |
562
- | `pnpm wu:verify` | Verify WU completion (stamp, commit, clean tree) |
563
- | `pnpm wu:escalate` | Show or resolve WU escalation status |
564
- | `pnpm wu:delete` | Delete WU spec and cleanup |
565
- | `pnpm approval:request` | Request control-plane approval for a workflow action |
566
- | `pnpm approval:review` | Resolve a control-plane approval decision |
567
- | `pnpm approval:list` | List control-plane approvals with optional filters |
547
+ | Command | Description |
548
+ | ----------------------- | ------------------------------------------------------------------------------- |
549
+ | `pnpm wu:create` | Create new WU spec (ID auto-generated) |
550
+ | `pnpm wu:claim` | Claim WU, update canonical state, create worktree (or `--cloud`) |
551
+ | `pnpm wu:sandbox` | Run command through hardened WU sandbox backend |
552
+ | `pnpm wu:prep` | Run gates in worktree, prep for wu:done |
553
+ | `pnpm wu:done` | Complete WU (merge, stamp, cleanup) |
554
+ | `pnpm wu:edit` | Edit WU spec fields (sizing flags + non-destructive in-progress edits, WU-2940) |
555
+ | `pnpm wu:block` | Block WU (transitions to blocked, frees lane) |
556
+ | `pnpm wu:unblock` | Unblock WU (transitions to in_progress) |
557
+ | `pnpm wu:release` | Release orphaned WU (in_progress to ready) AND reset lane branch (WU-2941) |
558
+ | `pnpm wu:status` | Show WU status, location, and valid commands |
559
+ | `pnpm wu:brief` | **MANDATORY after wu:claim.** Generate handoff prompt + record evidence |
560
+ | `pnpm wu:delegate` | Generate prompt + record lineage + brief hash attestation |
561
+ | `pnpm wu:validate` | Validate WU spec completeness and schema |
562
+ | `pnpm wu:recover` | Analyze and fix WU state inconsistencies |
563
+ | `pnpm wu:verify` | Verify WU completion (stamp, commit, clean tree) |
564
+ | `pnpm wu:escalate` | Show or resolve WU escalation status |
565
+ | `pnpm wu:delete` | Delete WU spec and cleanup |
566
+ | `pnpm approval:request` | Request control-plane approval for a workflow action |
567
+ | `pnpm approval:review` | Resolve a control-plane approval decision |
568
+ | `pnpm approval:list` | List control-plane approvals with optional filters |
569
+
570
+ #### wu:edit ergonomics (WU-2940)
571
+
572
+ `pnpm wu:edit` accepts the full sizing-estimate surface — `--estimated-files`,
573
+ `--estimated-tool-calls`, `--sizing-strategy`, `--sizing-exception-type`,
574
+ `--sizing-exception-reason` — for mid-flight scope correction without a
575
+ wu:delete + wu:create dance. Each flag is independently editable; partial edits
576
+ merge into the existing `sizing_estimate` sub-object.
577
+
578
+ In-progress WUs accept non-destructive edits (`--description`, `--notes`, and
579
+ the five sizing flags above) **from the main checkout** via the micro-worktree
580
+ path. Other edits — `--lane`, `--code-paths` replace, `--priority`, `--type`,
581
+ `--initiative` — still require the existing `wu:release`/`wu:recover` dance
582
+ because they restructure scope.
583
+
584
+ #### wu:release branch reset (WU-2941)
585
+
586
+ `pnpm wu:release` now resets the **lane branch** alongside the WU YAML so
587
+ subsequent `pnpm wu:status` reports no main↔worktree drift. Default behaviour
588
+ is to delete the lane branch locally and on `origin`. Override with:
589
+
590
+ - `--keep-branch` flag — retain the branch for diagnostic recovery.
591
+ - `software_delivery.wu_release.branch_action` in `workspace.yaml` —
592
+ `'delete'` (default), `'rebase'` (hard-reset local ref to
593
+ `baseline_main_sha`, no remote push), or `'keep'` (no-op for
594
+ air-gapped/offline repos).
595
+
596
+ Cloud `claimed_mode: branch-pr` releases are unaffected; the claimed branch
597
+ is owned by the cloud lifecycle and never touched by `wu:release`.
598
+
599
+ `pnpm wu:status` surfaces residual main↔worktree drift inline as a
600
+ structured remediation hint (matching the `wu:done` preflight error format):
601
+ `WU-X: main shows ready, branch shows in_progress. Run pnpm wu:recover --id
602
+ WU-X --action reset to reconcile.`
603
+
604
+ #### Pre-WU memory artifacts (WU-2940)
605
+
606
+ `pnpm mem:create` accepts pre-WU artifacts: omit `--wu` to capture findings
607
+ produced before any WU exists (Pre-Phase Audits per the wu-sizing-guide §1.5).
608
+ The persisted record stores `wu_id` null/absent and remains discoverable via
609
+ tag-only `mem:context --tags <csv>` queries.
610
+
611
+ ```bash
612
+ # Capture an orchestrator finding before claiming the next WU
613
+ pnpm mem:create --type discovery --tags pre-phase-audit \
614
+ --title 'INIT-XXX wave 1 needs review-audit exception in WU-YYY'
615
+ ```
568
616
 
569
617
  ### WU Maintenance
570
618
 
@@ -614,9 +662,12 @@ pnpm wu:prep --id WU-XXX \
614
662
  ```
615
663
 
616
664
  **Allow-list (skippable: true by default):** `migration-verify`,
617
- `lane-health`, `tdd-diff-evidence`, plus the WU-2927 opt-in local-prep gates
618
- `build`, `integration-test`, `e2e-smoke`. All other framework gates default to
619
- `skippable: false` and require a per-repo override at
665
+ `lane-health`, `tdd-diff-evidence`, the WU-2927 opt-in local-prep gates
666
+ `build`, `integration-test`, `e2e-smoke`, the WU-2943 advisory heuristic
667
+ gates `test-over-deletion`, `monolithic-file-contention`, and the WU-2944
668
+ opt-in `prod-migration-drift` gate (CI-scheduled; auto-skips not-applicable
669
+ when `PROD_DATABASE_URL` is not configured). All other framework gates
670
+ default to `skippable: false` and require a per-repo override at
620
671
  `software_delivery.gates.overrides.<gate_id>.skippable=true` to be skipped:
621
672
 
622
673
  ```bash
@@ -714,6 +765,71 @@ with `source: 'agent'`.
714
765
  Backward-compat: omitting `local_prep` (the default) preserves today's
715
766
  behaviour — none of the new gates is applicable, no new commands run.
716
767
 
768
+ #### Heuristic guard gates (WU-2943)
769
+
770
+ Two advisory gates ride the INIT-068 contract on every `wu:prep`. Both are
771
+ `skippable_default: true` and `prereq_strategy: 'auto-skip'`, surface their
772
+ findings via the `failed` `GateResult` state, and emit the standard six-state
773
+ legend (`✅ / ❌ / ⏭ / ⊘ / 🚫`). Each gate is per-repo configurable via
774
+ `software_delivery.gates.overrides.<gate_id>`.
775
+
776
+ | Gate | Driver | When applicable | When it fails |
777
+ | ---------------------------- | ---------------- | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
778
+ | `test-over-deletion` | INIT-105 item 12 | wu:prep diff includes deleted `.test.ts` / `.test.tsx` | Any deleted test's `describe()` / `describe.each()` symbol is not present in the current WU's `code_paths`. Output lists each unrecognised symbol. |
779
+ | `monolithic-file-contention` | INIT-105 item 13 | Always (every `wu:prep`) | A file appears in 2+ active WUs' `code_paths` (statuses: `in_progress`, `ready`) and the current WU is one of the claimants. Output lists the file + claimant WUs + a re-laning hint. |
780
+
781
+ Both gates are advisory (`skippable_default: true`); intentional triggers can
782
+ be skipped with `--skip-gate test-over-deletion` / `--skip-gate
783
+ monolithic-file-contention` (with `--reason` and `--fix-wu` per WU-2925).
784
+ Repos that want either gate to BLOCK can flip the default:
785
+
786
+ ```bash
787
+ pnpm config:set software_delivery.gates.overrides.test-over-deletion.skippable=false
788
+ pnpm config:set software_delivery.gates.overrides.monolithic-file-contention.skippable=false
789
+ ```
790
+
791
+ Backward-compat: when the heuristic does not fire, both gates auto-skip
792
+ cleanly; existing `wu:prep` flows are unaffected.
793
+
794
+ #### prod-migration-drift gate (WU-2944)
795
+
796
+ Optional CI-scheduled gate (INIT-105 item 14). Compares the canonical
797
+ `migrations/` directory on main against the production database's applied
798
+ migrations and fails when drift exceeds a configurable threshold (default 5).
799
+ Driver: cloud INIT-105 — prod silently accumulated 7+ migrations of drift
800
+ before any human noticed; no automated detection caught it.
801
+
802
+ | Property | Value |
803
+ | --------------------- | ------------------------------------------------------------------------------------------------ |
804
+ | Gate name | `prod-migration-drift` |
805
+ | Allow-list status | `skippable: true` (advisory) |
806
+ | `prereq_strategy` | `auto-skip` |
807
+ | Default applicability | `not-applicable` until repo opts in via override (clean opt-out — most repos won't enable day 1) |
808
+ | Default threshold | `5` (drift > threshold ⇒ `failed`) |
809
+
810
+ Per-repo opt-in (set both keys; without `connection_string_env_var` the gate
811
+ auto-skips not-applicable):
812
+
813
+ ```bash
814
+ pnpm config:set software_delivery.gates.overrides.prod-migration-drift.connection_string_env_var=PROD_DATABASE_URL
815
+ pnpm config:set software_delivery.gates.overrides.prod-migration-drift.threshold=5
816
+ # Optional: tighten to BLOCK on drift instead of advisory --skip-gate
817
+ pnpm config:set software_delivery.gates.overrides.prod-migration-drift.skippable=false
818
+ ```
819
+
820
+ Local `wu:prep` impact: zero by default. The gate is registered in the
821
+ default registry alongside the WU-2943 heuristic gates, but
822
+ `checkApplicability` returns `not-applicable` for repos that have not set
823
+ `connection_string_env_var`, so it auto-skips cleanly. CI integration is
824
+ scheduled (e.g. daily) — never on every PR — because it requires prod
825
+ credentials and a reachable DB.
826
+
827
+ `@lumenflow/cli` ships without a Postgres driver dependency to keep the CLI
828
+ slim. The gate accepts a probe injection seam (`ctx.__prodDriftProbe`) used
829
+ by the scheduled CI workflow to query the prod DB's applied-migrations list;
830
+ when no probe is supplied (e.g. local `wu:prep`), the gate cleanly auto-skips
831
+ under `prereq_strategy: 'auto-skip'`.
832
+
717
833
  ### Lane Management
718
834
 
719
835
  | Command | Description |
@@ -788,29 +904,62 @@ behaviour — none of the new gates is applicable, no new commands run.
788
904
  | `pnpm agent:log-issue` | Log issue during agent session |
789
905
  | `pnpm agent:issues-query` | Query GitHub issues for agent work |
790
906
 
791
- **ADR-010 control-plane rule:** planning, handoff, execution, reconciliation, finish, and status are separate concerns. Prompt emission is not launch. Worker return is not WU completion. Reconcile live initiative truth against WU status, worktree / branch state, `wu:prep` / `wu:done` or `wu:cleanup`, stamps, and integrity evidence. `mem:inbox` remains the signal channel, not the whole execution ledger.
907
+ **ADR-010 control-plane rule:** planning, handoff, execution, reconciliation, finish, and status are separate concerns. Prompt emission is not launch. Worker return is not WU completion. Reconcile live initiative truth against WU status, worktree / branch state, `pnpm wu:prep` / `pnpm wu:done` or `pnpm wu:cleanup`, stamps, and integrity evidence. `mem:inbox` remains the signal channel, not the whole execution ledger.
908
+
909
+ #### Plan-time preflight (WU-2942)
910
+
911
+ `pnpm orchestrate:initiative` emits two preflight sections before agent dispatch so plan-time intelligence catches starvation that used to surface at claim time:
912
+
913
+ - **File-overlap preflight** — flags WU pairs (or N-tuples) inside a parallel-claimable wave that share a `code_paths` entry. `wu:claim` rejects overlap by default, so a wave with shared paths is silently serial. Resolution: serialize the WUs, split file ownership, or `--force-overlap` knowingly.
914
+ - **Lane-fit suggestions** — when N≥2 WUs in a wave share a lane whose `lock_policy` holds the lane lock (`all` or `active`), only one runs at a time. The advisor calls the same logic as `pnpm wu:infer-lane` and recommends a re-lane via `pnpm wu:edit --id <WU> --lane '<inferred>'` for the contended WUs.
915
+
916
+ Modes (set via `software_delivery.orchestrate.preflight` in `workspace.yaml`, default `advisory`):
917
+
918
+ - `advisory` — emit warnings and proceed.
919
+ - `strict` — emit warnings and exit non-zero (also enabled per-run with `--strict-preflight`).
920
+ - `off` — skip preflight scans entirely.
792
921
 
793
922
  ### Setup & Configuration
794
923
 
795
- | Command | Description |
796
- | ------------------------------- | ----------------------------------------------------- |
797
- | `pnpm setup` | Install deps and build CLI (first time) |
798
- | `pnpm lumenflow` | Initialize LumenFlow in a project |
799
- | `pnpm lumenflow:doctor` | Diagnose LumenFlow configuration |
800
- | `pnpm lumenflow:commands` | List all available CLI commands |
801
- | `pnpm lumenflow:release` | Run release workflow |
802
- | `pnpm distribution:preflight` | Audit consumer repos for npm-access flip readiness |
803
- | `pnpm lumenflow:docs-sync` | Refresh core docs, onboarding docs, and vendor assets |
804
- | `pnpm lumenflow:sync-templates` | Sync templates to project |
805
- | `pnpm lumenflow:upgrade` | Upgrade LumenFlow packages |
806
- | `pnpm lumenflow:integrate` | Generate enforcement hooks for client |
807
- | `pnpm lumenflow:disable` | Reversibly turn enforcement off (hooks become no-ops) |
808
- | `pnpm lumenflow:enable` | Re-enable enforcement after `lumenflow:disable` |
809
- | `pnpm lumenflow:uninstall` | Remove LumenFlow files (dry-run by default) |
810
- | `pnpm config:set` | Safely update workspace.yaml via micro-worktree |
811
- | `pnpm config:get` | Read a value from workspace.yaml |
812
- | `pnpm cloud:connect` | Connect workspace.yaml to cloud control plane |
813
- | `pnpm backlog:prune` | Clean stale backlog entries |
924
+ | Command | Description |
925
+ | ------------------------------- | ------------------------------------------------------- |
926
+ | `pnpm setup` | Install deps and build CLI (first time) |
927
+ | `pnpm lumenflow` | Initialize LumenFlow in a project |
928
+ | `pnpm lumenflow:doctor` | Diagnose LumenFlow configuration |
929
+ | `pnpm lumenflow:commands` | List all available CLI commands |
930
+ | `pnpm lumenflow:release` | Run release workflow |
931
+ | `pnpm lumenflow:docs-sync` | Refresh core docs, onboarding docs, and vendor assets |
932
+ | `pnpm lumenflow:sync-templates` | Sync templates to project |
933
+ | `pnpm lumenflow:upgrade` | Upgrade LumenFlow packages |
934
+ | `pnpm lumenflow:integrate` | Generate enforcement hooks for client |
935
+ | `pnpm lumenflow:disable` | Reversibly turn enforcement off (hooks become no-ops) |
936
+ | `pnpm lumenflow:enable` | Re-enable enforcement after `lumenflow:disable` |
937
+ | `pnpm lumenflow:uninstall` | Remove LumenFlow files (dry-run by default) |
938
+ | `pnpm config:set` | Safely update workspace.yaml via micro-worktree |
939
+ | `pnpm config:get` | Read a value from workspace.yaml |
940
+ | `pnpm cloud:connect` | Connect workspace.yaml to cloud control plane |
941
+ | `pnpm backlog:prune` | Clean stale backlog entries |
942
+ | `pnpm db:journal-recover` | Recover drizzle journal collisions (dry-run default) |
943
+ | `pnpm lumenflow:setup-prereqs` | Surface external prereqs (e.g. Supabase CLI) post-setup |
944
+
945
+ #### db:journal-recover (WU-2945)
946
+
947
+ When two drizzle snapshots share the same `prevId` (chain fork) or journal entries share an `idx`,
948
+ `pnpm db:journal-recover` detects the collision and offers a safe-resequence path. Default mode is
949
+ dry-run; pass `--confirm` to apply. Every actual rewrite emits an audit log under
950
+ `.lumenflow/db-recovery/<timestamp>.log`. Idempotent — re-running on a healthy chain is a no-op.
951
+
952
+ ```bash
953
+ pnpm db:journal-recover # show plan
954
+ pnpm db:journal-recover --drizzle-dir packages/db/drizzle
955
+ pnpm db:journal-recover --confirm # apply, write audit log
956
+ ```
957
+
958
+ #### Supabase CLI prerequisite (WU-2945)
959
+
960
+ `pnpm setup` now runs `lumenflow:setup-prereqs` at the end. When the project uses Supabase
961
+ (via `supabase/config.toml` or `@supabase/*` deps), it surfaces a reminder that the Supabase CLI
962
+ is required for `pnpm db:reset` and similar workflows. Non-blocking informational only.
814
963
 
815
964
  ### Metrics & Flow
816
965
 
@@ -1,230 +0,0 @@
1
- #!/usr/bin/env node
2
- // Copyright (c) 2026 Hellmai Ltd
3
- // SPDX-License-Identifier: LicenseRef-LumenFlow-Proprietary
4
- /**
5
- * distribution:preflight CLI Command (WU-2898)
6
- *
7
- * Audits every consumer repo that installs @lumenflow/* and reports whether
8
- * each one is ready for the npm-access flip from "public" to "restricted".
9
- *
10
- * For each consumer the preflight checks:
11
- * - GitHub: NPM_TOKEN is registered as an Actions secret on the repo
12
- * (`gh api repos/<repo>/actions/secrets`), and the repo has a `.npmrc`
13
- * wired to the token.
14
- * - Azure DevOps: prints a documented manual-check instruction. We cannot
15
- * read pipeline secret variables programmatically without standing up an
16
- * `az` PAT, so the report flags Azure rows as "needs manual check" so
17
- * Tom can verify in the Azure DevOps web UI before the flip.
18
- *
19
- * Exits non-zero if any GitHub consumer is missing wiring, or if any Azure
20
- * DevOps row is unverified. Print a single table and a copy/paste remediation
21
- * footer so an operator can fix the gap and re-run.
22
- *
23
- * Usage:
24
- * pnpm distribution:preflight
25
- * pnpm distribution:preflight --json
26
- */
27
- import { execFileSync } from 'node:child_process';
28
- import { WU_STATUS } from '@lumenflow/core';
29
- import chalk from 'chalk';
30
- import Table from 'cli-table3';
31
- import { Command } from 'commander';
32
- export const DEFAULT_CONSUMERS = [
33
- { kind: 'github', repo: 'hellmai/lumenflow-dev' },
34
- { kind: 'github', repo: 'hellmai/lumenflow-cloud' },
35
- { kind: 'github', repo: 'hellmai/patientpath.co.uk' },
36
- { kind: 'azure-devops', project: 'skynexe', repo: 'skynexe-hub' },
37
- { kind: 'azure-devops', project: 'skynexe', repo: 'skynexe-fund-analyser' },
38
- { kind: 'azure-devops', project: 'skynexe', repo: 'skynexe-risk' },
39
- ];
40
- /**
41
- * Production GitHub probe. Shells out to `gh` (already required by other
42
- * lifecycle commands), expecting the operator to be authenticated.
43
- */
44
- export const defaultRunGh = (args) => {
45
- // sonarjs/no-os-command-from-path: this is a developer-machine CLI tool
46
- // intentionally invoked by name so the operator's `gh` install (homebrew,
47
- // apt, scoop, etc.) is picked up. Hard-coding an absolute path would
48
- // break portability across the Mac/Linux/CI surfaces this script targets.
49
- // eslint-disable-next-line sonarjs/no-os-command-from-path
50
- return execFileSync('gh', args, {
51
- encoding: 'utf8',
52
- stdio: ['ignore', 'pipe', 'pipe'],
53
- });
54
- };
55
- const NPM_TOKEN_NAME = 'NPM_TOKEN';
56
- /**
57
- * Probe a single GitHub consumer. Returns a ConsumerReport row.
58
- *
59
- * Failure semantics: if `gh` is unauthenticated or the repo is unreachable,
60
- * we return `npmTokenPresent: false` with a diagnostic note rather than
61
- * throwing — the preflight should report ALL consumer states in one pass,
62
- * not abort on the first error.
63
- */
64
- export function probeGitHubConsumer(consumer, deps) {
65
- const repo = consumer.repo;
66
- let npmTokenPresent = false;
67
- let npmrcWired = false;
68
- const notes = [];
69
- // Check 1: repo Actions secrets contain NPM_TOKEN
70
- try {
71
- const stdout = deps.runGh(['api', `repos/${repo}/actions/secrets`]);
72
- const parsed = JSON.parse(stdout);
73
- const secrets = parsed.secrets ?? [];
74
- npmTokenPresent = secrets.some((s) => s.name === NPM_TOKEN_NAME);
75
- if (!npmTokenPresent) {
76
- notes.push(`secret ${NPM_TOKEN_NAME} not found on ${repo}`);
77
- }
78
- }
79
- catch (error) {
80
- notes.push(`gh secrets probe failed: ${error instanceof Error ? error.message : String(error)}`);
81
- }
82
- // Check 2: repo root contains a .npmrc with NPM_TOKEN auth
83
- try {
84
- const stdout = deps.runGh(['api', `repos/${repo}/contents/.npmrc`]);
85
- const parsed = JSON.parse(stdout);
86
- if (parsed.content && parsed.encoding === 'base64') {
87
- const decoded = Buffer.from(parsed.content, 'base64').toString('utf8');
88
- npmrcWired = /_authToken=\$\{?NPM_TOKEN\}?/.test(decoded);
89
- if (!npmrcWired) {
90
- notes.push(`.npmrc found on ${repo} but does not reference NPM_TOKEN`);
91
- }
92
- }
93
- else {
94
- notes.push(`.npmrc payload on ${repo} not in expected base64 shape`);
95
- }
96
- }
97
- catch (error) {
98
- notes.push(`.npmrc probe failed (${repo}): ${error instanceof Error ? error.message : String(error)}`);
99
- }
100
- return {
101
- repo,
102
- platform: 'github',
103
- npmTokenPresent,
104
- npmrcWired,
105
- readyToFlip: npmTokenPresent && npmrcWired,
106
- note: notes.join('; ') || 'OK',
107
- };
108
- }
109
- /**
110
- * Build the manual-check report row for an Azure DevOps consumer.
111
- *
112
- * We deliberately do NOT shell out to `az` here. Authenticating the Azure
113
- * DevOps CLI requires per-developer PAT setup that the LumenFlow CI surface
114
- * does not own. Treating the row as "needs manual check" is honest — a false
115
- * green here would directly cause the consumer pipelines to break on the
116
- * privatization flip.
117
- */
118
- export function buildAzureDevOpsRow(consumer) {
119
- const repo = `${consumer.project}/${consumer.repo}`;
120
- return {
121
- repo,
122
- platform: 'azure-devops',
123
- npmTokenPresent: null,
124
- npmrcWired: null,
125
- readyToFlip: null,
126
- note: 'manual check required — verify pipeline variable NPM_TOKEN is set as a secret ' +
127
- 'and that .npmrc is wired in the repo root',
128
- };
129
- }
130
- /**
131
- * Run the full preflight against the supplied consumer list. Returns the
132
- * raw report rows — formatting/exit-code policy is applied by `main`.
133
- */
134
- export function runPreflight(consumers, deps = { runGh: defaultRunGh }) {
135
- return consumers.map((c) => c.kind === 'github' ? probeGitHubConsumer(c, deps) : buildAzureDevOpsRow(c));
136
- }
137
- /**
138
- * Pretty-print the report as an aligned ASCII table.
139
- *
140
- * The status column uses three glyphs:
141
- * ready = both checks passed (GitHub) — safe to flip
142
- * FAIL = GitHub repo missing NPM_TOKEN or .npmrc — fix before flip
143
- * manual = Azure DevOps row — operator must verify out-of-band
144
- */
145
- export function formatReport(rows) {
146
- const table = new Table({
147
- head: ['Consumer', 'Platform', 'NPM_TOKEN', '.npmrc', 'Ready', 'Note'],
148
- style: { head: [] },
149
- });
150
- const triBool = (v) => {
151
- if (v === null)
152
- return chalk.yellow('manual');
153
- return v ? chalk.green('yes') : chalk.red('no');
154
- };
155
- const status = (row) => {
156
- if (row.readyToFlip === null)
157
- return chalk.yellow('manual');
158
- if (row.readyToFlip)
159
- return chalk.green(WU_STATUS.READY);
160
- return chalk.red('FAIL');
161
- };
162
- for (const row of rows) {
163
- table.push([
164
- row.repo,
165
- row.platform,
166
- triBool(row.npmTokenPresent),
167
- triBool(row.npmrcWired),
168
- status(row),
169
- row.note,
170
- ]);
171
- }
172
- return table.toString();
173
- }
174
- /**
175
- * Compute the exit code policy. Non-zero if any GitHub row failed or any
176
- * row needs manual verification (since the operator MUST acknowledge the
177
- * Azure rows before the flip is safe).
178
- */
179
- export function computeExitCode(rows) {
180
- const anyFail = rows.some((r) => r.readyToFlip === false);
181
- const anyManual = rows.some((r) => r.readyToFlip === null);
182
- return anyFail || anyManual ? 1 : 0;
183
- }
184
- /**
185
- * CLI entry point. Returns the resolved exit code so tests can assert it
186
- * without spawning a subprocess.
187
- */
188
- export function main(argv = process.argv, deps = { runGh: defaultRunGh }, log = (m) => console.log(m)) {
189
- const program = new Command()
190
- .name('distribution-preflight')
191
- .description('Audit consumer repos for @lumenflow/* privatization readiness (WU-2898)')
192
- .option('--json', 'Emit raw JSON instead of an ASCII table', false)
193
- .parse(argv);
194
- const opts = program.opts();
195
- const rows = runPreflight(DEFAULT_CONSUMERS, deps);
196
- if (opts.json) {
197
- log(JSON.stringify(rows, null, 2));
198
- }
199
- else {
200
- log(formatReport(rows));
201
- const failed = rows.filter((r) => r.readyToFlip === false);
202
- const manual = rows.filter((r) => r.readyToFlip === null);
203
- if (failed.length > 0) {
204
- log('');
205
- log(chalk.red(`✗ ${failed.length} GitHub consumer(s) not ready:`));
206
- for (const r of failed)
207
- log(` - ${r.repo}: ${r.note}`);
208
- }
209
- if (manual.length > 0) {
210
- log('');
211
- log(chalk.yellow(`⚠ ${manual.length} Azure DevOps consumer(s) need manual verification:`));
212
- for (const r of manual)
213
- log(` - ${r.repo}: ${r.note}`);
214
- }
215
- if (failed.length === 0 && manual.length === 0) {
216
- log('');
217
- log(chalk.green('✓ all consumers ready to flip'));
218
- }
219
- }
220
- return computeExitCode(rows);
221
- }
222
- // Run when invoked directly (not when imported by tests).
223
- const isDirectInvocation = import.meta.url === `file://${process.argv[1]}` ||
224
- // tsup bundles the CLI as ESM with the bin shim — argv[1] still ends with
225
- // distribution-preflight.js so this guard remains correct after build.
226
- process.argv[1]?.endsWith('distribution-preflight.js');
227
- if (isDirectInvocation) {
228
- process.exit(main());
229
- }
230
- //# sourceMappingURL=distribution-preflight.js.map