@lumenflow/core 2.4.0 → 2.5.1

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.
@@ -253,6 +253,10 @@ export declare const LOG_PREFIX: {
253
253
  CONSISTENCY: string;
254
254
  PREFLIGHT: string;
255
255
  INITIATIVE_PLAN: string;
256
+ PLAN_CREATE: string;
257
+ PLAN_LINK: string;
258
+ PLAN_EDIT: string;
259
+ PLAN_PROMOTE: string;
256
260
  };
257
261
  /**
258
262
  * Consistency check types (WU-1276)
@@ -940,6 +944,8 @@ export declare const GATE_NAMES: {
940
944
  SYSTEM_MAP_VALIDATE: string;
941
945
  /** WU-1191: Lane health check (overlap detection) */
942
946
  LANE_HEALTH: string;
947
+ /** WU-1315: Onboarding smoke test (init + wu:create validation) */
948
+ ONBOARDING_SMOKE_TEST: string;
943
949
  };
944
950
  /**
945
951
  * Gate command sentinels (special values for non-shell commands)
@@ -959,6 +965,8 @@ export declare const GATE_COMMANDS: {
959
965
  SAFETY_CRITICAL_TEST: string;
960
966
  /** WU-2062: Triggers tiered test execution based on risk */
961
967
  TIERED_TEST: string;
968
+ /** WU-1315: Triggers onboarding smoke test */
969
+ ONBOARDING_SMOKE_TEST: string;
962
970
  };
963
971
  /**
964
972
  * CLI mode flags
@@ -269,6 +269,10 @@ export const LOG_PREFIX = {
269
269
  CONSISTENCY: '[wu-consistency]',
270
270
  PREFLIGHT: '[wu-preflight]',
271
271
  INITIATIVE_PLAN: '[initiative:plan]',
272
+ PLAN_CREATE: '[plan:create]',
273
+ PLAN_LINK: '[plan:link]',
274
+ PLAN_EDIT: '[plan:edit]',
275
+ PLAN_PROMOTE: '[plan:promote]',
272
276
  };
273
277
  /**
274
278
  * Consistency check types (WU-1276)
@@ -985,6 +989,8 @@ export const GATE_NAMES = {
985
989
  SYSTEM_MAP_VALIDATE: 'system-map:validate',
986
990
  /** WU-1191: Lane health check (overlap detection) */
987
991
  LANE_HEALTH: 'lane-health',
992
+ /** WU-1315: Onboarding smoke test (init + wu:create validation) */
993
+ ONBOARDING_SMOKE_TEST: 'onboarding-smoke-test',
988
994
  };
989
995
  /**
990
996
  * Gate command sentinels (special values for non-shell commands)
@@ -1004,6 +1010,8 @@ export const GATE_COMMANDS = {
1004
1010
  SAFETY_CRITICAL_TEST: 'safety-critical-test',
1005
1011
  /** WU-2062: Triggers tiered test execution based on risk */
1006
1012
  TIERED_TEST: 'tiered-test',
1013
+ /** WU-1315: Triggers onboarding smoke test */
1014
+ ONBOARDING_SMOKE_TEST: 'onboarding-smoke-test',
1007
1015
  };
1008
1016
  /**
1009
1017
  * CLI mode flags
@@ -86,6 +86,19 @@ export declare function mergeWithMainState(worktreeStateDir: string): Promise<WU
86
86
  * @returns Merged backlog.md content
87
87
  */
88
88
  export declare function computeBacklogContentWithMainMerge(backlogPath: string, wuId: string): Promise<string>;
89
+ /**
90
+ * Compute status.md content with merged state from origin/main.
91
+ *
92
+ * WU-1319: This function generates status.md from the merged state store
93
+ * instead of editing the local file snapshot. This prevents reintroducing
94
+ * stale "In Progress" entries when concurrent WUs complete on main.
95
+ *
96
+ * @param backlogPath - Path to backlog.md in the worktree (used to find state dir)
97
+ * @param wuId - WU ID being completed
98
+ * @param mainStateDir - Optional explicit path to main state dir (for testing)
99
+ * @returns Merged status.md content
100
+ */
101
+ export declare function computeStatusContentWithMainMerge(backlogPath: string, wuId: string, mainStateDir?: string): Promise<string>;
89
102
  /**
90
103
  * Compute wu-events.jsonl content with merged state from origin/main.
91
104
  *
@@ -15,7 +15,7 @@ import { existsSync, readFileSync } from 'node:fs';
15
15
  import { join } from 'node:path';
16
16
  import { WUStateStore, WU_EVENTS_FILE_NAME } from './wu-state-store.js';
17
17
  import { validateWUEvent } from './wu-state-schema.js';
18
- import { generateBacklog } from './backlog-generator.js';
18
+ import { generateBacklog, generateStatus } from './backlog-generator.js';
19
19
  import { getStateStoreDirFromBacklog } from './wu-paths.js';
20
20
  import { getGitForCwd } from './git-adapter.js';
21
21
  import { REMOTES, BRANCHES, BEACON_PATHS } from './wu-constants.js';
@@ -286,6 +286,46 @@ export async function computeBacklogContentWithMainMerge(backlogPath, wuId) {
286
286
  // Generate backlog from merged state
287
287
  return generateBacklog(mergedStore);
288
288
  }
289
+ /**
290
+ * Compute status.md content with merged state from origin/main.
291
+ *
292
+ * WU-1319: This function generates status.md from the merged state store
293
+ * instead of editing the local file snapshot. This prevents reintroducing
294
+ * stale "In Progress" entries when concurrent WUs complete on main.
295
+ *
296
+ * @param backlogPath - Path to backlog.md in the worktree (used to find state dir)
297
+ * @param wuId - WU ID being completed
298
+ * @param mainStateDir - Optional explicit path to main state dir (for testing)
299
+ * @returns Merged status.md content
300
+ */
301
+ export async function computeStatusContentWithMainMerge(backlogPath, wuId, mainStateDir) {
302
+ const worktreeStateDir = getStateStoreDirFromBacklog(backlogPath);
303
+ let mergedStore;
304
+ if (mainStateDir) {
305
+ // Direct merge with provided main state dir (for testing)
306
+ mergedStore = await mergeStateStores(worktreeStateDir, mainStateDir);
307
+ }
308
+ else {
309
+ // Merge with main state via git show
310
+ mergedStore = await mergeWithMainState(worktreeStateDir);
311
+ }
312
+ // Check if the WU exists in the merged state
313
+ const currentState = mergedStore.getWUState(wuId);
314
+ if (!currentState) {
315
+ throw new Error(`WU ${wuId} not found in merged state store. ` +
316
+ `This may indicate the WU was never properly claimed.`);
317
+ }
318
+ // If not already done, create and apply the complete event
319
+ if (currentState.status !== 'done') {
320
+ if (currentState.status !== 'in_progress') {
321
+ throw new Error(`WU ${wuId} is in status "${currentState.status}", expected "in_progress"`);
322
+ }
323
+ const completeEvent = mergedStore.createCompleteEvent(wuId);
324
+ mergedStore.applyEvent(completeEvent);
325
+ }
326
+ // Generate status from merged state
327
+ return generateStatus(mergedStore);
328
+ }
289
329
  /**
290
330
  * Compute wu-events.jsonl content with merged state from origin/main.
291
331
  *
@@ -10,7 +10,7 @@ import { updateStatusRemoveInProgress, addToStatusCompleted } from './wu-status-
10
10
  import { moveWUToDoneBacklog } from './wu-backlog-updater.js';
11
11
  import { createStamp } from './stamp-utils.js';
12
12
  import { WU_EVENTS_FILE_NAME } from './wu-state-store.js';
13
- import { computeWUYAMLContent, computeStatusContent, computeBacklogContent, computeWUEventsContentAfterComplete, computeStampContent, } from './wu-transaction-collectors.js';
13
+ import { computeWUYAMLContent, computeStatusContentFromMergedState, computeBacklogContent, computeWUEventsContentAfterComplete, computeStampContent, } from './wu-transaction-collectors.js';
14
14
  import { DEFAULTS, LOG_PREFIX, EMOJI, PKG_MANAGER, SCRIPTS, PRETTIER_FLAGS, BEACON_PATHS, } from './wu-constants.js';
15
15
  import { applyExposureDefaults } from './wu-done-validation.js';
16
16
  import { createFileNotFoundError, createValidationError } from './wu-done-errors.js';
@@ -139,8 +139,8 @@ export async function collectMetadataToTransaction({ id, title, doc, wuPath, sta
139
139
  // Compute WU YAML content (mutates doc, returns YAML string)
140
140
  const wuYAMLContent = computeWUYAMLContent(doc);
141
141
  transaction.addWrite(wuPath, wuYAMLContent, 'WU YAML');
142
- // Compute status.md content
143
- const statusContent = computeStatusContent(statusPath, id, title);
142
+ // Compute status.md content (WU-1319: now uses merged state)
143
+ const statusContent = await computeStatusContentFromMergedState(backlogPath, id);
144
144
  transaction.addWrite(statusPath, statusContent, 'status.md');
145
145
  // Compute backlog.md content (WU-1574: now async)
146
146
  const backlogContent = await computeBacklogContent(backlogPath, id, title);
@@ -82,6 +82,16 @@ export declare function createWuPaths(options?: {
82
82
  * @returns Path to plans directory (WU-1301)
83
83
  */
84
84
  PLANS_DIR: () => string;
85
+ /**
86
+ * Get path to templates directory
87
+ * @returns Path to templates directory (WU-1310)
88
+ */
89
+ TEMPLATES_DIR: () => string;
90
+ /**
91
+ * Get path to onboarding directory
92
+ * @returns Path to onboarding directory (WU-1310)
93
+ */
94
+ ONBOARDING_DIR: () => string;
85
95
  };
86
96
  /**
87
97
  * Default WU paths using default config
@@ -140,6 +150,16 @@ export declare const WU_PATHS: {
140
150
  * @returns Path to plans directory (WU-1301)
141
151
  */
142
152
  PLANS_DIR: () => string;
153
+ /**
154
+ * Get path to templates directory
155
+ * @returns Path to templates directory (WU-1310)
156
+ */
157
+ TEMPLATES_DIR: () => string;
158
+ /**
159
+ * Get path to onboarding directory
160
+ * @returns Path to onboarding directory (WU-1310)
161
+ */
162
+ ONBOARDING_DIR: () => string;
143
163
  };
144
164
  /**
145
165
  * Generate default worktree path from WU document
package/dist/wu-paths.js CHANGED
@@ -109,6 +109,16 @@ export function createWuPaths(options = {}) {
109
109
  * @returns Path to plans directory (WU-1301)
110
110
  */
111
111
  PLANS_DIR: () => config.directories.plansDir,
112
+ /**
113
+ * Get path to templates directory
114
+ * @returns Path to templates directory (WU-1310)
115
+ */
116
+ TEMPLATES_DIR: () => config.directories.templatesDir,
117
+ /**
118
+ * Get path to onboarding directory
119
+ * @returns Path to onboarding directory (WU-1310)
120
+ */
121
+ ONBOARDING_DIR: () => config.directories.onboardingDir,
112
122
  };
113
123
  }
114
124
  /**
@@ -50,6 +50,36 @@ export declare function createPreflightResult({ valid, errors, missingCodePaths,
50
50
  missingTestPaths: any[];
51
51
  suggestedTestPaths: {};
52
52
  };
53
+ /**
54
+ * Validate code_paths files exist
55
+ *
56
+ * WU-1329: Exported for use by wu:create strict validation.
57
+ *
58
+ * @param {string[]} codePaths - List of code paths from WU YAML
59
+ * @param {string} rootDir - Root directory to resolve paths against
60
+ * @returns {{ valid: boolean, errors: string[], missing: string[] }}
61
+ */
62
+ export declare function validateCodePathsExistence(codePaths: any, rootDir: any): {
63
+ valid: boolean;
64
+ errors: string[];
65
+ missing: any[];
66
+ };
67
+ /**
68
+ * Validate test file paths exist (unit, e2e, integration - not manual)
69
+ *
70
+ * Manual tests are descriptions, not file paths, so they're skipped.
71
+ *
72
+ * WU-1329: Exported for use by wu:create strict validation.
73
+ *
74
+ * @param {object} tests - tests object from WU YAML
75
+ * @param {string} rootDir - Root directory to resolve paths against
76
+ * @returns {{ valid: boolean, errors: string[], missing: string[] }}
77
+ */
78
+ export declare function validateTestPathsExistence(tests: any, rootDir: any): {
79
+ valid: boolean;
80
+ errors: string[];
81
+ missing: any[];
82
+ };
53
83
  /**
54
84
  * Run preflight validation for a WU
55
85
  *
@@ -105,11 +105,13 @@ function validateSchema(doc, id) {
105
105
  /**
106
106
  * Validate code_paths files exist
107
107
  *
108
+ * WU-1329: Exported for use by wu:create strict validation.
109
+ *
108
110
  * @param {string[]} codePaths - List of code paths from WU YAML
109
111
  * @param {string} rootDir - Root directory to resolve paths against
110
112
  * @returns {{ valid: boolean, errors: string[], missing: string[] }}
111
113
  */
112
- function validateCodePathsExistence(codePaths, rootDir) {
114
+ export function validateCodePathsExistence(codePaths, rootDir) {
113
115
  if (!codePaths || !Array.isArray(codePaths) || codePaths.length === 0) {
114
116
  return { valid: true, errors: [], missing: [] };
115
117
  }
@@ -136,11 +138,13 @@ function validateCodePathsExistence(codePaths, rootDir) {
136
138
  *
137
139
  * Manual tests are descriptions, not file paths, so they're skipped.
138
140
  *
141
+ * WU-1329: Exported for use by wu:create strict validation.
142
+ *
139
143
  * @param {object} tests - tests object from WU YAML
140
144
  * @param {string} rootDir - Root directory to resolve paths against
141
145
  * @returns {{ valid: boolean, errors: string[], missing: string[] }}
142
146
  */
143
- function validateTestPathsExistence(tests, rootDir) {
147
+ export function validateTestPathsExistence(tests, rootDir) {
144
148
  if (!tests || typeof tests !== 'object') {
145
149
  return { valid: true, errors: [], missing: [] };
146
150
  }
@@ -71,6 +71,18 @@ export declare function computeWUEventsContentAfterComplete(backlogPath: any, wu
71
71
  * @returns {Promise<string>} New backlog.md content
72
72
  */
73
73
  export declare function computeBacklogContent(backlogPath: any, id: any, _title: any): Promise<string>;
74
+ /**
75
+ * Compute updated status.md content from merged state store
76
+ *
77
+ * WU-1319: Generates status.md from merged state (origin/main + worktree events)
78
+ * instead of editing the local file snapshot. This prevents reintroducing
79
+ * stale "In Progress" entries when concurrent WUs complete on main.
80
+ *
81
+ * @param {string} backlogPath - Path to backlog.md (used to find state dir)
82
+ * @param {string} id - WU ID to mark complete
83
+ * @returns {Promise<string>} New status.md content
84
+ */
85
+ export declare function computeStatusContentFromMergedState(backlogPath: any, id: any): Promise<string>;
74
86
  /**
75
87
  * Compute stamp file content
76
88
  *
@@ -82,6 +94,7 @@ export declare function computeStampContent(id: any, title: any): string;
82
94
  /**
83
95
  * Collect all metadata updates for a transaction
84
96
  * WU-1574: Made async for computeBacklogContent
97
+ * WU-1319: Made status.md generation use merged state
85
98
  *
86
99
  * Convenience function that computes all file contents at once.
87
100
  * Returns an object with all computed content.
@@ -30,12 +30,8 @@ import { getSectionHeadingsWithDefaults } from './section-headings.js';
30
30
  import { todayISO } from './date-utils.js';
31
31
  import { createError, ErrorCodes } from './error-handler.js';
32
32
  import { STRING_LITERALS } from './wu-constants.js';
33
- // WU-1574: BacklogManager removed - using state store + generator
34
- import { WUStateStore } from './wu-state-store.js';
35
- // WU-1734: Import proper path resolution utility
36
- import { getStateStoreDirFromBacklog } from './wu-paths.js';
37
- // WU-1145: Import concurrent merge utilities
38
- import { computeBacklogContentWithMainMerge, computeWUEventsContentWithMainMerge, } from './wu-done-concurrent-merge.js';
33
+ // WU-1145, WU-1319: Import concurrent merge utilities
34
+ import { computeBacklogContentWithMainMerge, computeStatusContentWithMainMerge, computeWUEventsContentWithMainMerge, } from './wu-done-concurrent-merge.js';
39
35
  /**
40
36
  * Compute WU YAML content for done state
41
37
  *
@@ -167,31 +163,6 @@ export function computeStatusContent(statusPath, id, title) {
167
163
  const frontmatterText = frontmatterMatch ? frontmatterMatch[0] : '';
168
164
  return frontmatterText + lines.join(STRING_LITERALS.NEWLINE);
169
165
  }
170
- function ensureTrailingNewline(content) {
171
- if (content === '')
172
- return content;
173
- if (content.endsWith(STRING_LITERALS.NEWLINE))
174
- return content;
175
- return content + STRING_LITERALS.NEWLINE;
176
- }
177
- async function computeCompletionUpdatesFromStateStore(backlogPath, wuId) {
178
- const stateDir = getStateStoreDirFromBacklog(backlogPath);
179
- const store = new WUStateStore(stateDir);
180
- await store.load();
181
- const current = store.getWUState(wuId);
182
- if (!current) {
183
- throw new Error(`WU ${wuId} is not in_progress`);
184
- }
185
- if (current.status === 'done') {
186
- return { store, stateDir, shouldAppendCompleteEvent: false, completeEvent: null };
187
- }
188
- if (current.status !== 'in_progress') {
189
- throw new Error(`WU ${wuId} is not in_progress`);
190
- }
191
- const completeEvent = store.createCompleteEvent(wuId);
192
- store.applyEvent(completeEvent);
193
- return { store, stateDir, shouldAppendCompleteEvent: true, completeEvent };
194
- }
195
166
  /**
196
167
  * Compute wu-events.jsonl content after completing a WU.
197
168
  *
@@ -221,6 +192,21 @@ export async function computeBacklogContent(backlogPath, id, _title) {
221
192
  // WU-1145: Use merged state to preserve concurrent changes
222
193
  return computeBacklogContentWithMainMerge(backlogPath, id);
223
194
  }
195
+ /**
196
+ * Compute updated status.md content from merged state store
197
+ *
198
+ * WU-1319: Generates status.md from merged state (origin/main + worktree events)
199
+ * instead of editing the local file snapshot. This prevents reintroducing
200
+ * stale "In Progress" entries when concurrent WUs complete on main.
201
+ *
202
+ * @param {string} backlogPath - Path to backlog.md (used to find state dir)
203
+ * @param {string} id - WU ID to mark complete
204
+ * @returns {Promise<string>} New status.md content
205
+ */
206
+ export async function computeStatusContentFromMergedState(backlogPath, id) {
207
+ // WU-1319: Use merged state to preserve concurrent changes
208
+ return computeStatusContentWithMainMerge(backlogPath, id);
209
+ }
224
210
  /**
225
211
  * Compute stamp file content
226
212
  *
@@ -235,6 +221,7 @@ export function computeStampContent(id, title) {
235
221
  /**
236
222
  * Collect all metadata updates for a transaction
237
223
  * WU-1574: Made async for computeBacklogContent
224
+ * WU-1319: Made status.md generation use merged state
238
225
  *
239
226
  * Convenience function that computes all file contents at once.
240
227
  * Returns an object with all computed content.
@@ -258,7 +245,8 @@ export async function collectMetadataUpdates({ doc, id, title, wuPath, statusPat
258
245
  },
259
246
  status: {
260
247
  path: statusPath,
261
- content: computeStatusContent(statusPath, id, title),
248
+ // WU-1319: Use merged state to preserve concurrent changes
249
+ content: await computeStatusContentFromMergedState(backlogPath, id),
262
250
  description: 'status.md',
263
251
  },
264
252
  backlog: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lumenflow/core",
3
- "version": "2.4.0",
3
+ "version": "2.5.1",
4
4
  "description": "Core WU lifecycle tools for LumenFlow workflow framework",
5
5
  "keywords": [
6
6
  "lumenflow",
@@ -72,6 +72,7 @@
72
72
  "dependencies": {
73
73
  "chalk": "^5.6.2",
74
74
  "change-case": "^5.4.4",
75
+ "p-retry": "^6.2.1",
75
76
  "cli-progress": "^3.12.0",
76
77
  "cli-table3": "^0.6.5",
77
78
  "commander": "^14.0.2",
@@ -98,7 +99,7 @@
98
99
  "vitest": "^4.0.17"
99
100
  },
100
101
  "peerDependencies": {
101
- "@lumenflow/memory": "2.4.0"
102
+ "@lumenflow/memory": "2.5.1"
102
103
  },
103
104
  "peerDependenciesMeta": {
104
105
  "@lumenflow/memory": {