@lumenflow/core 2.2.2 → 2.3.2

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 (213) hide show
  1. package/dist/active-wu-detector.d.ts +1 -1
  2. package/dist/active-wu-detector.js +1 -1
  3. package/dist/arg-parser.js +51 -18
  4. package/dist/backlog-generator.d.ts +4 -4
  5. package/dist/backlog-generator.js +4 -4
  6. package/dist/backlog-sync-validator.js +1 -1
  7. package/dist/cleanup-lock.d.ts +9 -2
  8. package/dist/cleanup-lock.js +17 -7
  9. package/dist/code-path-validator.d.ts +3 -3
  10. package/dist/code-path-validator.js +3 -3
  11. package/dist/compliance-parser.d.ts +1 -1
  12. package/dist/compliance-parser.js +1 -1
  13. package/dist/constants/backlog-patterns.d.ts +1 -1
  14. package/dist/constants/backlog-patterns.js +1 -1
  15. package/dist/constants/dora-constants.d.ts +1 -1
  16. package/dist/constants/dora-constants.js +1 -1
  17. package/dist/constants/gate-constants.d.ts +1 -1
  18. package/dist/constants/gate-constants.js +1 -1
  19. package/dist/constants/linter-constants.d.ts +1 -1
  20. package/dist/constants/linter-constants.js +1 -1
  21. package/dist/constants/tokenizer-constants.d.ts +1 -1
  22. package/dist/constants/tokenizer-constants.js +1 -1
  23. package/dist/context/location-resolver.js +2 -1
  24. package/dist/context-validation-integration.d.ts +1 -0
  25. package/dist/core/scope-checker.d.ts +3 -3
  26. package/dist/core/scope-checker.js +3 -3
  27. package/dist/core/tool-runner.d.ts +5 -5
  28. package/dist/core/tool-runner.js +5 -5
  29. package/dist/core/tool.constants.d.ts +1 -1
  30. package/dist/core/tool.constants.js +1 -1
  31. package/dist/core/tool.schemas.d.ts +2 -2
  32. package/dist/core/tool.schemas.js +1 -1
  33. package/dist/core/worktree-guard.d.ts +1 -1
  34. package/dist/core/worktree-guard.js +1 -1
  35. package/dist/coverage-gate.d.ts +12 -3
  36. package/dist/coverage-gate.js +15 -8
  37. package/dist/date-utils.d.ts +4 -4
  38. package/dist/date-utils.js +4 -4
  39. package/dist/dependency-graph.d.ts +6 -0
  40. package/dist/dependency-graph.js +43 -2
  41. package/dist/dependency-guard.d.ts +2 -2
  42. package/dist/dependency-guard.js +3 -3
  43. package/dist/dependency-validator.d.ts +4 -4
  44. package/dist/dependency-validator.js +4 -7
  45. package/dist/domain/orchestration.constants.d.ts +31 -10
  46. package/dist/domain/orchestration.constants.js +45 -16
  47. package/dist/domain/orchestration.schemas.d.ts +54 -28
  48. package/dist/domain/orchestration.schemas.js +2 -2
  49. package/dist/domain/orchestration.types.d.ts +2 -2
  50. package/dist/domain/orchestration.types.js +2 -2
  51. package/dist/error-handler.d.ts +10 -10
  52. package/dist/error-handler.js +10 -10
  53. package/dist/file-classifiers.d.ts +6 -6
  54. package/dist/file-classifiers.js +6 -6
  55. package/dist/gates-config.d.ts +74 -0
  56. package/dist/gates-config.js +209 -2
  57. package/dist/git-adapter.d.ts +11 -11
  58. package/dist/git-adapter.js +11 -11
  59. package/dist/git-context-extractor.d.ts +112 -0
  60. package/dist/git-context-extractor.js +559 -0
  61. package/dist/hardcoded-strings.d.ts +1 -1
  62. package/dist/hardcoded-strings.js +1 -1
  63. package/dist/incremental-lint.d.ts +1 -1
  64. package/dist/incremental-lint.js +2 -2
  65. package/dist/incremental-test.d.ts +1 -1
  66. package/dist/incremental-test.js +1 -1
  67. package/dist/index.d.ts +13 -0
  68. package/dist/index.js +25 -0
  69. package/dist/invariants/check-automated-tests.d.ts +2 -2
  70. package/dist/invariants/check-automated-tests.js +3 -3
  71. package/dist/lane-checker.d.ts +28 -7
  72. package/dist/lane-checker.js +316 -159
  73. package/dist/lane-suggest-prompt.d.ts +108 -0
  74. package/dist/lane-suggest-prompt.js +359 -0
  75. package/dist/lane-validator.d.ts +3 -3
  76. package/dist/lane-validator.js +3 -3
  77. package/dist/logs-lib.d.ts +1 -1
  78. package/dist/logs-lib.js +1 -1
  79. package/dist/lumenflow-config-schema.d.ts +162 -0
  80. package/dist/lumenflow-config-schema.js +180 -0
  81. package/dist/manual-test-validator.d.ts +2 -2
  82. package/dist/manual-test-validator.js +3 -3
  83. package/dist/merge-lock.d.ts +8 -1
  84. package/dist/merge-lock.js +16 -7
  85. package/dist/micro-worktree.d.ts +81 -13
  86. package/dist/micro-worktree.js +98 -17
  87. package/dist/migration-deployer.d.ts +1 -1
  88. package/dist/migration-deployer.js +1 -1
  89. package/dist/orchestration-advisory-loader.d.ts +2 -2
  90. package/dist/orchestration-advisory-loader.js +10 -6
  91. package/dist/orchestration-advisory.d.ts +3 -3
  92. package/dist/orchestration-advisory.js +4 -4
  93. package/dist/orchestration-di.d.ts +4 -4
  94. package/dist/orchestration-di.js +4 -4
  95. package/dist/orchestration-rules.d.ts +4 -4
  96. package/dist/orchestration-rules.js +18 -10
  97. package/dist/orphan-detector.d.ts +3 -3
  98. package/dist/orphan-detector.js +3 -3
  99. package/dist/patrol-loop.d.ts +170 -0
  100. package/dist/patrol-loop.js +186 -0
  101. package/dist/process-detector.d.ts +5 -5
  102. package/dist/process-detector.js +5 -5
  103. package/dist/rebase-artifact-cleanup.d.ts +3 -3
  104. package/dist/rebase-artifact-cleanup.js +3 -3
  105. package/dist/resolve-policy.d.ts +195 -0
  106. package/dist/resolve-policy.js +203 -0
  107. package/dist/risk-detector.d.ts +2 -2
  108. package/dist/risk-detector.js +2 -2
  109. package/dist/rollback-utils.d.ts +1 -1
  110. package/dist/rollback-utils.js +1 -1
  111. package/dist/section-headings.d.ts +1 -1
  112. package/dist/section-headings.js +1 -1
  113. package/dist/spawn-escalation.d.ts +4 -4
  114. package/dist/spawn-escalation.js +3 -3
  115. package/dist/spawn-monitor.d.ts +4 -4
  116. package/dist/spawn-monitor.js +4 -4
  117. package/dist/spawn-recovery.d.ts +3 -3
  118. package/dist/spawn-recovery.js +3 -3
  119. package/dist/spawn-registry-schema.d.ts +2 -2
  120. package/dist/spawn-registry-schema.js +2 -2
  121. package/dist/spawn-registry-store.d.ts +2 -2
  122. package/dist/spawn-registry-store.js +2 -2
  123. package/dist/spawn-strategy.d.ts +17 -11
  124. package/dist/spawn-strategy.js +47 -44
  125. package/dist/spawn-tree.d.ts +3 -3
  126. package/dist/spawn-tree.js +3 -3
  127. package/dist/state-cleanup-core.d.ts +205 -0
  128. package/dist/state-cleanup-core.js +240 -0
  129. package/dist/state-doctor-core.d.ts +168 -0
  130. package/dist/state-doctor-core.js +251 -0
  131. package/dist/stream-error-handler.d.ts +67 -0
  132. package/dist/stream-error-handler.js +94 -0
  133. package/dist/telemetry.d.ts +1 -1
  134. package/dist/telemetry.js +1 -1
  135. package/dist/template-loader.d.ts +162 -0
  136. package/dist/template-loader.js +372 -0
  137. package/dist/test-baseline.d.ts +176 -0
  138. package/dist/test-baseline.js +282 -0
  139. package/dist/usecases/get-suggestions.usecase.d.ts +1 -1
  140. package/dist/validation/command-registry.js +37 -0
  141. package/dist/validators/backlog-sync.js +4 -2
  142. package/dist/worktree-scanner.d.ts +1 -1
  143. package/dist/worktree-scanner.js +1 -1
  144. package/dist/worktree-symlink.d.ts +3 -3
  145. package/dist/worktree-symlink.js +3 -3
  146. package/dist/wu-backlog-updater.d.ts +1 -1
  147. package/dist/wu-backlog-updater.js +1 -1
  148. package/dist/wu-claim-helpers.d.ts +1 -1
  149. package/dist/wu-claim-helpers.js +1 -1
  150. package/dist/wu-claim-resume.d.ts +1 -1
  151. package/dist/wu-claim-resume.js +1 -1
  152. package/dist/wu-consistency-checker.d.ts +1 -1
  153. package/dist/wu-consistency-checker.js +17 -11
  154. package/dist/wu-constants.d.ts +73 -21
  155. package/dist/wu-constants.js +65 -22
  156. package/dist/wu-done-branch-only.d.ts +1 -1
  157. package/dist/wu-done-branch-only.js +1 -1
  158. package/dist/wu-done-docs-generate.d.ts +1 -1
  159. package/dist/wu-done-docs-generate.js +1 -1
  160. package/dist/wu-done-messages.d.ts +2 -2
  161. package/dist/wu-done-messages.js +2 -2
  162. package/dist/wu-done-metadata.d.ts +3 -3
  163. package/dist/wu-done-metadata.js +3 -3
  164. package/dist/wu-done-pr.d.ts +1 -1
  165. package/dist/wu-done-pr.js +4 -2
  166. package/dist/wu-done-preflight.d.ts +8 -0
  167. package/dist/wu-done-preflight.js +18 -2
  168. package/dist/wu-done-ui.d.ts +3 -3
  169. package/dist/wu-done-ui.js +3 -3
  170. package/dist/wu-done-validation.d.ts +30 -0
  171. package/dist/wu-done-validation.js +106 -1
  172. package/dist/wu-done-worktree.d.ts +1 -1
  173. package/dist/wu-done-worktree.js +11 -1
  174. package/dist/wu-events-cleanup.d.ts +148 -0
  175. package/dist/wu-events-cleanup.js +401 -0
  176. package/dist/wu-helpers.d.ts +2 -2
  177. package/dist/wu-helpers.js +2 -2
  178. package/dist/wu-id-generator.d.ts +58 -0
  179. package/dist/wu-id-generator.js +103 -0
  180. package/dist/wu-lint.js +1 -1
  181. package/dist/wu-preflight-validators.d.ts +13 -1
  182. package/dist/wu-preflight-validators.js +56 -1
  183. package/dist/wu-recovery.d.ts +2 -2
  184. package/dist/wu-recovery.js +4 -4
  185. package/dist/wu-repair-core.d.ts +5 -5
  186. package/dist/wu-repair-core.js +6 -6
  187. package/dist/wu-schema-normalization.d.ts +1 -1
  188. package/dist/wu-schema-normalization.js +1 -1
  189. package/dist/wu-schema.d.ts +7 -7
  190. package/dist/wu-schema.js +8 -8
  191. package/dist/wu-spawn-context.d.ts +87 -0
  192. package/dist/wu-spawn-context.js +175 -0
  193. package/dist/wu-spawn-helpers.d.ts +1 -1
  194. package/dist/wu-spawn-helpers.js +1 -1
  195. package/dist/wu-spawn.d.ts +177 -4
  196. package/dist/wu-spawn.js +694 -72
  197. package/dist/wu-state-schema.d.ts +1 -1
  198. package/dist/wu-state-schema.js +1 -1
  199. package/dist/wu-state-store.d.ts +3 -3
  200. package/dist/wu-state-store.js +3 -3
  201. package/dist/wu-status-transition.d.ts +1 -1
  202. package/dist/wu-status-transition.js +1 -1
  203. package/dist/wu-status-updater.d.ts +3 -3
  204. package/dist/wu-status-updater.js +3 -3
  205. package/dist/wu-validation-constants.d.ts +2 -2
  206. package/dist/wu-validation-constants.js +2 -2
  207. package/dist/wu-validation.d.ts +3 -3
  208. package/dist/wu-validation.js +3 -3
  209. package/dist/wu-yaml-fixer.d.ts +2 -2
  210. package/dist/wu-yaml-fixer.js +3 -3
  211. package/dist/wu-yaml.d.ts +23 -0
  212. package/dist/wu-yaml.js +76 -2
  213. package/package.json +5 -2
@@ -0,0 +1,282 @@
1
+ /**
2
+ * Test Baseline - Test Ratchet Pattern (WU-1253)
3
+ *
4
+ * Implements a "ratchet" pattern for test failures:
5
+ * - Track known failures in a baseline file (.lumenflow/test-baseline.json)
6
+ * - Block NEW failures (not in baseline)
7
+ * - Allow pre-existing failures with warning
8
+ * - Auto-update baseline when tests are fixed (ratchet forward)
9
+ *
10
+ * This enables agents to work on WUs without being blocked by unrelated
11
+ * test failures, while still preventing introduction of NEW failures.
12
+ *
13
+ * @see https://lumenflow.dev/reference/test-ratchet/
14
+ */
15
+ import { z } from 'zod';
16
+ import { parseISO, isValid } from 'date-fns';
17
+ // ============================================================================
18
+ // Constants
19
+ // ============================================================================
20
+ /** Default path for the test baseline file */
21
+ export const DEFAULT_BASELINE_PATH = '.lumenflow/test-baseline.json';
22
+ /** Environment variable to override baseline path */
23
+ export const BASELINE_PATH_ENV = 'LUMENFLOW_TEST_BASELINE';
24
+ /** Current schema version */
25
+ export const BASELINE_VERSION = 1;
26
+ /**
27
+ * Zod schema for ISO8601 datetime strings.
28
+ * Uses date-fns (well-maintained library) for validation instead of regex.
29
+ */
30
+ const isoDateTimeString = z.string().refine((val) => {
31
+ const parsed = parseISO(val);
32
+ return isValid(parsed);
33
+ }, { message: 'Invalid ISO8601 datetime string' });
34
+ // ============================================================================
35
+ // Schema Definitions
36
+ // ============================================================================
37
+ /**
38
+ * Schema for a known test failure entry
39
+ */
40
+ export const KnownFailureSchema = z.object({
41
+ /** Name of the failing test (describe > it format) */
42
+ test_name: z.string().min(1),
43
+ /** Path to the test file */
44
+ file_path: z.string().min(1),
45
+ /** Why this failure is in the baseline */
46
+ failure_reason: z.string().min(1),
47
+ /** When this failure was added to baseline */
48
+ added_at: isoDateTimeString,
49
+ /** Which WU added this to baseline */
50
+ added_by_wu: z.string().regex(/^WU-\d+$/),
51
+ /** WU expected to fix this failure (optional) */
52
+ expected_fix_wu: z
53
+ .string()
54
+ .regex(/^WU-\d+$/)
55
+ .optional(),
56
+ /** Optional reason for skipping this test */
57
+ skip_reason: z.string().optional(),
58
+ });
59
+ /**
60
+ * Schema for baseline statistics
61
+ */
62
+ export const BaselineStatsSchema = z.object({
63
+ /** Total number of known failures */
64
+ total_known_failures: z.number().int().min(0),
65
+ /** Last time the baseline ratcheted forward (tests fixed) */
66
+ last_ratchet_forward: isoDateTimeString.optional(),
67
+ });
68
+ /**
69
+ * Schema for the complete test baseline file
70
+ */
71
+ export const TestBaselineSchema = z.object({
72
+ /** Schema version for future migrations */
73
+ version: z.literal(BASELINE_VERSION),
74
+ /** When the baseline was last updated */
75
+ updated_at: isoDateTimeString,
76
+ /** Which WU last updated the baseline */
77
+ updated_by: z.string().regex(/^WU-\d+$/),
78
+ /** List of known test failures */
79
+ known_failures: z.array(KnownFailureSchema),
80
+ /** Baseline statistics */
81
+ stats: BaselineStatsSchema,
82
+ });
83
+ // ============================================================================
84
+ // Core Functions
85
+ // ============================================================================
86
+ /**
87
+ * Parse a test baseline JSON string
88
+ *
89
+ * @param json - JSON string content of baseline file
90
+ * @returns Parse result with baseline data or error
91
+ */
92
+ export function parseTestBaseline(json) {
93
+ let parsed;
94
+ try {
95
+ parsed = JSON.parse(json);
96
+ }
97
+ catch (e) {
98
+ return {
99
+ success: false,
100
+ error: `Invalid JSON: ${e instanceof Error ? e.message : 'Unknown error'}`,
101
+ };
102
+ }
103
+ const result = TestBaselineSchema.safeParse(parsed);
104
+ if (!result.success) {
105
+ return {
106
+ success: false,
107
+ error: `Schema validation failed: ${result.error.message}`,
108
+ };
109
+ }
110
+ return { success: true, data: result.data };
111
+ }
112
+ /**
113
+ * Create a new test baseline
114
+ *
115
+ * @param wuId - WU creating the baseline
116
+ * @param initialFailures - Optional initial failures to add
117
+ * @returns New test baseline
118
+ */
119
+ export function createTestBaseline(wuId, initialFailures) {
120
+ const now = new Date().toISOString();
121
+ const knownFailures = (initialFailures ?? []).map((f) => ({
122
+ test_name: f.test_name,
123
+ file_path: f.file_path,
124
+ failure_reason: f.failure_reason,
125
+ added_at: now,
126
+ added_by_wu: wuId,
127
+ expected_fix_wu: f.expected_fix_wu,
128
+ }));
129
+ return {
130
+ version: BASELINE_VERSION,
131
+ updated_at: now,
132
+ updated_by: wuId,
133
+ known_failures: knownFailures,
134
+ stats: {
135
+ total_known_failures: knownFailures.length,
136
+ },
137
+ };
138
+ }
139
+ /**
140
+ * Compare current test results against the baseline
141
+ *
142
+ * This is the core ratchet logic:
143
+ * - NEW failures (not in baseline) block the gate
144
+ * - Pre-existing failures (in baseline) show warning
145
+ * - Fixed tests (in baseline but now passing) trigger ratchet forward
146
+ *
147
+ * @param baseline - The test baseline
148
+ * @param currentFailures - Current test failures from test run
149
+ * @returns Comparison result
150
+ */
151
+ export function compareTestResults(baseline, currentFailures) {
152
+ const failingTests = currentFailures.filter((t) => !t.passed);
153
+ // Build lookup sets for efficient comparison
154
+ const baselineTestNames = new Set(baseline.known_failures.map((f) => f.test_name));
155
+ const currentFailingNames = new Set(failingTests.map((f) => f.test_name));
156
+ // Find NEW failures (in current failures, NOT in baseline)
157
+ const newFailures = failingTests.filter((f) => !baselineTestNames.has(f.test_name));
158
+ // Find pre-existing failures (in current failures AND in baseline)
159
+ const preExistingFailures = baseline.known_failures.filter((f) => currentFailingNames.has(f.test_name));
160
+ // Find fixed tests (in baseline but NOT in current failures)
161
+ const fixedTests = baseline.known_failures.filter((f) => !currentFailingNames.has(f.test_name));
162
+ return {
163
+ newFailures,
164
+ preExistingFailures,
165
+ fixedTests,
166
+ shouldBlock: newFailures.length > 0,
167
+ hasWarnings: preExistingFailures.length > 0,
168
+ shouldRatchetForward: fixedTests.length > 0,
169
+ };
170
+ }
171
+ /**
172
+ * Update the baseline (ratchet forward or add new known failures)
173
+ *
174
+ * @param baseline - Current baseline
175
+ * @param wuId - WU making the update
176
+ * @param options - Update options
177
+ * @returns Updated baseline (immutable)
178
+ */
179
+ export function updateBaseline(baseline, wuId, options) {
180
+ const now = new Date().toISOString();
181
+ const { fixedTests = [], newKnownFailures = [] } = options;
182
+ // Remove fixed tests (ratchet forward)
183
+ const fixedTestSet = new Set(fixedTests);
184
+ let knownFailures = baseline.known_failures.filter((f) => !fixedTestSet.has(f.test_name));
185
+ // Add new known failures
186
+ const newEntries = newKnownFailures.map((f) => ({
187
+ test_name: f.test_name,
188
+ file_path: f.file_path,
189
+ failure_reason: f.failure_reason,
190
+ added_at: now,
191
+ added_by_wu: wuId,
192
+ expected_fix_wu: f.expected_fix_wu,
193
+ }));
194
+ knownFailures = [...knownFailures, ...newEntries];
195
+ const stats = {
196
+ total_known_failures: knownFailures.length,
197
+ };
198
+ // Record ratchet forward if we removed tests
199
+ if (fixedTests.length > 0) {
200
+ stats.last_ratchet_forward = now;
201
+ }
202
+ return {
203
+ version: BASELINE_VERSION,
204
+ updated_at: now,
205
+ updated_by: wuId,
206
+ known_failures: knownFailures,
207
+ stats,
208
+ };
209
+ }
210
+ // ============================================================================
211
+ // Formatting Functions
212
+ // ============================================================================
213
+ /**
214
+ * Format a warning message for pre-existing failures
215
+ *
216
+ * @param preExisting - Pre-existing failures from baseline
217
+ * @returns Formatted warning string
218
+ */
219
+ export function formatBaselineWarning(preExisting) {
220
+ const lines = [
221
+ '',
222
+ '='.repeat(70),
223
+ ' Pre-existing test failures (from baseline)',
224
+ '='.repeat(70),
225
+ '',
226
+ ` These failures are tracked in .lumenflow/test-baseline.json`,
227
+ ' They do not block your WU, but should be fixed eventually.',
228
+ '',
229
+ ];
230
+ for (const failure of preExisting) {
231
+ lines.push(` - ${failure.test_name}`);
232
+ lines.push(` File: ${failure.file_path}`);
233
+ lines.push(` Reason: ${failure.failure_reason}`);
234
+ if (failure.expected_fix_wu) {
235
+ lines.push(` Expected fix: ${failure.expected_fix_wu}`);
236
+ }
237
+ lines.push('');
238
+ }
239
+ lines.push('='.repeat(70));
240
+ return lines.join('\n');
241
+ }
242
+ /**
243
+ * Format an error message for new failures
244
+ *
245
+ * @param newFailures - New test failures
246
+ * @returns Formatted error string
247
+ */
248
+ export function formatNewFailureError(newFailures) {
249
+ const lines = [
250
+ '',
251
+ '='.repeat(70),
252
+ ' NEW test failure(s) detected!',
253
+ '='.repeat(70),
254
+ '',
255
+ ' The following tests failed and are NOT in the baseline.',
256
+ ' This blocks your WU from completion.',
257
+ '',
258
+ ' Options:',
259
+ ' 1. Fix the test or add to baseline with:',
260
+ ' pnpm baseline:add --test "<test_name>" --reason "<why>" --fix-wu WU-XXXX',
261
+ ' 2. If this is a pre-existing failure on main, it should be in the baseline.',
262
+ '',
263
+ ];
264
+ for (const failure of newFailures) {
265
+ lines.push(` - ${failure.test_name}`);
266
+ lines.push(` File: ${failure.file_path}`);
267
+ if (failure.error_message) {
268
+ lines.push(` Error: ${failure.error_message.substring(0, 100)}...`);
269
+ }
270
+ lines.push('');
271
+ }
272
+ lines.push('='.repeat(70));
273
+ return lines.join('\n');
274
+ }
275
+ /**
276
+ * Get the path to the test baseline file
277
+ *
278
+ * @returns Baseline file path
279
+ */
280
+ export function getBaselineFilePath() {
281
+ return process.env[BASELINE_PATH_ENV] ?? DEFAULT_BASELINE_PATH;
282
+ }
@@ -29,7 +29,7 @@ export interface GetSuggestionsOptions {
29
29
  * When provided, suggestions for high-impact WUs are ranked higher
30
30
  * within the same priority level.
31
31
  *
32
- * @see flow-bottlenecks.mjs for score calculation
32
+ * @see flow-bottlenecks.ts for score calculation
33
33
  */
34
34
  bottleneckScores?: BottleneckScores;
35
35
  }
@@ -92,8 +92,44 @@ const wuClaim = {
92
92
  ];
93
93
  },
94
94
  };
95
+ /**
96
+ * Predicate: Check if worktree has no uncommitted changes.
97
+ * For wu:prep running from worktree itself.
98
+ */
99
+ const gitCleanPredicate = {
100
+ id: 'git-clean',
101
+ description: 'Worktree must not have uncommitted changes',
102
+ severity: SEVERITY.ERROR,
103
+ check: (context) => !context.git.isDirty,
104
+ getFixMessage: () => 'Commit or stash changes in worktree before running wu:prep',
105
+ };
106
+ /**
107
+ * Command definition for wu:prep (WU-1223).
108
+ *
109
+ * Runs gates and generates docs in the worktree, then prints copy-paste
110
+ * instruction to run wu:done from main checkout for merge/cleanup.
111
+ */
112
+ const wuPrep = {
113
+ name: COMMANDS.WU_PREP,
114
+ description: 'Run gates + docs in worktree, prepare for wu:done',
115
+ requiredLocation: LOCATION_TYPES.WORKTREE,
116
+ requiredWuStatus: WU_STATUS.IN_PROGRESS,
117
+ predicates: [gitCleanPredicate, stateConsistentPredicate],
118
+ getNextSteps: (context) => {
119
+ const mainPath = context.location.mainCheckout || '/path/to/main';
120
+ const wuId = context.wu?.id || 'WU-XXX';
121
+ return [
122
+ `1. Gates and docs generated in worktree`,
123
+ `2. Return to main checkout and complete:`,
124
+ ` cd ${mainPath} && pnpm wu:done --id ${wuId}`,
125
+ ];
126
+ },
127
+ };
95
128
  /**
96
129
  * Command definition for wu:done.
130
+ *
131
+ * WU-1223: Now requires main checkout (merge + cleanup only).
132
+ * Use wu:prep from worktree for gates and docs generation.
97
133
  */
98
134
  const wuDone = {
99
135
  name: COMMANDS.WU_DONE,
@@ -161,6 +197,7 @@ const wuRecover = {
161
197
  export const COMMAND_REGISTRY = new Map([
162
198
  [COMMANDS.WU_CREATE, wuCreate],
163
199
  [COMMANDS.WU_CLAIM, wuClaim],
200
+ [COMMANDS.WU_PREP, wuPrep],
164
201
  [COMMANDS.WU_DONE, wuDone],
165
202
  [COMMANDS.WU_BLOCK, wuBlock],
166
203
  [COMMANDS.WU_UNBLOCK, wuUnblock],
@@ -5,6 +5,7 @@
5
5
  import { existsSync, readFileSync, readdirSync } from 'node:fs';
6
6
  import path from 'node:path';
7
7
  import { FILE_SYSTEM } from '../wu-constants.js';
8
+ import { createWuPaths } from '../wu-paths.js';
8
9
  function extractWUIDsFromBacklog(content) {
9
10
  const wuIds = [];
10
11
  const pattern = /WU-\d+/gi;
@@ -29,8 +30,9 @@ export async function validateBacklogSync(options = {}) {
29
30
  const { cwd = process.cwd() } = options;
30
31
  const errors = [];
31
32
  const warnings = [];
32
- const backlogPath = path.join(cwd, 'docs', '04-operations', 'tasks', 'backlog.md');
33
- const wuDir = path.join(cwd, 'docs', '04-operations', 'tasks', 'wu');
33
+ const paths = createWuPaths({ projectRoot: cwd });
34
+ const backlogPath = path.join(cwd, paths.BACKLOG());
35
+ const wuDir = path.join(cwd, paths.WU_DIR());
34
36
  if (!existsSync(backlogPath)) {
35
37
  errors.push(`Backlog file not found: ${backlogPath}`);
36
38
  return { valid: false, errors, warnings, wuCount: 0, backlogCount: 0 };
@@ -10,7 +10,7 @@
10
10
  * - Reports last activity timestamp
11
11
  * - Identifies potentially abandoned WUs
12
12
  *
13
- * @see {@link tools/lib/__tests__/worktree-scanner.test.mjs} - Tests
13
+ * @see {@link packages/@lumenflow/cli/src/lib/__tests__/worktree-scanner.test.ts} - Tests
14
14
  */
15
15
  /**
16
16
  * @typedef {object} WorktreeInfo
@@ -10,7 +10,7 @@
10
10
  * - Reports last activity timestamp
11
11
  * - Identifies potentially abandoned WUs
12
12
  *
13
- * @see {@link tools/lib/__tests__/worktree-scanner.test.mjs} - Tests
13
+ * @see {@link packages/@lumenflow/cli/src/lib/__tests__/worktree-scanner.test.ts} - Tests
14
14
  */
15
15
  import { exec } from 'node:child_process';
16
16
  import { promisify } from 'node:util';
@@ -10,7 +10,7 @@
10
10
  * auto-create the node_modules symlink pointing to the main repo's
11
11
  * node_modules directory, including nested package node_modules.
12
12
  *
13
- * @module tools/lib/worktree-symlink.mjs
13
+ * @module tools/lib/worktree-symlink.ts
14
14
  */
15
15
  /**
16
16
  * List of nested package/app paths that have their own node_modules
@@ -55,7 +55,7 @@ export declare function hasWorktreePathSymlinks(nodeModulesPath: any): {
55
55
  * @returns {{created: boolean, skipped: boolean, refused?: boolean, reason?: string, error?: Error}}
56
56
  *
57
57
  * @example
58
- * // In wu-claim.mjs after worktree creation:
58
+ * // In wu-claim.ts after worktree creation:
59
59
  * symlinkNodeModules('/path/to/worktrees/operations-tooling-wu-1443');
60
60
  */
61
61
  export declare function symlinkNodeModules(worktreePath: any, logger?: Console, mainRepoPath?: any): {
@@ -90,7 +90,7 @@ export declare function symlinkNodeModules(worktreePath: any, logger?: Console,
90
90
  * @returns {{created: number, skipped: number, errors: Error[]}}
91
91
  *
92
92
  * @example
93
- * // In wu-claim.mjs after worktree creation:
93
+ * // In wu-claim.ts after worktree creation:
94
94
  * symlinkNestedNodeModules(
95
95
  * '/path/to/worktrees/operations-tooling-wu-1579',
96
96
  * '/path/to/main-repo'
@@ -10,7 +10,7 @@
10
10
  * auto-create the node_modules symlink pointing to the main repo's
11
11
  * node_modules directory, including nested package node_modules.
12
12
  *
13
- * @module tools/lib/worktree-symlink.mjs
13
+ * @module tools/lib/worktree-symlink.ts
14
14
  */
15
15
  import fs from 'node:fs';
16
16
  import path from 'node:path';
@@ -202,7 +202,7 @@ function checkMainNodeModulesHealth(mainRepoPath, logger) {
202
202
  * @returns {{created: boolean, skipped: boolean, refused?: boolean, reason?: string, error?: Error}}
203
203
  *
204
204
  * @example
205
- * // In wu-claim.mjs after worktree creation:
205
+ * // In wu-claim.ts after worktree creation:
206
206
  * symlinkNodeModules('/path/to/worktrees/operations-tooling-wu-1443');
207
207
  */
208
208
  export function symlinkNodeModules(worktreePath, logger = console, mainRepoPath = null) {
@@ -310,7 +310,7 @@ function handleExistingNestedNodeModules(targetNodeModules, pkgPath, logger, err
310
310
  * @returns {{created: number, skipped: number, errors: Error[]}}
311
311
  *
312
312
  * @example
313
- * // In wu-claim.mjs after worktree creation:
313
+ * // In wu-claim.ts after worktree creation:
314
314
  * symlinkNestedNodeModules(
315
315
  * '/path/to/worktrees/operations-tooling-wu-1579',
316
316
  * '/path/to/main-repo'
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Backlog.md Update Utilities
3
3
  *
4
- * Centralized backlog.md update functions (extracted from wu-done.mjs)
4
+ * Centralized backlog.md update functions (extracted from wu-done.ts)
5
5
  * Refactored to use BacklogManager (WU-1212) for AST-based manipulation
6
6
  *
7
7
  * Used by both main wu:done flow AND recovery mode (DRY principle)
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Backlog.md Update Utilities
3
3
  *
4
- * Centralized backlog.md update functions (extracted from wu-done.mjs)
4
+ * Centralized backlog.md update functions (extracted from wu-done.ts)
5
5
  * Refactored to use BacklogManager (WU-1212) for AST-based manipulation
6
6
  *
7
7
  * Used by both main wu:done flow AND recovery mode (DRY principle)
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @file wu-claim-helpers.mjs
2
+ * @file wu-claim-helpers.ts
3
3
  * Helper functions for wu:claim (WU-1423)
4
4
  *
5
5
  * Extracted email validation logic for testability and SOLID compliance.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @file wu-claim-helpers.mjs
2
+ * @file wu-claim-helpers.ts
3
3
  * Helper functions for wu:claim (WU-1423)
4
4
  *
5
5
  * Extracted email validation logic for testability and SOLID compliance.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @file wu-claim-resume.mjs
2
+ * @file wu-claim-resume.ts
3
3
  * Helper functions for wu:claim --resume agent handoff (WU-2411)
4
4
  *
5
5
  * When an agent crashes or is killed, its worktree remains with uncommitted work.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @file wu-claim-resume.mjs
2
+ * @file wu-claim-resume.ts
3
3
  * Helper functions for wu:claim --resume agent handoff (WU-2411)
4
4
  *
5
5
  * When an agent crashes or is killed, its worktree remains with uncommitted work.
@@ -10,7 +10,7 @@
10
10
  * - ORPHAN_WORKTREE_DONE: Done WU still has worktree
11
11
  * - STAMP_EXISTS_YAML_NOT_DONE: Stamp exists but YAML status is not done (WU-2412)
12
12
  *
13
- * @see {@link ../wu-repair.mjs} CLI interface
13
+ * @see {@link ../wu-repair.ts} CLI interface
14
14
  */
15
15
  /**
16
16
  * Check a single WU for state inconsistencies
@@ -10,13 +10,13 @@
10
10
  * - ORPHAN_WORKTREE_DONE: Done WU still has worktree
11
11
  * - STAMP_EXISTS_YAML_NOT_DONE: Stamp exists but YAML status is not done (WU-2412)
12
12
  *
13
- * @see {@link ../wu-repair.mjs} CLI interface
13
+ * @see {@link ../wu-repair.ts} CLI interface
14
14
  */
15
15
  import { readFile, writeFile, readdir, mkdir, access } from 'node:fs/promises';
16
16
  import { constants, existsSync, mkdirSync, writeFileSync, readFileSync } from 'node:fs';
17
17
  import path from 'node:path';
18
18
  import { parseYAML, stringifyYAML } from './wu-yaml.js';
19
- import { WU_PATHS } from './wu-paths.js';
19
+ import { createWuPaths, WU_PATHS } from './wu-paths.js';
20
20
  import { CONSISTENCY_TYPES, CONSISTENCY_MESSAGES, LOG_PREFIX, REMOTES, STRING_LITERALS, toKebab, WU_STATUS, YAML_OPTIONS, } from './wu-constants.js';
21
21
  import { todayISO } from './date-utils.js';
22
22
  import { createGitForPath } from './git-adapter.js';
@@ -30,10 +30,11 @@ import { withMicroWorktree } from './micro-worktree.js';
30
30
  */
31
31
  export async function checkWUConsistency(id, projectRoot = process.cwd()) {
32
32
  const errors = [];
33
- const wuPath = path.join(projectRoot, WU_PATHS.WU(id));
34
- const stampPath = path.join(projectRoot, WU_PATHS.STAMP(id));
35
- const backlogPath = path.join(projectRoot, WU_PATHS.BACKLOG());
36
- const statusPath = path.join(projectRoot, WU_PATHS.STATUS());
33
+ const paths = createWuPaths({ projectRoot });
34
+ const wuPath = path.join(projectRoot, paths.WU(id));
35
+ const stampPath = path.join(projectRoot, paths.STAMP(id));
36
+ const backlogPath = path.join(projectRoot, paths.BACKLOG());
37
+ const statusPath = path.join(projectRoot, paths.STATUS());
37
38
  // Handle missing WU YAML gracefully
38
39
  try {
39
40
  await access(wuPath, constants.R_OK);
@@ -166,7 +167,8 @@ export async function checkWUConsistency(id, projectRoot = process.cwd()) {
166
167
  * @returns {Promise<object>} Aggregated report with valid, errors, and checked count
167
168
  */
168
169
  export async function checkAllWUConsistency(projectRoot = process.cwd()) {
169
- const wuDir = path.join(projectRoot, 'docs/04-operations/tasks/wu');
170
+ const paths = createWuPaths({ projectRoot });
171
+ const wuDir = path.join(projectRoot, paths.WU_DIR());
170
172
  try {
171
173
  await access(wuDir, constants.R_OK);
172
174
  }
@@ -195,7 +197,8 @@ export async function checkAllWUConsistency(projectRoot = process.cwd()) {
195
197
  * @returns {Promise<object>} Result with valid, orphans list, and reports
196
198
  */
197
199
  export async function checkLaneForOrphanDoneWU(lane, excludeId, projectRoot = process.cwd()) {
198
- const wuDir = path.join(projectRoot, 'docs/04-operations/tasks/wu');
200
+ const paths = createWuPaths({ projectRoot });
201
+ const wuDir = path.join(projectRoot, paths.WU_DIR());
199
202
  try {
200
203
  await access(wuDir, constants.R_OK);
201
204
  }
@@ -486,7 +489,8 @@ async function createStampInProject(id, title, projectRoot) {
486
489
  * @returns {Promise<string[]>} List of files modified (relative paths)
487
490
  */
488
491
  async function updateYamlToDoneInWorktree(id, worktreePath, projectRoot) {
489
- const wuRelPath = WU_PATHS.WU(id);
492
+ const paths = createWuPaths({ projectRoot });
493
+ const wuRelPath = paths.WU(id);
490
494
  const wuSrcPath = path.join(projectRoot, wuRelPath);
491
495
  const wuDestPath = path.join(worktreePath, wuRelPath);
492
496
  // Read current YAML from project root
@@ -522,7 +526,8 @@ async function updateYamlToDoneInWorktree(id, worktreePath, projectRoot) {
522
526
  * @returns {Promise<void>}
523
527
  */
524
528
  async function updateYamlToDone(id, projectRoot) {
525
- const wuPath = path.join(projectRoot, WU_PATHS.WU(id));
529
+ const paths = createWuPaths({ projectRoot });
530
+ const wuPath = path.join(projectRoot, paths.WU(id));
526
531
  // Read current YAML
527
532
  const content = await readFile(wuPath, { encoding: 'utf-8' });
528
533
  const wuDoc = parseYAML(content);
@@ -690,7 +695,8 @@ async function removeOrphanWorktree(id, lane, projectRoot) {
690
695
  // Worktree doesn't exist, that's fine
691
696
  }
692
697
  // 🚨 SAFETY GUARD 3: Check stamp exists (not rollback state)
693
- const stampPath = path.join(projectRoot, WU_PATHS.STAMP(id));
698
+ const paths = createWuPaths({ projectRoot });
699
+ const stampPath = path.join(projectRoot, paths.STAMP(id));
694
700
  try {
695
701
  await access(stampPath, constants.R_OK);
696
702
  }