@lumenflow/cli 1.1.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/wu-create.js CHANGED
@@ -40,7 +40,7 @@ import { inferSubLane } from '@lumenflow/core/dist/lane-inference.js';
40
40
  import { parseBacklogFrontmatter } from '@lumenflow/core/dist/backlog-parser.js';
41
41
  import { createWUParser, WU_OPTIONS } from '@lumenflow/core/dist/arg-parser.js';
42
42
  import { WU_PATHS } from '@lumenflow/core/dist/wu-paths.js';
43
- import { PLACEHOLDER_SENTINEL, validateReadyWU } from '@lumenflow/core/dist/wu-schema.js';
43
+ import { validateWU } from '@lumenflow/core/dist/wu-schema.js';
44
44
  import { COMMIT_FORMATS, FILE_SYSTEM, STRING_LITERALS, READINESS_UI, } from '@lumenflow/core/dist/wu-constants.js';
45
45
  // WU-1593: Use centralized validateWUIDFormat (DRY)
46
46
  import { ensureOnMain, validateWUIDFormat } from '@lumenflow/core/dist/wu-helpers.js';
@@ -52,6 +52,8 @@ import { validateSpecCompleteness } from '@lumenflow/core/dist/wu-done-validator
52
52
  import { readWU } from '@lumenflow/core/dist/wu-yaml.js';
53
53
  // WU-2253: Import WU spec linter for acceptance/code_paths validation
54
54
  import { lintWUSpec, formatLintErrors } from '@lumenflow/core/dist/wu-lint.js';
55
+ // WU-1025: Import placeholder validator for inline content validation
56
+ import { validateNoPlaceholders, buildPlaceholderErrorMessage, } from '@lumenflow/core/dist/wu-validator.js';
55
57
  /** Log prefix for console output */
56
58
  const LOG_PREFIX = '[wu:create]';
57
59
  /** Micro-worktree operation name */
@@ -175,67 +177,29 @@ function displayReadinessSummary(id) {
175
177
  console.warn(`${LOG_PREFIX} ⚠️ Could not validate readiness: ${err.message}`);
176
178
  }
177
179
  }
178
- /**
179
- * Create WU YAML file in micro-worktree
180
- *
181
- * @param {string} worktreePath - Path to micro-worktree
182
- * @param {string} id - WU ID
183
- * @param {string} lane - WU lane
184
- * @param {string} title - WU title
185
- * @param {string} priority - WU priority
186
- * @param {string} type - WU type
187
- * @param {Object} opts - Additional options
188
- * @returns {string} Relative path to created YAML file
189
- */
190
- function createWUYamlInWorktree(worktreePath, id, lane, title, priority, type, opts = {}) {
191
- const wuRelativePath = WU_PATHS.WU(id);
192
- const wuAbsolutePath = join(worktreePath, wuRelativePath);
193
- const wuDir = join(worktreePath, WU_PATHS.WU_DIR());
194
- mkdirSync(wuDir, { recursive: true });
195
- // WU-1428: Use todayISO() for consistent YYYY-MM-DD format (library-first)
196
- const today = todayISO();
197
- // Parse initiative system fields from opts (WU-1247) and assigned_to (WU-1368)
198
- const { initiative, phase, blockedBy, blocks, labels, assignedTo } = opts;
199
- // WU-1364: Parse full spec inline options
200
- const { description: inlineDescription, acceptance: inlineAcceptance, codePaths, testPathsManual, testPathsUnit, testPathsE2e, } = opts;
201
- // WU-1998: Parse exposure field options
202
- const { exposure, userJourney, uiPairingWus } = opts;
203
- // WU-2320: Parse spec_refs option
204
- const { specRefs } = opts;
205
- // Helper to parse comma-separated strings into arrays (DRY)
206
- const parseCommaSeparated = (value) => value
207
- ? value
208
- .split(',')
209
- .map((s) => s.trim())
210
- .filter(Boolean)
211
- : [];
212
- // WU-1364: Build description (inline or placeholder)
213
- const description = inlineDescription
214
- ? inlineDescription
215
- : `${PLACEHOLDER_SENTINEL} Describe the work to be done.\n\nContext: ...\nProblem: ...\nSolution: ...\n`;
216
- // WU-1364: Build acceptance (inline array or placeholder)
217
- const acceptance = inlineAcceptance && inlineAcceptance.length > 0
218
- ? inlineAcceptance
219
- : [
220
- `${PLACEHOLDER_SENTINEL} Define acceptance criteria`,
221
- 'pnpm format, lint, typecheck → PASS',
222
- ];
223
- // WU-1364: Build code_paths from inline flag
180
+ // Helper to parse comma-separated strings into arrays (DRY)
181
+ const parseCommaSeparated = (value) => value
182
+ ? value
183
+ .split(',')
184
+ .map((s) => s.trim())
185
+ .filter(Boolean)
186
+ : [];
187
+ function buildWUContent({ id, lane, title, priority, type, created, opts, }) {
188
+ const { description, acceptance, codePaths, testPathsManual, testPathsUnit, testPathsE2e, initiative, phase, blockedBy, blocks, labels, assignedTo, exposure, userJourney, uiPairingWus, specRefs, } = opts;
224
189
  const code_paths = parseCommaSeparated(codePaths);
225
- // WU-1364: Build tests object from inline flags
226
190
  const tests = {
227
191
  manual: parseCommaSeparated(testPathsManual),
228
192
  unit: parseCommaSeparated(testPathsUnit),
229
193
  e2e: parseCommaSeparated(testPathsE2e),
230
194
  };
231
- const wuContent = {
195
+ return {
232
196
  id,
233
197
  title,
234
198
  lane,
235
199
  type,
236
200
  status: 'ready',
237
201
  priority,
238
- created: today,
202
+ created,
239
203
  description,
240
204
  acceptance,
241
205
  code_paths,
@@ -245,24 +209,107 @@ function createWUYamlInWorktree(worktreePath, id, lane, title, priority, type, o
245
209
  risks: [],
246
210
  notes: '',
247
211
  requires_review: false,
248
- // Initiative system fields - only include if provided (WU-1247)
249
212
  ...(initiative && { initiative }),
250
213
  ...(phase && { phase: parseInt(phase, 10) }),
251
214
  ...(blockedBy && { blocked_by: blockedBy.split(',').map((s) => s.trim()) }),
252
215
  ...(blocks && { blocks: blocks.split(',').map((s) => s.trim()) }),
253
216
  ...(labels && { labels: labels.split(',').map((s) => s.trim()) }),
254
- // WU-1368: Default assigned_to from git config user.email
255
217
  ...(assignedTo && { assigned_to: assignedTo }),
256
- // WU-1998: Exposure field options - only include if provided
257
218
  ...(exposure && { exposure }),
258
219
  ...(userJourney && { user_journey: userJourney }),
259
220
  ...(uiPairingWus && { ui_pairing_wus: parseCommaSeparated(uiPairingWus) }),
260
- // WU-2320: Spec references - only include if provided
261
221
  ...(specRefs && { spec_refs: parseCommaSeparated(specRefs) }),
262
222
  };
263
- // WU-1539: Validate WU structure before writing (fail-fast, allows placeholders)
223
+ }
224
+ export function validateCreateSpec({ id, lane, title, priority, type, opts, }) {
225
+ const errors = [];
226
+ const effectiveType = type || DEFAULT_TYPE;
227
+ if (!opts.description) {
228
+ errors.push('--description is required');
229
+ }
230
+ if (!opts.acceptance || opts.acceptance.length === 0) {
231
+ errors.push('--acceptance is required (repeatable)');
232
+ }
233
+ if (!opts.exposure) {
234
+ errors.push('--exposure is required');
235
+ }
236
+ const hasTestPaths = opts.testPathsManual || opts.testPathsUnit || opts.testPathsE2e;
237
+ if (effectiveType !== 'documentation' && effectiveType !== 'process') {
238
+ if (!opts.codePaths) {
239
+ errors.push('--code-paths is required for non-documentation WUs');
240
+ }
241
+ if (!hasTestPaths) {
242
+ errors.push('At least one test path flag is required (--test-paths-manual, --test-paths-unit, or --test-paths-e2e)');
243
+ }
244
+ }
245
+ if (effectiveType === 'feature' && !opts.specRefs) {
246
+ errors.push('--spec-refs is required for type: feature WUs');
247
+ }
248
+ if (errors.length > 0) {
249
+ return { valid: false, errors };
250
+ }
251
+ const placeholderResult = validateNoPlaceholders({
252
+ description: opts.description,
253
+ acceptance: opts.acceptance,
254
+ });
255
+ if (!placeholderResult.valid) {
256
+ return {
257
+ valid: false,
258
+ errors: [buildPlaceholderErrorMessage('wu:create', placeholderResult)],
259
+ };
260
+ }
261
+ const today = todayISO();
262
+ const wuContent = buildWUContent({
263
+ id,
264
+ lane,
265
+ title,
266
+ priority,
267
+ type: effectiveType,
268
+ created: today,
269
+ opts,
270
+ });
271
+ const schemaResult = validateWU(wuContent);
272
+ if (!schemaResult.success) {
273
+ const schemaErrors = schemaResult.error.issues.map((issue) => `${issue.path.join('.')}: ${issue.message}`);
274
+ return { valid: false, errors: schemaErrors };
275
+ }
276
+ const completeness = validateSpecCompleteness(wuContent, id);
277
+ if (!completeness.valid) {
278
+ return { valid: false, errors: completeness.errors };
279
+ }
280
+ return { valid: true, errors: [] };
281
+ }
282
+ /**
283
+ * Create WU YAML file in micro-worktree
284
+ *
285
+ * @param {string} worktreePath - Path to micro-worktree
286
+ * @param {string} id - WU ID
287
+ * @param {string} lane - WU lane
288
+ * @param {string} title - WU title
289
+ * @param {string} priority - WU priority
290
+ * @param {string} type - WU type
291
+ * @param {Object} opts - Additional options
292
+ * @returns {string} Relative path to created YAML file
293
+ */
294
+ function createWUYamlInWorktree(worktreePath, id, lane, title, priority, type, opts = {}) {
295
+ const wuRelativePath = WU_PATHS.WU(id);
296
+ const wuAbsolutePath = join(worktreePath, wuRelativePath);
297
+ const wuDir = join(worktreePath, WU_PATHS.WU_DIR());
298
+ mkdirSync(wuDir, { recursive: true });
299
+ // WU-1428: Use todayISO() for consistent YYYY-MM-DD format (library-first)
300
+ const today = todayISO();
301
+ const wuContent = buildWUContent({
302
+ id,
303
+ lane,
304
+ title,
305
+ priority,
306
+ type,
307
+ created: today,
308
+ opts,
309
+ });
310
+ // WU-1539: Validate WU structure before writing (fail-fast, no placeholders)
264
311
  // WU-1750: Zod transforms normalize embedded newlines in arrays and strings
265
- const validationResult = validateReadyWU(wuContent);
312
+ const validationResult = validateWU(wuContent);
266
313
  if (!validationResult.success) {
267
314
  const errors = validationResult.error.issues
268
315
  .map((issue) => ` • ${issue.path.join('.')}: ${issue.message}`)
@@ -270,6 +317,14 @@ function createWUYamlInWorktree(worktreePath, id, lane, title, priority, type, o
270
317
  die(`${LOG_PREFIX} ❌ WU YAML validation failed:\n\n${errors}\n\n` +
271
318
  `Fix the issues above and retry.`);
272
319
  }
320
+ const completenessResult = validateSpecCompleteness(wuContent, id);
321
+ if (!completenessResult.valid) {
322
+ const errorList = completenessResult.errors
323
+ .map((error) => ` • ${error}`)
324
+ .join(STRING_LITERALS.NEWLINE);
325
+ die(`${LOG_PREFIX} ❌ WU SPEC INCOMPLETE:\n\n${errorList}\n\n` +
326
+ `Provide the missing fields and retry.`);
327
+ }
273
328
  // WU-2253: Validate acceptance/code_paths consistency and invariants compliance
274
329
  // This blocks WU creation if acceptance references paths not in code_paths
275
330
  // or if code_paths conflicts with tools/invariants.yml
@@ -429,76 +484,95 @@ async function main() {
429
484
  if (!assignedTo) {
430
485
  console.warn(`${LOG_PREFIX} ⚠️ No assigned_to set - WU will need manual assignment`);
431
486
  }
432
- // WU-1364: Validate spec completeness when --validate is set
433
- if (args.validate) {
434
- const validationErrors = [];
435
- if (!args.description) {
436
- validationErrors.push('--description is required when --validate is set');
437
- }
438
- // acceptance is an array from repeatable flag (empty array if not provided)
439
- if (!args.acceptance || args.acceptance.length === 0) {
440
- validationErrors.push('--acceptance is required when --validate is set (use multiple times)');
441
- }
442
- const hasTestPaths = args.testPathsManual || args.testPathsUnit || args.testPathsE2e;
443
- if (!hasTestPaths) {
444
- validationErrors.push('--validate requires at least one test path flag (--test-paths-manual, --test-paths-unit, or --test-paths-e2e)');
445
- }
446
- // WU-2320: spec_refs required for feature WUs (type defaults to feature)
447
- const effectiveType = args.type || DEFAULT_TYPE;
448
- if (effectiveType === 'feature' && !args.specRefs) {
449
- validationErrors.push('--spec-refs is required for type: feature WUs (link to plan file in docs/04-operations/plans/)');
450
- }
451
- if (validationErrors.length > 0) {
452
- const errorList = validationErrors.map((e) => ` • ${e}`).join(STRING_LITERALS.NEWLINE);
453
- die(`Spec validation failed:\n\n${errorList}\n\nTo create without validation, omit the --validate flag.`);
454
- }
455
- console.log(`${LOG_PREFIX} ✅ Spec validation passed`);
487
+ const createSpecValidation = validateCreateSpec({
488
+ id: args.id,
489
+ lane: args.lane,
490
+ title: args.title,
491
+ priority: args.priority || DEFAULT_PRIORITY,
492
+ type: args.type || DEFAULT_TYPE,
493
+ opts: {
494
+ description: args.description,
495
+ acceptance: args.acceptance,
496
+ codePaths: args.codePaths,
497
+ testPathsManual: args.testPathsManual,
498
+ testPathsUnit: args.testPathsUnit,
499
+ testPathsE2e: args.testPathsE2e,
500
+ exposure: args.exposure,
501
+ userJourney: args.userJourney,
502
+ uiPairingWus: args.uiPairingWus,
503
+ specRefs: args.specRefs,
504
+ initiative: args.initiative,
505
+ phase: args.phase,
506
+ blockedBy: args.blockedBy,
507
+ blocks: args.blocks,
508
+ labels: args.labels,
509
+ assignedTo,
510
+ },
511
+ });
512
+ if (!createSpecValidation.valid) {
513
+ const errorList = createSpecValidation.errors
514
+ .map((error) => ` • ${error}`)
515
+ .join(STRING_LITERALS.NEWLINE);
516
+ die(`${LOG_PREFIX} ❌ Spec validation failed:\n\n${errorList}`);
456
517
  }
518
+ console.log(`${LOG_PREFIX} ✅ Spec validation passed`);
457
519
  // Transaction: micro-worktree isolation (WU-1439)
458
520
  try {
459
521
  const priority = args.priority || DEFAULT_PRIORITY;
460
522
  const type = args.type || DEFAULT_TYPE;
461
- await withMicroWorktree({
462
- operation: OPERATION_NAME,
463
- id: args.id,
464
- logPrefix: LOG_PREFIX,
465
- execute: async ({ worktreePath }) => {
466
- // Create WU YAML in micro-worktree
467
- const wuPath = createWUYamlInWorktree(worktreePath, args.id, args.lane, args.title, priority, type, {
468
- // Initiative system fields (WU-1247)
469
- initiative: args.initiative,
470
- phase: args.phase,
471
- blockedBy: args.blockedBy,
472
- blocks: args.blocks,
473
- labels: args.labels,
474
- // WU-1368: Assigned to
475
- assignedTo,
476
- // WU-1364: Full spec inline options
477
- description: args.description,
478
- acceptance: args.acceptance,
479
- codePaths: args.codePaths,
480
- testPathsManual: args.testPathsManual,
481
- testPathsUnit: args.testPathsUnit,
482
- testPathsE2e: args.testPathsE2e,
483
- // WU-1998: Exposure field options
484
- exposure: args.exposure,
485
- userJourney: args.userJourney,
486
- uiPairingWus: args.uiPairingWus,
487
- // WU-2320: Spec references
488
- specRefs: args.specRefs,
489
- });
490
- // Update backlog.md in micro-worktree
491
- const backlogPath = updateBacklogInWorktree(worktreePath, args.id, args.lane, args.title);
492
- // Build commit message
493
- const shortTitle = truncateTitle(args.title);
494
- const commitMessage = COMMIT_FORMATS.CREATE(args.id, shortTitle);
495
- // Return commit message and files to commit
496
- return {
497
- commitMessage,
498
- files: [wuPath, backlogPath],
499
- };
500
- },
501
- });
523
+ const previousWuTool = process.env.LUMENFLOW_WU_TOOL;
524
+ process.env.LUMENFLOW_WU_TOOL = OPERATION_NAME;
525
+ try {
526
+ await withMicroWorktree({
527
+ operation: OPERATION_NAME,
528
+ id: args.id,
529
+ logPrefix: LOG_PREFIX,
530
+ execute: async ({ worktreePath }) => {
531
+ // Create WU YAML in micro-worktree
532
+ const wuPath = createWUYamlInWorktree(worktreePath, args.id, args.lane, args.title, priority, type, {
533
+ // Initiative system fields (WU-1247)
534
+ initiative: args.initiative,
535
+ phase: args.phase,
536
+ blockedBy: args.blockedBy,
537
+ blocks: args.blocks,
538
+ labels: args.labels,
539
+ // WU-1368: Assigned to
540
+ assignedTo,
541
+ // WU-1364: Full spec inline options
542
+ description: args.description,
543
+ acceptance: args.acceptance,
544
+ codePaths: args.codePaths,
545
+ testPathsManual: args.testPathsManual,
546
+ testPathsUnit: args.testPathsUnit,
547
+ testPathsE2e: args.testPathsE2e,
548
+ // WU-1998: Exposure field options
549
+ exposure: args.exposure,
550
+ userJourney: args.userJourney,
551
+ uiPairingWus: args.uiPairingWus,
552
+ // WU-2320: Spec references
553
+ specRefs: args.specRefs,
554
+ });
555
+ // Update backlog.md in micro-worktree
556
+ const backlogPath = updateBacklogInWorktree(worktreePath, args.id, args.lane, args.title);
557
+ // Build commit message
558
+ const shortTitle = truncateTitle(args.title);
559
+ const commitMessage = COMMIT_FORMATS.CREATE(args.id, shortTitle);
560
+ // Return commit message and files to commit
561
+ return {
562
+ commitMessage,
563
+ files: [wuPath, backlogPath],
564
+ };
565
+ },
566
+ });
567
+ }
568
+ finally {
569
+ if (previousWuTool === undefined) {
570
+ delete process.env.LUMENFLOW_WU_TOOL;
571
+ }
572
+ else {
573
+ process.env.LUMENFLOW_WU_TOOL = previousWuTool;
574
+ }
575
+ }
502
576
  console.log(`\n${LOG_PREFIX} ✅ Transaction complete!`);
503
577
  console.log(`\nWU ${args.id} created successfully:`);
504
578
  console.log(` File: ${WU_PATHS.WU(args.id)}`);
package/dist/wu-done.js CHANGED
@@ -147,7 +147,7 @@ async function validateClaimMetadataBeforeGates(id, worktreePath, yamlStatus) {
147
147
  ` pnpm wu:repair-claim --id ${id}\n\n` +
148
148
  `After repair, retry:\n` +
149
149
  ` pnpm wu:done --id ${id}\n\n` +
150
- `See: ai/onboarding/troubleshooting-wu-done.md for more recovery options.`);
150
+ `See: docs/04-operations/_frameworks/lumenflow/agent/onboarding/troubleshooting-wu-done.md for more recovery options.`);
151
151
  }
152
152
  export function printExposureWarnings(wu, options = {}) {
153
153
  // Validate exposure
@@ -1793,7 +1793,8 @@ async function executeGates({ id, args, isBranchOnly, isDocsOnly, worktreePath,
1793
1793
  }
1794
1794
  else {
1795
1795
  die(`Worktree not found (${worktreePath || 'unknown'}). Gates must run in the lane worktree.\n` +
1796
- `If the worktree was removed, recreate it and retry, or use --skip-gates with justification.`);
1796
+ `If the worktree was removed, recreate it and retry, or rerun with --branch-only when the lane branch exists.\n` +
1797
+ `Use --skip-gates only with justification.`);
1797
1798
  }
1798
1799
  // Step 0.75: Run COS governance gates (WU-614, COS v1.3 §7)
1799
1800
  if (!args.skipCosGates) {
@@ -1858,6 +1859,13 @@ async function executeGates({ id, args, isBranchOnly, isDocsOnly, worktreePath,
1858
1859
  * @param {string|null} params.derivedWorktree - Derived worktree path
1859
1860
  * @param {string} params.STAMPS_DIR - Stamps directory path
1860
1861
  */
1862
+ export function computeBranchOnlyFallback({ isBranchOnly, branchOnlyRequested, worktreeExists, derivedWorktree, }) {
1863
+ const allowFallback = Boolean(branchOnlyRequested) && !isBranchOnly && !worktreeExists && Boolean(derivedWorktree);
1864
+ return {
1865
+ allowFallback,
1866
+ effectiveBranchOnly: isBranchOnly || allowFallback,
1867
+ };
1868
+ }
1861
1869
  function printStateHUD({ id, docMain, isBranchOnly, isDocsOnly, derivedWorktree, STAMPS_DIR }) {
1862
1870
  const stampExists = existsSync(path.join(STAMPS_DIR, `${id}.done`)) ? 'yes' : 'no';
1863
1871
  const yamlStatus = docMain.status || 'unknown';
@@ -1869,6 +1877,8 @@ function printStateHUD({ id, docMain, isBranchOnly, isDocsOnly, derivedWorktree,
1869
1877
  }
1870
1878
  // eslint-disable-next-line sonarjs/cognitive-complexity -- Pre-existing complexity, refactor tracked separately
1871
1879
  async function main() {
1880
+ // Allow pre-push hook to recognize wu:done automation (WU-1030)
1881
+ process.env.LUMENFLOW_WU_TOOL = 'wu-done';
1872
1882
  // Validate CLI arguments and WU ID format (extracted to wu-done-validators.mjs)
1873
1883
  const { args, id } = validateInputs(process.argv);
1874
1884
  // Detect workspace mode and calculate paths (WU-1215: extracted to validators module)
@@ -1876,25 +1886,39 @@ async function main() {
1876
1886
  const { WU_PATH, STATUS_PATH, BACKLOG_PATH, STAMPS_DIR, docMain, isBranchOnly, derivedWorktree, docForValidation: initialDocForValidation, isDocsOnly, } = pathInfo;
1877
1887
  // Capture main checkout path once. process.cwd() may drift later during recovery flows.
1878
1888
  const mainCheckoutPath = process.cwd();
1889
+ // Resolve worktree path early so we can detect missing worktree before pre-flight checks
1890
+ const resolvedWorktreePath = derivedWorktree && !isBranchOnly
1891
+ ? path.isAbsolute(derivedWorktree)
1892
+ ? derivedWorktree
1893
+ : path.resolve(mainCheckoutPath, derivedWorktree)
1894
+ : null;
1895
+ const worktreeExists = resolvedWorktreePath ? existsSync(resolvedWorktreePath) : false;
1896
+ const { allowFallback: allowBranchOnlyFallback, effectiveBranchOnly } = computeBranchOnlyFallback({
1897
+ isBranchOnly,
1898
+ branchOnlyRequested: args.branchOnly,
1899
+ worktreeExists,
1900
+ derivedWorktree,
1901
+ });
1902
+ if (allowBranchOnlyFallback) {
1903
+ console.warn(`${LOG_PREFIX.DONE} ${EMOJI.WARNING} Worktree missing (${resolvedWorktreePath}). Proceeding in branch-only mode because --branch-only was provided.`);
1904
+ }
1905
+ const effectiveDerivedWorktree = effectiveBranchOnly ? null : derivedWorktree;
1906
+ const effectiveWorktreePath = effectiveBranchOnly ? null : resolvedWorktreePath;
1879
1907
  // Pre-flight checks (WU-1215: extracted to executePreFlightChecks function)
1880
1908
  const preFlightResult = await executePreFlightChecks({
1881
1909
  id,
1882
1910
  args,
1883
- isBranchOnly,
1911
+ isBranchOnly: effectiveBranchOnly,
1884
1912
  isDocsOnly,
1885
1913
  docMain,
1886
1914
  docForValidation: initialDocForValidation,
1887
- derivedWorktree,
1915
+ derivedWorktree: effectiveDerivedWorktree,
1888
1916
  });
1889
1917
  const title = preFlightResult.title;
1890
1918
  // Note: docForValidation is returned but not used after pre-flight checks
1891
1919
  // The metadata transaction uses docForUpdate instead
1892
1920
  // Step 0: Run gates (WU-1215: extracted to executeGates function)
1893
- const worktreePath = derivedWorktree && !isBranchOnly
1894
- ? path.isAbsolute(derivedWorktree)
1895
- ? derivedWorktree
1896
- : path.resolve(mainCheckoutPath, derivedWorktree)
1897
- : null;
1921
+ const worktreePath = effectiveWorktreePath;
1898
1922
  // WU-1943: Check if any checkpoints exist for this WU session
1899
1923
  // Warn (don't block) if no checkpoints - agent should have been checkpointing periodically
1900
1924
  try {
@@ -1908,9 +1932,16 @@ async function main() {
1908
1932
  catch {
1909
1933
  // Non-blocking: checkpoint check failure should not block wu:done
1910
1934
  }
1911
- await executeGates({ id, args, isBranchOnly, isDocsOnly, worktreePath });
1935
+ await executeGates({ id, args, isBranchOnly: effectiveBranchOnly, isDocsOnly, worktreePath });
1912
1936
  // Print State HUD for visibility (WU-1215: extracted to printStateHUD function)
1913
- printStateHUD({ id, docMain, isBranchOnly, isDocsOnly, derivedWorktree, STAMPS_DIR });
1937
+ printStateHUD({
1938
+ id,
1939
+ docMain,
1940
+ isBranchOnly: effectiveBranchOnly,
1941
+ isDocsOnly,
1942
+ derivedWorktree: effectiveDerivedWorktree,
1943
+ STAMPS_DIR,
1944
+ });
1914
1945
  // Step 0.5: Pre-flight validation - run ALL pre-commit hooks BEFORE merge
1915
1946
  // This prevents partial completion states where merge succeeds but commit fails
1916
1947
  // Validates all 8 gates: secrets, file size, ESLint, Prettier, TypeScript, audit, architecture, tasks
@@ -1947,7 +1978,7 @@ async function main() {
1947
1978
  validateStagedFiles,
1948
1979
  };
1949
1980
  try {
1950
- if (isBranchOnly) {
1981
+ if (effectiveBranchOnly) {
1951
1982
  // Branch-Only mode: merge first, then update metadata on main
1952
1983
  // NOTE: Branch-only still uses old rollback mechanism
1953
1984
  const branchOnlyContext = {
@@ -2165,7 +2196,7 @@ async function detectChangedDocPaths(worktreePath, baseBranch) {
2165
2196
  // Get files changed in this branch vs base
2166
2197
  const diff = await git.raw(['diff', '--name-only', baseBranch]);
2167
2198
  const changedFiles = diff.split('\n').filter(Boolean);
2168
- // Filter to docs: ai/onboarding/, docs/, CLAUDE.md, README.md, *.md in root
2199
+ // Filter to docs: docs/04-operations/_frameworks/lumenflow/agent/onboarding/, docs/, CLAUDE.md, README.md, *.md in root
2169
2200
  const docPatterns = [
2170
2201
  /^ai\/onboarding\//,
2171
2202
  /^docs\//,