@lumenflow/core 2.4.0 → 2.5.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.
@@ -11,6 +11,24 @@ import { z } from 'zod';
11
11
  import { GatesExecutionConfigSchema } from './gates-config.js';
12
12
  // WU-1259: Import methodology config schema for resolvePolicy()
13
13
  import { MethodologyConfigSchema } from './resolve-policy.js';
14
+ /**
15
+ * WU-1325: Lock policy for lane-level WIP enforcement
16
+ *
17
+ * Controls how lane locks behave:
18
+ * - 'all' (default): Lock acquired on claim, held through block, released on done
19
+ * - 'active': Lock acquired on claim, released on block, re-acquired on unblock
20
+ * - 'none': No lock files created, WIP checking disabled
21
+ *
22
+ * @example
23
+ * ```yaml
24
+ * lanes:
25
+ * definitions:
26
+ * - name: 'Content: Documentation'
27
+ * wip_limit: 4
28
+ * lock_policy: 'none' # Docs don't need lock coordination
29
+ * ```
30
+ */
31
+ export const LockPolicySchema = z.enum(['all', 'active', 'none']).default('all');
14
32
  /**
15
33
  * Event archival configuration (WU-1207)
16
34
  *
@@ -69,6 +87,10 @@ export const DirectoriesSchema = z.object({
69
87
  agentsDir: z.string().default('.claude/agents'),
70
88
  /** Plans directory (default: 'docs/04-operations/plans') - WU-1301 */
71
89
  plansDir: z.string().default('docs/04-operations/plans'),
90
+ /** Templates directory (default: '.lumenflow/templates') - WU-1310 */
91
+ templatesDir: z.string().default('.lumenflow/templates'),
92
+ /** Onboarding directory (default: 'docs/04-operations/_frameworks/lumenflow/agent/onboarding') - WU-1310 */
93
+ onboardingDir: z.string().default('docs/04-operations/_frameworks/lumenflow/agent/onboarding'),
72
94
  });
73
95
  /**
74
96
  * Beacon paths configuration (.lumenflow directory structure)
@@ -100,6 +122,45 @@ export const BeaconPathsSchema = z.object({
100
122
  */
101
123
  eventArchival: EventArchivalConfigSchema.default(() => EventArchivalConfigSchema.parse({})),
102
124
  });
125
+ /**
126
+ * WU-1332: Push retry configuration for micro-worktree operations
127
+ *
128
+ * When non-fast-forward push errors occur (origin/main moved during operation),
129
+ * retry with exponential backoff. Uses p-retry for robust retry behavior.
130
+ */
131
+ export const PushRetryConfigSchema = z.object({
132
+ /**
133
+ * Enable push retry with rebase on non-fast-forward errors.
134
+ * When true, failed pushes trigger automatic rebase and retry.
135
+ * When false, the original error is thrown immediately.
136
+ * @default true
137
+ */
138
+ enabled: z.boolean().default(true),
139
+ /**
140
+ * Maximum number of retry attempts (including the initial attempt).
141
+ * After this many failures, the operation fails with clear guidance.
142
+ * @default 3
143
+ */
144
+ retries: z.number().int().positive().default(3),
145
+ /**
146
+ * Minimum delay in milliseconds between retries.
147
+ * Used as the base for exponential backoff.
148
+ * @default 100
149
+ */
150
+ min_delay_ms: z.number().int().nonnegative().default(100),
151
+ /**
152
+ * Maximum delay in milliseconds between retries.
153
+ * Caps the exponential backoff to prevent excessive waits.
154
+ * @default 1000
155
+ */
156
+ max_delay_ms: z.number().int().positive().default(1000),
157
+ /**
158
+ * Add randomization to retry delays (recommended for concurrent agents).
159
+ * Helps prevent thundering herd when multiple agents retry simultaneously.
160
+ * @default true
161
+ */
162
+ jitter: z.boolean().default(true),
163
+ });
103
164
  /**
104
165
  * Git configuration
105
166
  */
@@ -189,6 +250,23 @@ export const GitConfigSchema = z.object({
189
250
  * ```
190
251
  */
191
252
  disableAgentPatternRegistry: z.boolean().default(false),
253
+ /**
254
+ * WU-1332: Push retry configuration for micro-worktree operations.
255
+ * When push fails due to non-fast-forward (origin moved), automatically
256
+ * rebase and retry with exponential backoff.
257
+ *
258
+ * @example
259
+ * ```yaml
260
+ * git:
261
+ * push_retry:
262
+ * enabled: true
263
+ * retries: 5 # Try 5 times total
264
+ * min_delay_ms: 200 # Start with 200ms delay
265
+ * max_delay_ms: 2000 # Cap at 2 second delay
266
+ * jitter: true # Add randomization
267
+ * ```
268
+ */
269
+ push_retry: PushRetryConfigSchema.default(() => PushRetryConfigSchema.parse({})),
192
270
  });
193
271
  /**
194
272
  * WU (Work Unit) configuration
@@ -504,6 +582,40 @@ export const TelemetryConfigSchema = z.object({
504
582
  */
505
583
  methodology: MethodologyTelemetryConfigSchema.default(() => MethodologyTelemetryConfigSchema.parse({})),
506
584
  });
585
+ /**
586
+ * WU-1322: Lane definition schema for .lumenflow.config.yaml
587
+ *
588
+ * Extends the existing lane configuration with lock_policy field.
589
+ * Compatible with WU-1016 (wip_limit) and WU-1187 (wip_justification).
590
+ */
591
+ export const LaneDefinitionSchema = z.object({
592
+ /** Lane name in "Parent: Sublane" format (e.g., "Framework: Core") */
593
+ name: z.string(),
594
+ /** WU-1016: Maximum WUs allowed in progress concurrently for this lane */
595
+ wip_limit: z.number().int().positive().optional(),
596
+ /** WU-1187: Required justification when wip_limit > 1 */
597
+ wip_justification: z.string().optional(),
598
+ /**
599
+ * WU-1322: Lock policy for this lane.
600
+ * - 'all': Lock lane for all other agents (default)
601
+ * - 'active': Lock only for agents with overlapping code_paths
602
+ * - 'none': No locking (suitable for documentation lanes)
603
+ *
604
+ * @default 'all'
605
+ *
606
+ * @example
607
+ * ```yaml
608
+ * lanes:
609
+ * definitions:
610
+ * - name: 'Content: Documentation'
611
+ * wip_limit: 4
612
+ * lock_policy: 'none' # Docs can be worked in parallel
613
+ * ```
614
+ */
615
+ lock_policy: LockPolicySchema.default('all'),
616
+ /** Code paths associated with this lane (glob patterns) */
617
+ code_paths: z.array(z.string()).optional(),
618
+ });
507
619
  /**
508
620
  * Complete LumenFlow configuration schema
509
621
  */
@@ -72,6 +72,8 @@ export declare function getResolvedPaths(options?: {
72
72
  agentsDir: string;
73
73
  memoryBank: string;
74
74
  plansDir: string;
75
+ templatesDir: string;
76
+ onboardingDir: string;
75
77
  };
76
78
  /**
77
79
  * Validate a config file
@@ -93,5 +95,5 @@ export declare function validateConfigFile(configPath: string): {
93
95
  export declare function createSampleConfig(outputPath: string, options?: {
94
96
  includeComments?: boolean;
95
97
  }): void;
96
- export type { LumenFlowConfig, Directories, BeaconPaths, GitConfig, WuConfig, GatesConfig, MemoryConfig, UiConfig, YamlConfig, } from './lumenflow-config-schema.js';
98
+ export type { LumenFlowConfig, Directories, BeaconPaths, PushRetryConfig, GitConfig, WuConfig, GatesConfig, MemoryConfig, UiConfig, YamlConfig, } from './lumenflow-config-schema.js';
97
99
  export { getDefaultConfig } from './lumenflow-config-schema.js';
@@ -55,6 +55,7 @@ function loadConfigFile(projectRoot) {
55
55
  return data || {};
56
56
  }
57
57
  catch (error) {
58
+ // eslint-disable-next-line no-console -- Config loading runs before logger init
58
59
  console.warn(`Warning: Failed to parse ${CONFIG_FILE_NAME}:`, error);
59
60
  return null;
60
61
  }
@@ -145,6 +146,8 @@ export function getResolvedPaths(options = {}) {
145
146
  agentsDir: path.join(projectRoot, config.directories.agentsDir),
146
147
  memoryBank: path.join(projectRoot, config.directories.memoryBank),
147
148
  plansDir: path.join(projectRoot, config.directories.plansDir),
149
+ templatesDir: path.join(projectRoot, config.directories.templatesDir),
150
+ onboardingDir: path.join(projectRoot, config.directories.onboardingDir),
148
151
  };
149
152
  }
150
153
  /**
@@ -206,6 +209,12 @@ directories:
206
209
  skillsDir: "${defaultConfig.directories.skillsDir}"
207
210
  # Agents directory
208
211
  agentsDir: "${defaultConfig.directories.agentsDir}"
212
+ # Plans directory
213
+ plansDir: "${defaultConfig.directories.plansDir}"
214
+ # Templates directory
215
+ templatesDir: "${defaultConfig.directories.templatesDir}"
216
+ # Onboarding directory
217
+ onboardingDir: "${defaultConfig.directories.onboardingDir}"
209
218
 
210
219
  # Beacon paths (.lumenflow directory structure)
211
220
  beacon:
@@ -28,6 +28,7 @@
28
28
  * @see {@link packages/@lumenflow/cli/src/initiative-create.ts} - Initiative creation (WU-1439)
29
29
  */
30
30
  import type { GitAdapter } from './git-adapter.js';
31
+ import type { PushRetryConfig } from './lumenflow-config-schema.js';
31
32
  /**
32
33
  * Context passed to the execute function in withMicroWorktree
33
34
  */
@@ -81,8 +82,17 @@ export declare const MAX_MERGE_RETRIES = 3;
81
82
  * WU-1179: When push fails due to race condition (origin advanced while we
82
83
  * were working), rollback local main to origin/main and retry.
83
84
  * Each retry: fetch -> rebase temp branch -> re-merge -> push.
85
+ *
86
+ * @deprecated Use DEFAULT_PUSH_RETRY_CONFIG.retries instead (WU-1332)
84
87
  */
85
88
  export declare const MAX_PUSH_RETRIES = 3;
89
+ /**
90
+ * WU-1332: Default push retry configuration
91
+ *
92
+ * Provides sensible defaults for micro-worktree push operations.
93
+ * Can be overridden via .lumenflow.config.yaml git.push_retry section.
94
+ */
95
+ export declare const DEFAULT_PUSH_RETRY_CONFIG: PushRetryConfig;
86
96
  /**
87
97
  * Environment variable name for LUMENFLOW_FORCE bypass
88
98
  *
@@ -101,6 +111,102 @@ export declare const LUMENFLOW_FORCE_REASON_ENV = "LUMENFLOW_FORCE_REASON";
101
111
  * Extracted to constant to satisfy sonarjs/no-duplicate-string rule.
102
112
  */
103
113
  export declare const DEFAULT_LOG_PREFIX = "[micro-wt]";
114
+ /**
115
+ * WU-1336: Typed error for retry exhaustion in micro-worktree operations
116
+ *
117
+ * Thrown when push retries are exhausted due to race conditions with parallel agents.
118
+ * CLI commands should use `isRetryExhaustionError` to detect this error type and
119
+ * `formatRetryExhaustionError` to generate actionable user-facing messages.
120
+ *
121
+ * This centralizes retry exhaustion handling so CLI commands do not need to
122
+ * duplicate detection logic or error formatting.
123
+ *
124
+ * @example
125
+ * ```typescript
126
+ * import { RetryExhaustionError, isRetryExhaustionError, formatRetryExhaustionError } from '@lumenflow/core';
127
+ *
128
+ * try {
129
+ * await withMicroWorktree({ ... });
130
+ * } catch (error) {
131
+ * if (isRetryExhaustionError(error)) {
132
+ * console.error(formatRetryExhaustionError(error, { command: 'pnpm initiative:add-wu ...' }));
133
+ * } else {
134
+ * throw error;
135
+ * }
136
+ * }
137
+ * ```
138
+ */
139
+ export declare class RetryExhaustionError extends Error {
140
+ /** Name of the error class (for instanceof checks across module boundaries) */
141
+ readonly name = "RetryExhaustionError";
142
+ /** Operation that was being performed (e.g., 'initiative-add-wu') */
143
+ readonly operation: string;
144
+ /** Number of retry attempts that were exhausted */
145
+ readonly retries: number;
146
+ constructor(operation: string, retries: number);
147
+ }
148
+ /**
149
+ * WU-1336: Options for formatting retry exhaustion error messages
150
+ */
151
+ export interface FormatRetryExhaustionOptions {
152
+ /** Command to suggest for retrying (e.g., 'pnpm initiative:add-wu --wu WU-123 --initiative INIT-001') */
153
+ command: string;
154
+ }
155
+ /**
156
+ * WU-1336: Type guard to check if an error is a retry exhaustion error
157
+ *
158
+ * Detects both the typed `RetryExhaustionError` class and legacy error messages
159
+ * that match the "Push failed after N attempts" pattern.
160
+ *
161
+ * @param {unknown} error - Error to check
162
+ * @returns {boolean} True if this is a retry exhaustion error
163
+ *
164
+ * @example
165
+ * ```typescript
166
+ * if (isRetryExhaustionError(error)) {
167
+ * // Handle retry exhaustion
168
+ * }
169
+ * ```
170
+ */
171
+ export declare function isRetryExhaustionError(error: unknown): error is Error;
172
+ /**
173
+ * WU-1336: Format retry exhaustion error with actionable next steps
174
+ *
175
+ * When push retries are exhausted, provides clear guidance on how to proceed.
176
+ * CLI commands should use this instead of duplicating error formatting logic.
177
+ *
178
+ * @param {Error} error - The retry exhaustion error
179
+ * @param {FormatRetryExhaustionOptions} options - Formatting options
180
+ * @returns {string} Formatted error message with next steps
181
+ *
182
+ * @example
183
+ * ```typescript
184
+ * const message = formatRetryExhaustionError(error, {
185
+ * command: 'pnpm initiative:add-wu --wu WU-123 --initiative INIT-001',
186
+ * });
187
+ * console.error(message);
188
+ * ```
189
+ */
190
+ export declare function formatRetryExhaustionError(error: Error, options: FormatRetryExhaustionOptions): string;
191
+ /**
192
+ * WU-1308: Check if remote operations should be skipped based on git.requireRemote config
193
+ *
194
+ * When git.requireRemote is false, micro-worktree operations skip:
195
+ * - Fetching origin/main before starting
196
+ * - Pushing to origin/main after completion
197
+ *
198
+ * This enables local-only development without a remote repository.
199
+ *
200
+ * @returns {boolean} True if remote operations should be skipped (requireRemote=false)
201
+ *
202
+ * @example
203
+ * ```yaml
204
+ * # .lumenflow.config.yaml
205
+ * git:
206
+ * requireRemote: false # Enable local-only mode
207
+ * ```
208
+ */
209
+ export declare function shouldSkipRemoteOperations(): boolean;
104
210
  /**
105
211
  * Temp branch prefix for micro-worktree operations
106
212
  *
@@ -215,6 +321,23 @@ export declare function mergeWithRetry(tempBranchName: string, microWorktreePath
215
321
  * @throws {Error} If push fails after all retries
216
322
  */
217
323
  export declare function pushWithRetry(mainGit: GitAdapter, worktreeGit: GitAdapter, remote: string, branch: string, tempBranchName: string, logPrefix?: string): Promise<void>;
324
+ /**
325
+ * WU-1332: Push to origin with configurable retry using p-retry
326
+ *
327
+ * Enhanced version of pushWithRetry that uses p-retry for exponential backoff
328
+ * and supports configuration via PushRetryConfig. When push fails due to
329
+ * non-fast-forward (origin moved), automatically rebases and retries.
330
+ *
331
+ * @param {Object} mainGit - GitAdapter instance for main checkout
332
+ * @param {Object} worktreeGit - GitAdapter instance for micro-worktree
333
+ * @param {string} remote - Remote name (e.g., 'origin')
334
+ * @param {string} branch - Branch name (e.g., 'main')
335
+ * @param {string} tempBranchName - Temp branch that was merged (for rebase)
336
+ * @param {string} logPrefix - Log prefix for console output
337
+ * @param {PushRetryConfig} config - Push retry configuration
338
+ * @throws {Error} If push fails after all retries or if retry is disabled
339
+ */
340
+ export declare function pushWithRetryConfig(mainGit: GitAdapter, worktreeGit: GitAdapter, remote: string, branch: string, tempBranchName: string, logPrefix?: string, config?: PushRetryConfig): Promise<void>;
218
341
  /**
219
342
  * Push using refspec with LUMENFLOW_FORCE to bypass pre-push hooks
220
343
  *
@@ -234,6 +357,33 @@ export declare function pushWithRetry(mainGit: GitAdapter, worktreeGit: GitAdapt
234
357
  * @throws {Error} If push fails (env vars still restored)
235
358
  */
236
359
  export declare function pushRefspecWithForce(gitAdapter: GitAdapter, remote: string, localRef: string, remoteRef: string, reason: string): Promise<void>;
360
+ /**
361
+ * WU-1337: Push using refspec with LUMENFLOW_FORCE and retry logic
362
+ *
363
+ * Enhanced version of pushRefspecWithForce that adds retry with rebase
364
+ * on non-fast-forward errors. Uses p-retry for exponential backoff and
365
+ * respects git.push_retry configuration.
366
+ *
367
+ * On each retry:
368
+ * 1. Fetch origin/main to get latest state
369
+ * 2. Rebase the temp branch onto the updated main
370
+ * 3. Retry the push with LUMENFLOW_FORCE
371
+ *
372
+ * This is used by pushOnly mode in withMicroWorktree to handle race conditions
373
+ * when multiple agents are pushing to origin/main concurrently.
374
+ *
375
+ * @param {GitAdapter} gitWorktree - GitAdapter instance for the worktree (for rebase)
376
+ * @param {GitAdapter} mainGit - GitAdapter instance for main checkout (for fetch)
377
+ * @param {string} remote - Remote name (e.g., 'origin')
378
+ * @param {string} localRef - Local ref to push (e.g., 'tmp/wu-claim/wu-123')
379
+ * @param {string} remoteRef - Remote ref to update (e.g., 'main')
380
+ * @param {string} reason - Audit reason for the LUMENFLOW_FORCE bypass
381
+ * @param {string} logPrefix - Log prefix for console output
382
+ * @param {PushRetryConfig} config - Push retry configuration
383
+ * @returns {Promise<void>}
384
+ * @throws {RetryExhaustionError} If push fails after all retries
385
+ */
386
+ export declare function pushRefspecWithRetry(gitWorktree: GitAdapter, mainGit: GitAdapter, remote: string, localRef: string, remoteRef: string, reason: string, logPrefix?: string, config?: PushRetryConfig): Promise<void>;
237
387
  /**
238
388
  * Execute an operation in a micro-worktree with full isolation
239
389
  *
@@ -243,6 +393,7 @@ export declare function pushRefspecWithForce(gitAdapter: GitAdapter, remote: str
243
393
  *
244
394
  * WU-1435: Added pushOnly option to keep local main pristine.
245
395
  * WU-2237: Added pre-creation cleanup of orphaned temp branches/worktrees.
396
+ * WU-1337: Push-only path now uses retry with rebase.
246
397
  *
247
398
  * @param {Object} options - Options for the operation
248
399
  * @param {string} options.operation - Operation name (e.g., 'wu-create', 'wu-edit')