@lumenflow/core 2.2.1 → 2.3.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.
- package/dist/active-wu-detector.d.ts +1 -1
- package/dist/active-wu-detector.js +1 -1
- package/dist/arg-parser.js +51 -18
- package/dist/backlog-generator.d.ts +4 -4
- package/dist/backlog-generator.js +4 -4
- package/dist/backlog-sync-validator.js +1 -1
- package/dist/cleanup-lock.d.ts +9 -2
- package/dist/cleanup-lock.js +17 -7
- package/dist/code-path-validator.d.ts +3 -3
- package/dist/code-path-validator.js +3 -3
- package/dist/compliance-parser.d.ts +1 -1
- package/dist/compliance-parser.js +1 -1
- package/dist/constants/backlog-patterns.d.ts +1 -1
- package/dist/constants/backlog-patterns.js +1 -1
- package/dist/constants/dora-constants.d.ts +1 -1
- package/dist/constants/dora-constants.js +1 -1
- package/dist/constants/gate-constants.d.ts +1 -1
- package/dist/constants/gate-constants.js +1 -1
- package/dist/constants/linter-constants.d.ts +1 -1
- package/dist/constants/linter-constants.js +1 -1
- package/dist/constants/tokenizer-constants.d.ts +1 -1
- package/dist/constants/tokenizer-constants.js +1 -1
- package/dist/context/location-resolver.js +2 -1
- package/dist/context-validation-integration.d.ts +1 -0
- package/dist/core/scope-checker.d.ts +3 -3
- package/dist/core/scope-checker.js +3 -3
- package/dist/core/tool-runner.d.ts +5 -5
- package/dist/core/tool-runner.js +5 -5
- package/dist/core/tool.constants.d.ts +1 -1
- package/dist/core/tool.constants.js +1 -1
- package/dist/core/tool.schemas.d.ts +2 -2
- package/dist/core/tool.schemas.js +1 -1
- package/dist/core/worktree-guard.d.ts +1 -1
- package/dist/core/worktree-guard.js +1 -1
- package/dist/coverage-gate.d.ts +12 -3
- package/dist/coverage-gate.js +15 -8
- package/dist/date-utils.d.ts +4 -4
- package/dist/date-utils.js +4 -4
- package/dist/dependency-graph.d.ts +6 -0
- package/dist/dependency-graph.js +43 -2
- package/dist/dependency-guard.d.ts +2 -2
- package/dist/dependency-guard.js +3 -3
- package/dist/dependency-validator.d.ts +4 -4
- package/dist/dependency-validator.js +4 -7
- package/dist/domain/orchestration.constants.d.ts +31 -10
- package/dist/domain/orchestration.constants.js +45 -16
- package/dist/domain/orchestration.schemas.d.ts +54 -28
- package/dist/domain/orchestration.schemas.js +2 -2
- package/dist/domain/orchestration.types.d.ts +2 -2
- package/dist/domain/orchestration.types.js +2 -2
- package/dist/error-handler.d.ts +10 -10
- package/dist/error-handler.js +10 -10
- package/dist/file-classifiers.d.ts +6 -6
- package/dist/file-classifiers.js +6 -6
- package/dist/gates-config.d.ts +74 -0
- package/dist/gates-config.js +209 -2
- package/dist/git-adapter.d.ts +11 -11
- package/dist/git-adapter.js +11 -11
- package/dist/git-context-extractor.d.ts +112 -0
- package/dist/git-context-extractor.js +559 -0
- package/dist/hardcoded-strings.d.ts +1 -1
- package/dist/hardcoded-strings.js +1 -1
- package/dist/incremental-lint.d.ts +1 -1
- package/dist/incremental-lint.js +2 -2
- package/dist/incremental-test.d.ts +1 -1
- package/dist/incremental-test.js +1 -1
- package/dist/index.d.ts +13 -0
- package/dist/index.js +25 -0
- package/dist/invariants/check-automated-tests.d.ts +2 -2
- package/dist/invariants/check-automated-tests.js +3 -3
- package/dist/lane-checker.d.ts +28 -7
- package/dist/lane-checker.js +316 -159
- package/dist/lane-suggest-prompt.d.ts +108 -0
- package/dist/lane-suggest-prompt.js +359 -0
- package/dist/lane-validator.d.ts +3 -3
- package/dist/lane-validator.js +3 -3
- package/dist/logs-lib.d.ts +1 -1
- package/dist/logs-lib.js +1 -1
- package/dist/lumenflow-config-schema.d.ts +162 -0
- package/dist/lumenflow-config-schema.js +180 -0
- package/dist/manual-test-validator.d.ts +2 -2
- package/dist/manual-test-validator.js +3 -3
- package/dist/merge-lock.d.ts +8 -1
- package/dist/merge-lock.js +16 -7
- package/dist/micro-worktree.d.ts +81 -13
- package/dist/micro-worktree.js +98 -17
- package/dist/migration-deployer.d.ts +1 -1
- package/dist/migration-deployer.js +1 -1
- package/dist/orchestration-advisory-loader.d.ts +2 -2
- package/dist/orchestration-advisory-loader.js +10 -6
- package/dist/orchestration-advisory.d.ts +3 -3
- package/dist/orchestration-advisory.js +4 -4
- package/dist/orchestration-di.d.ts +4 -4
- package/dist/orchestration-di.js +4 -4
- package/dist/orchestration-rules.d.ts +4 -4
- package/dist/orchestration-rules.js +18 -10
- package/dist/orphan-detector.d.ts +3 -3
- package/dist/orphan-detector.js +3 -3
- package/dist/patrol-loop.d.ts +170 -0
- package/dist/patrol-loop.js +186 -0
- package/dist/process-detector.d.ts +5 -5
- package/dist/process-detector.js +5 -5
- package/dist/rebase-artifact-cleanup.d.ts +3 -3
- package/dist/rebase-artifact-cleanup.js +3 -3
- package/dist/resolve-policy.d.ts +195 -0
- package/dist/resolve-policy.js +203 -0
- package/dist/risk-detector.d.ts +2 -2
- package/dist/risk-detector.js +2 -2
- package/dist/rollback-utils.d.ts +1 -1
- package/dist/rollback-utils.js +1 -1
- package/dist/section-headings.d.ts +1 -1
- package/dist/section-headings.js +1 -1
- package/dist/spawn-escalation.d.ts +4 -4
- package/dist/spawn-escalation.js +3 -3
- package/dist/spawn-monitor.d.ts +4 -4
- package/dist/spawn-monitor.js +4 -4
- package/dist/spawn-recovery.d.ts +3 -3
- package/dist/spawn-recovery.js +3 -3
- package/dist/spawn-registry-schema.d.ts +2 -2
- package/dist/spawn-registry-schema.js +2 -2
- package/dist/spawn-registry-store.d.ts +2 -2
- package/dist/spawn-registry-store.js +2 -2
- package/dist/spawn-strategy.d.ts +17 -11
- package/dist/spawn-strategy.js +47 -44
- package/dist/spawn-tree.d.ts +3 -3
- package/dist/spawn-tree.js +3 -3
- package/dist/state-cleanup-core.d.ts +205 -0
- package/dist/state-cleanup-core.js +240 -0
- package/dist/state-doctor-core.d.ts +168 -0
- package/dist/state-doctor-core.js +251 -0
- package/dist/stream-error-handler.d.ts +67 -0
- package/dist/stream-error-handler.js +94 -0
- package/dist/system-map-validator.d.ts +18 -0
- package/dist/system-map-validator.js +50 -16
- package/dist/telemetry.d.ts +1 -1
- package/dist/telemetry.js +1 -1
- package/dist/template-loader.d.ts +162 -0
- package/dist/template-loader.js +372 -0
- package/dist/test-baseline.d.ts +176 -0
- package/dist/test-baseline.js +282 -0
- package/dist/usecases/get-suggestions.usecase.d.ts +1 -1
- package/dist/validation/command-registry.js +37 -0
- package/dist/validators/backlog-sync.d.ts +14 -0
- package/dist/validators/backlog-sync.js +62 -0
- package/dist/validators/supabase-docs-linter.d.ts +18 -0
- package/dist/validators/supabase-docs-linter.js +42 -0
- package/dist/validators/wu-tasks.d.ts +24 -0
- package/dist/validators/wu-tasks.js +90 -0
- package/dist/worktree-scanner.d.ts +1 -1
- package/dist/worktree-scanner.js +1 -1
- package/dist/worktree-symlink.d.ts +3 -3
- package/dist/worktree-symlink.js +3 -3
- package/dist/wu-backlog-updater.d.ts +1 -1
- package/dist/wu-backlog-updater.js +1 -1
- package/dist/wu-claim-helpers.d.ts +1 -1
- package/dist/wu-claim-helpers.js +1 -1
- package/dist/wu-claim-resume.d.ts +1 -1
- package/dist/wu-claim-resume.js +1 -1
- package/dist/wu-consistency-checker.d.ts +1 -1
- package/dist/wu-consistency-checker.js +17 -11
- package/dist/wu-constants.d.ts +73 -36
- package/dist/wu-constants.js +65 -92
- package/dist/wu-done-branch-only.d.ts +1 -1
- package/dist/wu-done-branch-only.js +1 -1
- package/dist/wu-done-docs-generate.d.ts +1 -1
- package/dist/wu-done-docs-generate.js +1 -1
- package/dist/wu-done-messages.d.ts +2 -2
- package/dist/wu-done-messages.js +2 -2
- package/dist/wu-done-metadata.d.ts +3 -3
- package/dist/wu-done-metadata.js +3 -3
- package/dist/wu-done-pr.d.ts +1 -1
- package/dist/wu-done-pr.js +4 -2
- package/dist/wu-done-preflight.d.ts +20 -10
- package/dist/wu-done-preflight.js +48 -47
- package/dist/wu-done-ui.d.ts +3 -3
- package/dist/wu-done-ui.js +3 -3
- package/dist/wu-done-validation.d.ts +30 -0
- package/dist/wu-done-validation.js +106 -1
- package/dist/wu-done-validators.d.ts +1 -1
- package/dist/wu-done-worktree.d.ts +1 -1
- package/dist/wu-done-worktree.js +11 -1
- package/dist/wu-events-cleanup.d.ts +148 -0
- package/dist/wu-events-cleanup.js +401 -0
- package/dist/wu-helpers.d.ts +2 -2
- package/dist/wu-helpers.js +2 -2
- package/dist/wu-id-generator.d.ts +58 -0
- package/dist/wu-id-generator.js +103 -0
- package/dist/wu-lint.js +1 -1
- package/dist/wu-preflight-validators.d.ts +13 -1
- package/dist/wu-preflight-validators.js +56 -1
- package/dist/wu-recovery.d.ts +2 -2
- package/dist/wu-recovery.js +4 -4
- package/dist/wu-repair-core.d.ts +5 -5
- package/dist/wu-repair-core.js +6 -6
- package/dist/wu-schema-normalization.d.ts +1 -1
- package/dist/wu-schema-normalization.js +1 -1
- package/dist/wu-schema.d.ts +7 -7
- package/dist/wu-schema.js +8 -8
- package/dist/wu-spawn-context.d.ts +87 -0
- package/dist/wu-spawn-context.js +175 -0
- package/dist/wu-spawn-helpers.d.ts +1 -1
- package/dist/wu-spawn-helpers.js +1 -1
- package/dist/wu-spawn.d.ts +177 -4
- package/dist/wu-spawn.js +694 -72
- package/dist/wu-state-schema.d.ts +1 -1
- package/dist/wu-state-schema.js +1 -1
- package/dist/wu-state-store.d.ts +3 -3
- package/dist/wu-state-store.js +3 -3
- package/dist/wu-status-transition.d.ts +1 -1
- package/dist/wu-status-transition.js +1 -1
- package/dist/wu-status-updater.d.ts +3 -3
- package/dist/wu-status-updater.js +3 -3
- package/dist/wu-validation-constants.d.ts +2 -2
- package/dist/wu-validation-constants.js +2 -2
- package/dist/wu-validation.d.ts +3 -3
- package/dist/wu-validation.js +3 -3
- package/dist/wu-yaml-fixer.d.ts +2 -2
- package/dist/wu-yaml-fixer.js +3 -3
- package/dist/wu-yaml.d.ts +23 -0
- package/dist/wu-yaml.js +76 -2
- 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.
|
|
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],
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file backlog-sync.ts
|
|
3
|
+
* @description Validates backlog.md is in sync with WU YAML files (WU-1111)
|
|
4
|
+
*/
|
|
5
|
+
export interface BacklogSyncResult {
|
|
6
|
+
valid: boolean;
|
|
7
|
+
errors: string[];
|
|
8
|
+
warnings: string[];
|
|
9
|
+
wuCount: number;
|
|
10
|
+
backlogCount: number;
|
|
11
|
+
}
|
|
12
|
+
export declare function validateBacklogSync(options?: {
|
|
13
|
+
cwd?: string;
|
|
14
|
+
}): Promise<BacklogSyncResult>;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file backlog-sync.ts
|
|
3
|
+
* @description Validates backlog.md is in sync with WU YAML files (WU-1111)
|
|
4
|
+
*/
|
|
5
|
+
import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import { FILE_SYSTEM } from '../wu-constants.js';
|
|
8
|
+
import { createWuPaths } from '../wu-paths.js';
|
|
9
|
+
function extractWUIDsFromBacklog(content) {
|
|
10
|
+
const wuIds = [];
|
|
11
|
+
const pattern = /WU-\d+/gi;
|
|
12
|
+
let match;
|
|
13
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
14
|
+
const wuId = match[0].toUpperCase();
|
|
15
|
+
if (!wuIds.includes(wuId)) {
|
|
16
|
+
wuIds.push(wuId);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return wuIds;
|
|
20
|
+
}
|
|
21
|
+
function getWUIDsFromFiles(wuDir) {
|
|
22
|
+
if (!existsSync(wuDir)) {
|
|
23
|
+
return [];
|
|
24
|
+
}
|
|
25
|
+
return readdirSync(wuDir)
|
|
26
|
+
.filter((f) => f.endsWith('.yaml'))
|
|
27
|
+
.map((f) => f.replace('.yaml', '').toUpperCase());
|
|
28
|
+
}
|
|
29
|
+
export async function validateBacklogSync(options = {}) {
|
|
30
|
+
const { cwd = process.cwd() } = options;
|
|
31
|
+
const errors = [];
|
|
32
|
+
const warnings = [];
|
|
33
|
+
const paths = createWuPaths({ projectRoot: cwd });
|
|
34
|
+
const backlogPath = path.join(cwd, paths.BACKLOG());
|
|
35
|
+
const wuDir = path.join(cwd, paths.WU_DIR());
|
|
36
|
+
if (!existsSync(backlogPath)) {
|
|
37
|
+
errors.push(`Backlog file not found: ${backlogPath}`);
|
|
38
|
+
return { valid: false, errors, warnings, wuCount: 0, backlogCount: 0 };
|
|
39
|
+
}
|
|
40
|
+
const wuIdsFromFiles = getWUIDsFromFiles(wuDir);
|
|
41
|
+
const backlogContent = readFileSync(backlogPath, {
|
|
42
|
+
encoding: FILE_SYSTEM.UTF8,
|
|
43
|
+
});
|
|
44
|
+
const wuIdsFromBacklog = extractWUIDsFromBacklog(backlogContent);
|
|
45
|
+
for (const wuId of wuIdsFromFiles) {
|
|
46
|
+
if (!wuIdsFromBacklog.includes(wuId)) {
|
|
47
|
+
errors.push(`${wuId} not found in backlog.md (exists as ${wuId}.yaml)`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
for (const wuId of wuIdsFromBacklog) {
|
|
51
|
+
if (!wuIdsFromFiles.includes(wuId)) {
|
|
52
|
+
warnings.push(`${wuId} referenced in backlog.md but ${wuId}.yaml not found`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
valid: errors.length === 0,
|
|
57
|
+
errors,
|
|
58
|
+
warnings,
|
|
59
|
+
wuCount: wuIdsFromFiles.length,
|
|
60
|
+
backlogCount: wuIdsFromBacklog.length,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file supabase-docs-linter.ts
|
|
3
|
+
* @description Runs Supabase docs linter when available (optional in consumer repos)
|
|
4
|
+
*/
|
|
5
|
+
export interface SupabaseDocsLinterResult {
|
|
6
|
+
ok: boolean;
|
|
7
|
+
skipped: boolean;
|
|
8
|
+
message?: string;
|
|
9
|
+
errors?: string[];
|
|
10
|
+
}
|
|
11
|
+
export interface SupabaseDocsLinterOptions {
|
|
12
|
+
cwd?: string;
|
|
13
|
+
logger?: {
|
|
14
|
+
log: (message: string) => void;
|
|
15
|
+
warn?: (message: string) => void;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export declare function runSupabaseDocsLinter(options?: SupabaseDocsLinterOptions): Promise<SupabaseDocsLinterResult>;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file supabase-docs-linter.ts
|
|
3
|
+
* @description Runs Supabase docs linter when available (optional in consumer repos)
|
|
4
|
+
*/
|
|
5
|
+
import { existsSync } from 'node:fs';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import { pathToFileURL } from 'node:url';
|
|
8
|
+
export async function runSupabaseDocsLinter(options = {}) {
|
|
9
|
+
const { cwd = process.cwd(), logger = console } = options;
|
|
10
|
+
const linterPath = path.join(cwd, 'packages', 'linters', 'supabase-docs-linter.js');
|
|
11
|
+
if (!existsSync(linterPath)) {
|
|
12
|
+
return {
|
|
13
|
+
ok: true,
|
|
14
|
+
skipped: true,
|
|
15
|
+
message: 'Supabase docs linter not found; skipping.',
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
const moduleUrl = pathToFileURL(linterPath).href;
|
|
19
|
+
const module = await import(moduleUrl);
|
|
20
|
+
const runFn = module.runSupabaseDocsLinter ?? module.default;
|
|
21
|
+
if (typeof runFn !== 'function') {
|
|
22
|
+
return {
|
|
23
|
+
ok: false,
|
|
24
|
+
skipped: false,
|
|
25
|
+
errors: ['Supabase docs linter does not export runSupabaseDocsLinter.'],
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
const result = await runFn({ cwd, logger });
|
|
29
|
+
if (result && typeof result === 'object' && 'ok' in result) {
|
|
30
|
+
return {
|
|
31
|
+
ok: Boolean(result.ok),
|
|
32
|
+
skipped: Boolean(result.skipped),
|
|
33
|
+
message: result.message,
|
|
34
|
+
errors: result.errors,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
ok: true,
|
|
39
|
+
skipped: false,
|
|
40
|
+
message: 'Supabase docs linter completed.',
|
|
41
|
+
};
|
|
42
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file wu-tasks.ts
|
|
3
|
+
* @description WU YAML validation helpers (shared by CLI and preflight)
|
|
4
|
+
*/
|
|
5
|
+
export interface ValidationResult {
|
|
6
|
+
valid: boolean;
|
|
7
|
+
warnings: string[];
|
|
8
|
+
errors: string[];
|
|
9
|
+
}
|
|
10
|
+
export interface ValidationSummary {
|
|
11
|
+
totalValid: number;
|
|
12
|
+
totalInvalid: number;
|
|
13
|
+
totalWarnings: number;
|
|
14
|
+
results: Array<{
|
|
15
|
+
wuId: string;
|
|
16
|
+
} & ValidationResult>;
|
|
17
|
+
}
|
|
18
|
+
export declare function validateSingleWU(wuPath: string, options?: {
|
|
19
|
+
strict?: boolean;
|
|
20
|
+
}): ValidationResult;
|
|
21
|
+
export declare function validateAllWUs(options?: {
|
|
22
|
+
strict?: boolean;
|
|
23
|
+
doneOnly?: boolean;
|
|
24
|
+
}): ValidationSummary;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file wu-tasks.ts
|
|
3
|
+
* @description WU YAML validation helpers (shared by CLI and preflight)
|
|
4
|
+
*/
|
|
5
|
+
import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
6
|
+
import { WU_PATHS } from '../wu-paths.js';
|
|
7
|
+
import { parseYAML } from '../wu-yaml.js';
|
|
8
|
+
import { validateWU, validateWUCompleteness } from '../wu-schema.js';
|
|
9
|
+
import { FILE_SYSTEM } from '../wu-constants.js';
|
|
10
|
+
export function validateSingleWU(wuPath, options = {}) {
|
|
11
|
+
const { strict = false } = options;
|
|
12
|
+
const errors = [];
|
|
13
|
+
const warnings = [];
|
|
14
|
+
if (!existsSync(wuPath)) {
|
|
15
|
+
errors.push(`WU file not found: ${wuPath}`);
|
|
16
|
+
return { valid: false, warnings, errors };
|
|
17
|
+
}
|
|
18
|
+
let doc;
|
|
19
|
+
try {
|
|
20
|
+
const text = readFileSync(wuPath, { encoding: FILE_SYSTEM.UTF8 });
|
|
21
|
+
doc = parseYAML(text);
|
|
22
|
+
}
|
|
23
|
+
catch (e) {
|
|
24
|
+
errors.push(`Failed to parse YAML: ${e.message}`);
|
|
25
|
+
return { valid: false, warnings, errors };
|
|
26
|
+
}
|
|
27
|
+
const schemaResult = validateWU(doc);
|
|
28
|
+
if (!schemaResult.success) {
|
|
29
|
+
const schemaErrors = schemaResult.error.issues.map((issue) => `${issue.path.join('.')}: ${issue.message}`);
|
|
30
|
+
errors.push(...schemaErrors);
|
|
31
|
+
return { valid: false, warnings, errors };
|
|
32
|
+
}
|
|
33
|
+
const completenessResult = validateWUCompleteness(schemaResult.data);
|
|
34
|
+
warnings.push(...completenessResult.warnings);
|
|
35
|
+
if (strict && warnings.length > 0) {
|
|
36
|
+
errors.push(...warnings.map((w) => `[STRICT] ${w}`));
|
|
37
|
+
return { valid: false, warnings: [], errors };
|
|
38
|
+
}
|
|
39
|
+
return { valid: true, warnings, errors };
|
|
40
|
+
}
|
|
41
|
+
export function validateAllWUs(options = {}) {
|
|
42
|
+
const { strict = false, doneOnly = false } = options;
|
|
43
|
+
const wuDir = WU_PATHS.WU_DIR();
|
|
44
|
+
if (!existsSync(wuDir)) {
|
|
45
|
+
return {
|
|
46
|
+
totalValid: 0,
|
|
47
|
+
totalInvalid: 1,
|
|
48
|
+
totalWarnings: 0,
|
|
49
|
+
results: [
|
|
50
|
+
{
|
|
51
|
+
wuId: 'DIRECTORY',
|
|
52
|
+
valid: false,
|
|
53
|
+
warnings: [],
|
|
54
|
+
errors: [`WU directory not found: ${wuDir}`],
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
const files = readdirSync(wuDir).filter((f) => f.endsWith('.yaml'));
|
|
60
|
+
const results = [];
|
|
61
|
+
let totalValid = 0;
|
|
62
|
+
let totalInvalid = 0;
|
|
63
|
+
let totalWarnings = 0;
|
|
64
|
+
for (const file of files) {
|
|
65
|
+
const wuPath = `${wuDir}/${file}`;
|
|
66
|
+
const wuId = file.replace('.yaml', '');
|
|
67
|
+
if (doneOnly) {
|
|
68
|
+
try {
|
|
69
|
+
const text = readFileSync(wuPath, { encoding: FILE_SYSTEM.UTF8 });
|
|
70
|
+
const doc = parseYAML(text);
|
|
71
|
+
if (doc.status !== 'done') {
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
// If we can't read, still validate to catch the error
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const result = validateSingleWU(wuPath, { strict });
|
|
80
|
+
results.push({ wuId, ...result });
|
|
81
|
+
if (result.valid) {
|
|
82
|
+
totalValid++;
|
|
83
|
+
totalWarnings += result.warnings.length;
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
totalInvalid++;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return { totalValid, totalInvalid, totalWarnings, results };
|
|
90
|
+
}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* - Reports last activity timestamp
|
|
11
11
|
* - Identifies potentially abandoned WUs
|
|
12
12
|
*
|
|
13
|
-
* @see {@link
|
|
13
|
+
* @see {@link packages/@lumenflow/cli/src/lib/__tests__/worktree-scanner.test.ts} - Tests
|
|
14
14
|
*/
|
|
15
15
|
/**
|
|
16
16
|
* @typedef {object} WorktreeInfo
|
package/dist/worktree-scanner.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* - Reports last activity timestamp
|
|
11
11
|
* - Identifies potentially abandoned WUs
|
|
12
12
|
*
|
|
13
|
-
* @see {@link
|
|
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.
|
|
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.
|
|
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.
|
|
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'
|
package/dist/worktree-symlink.js
CHANGED
|
@@ -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.
|
|
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.
|
|
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.
|
|
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.
|
|
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)
|