@lumenflow/core 2.5.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.
@@ -357,6 +357,15 @@ export declare const TelemetryConfigSchema: z.ZodObject<{
357
357
  enabled: z.ZodDefault<z.ZodBoolean>;
358
358
  }, z.core.$strip>>;
359
359
  }, z.core.$strip>;
360
+ /**
361
+ * WU-1345: Lane enforcement configuration schema
362
+ *
363
+ * Controls how lane format validation behaves.
364
+ */
365
+ export declare const LanesEnforcementSchema: z.ZodObject<{
366
+ require_parent: z.ZodDefault<z.ZodBoolean>;
367
+ allow_custom: z.ZodDefault<z.ZodBoolean>;
368
+ }, z.core.$strip>;
360
369
  /**
361
370
  * WU-1322: Lane definition schema for .lumenflow.config.yaml
362
371
  *
@@ -374,6 +383,66 @@ export declare const LaneDefinitionSchema: z.ZodObject<{
374
383
  }>>>;
375
384
  code_paths: z.ZodOptional<z.ZodArray<z.ZodString>>;
376
385
  }, z.core.$strip>;
386
+ /**
387
+ * WU-1345: Complete lanes configuration schema
388
+ *
389
+ * Supports three formats:
390
+ * 1. definitions array (recommended)
391
+ * 2. engineering + business arrays (legacy/alternate)
392
+ * 3. flat array (simple format - parsed as definitions)
393
+ *
394
+ * @example
395
+ * ```yaml
396
+ * lanes:
397
+ * enforcement:
398
+ * require_parent: true
399
+ * allow_custom: false
400
+ * definitions:
401
+ * - name: 'Framework: Core'
402
+ * wip_limit: 1
403
+ * code_paths:
404
+ * - 'packages/@lumenflow/core/**'
405
+ * ```
406
+ */
407
+ export declare const LanesConfigSchema: z.ZodObject<{
408
+ enforcement: z.ZodOptional<z.ZodObject<{
409
+ require_parent: z.ZodDefault<z.ZodBoolean>;
410
+ allow_custom: z.ZodDefault<z.ZodBoolean>;
411
+ }, z.core.$strip>>;
412
+ definitions: z.ZodOptional<z.ZodArray<z.ZodObject<{
413
+ name: z.ZodString;
414
+ wip_limit: z.ZodOptional<z.ZodNumber>;
415
+ wip_justification: z.ZodOptional<z.ZodString>;
416
+ lock_policy: z.ZodDefault<z.ZodDefault<z.ZodEnum<{
417
+ none: "none";
418
+ all: "all";
419
+ active: "active";
420
+ }>>>;
421
+ code_paths: z.ZodOptional<z.ZodArray<z.ZodString>>;
422
+ }, z.core.$strip>>>;
423
+ engineering: z.ZodOptional<z.ZodArray<z.ZodObject<{
424
+ name: z.ZodString;
425
+ wip_limit: z.ZodOptional<z.ZodNumber>;
426
+ wip_justification: z.ZodOptional<z.ZodString>;
427
+ lock_policy: z.ZodDefault<z.ZodDefault<z.ZodEnum<{
428
+ none: "none";
429
+ all: "all";
430
+ active: "active";
431
+ }>>>;
432
+ code_paths: z.ZodOptional<z.ZodArray<z.ZodString>>;
433
+ }, z.core.$strip>>>;
434
+ business: z.ZodOptional<z.ZodArray<z.ZodObject<{
435
+ name: z.ZodString;
436
+ wip_limit: z.ZodOptional<z.ZodNumber>;
437
+ wip_justification: z.ZodOptional<z.ZodString>;
438
+ lock_policy: z.ZodDefault<z.ZodDefault<z.ZodEnum<{
439
+ none: "none";
440
+ all: "all";
441
+ active: "active";
442
+ }>>>;
443
+ code_paths: z.ZodOptional<z.ZodArray<z.ZodString>>;
444
+ }, z.core.$strip>>>;
445
+ }, z.core.$strip>;
377
446
  /**
378
447
  * Complete LumenFlow configuration schema
379
448
  */
@@ -571,6 +640,45 @@ export declare const LumenFlowConfigSchema: z.ZodObject<{
571
640
  }>>;
572
641
  }, z.core.$strip>>;
573
642
  }, z.core.$strip>>;
643
+ lanes: z.ZodOptional<z.ZodObject<{
644
+ enforcement: z.ZodOptional<z.ZodObject<{
645
+ require_parent: z.ZodDefault<z.ZodBoolean>;
646
+ allow_custom: z.ZodDefault<z.ZodBoolean>;
647
+ }, z.core.$strip>>;
648
+ definitions: z.ZodOptional<z.ZodArray<z.ZodObject<{
649
+ name: z.ZodString;
650
+ wip_limit: z.ZodOptional<z.ZodNumber>;
651
+ wip_justification: z.ZodOptional<z.ZodString>;
652
+ lock_policy: z.ZodDefault<z.ZodDefault<z.ZodEnum<{
653
+ none: "none";
654
+ all: "all";
655
+ active: "active";
656
+ }>>>;
657
+ code_paths: z.ZodOptional<z.ZodArray<z.ZodString>>;
658
+ }, z.core.$strip>>>;
659
+ engineering: z.ZodOptional<z.ZodArray<z.ZodObject<{
660
+ name: z.ZodString;
661
+ wip_limit: z.ZodOptional<z.ZodNumber>;
662
+ wip_justification: z.ZodOptional<z.ZodString>;
663
+ lock_policy: z.ZodDefault<z.ZodDefault<z.ZodEnum<{
664
+ none: "none";
665
+ all: "all";
666
+ active: "active";
667
+ }>>>;
668
+ code_paths: z.ZodOptional<z.ZodArray<z.ZodString>>;
669
+ }, z.core.$strip>>>;
670
+ business: z.ZodOptional<z.ZodArray<z.ZodObject<{
671
+ name: z.ZodString;
672
+ wip_limit: z.ZodOptional<z.ZodNumber>;
673
+ wip_justification: z.ZodOptional<z.ZodString>;
674
+ lock_policy: z.ZodDefault<z.ZodDefault<z.ZodEnum<{
675
+ none: "none";
676
+ all: "all";
677
+ active: "active";
678
+ }>>>;
679
+ code_paths: z.ZodOptional<z.ZodArray<z.ZodString>>;
680
+ }, z.core.$strip>>>;
681
+ }, z.core.$strip>>;
574
682
  }, z.core.$strip>;
575
683
  /**
576
684
  * TypeScript types inferred from schemas
@@ -598,6 +706,8 @@ export type MethodologyTelemetryConfig = z.infer<typeof MethodologyTelemetryConf
598
706
  export type TelemetryConfig = z.infer<typeof TelemetryConfigSchema>;
599
707
  export type LumenFlowConfig = z.infer<typeof LumenFlowConfigSchema>;
600
708
  export type LaneDefinition = z.infer<typeof LaneDefinitionSchema>;
709
+ export type LanesEnforcement = z.infer<typeof LanesEnforcementSchema>;
710
+ export type LanesConfig = z.infer<typeof LanesConfigSchema>;
601
711
  export type { MethodologyConfig, MethodologyOverrides } from './resolve-policy.js';
602
712
  /**
603
713
  * Validate configuration data
@@ -780,6 +890,33 @@ export declare function validateConfig(data: unknown): z.ZodSafeParseResult<{
780
890
  coverage_mode?: "off" | "warn" | "block";
781
891
  };
782
892
  };
893
+ lanes?: {
894
+ enforcement?: {
895
+ require_parent: boolean;
896
+ allow_custom: boolean;
897
+ };
898
+ definitions?: {
899
+ name: string;
900
+ lock_policy: "none" | "all" | "active";
901
+ wip_limit?: number;
902
+ wip_justification?: string;
903
+ code_paths?: string[];
904
+ }[];
905
+ engineering?: {
906
+ name: string;
907
+ lock_policy: "none" | "all" | "active";
908
+ wip_limit?: number;
909
+ wip_justification?: string;
910
+ code_paths?: string[];
911
+ }[];
912
+ business?: {
913
+ name: string;
914
+ lock_policy: "none" | "all" | "active";
915
+ wip_limit?: number;
916
+ wip_justification?: string;
917
+ code_paths?: string[];
918
+ }[];
919
+ };
783
920
  }>;
784
921
  /**
785
922
  * Parse configuration with defaults
@@ -582,6 +582,24 @@ export const TelemetryConfigSchema = z.object({
582
582
  */
583
583
  methodology: MethodologyTelemetryConfigSchema.default(() => MethodologyTelemetryConfigSchema.parse({})),
584
584
  });
585
+ /**
586
+ * WU-1345: Lane enforcement configuration schema
587
+ *
588
+ * Controls how lane format validation behaves.
589
+ */
590
+ export const LanesEnforcementSchema = z.object({
591
+ /**
592
+ * When true, lanes MUST use "Parent: Sublane" format if parent has taxonomy.
593
+ * @default true
594
+ */
595
+ require_parent: z.boolean().default(true),
596
+ /**
597
+ * When false, only lanes in the taxonomy are allowed.
598
+ * When true, custom lanes can be used.
599
+ * @default false
600
+ */
601
+ allow_custom: z.boolean().default(false),
602
+ });
585
603
  /**
586
604
  * WU-1322: Lane definition schema for .lumenflow.config.yaml
587
605
  *
@@ -616,6 +634,37 @@ export const LaneDefinitionSchema = z.object({
616
634
  /** Code paths associated with this lane (glob patterns) */
617
635
  code_paths: z.array(z.string()).optional(),
618
636
  });
637
+ /**
638
+ * WU-1345: Complete lanes configuration schema
639
+ *
640
+ * Supports three formats:
641
+ * 1. definitions array (recommended)
642
+ * 2. engineering + business arrays (legacy/alternate)
643
+ * 3. flat array (simple format - parsed as definitions)
644
+ *
645
+ * @example
646
+ * ```yaml
647
+ * lanes:
648
+ * enforcement:
649
+ * require_parent: true
650
+ * allow_custom: false
651
+ * definitions:
652
+ * - name: 'Framework: Core'
653
+ * wip_limit: 1
654
+ * code_paths:
655
+ * - 'packages/@lumenflow/core/**'
656
+ * ```
657
+ */
658
+ export const LanesConfigSchema = z.object({
659
+ /** Lane enforcement configuration (validation rules) */
660
+ enforcement: LanesEnforcementSchema.optional(),
661
+ /** Primary lane definitions array (recommended format) */
662
+ definitions: z.array(LaneDefinitionSchema).optional(),
663
+ /** Engineering lanes (alternate format) */
664
+ engineering: z.array(LaneDefinitionSchema).optional(),
665
+ /** Business lanes (alternate format) */
666
+ business: z.array(LaneDefinitionSchema).optional(),
667
+ });
619
668
  /**
620
669
  * Complete LumenFlow configuration schema
621
670
  */
@@ -663,6 +712,25 @@ export const LumenFlowConfigSchema = z.object({
663
712
  * ```
664
713
  */
665
714
  methodology: MethodologyConfigSchema.optional(),
715
+ /**
716
+ * WU-1345: Lanes configuration
717
+ * Defines delivery lanes with WIP limits, code paths, and lock policies.
718
+ * Required for resolveLaneConfigsFromConfig() to work with getConfig().
719
+ *
720
+ * @example
721
+ * ```yaml
722
+ * lanes:
723
+ * enforcement:
724
+ * require_parent: true
725
+ * allow_custom: false
726
+ * definitions:
727
+ * - name: 'Framework: Core'
728
+ * wip_limit: 1
729
+ * code_paths:
730
+ * - 'packages/@lumenflow/core/**'
731
+ * ```
732
+ */
733
+ lanes: LanesConfigSchema.optional(),
666
734
  });
667
735
  /**
668
736
  * Validate configuration data
@@ -306,11 +306,16 @@ export declare function mergeWithRetry(tempBranchName: string, microWorktreePath
306
306
  * Push to origin/main with retry logic for race conditions
307
307
  *
308
308
  * WU-1179: When push fails because origin/main advanced (race condition with
309
- * parallel agents), this function rolls back local main to origin/main and
310
- * retries the full sequence: fetch -> rebase temp branch -> re-merge -> push.
309
+ * parallel agents), this function retries with fetch and rebase.
311
310
  *
312
- * This prevents the scenario where local main is left diverged from origin
313
- * after a push failure.
311
+ * WU-1348: The retry logic no longer resets the main checkout. Instead, it:
312
+ * 1. Fetches origin/main to get latest remote state
313
+ * 2. Rebases the temp branch onto origin/main (in the micro-worktree)
314
+ * 3. Re-merges the rebased temp branch to local main (ff-only)
315
+ * 4. Retries the push
316
+ *
317
+ * This preserves micro-worktree isolation - the main checkout files are never
318
+ * hard-reset, preventing file flash and preserving any uncommitted work.
314
319
  *
315
320
  * @param {Object} mainGit - GitAdapter instance for main checkout
316
321
  * @param {Object} worktreeGit - GitAdapter instance for micro-worktree
@@ -328,6 +333,15 @@ export declare function pushWithRetry(mainGit: GitAdapter, worktreeGit: GitAdapt
328
333
  * and supports configuration via PushRetryConfig. When push fails due to
329
334
  * non-fast-forward (origin moved), automatically rebases and retries.
330
335
  *
336
+ * WU-1348: The retry logic no longer resets the main checkout. Instead, it:
337
+ * 1. Fetches origin/main to get latest remote state
338
+ * 2. Rebases the temp branch onto origin/main (in the micro-worktree)
339
+ * 3. Re-merges the rebased temp branch to local main (ff-only)
340
+ * 4. Retries the push
341
+ *
342
+ * This preserves micro-worktree isolation - the main checkout files are never
343
+ * hard-reset, preventing file flash and preserving any uncommitted work.
344
+ *
331
345
  * @param {Object} mainGit - GitAdapter instance for main checkout
332
346
  * @param {Object} worktreeGit - GitAdapter instance for micro-worktree
333
347
  * @param {string} remote - Remote name (e.g., 'origin')
@@ -518,11 +518,16 @@ export async function mergeWithRetry(tempBranchName, microWorktreePath, logPrefi
518
518
  * Push to origin/main with retry logic for race conditions
519
519
  *
520
520
  * WU-1179: When push fails because origin/main advanced (race condition with
521
- * parallel agents), this function rolls back local main to origin/main and
522
- * retries the full sequence: fetch -> rebase temp branch -> re-merge -> push.
521
+ * parallel agents), this function retries with fetch and rebase.
523
522
  *
524
- * This prevents the scenario where local main is left diverged from origin
525
- * after a push failure.
523
+ * WU-1348: The retry logic no longer resets the main checkout. Instead, it:
524
+ * 1. Fetches origin/main to get latest remote state
525
+ * 2. Rebases the temp branch onto origin/main (in the micro-worktree)
526
+ * 3. Re-merges the rebased temp branch to local main (ff-only)
527
+ * 4. Retries the push
528
+ *
529
+ * This preserves micro-worktree isolation - the main checkout files are never
530
+ * hard-reset, preventing file flash and preserving any uncommitted work.
526
531
  *
527
532
  * @param {Object} mainGit - GitAdapter instance for main checkout
528
533
  * @param {Object} worktreeGit - GitAdapter instance for micro-worktree
@@ -544,27 +549,27 @@ export async function pushWithRetry(mainGit, worktreeGit, remote, branch, tempBr
544
549
  }
545
550
  catch (pushErr) {
546
551
  if (attempt < maxRetries) {
547
- console.log(`${logPrefix} ⚠️ Push failed (origin moved). Rolling back and retrying...`);
548
- // Step 1: Rollback local main to origin/main
549
- console.log(`${logPrefix} Rolling back local ${branch} to ${remote}/${branch}...`);
550
- await mainGit.reset(`${remote}/${branch}`, { hard: true });
551
- // Step 2: Fetch latest origin/main
552
+ console.log(`${logPrefix} ⚠️ Push failed (origin moved). Fetching and rebasing before retry...`);
553
+ // WU-1348: Do NOT reset main checkout - preserve micro-worktree isolation
554
+ // Instead, fetch latest remote state and rebase the temp branch
555
+ // Step 1: Fetch latest origin/main
552
556
  console.log(`${logPrefix} Fetching ${remote}/${branch}...`);
553
557
  await mainGit.fetch(remote, branch);
554
- // Step 3: Update local main to match origin/main (ff-only)
555
- console.log(`${logPrefix} Updating local ${branch}...`);
556
- await mainGit.merge(`${remote}/${branch}`, { ffOnly: true });
557
- // Step 4: Rebase temp branch onto updated main
558
- console.log(`${logPrefix} Rebasing temp branch onto ${branch}...`);
559
- await worktreeGit.rebase(branch);
560
- // Step 5: Re-merge temp branch to local main
558
+ // Step 2: Rebase temp branch onto updated origin/main
559
+ console.log(`${logPrefix} Rebasing temp branch onto ${remote}/${branch}...`);
560
+ await worktreeGit.rebase(`${remote}/${branch}`);
561
+ // Step 3: Re-merge temp branch to local main (ff-only)
562
+ // This updates local main to include the rebased commits
561
563
  console.log(`${logPrefix} Re-merging temp branch to ${branch}...`);
562
564
  await mainGit.merge(tempBranchName, { ffOnly: true });
563
565
  }
564
566
  else {
565
567
  const errMsg = pushErr instanceof Error ? pushErr.message : String(pushErr);
566
568
  throw new Error(`Push failed after ${maxRetries} attempts. ` +
567
- `Origin ${branch} may have significant traffic.\n` +
569
+ `Origin ${branch} may have significant traffic.\n\n` +
570
+ `Suggestions:\n` +
571
+ ` - Wait a few seconds and retry the operation\n` +
572
+ ` - Check if another agent is rapidly pushing changes\n` +
568
573
  `Error: ${errMsg}`);
569
574
  }
570
575
  }
@@ -577,6 +582,15 @@ export async function pushWithRetry(mainGit, worktreeGit, remote, branch, tempBr
577
582
  * and supports configuration via PushRetryConfig. When push fails due to
578
583
  * non-fast-forward (origin moved), automatically rebases and retries.
579
584
  *
585
+ * WU-1348: The retry logic no longer resets the main checkout. Instead, it:
586
+ * 1. Fetches origin/main to get latest remote state
587
+ * 2. Rebases the temp branch onto origin/main (in the micro-worktree)
588
+ * 3. Re-merges the rebased temp branch to local main (ff-only)
589
+ * 4. Retries the push
590
+ *
591
+ * This preserves micro-worktree isolation - the main checkout files are never
592
+ * hard-reset, preventing file flash and preserving any uncommitted work.
593
+ *
580
594
  * @param {Object} mainGit - GitAdapter instance for main checkout
581
595
  * @param {Object} worktreeGit - GitAdapter instance for micro-worktree
582
596
  * @param {string} remote - Remote name (e.g., 'origin')
@@ -603,20 +617,17 @@ export async function pushWithRetryConfig(mainGit, worktreeGit, remote, branch,
603
617
  console.log(`${logPrefix} ✅ Pushed to ${remote}/${branch}`);
604
618
  }
605
619
  catch (pushErr) {
606
- console.log(`${logPrefix} ⚠️ Push failed (origin moved). Rolling back and retrying...`);
607
- // Rollback local main to origin/main
608
- console.log(`${logPrefix} Rolling back local ${branch} to ${remote}/${branch}...`);
609
- await mainGit.reset(`${remote}/${branch}`, { hard: true });
620
+ console.log(`${logPrefix} ⚠️ Push failed (origin moved). Fetching and rebasing before retry...`);
621
+ // WU-1348: Do NOT reset main checkout - preserve micro-worktree isolation
622
+ // Instead, fetch latest remote state and rebase the temp branch
610
623
  // Fetch latest origin/main
611
624
  console.log(`${logPrefix} Fetching ${remote}/${branch}...`);
612
625
  await mainGit.fetch(remote, branch);
613
- // Update local main to match origin/main (ff-only)
614
- console.log(`${logPrefix} Updating local ${branch}...`);
615
- await mainGit.merge(`${remote}/${branch}`, { ffOnly: true });
616
- // Rebase temp branch onto updated main
617
- console.log(`${logPrefix} Rebasing temp branch onto ${branch}...`);
618
- await worktreeGit.rebase(branch);
619
- // Re-merge temp branch to local main
626
+ // Rebase temp branch onto updated origin/main
627
+ console.log(`${logPrefix} Rebasing temp branch onto ${remote}/${branch}...`);
628
+ await worktreeGit.rebase(`${remote}/${branch}`);
629
+ // Re-merge temp branch to local main (ff-only)
630
+ // This updates local main to include the rebased commits
620
631
  console.log(`${logPrefix} Re-merging temp branch to ${branch}...`);
621
632
  await mainGit.merge(tempBranchName, { ffOnly: true });
622
633
  // Re-throw to trigger p-retry
@@ -627,11 +638,8 @@ export async function pushWithRetryConfig(mainGit, worktreeGit, remote, branch,
627
638
  minTimeout: config.min_delay_ms,
628
639
  maxTimeout: config.max_delay_ms,
629
640
  randomize: config.jitter,
630
- onFailedAttempt: (error) => {
631
- // Log is handled in the try/catch above
632
- if (error.retriesLeft === 0) {
633
- // This will be the final failure
634
- }
641
+ onFailedAttempt: () => {
642
+ // Logging is handled in the try/catch above
635
643
  },
636
644
  }).catch(() => {
637
645
  // p-retry exhausted all retries, throw descriptive error
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lumenflow/core",
3
- "version": "2.5.0",
3
+ "version": "2.5.1",
4
4
  "description": "Core WU lifecycle tools for LumenFlow workflow framework",
5
5
  "keywords": [
6
6
  "lumenflow",
@@ -99,7 +99,7 @@
99
99
  "vitest": "^4.0.17"
100
100
  },
101
101
  "peerDependencies": {
102
- "@lumenflow/memory": "2.5.0"
102
+ "@lumenflow/memory": "2.5.1"
103
103
  },
104
104
  "peerDependenciesMeta": {
105
105
  "@lumenflow/memory": {