@lumenflow/core 1.0.0 → 1.3.2
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/arg-parser.d.ts +6 -0
- package/dist/arg-parser.js +57 -1
- package/dist/backlog-generator.js +1 -1
- package/dist/backlog-sync-validator.js +3 -3
- package/dist/branch-check.d.ts +21 -0
- package/dist/branch-check.js +77 -0
- package/dist/cli/is-agent-branch.d.ts +11 -0
- package/dist/cli/is-agent-branch.js +15 -0
- package/dist/code-paths-overlap.js +2 -2
- package/dist/error-handler.d.ts +1 -0
- package/dist/error-handler.js +4 -1
- package/dist/git-adapter.d.ts +23 -0
- package/dist/git-adapter.js +38 -2
- package/dist/index.d.ts +3 -0
- package/dist/index.js +5 -0
- package/dist/lane-checker.d.ts +36 -3
- package/dist/lane-checker.js +128 -17
- package/dist/lane-inference.js +3 -4
- package/dist/lumenflow-config-schema.d.ts +125 -0
- package/dist/lumenflow-config-schema.js +76 -0
- package/dist/lumenflow-home.d.ts +130 -0
- package/dist/lumenflow-home.js +208 -0
- package/dist/manual-test-validator.js +1 -1
- package/dist/orchestration-rules.d.ts +1 -1
- package/dist/orchestration-rules.js +2 -2
- package/dist/orphan-detector.d.ts +16 -0
- package/dist/orphan-detector.js +24 -0
- package/dist/path-classifiers.d.ts +1 -1
- package/dist/path-classifiers.js +1 -1
- package/dist/rebase-artifact-cleanup.d.ts +17 -0
- package/dist/rebase-artifact-cleanup.js +49 -8
- package/dist/spawn-strategy.d.ts +53 -0
- package/dist/spawn-strategy.js +106 -0
- package/dist/spec-branch-helpers.d.ts +118 -0
- package/dist/spec-branch-helpers.js +192 -0
- package/dist/stamp-utils.d.ts +10 -0
- package/dist/stamp-utils.js +17 -19
- package/dist/token-counter.js +2 -2
- package/dist/wu-consistency-checker.d.ts +2 -0
- package/dist/wu-consistency-checker.js +40 -6
- package/dist/wu-constants.d.ts +98 -3
- package/dist/wu-constants.js +108 -3
- package/dist/wu-create-validators.d.ts +40 -2
- package/dist/wu-create-validators.js +76 -2
- package/dist/wu-done-branch-only.js +9 -0
- package/dist/wu-done-branch-utils.d.ts +10 -0
- package/dist/wu-done-branch-utils.js +31 -0
- package/dist/wu-done-cleanup.d.ts +8 -0
- package/dist/wu-done-cleanup.js +122 -0
- package/dist/wu-done-docs-generate.d.ts +73 -0
- package/dist/wu-done-docs-generate.js +108 -0
- package/dist/wu-done-docs-only.d.ts +20 -0
- package/dist/wu-done-docs-only.js +65 -0
- package/dist/wu-done-errors.d.ts +17 -0
- package/dist/wu-done-errors.js +24 -0
- package/dist/wu-done-inputs.d.ts +12 -0
- package/dist/wu-done-inputs.js +51 -0
- package/dist/wu-done-metadata.d.ts +100 -0
- package/dist/wu-done-metadata.js +193 -0
- package/dist/wu-done-paths.d.ts +69 -0
- package/dist/wu-done-paths.js +237 -0
- package/dist/wu-done-preflight.d.ts +48 -0
- package/dist/wu-done-preflight.js +185 -0
- package/dist/wu-done-validation.d.ts +82 -0
- package/dist/wu-done-validation.js +340 -0
- package/dist/wu-done-validators.d.ts +13 -409
- package/dist/wu-done-validators.js +9 -1225
- package/dist/wu-done-worktree.d.ts +0 -1
- package/dist/wu-done-worktree.js +24 -30
- package/dist/wu-schema.js +4 -4
- package/dist/wu-spawn-skills.d.ts +19 -0
- package/dist/wu-spawn-skills.js +148 -0
- package/dist/wu-spawn.d.ts +17 -4
- package/dist/wu-spawn.js +113 -177
- package/dist/wu-validation.d.ts +1 -0
- package/dist/wu-validation.js +21 -1
- package/dist/wu-validator.d.ts +51 -0
- package/dist/wu-validator.js +108 -0
- package/package.json +12 -8
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Path and workspace detection helpers for wu:done.
|
|
3
|
+
*/
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { existsSync, readFileSync, statSync } from 'node:fs';
|
|
6
|
+
import { access } from 'node:fs/promises';
|
|
7
|
+
import { getGitForCwd } from './git-adapter.js';
|
|
8
|
+
import { WU_PATHS } from './wu-paths.js';
|
|
9
|
+
import { parseYAML, readWU } from './wu-yaml.js';
|
|
10
|
+
import { CLAIMED_MODES, EMOJI, LOG_PREFIX, STRING_LITERALS, toKebab } from './wu-constants.js';
|
|
11
|
+
import { detectDocsOnlyByPaths } from './wu-done-docs-only.js';
|
|
12
|
+
/**
|
|
13
|
+
* Read WU YAML preferring worktree version over main version
|
|
14
|
+
*
|
|
15
|
+
* WU-1584 Fix #4: Added diagnostic logging to confirm which YAML file is being
|
|
16
|
+
* read for code_paths validation. This helps debug issues where worktree YAML
|
|
17
|
+
* differs from main checkout YAML.
|
|
18
|
+
*
|
|
19
|
+
* @param {string} id - WU ID
|
|
20
|
+
* @param {string|null} worktreePath - Worktree path (null if branch-only mode)
|
|
21
|
+
* @param {string} mainWUPath - Path to WU YAML in main checkout
|
|
22
|
+
* @returns {object} Parsed WU document
|
|
23
|
+
*/
|
|
24
|
+
export function readWUPreferWorktree(id, worktreePath, mainWUPath) {
|
|
25
|
+
if (worktreePath) {
|
|
26
|
+
const wtWUPath = path.join(worktreePath, WU_PATHS.WU(id));
|
|
27
|
+
if (existsSync(wtWUPath)) {
|
|
28
|
+
try {
|
|
29
|
+
const text = readFileSync(wtWUPath, { encoding: 'utf-8' });
|
|
30
|
+
const doc = parseYAML(text);
|
|
31
|
+
if (doc && doc.id === id) {
|
|
32
|
+
// WU-1584: Log source file for validation debugging
|
|
33
|
+
console.log(`${LOG_PREFIX.DONE} ${EMOJI.INFO} Reading WU YAML from worktree: ${wtWUPath}`);
|
|
34
|
+
if (doc.code_paths && doc.code_paths.length > 0) {
|
|
35
|
+
console.log(`${LOG_PREFIX.DONE} code_paths source: worktree (${doc.code_paths.length} path(s))`);
|
|
36
|
+
}
|
|
37
|
+
return doc;
|
|
38
|
+
}
|
|
39
|
+
// If ID mismatch, log warning but continue
|
|
40
|
+
console.warn(`${LOG_PREFIX.DONE} Warning: Worktree YAML ID mismatch (expected ${id}, got ${doc?.id})`);
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
// Log parse errors for debugging
|
|
44
|
+
console.warn(`${LOG_PREFIX.DONE} Warning: Failed to read worktree YAML: ${err.message}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
// Log missing worktree YAML for debugging
|
|
49
|
+
console.warn(`${LOG_PREFIX.DONE} Warning: Worktree YAML not found at ${wtWUPath}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// WU-1584: Log when falling back to main checkout YAML
|
|
53
|
+
console.log(`${LOG_PREFIX.DONE} ${EMOJI.INFO} Reading WU YAML from main: ${mainWUPath}`);
|
|
54
|
+
const doc = readWU(mainWUPath, id);
|
|
55
|
+
if (doc.code_paths && doc.code_paths.length > 0) {
|
|
56
|
+
console.log(`${LOG_PREFIX.DONE} code_paths source: main checkout (${doc.code_paths.length} path(s))`);
|
|
57
|
+
}
|
|
58
|
+
return doc;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Detect if currently running inside a worktree
|
|
62
|
+
* Checks for .git file (not directory) which indicates a worktree
|
|
63
|
+
* @returns {string|null} Current directory path if inside worktree, null otherwise
|
|
64
|
+
*/
|
|
65
|
+
export function detectCurrentWorktree() {
|
|
66
|
+
const cwd = process.cwd();
|
|
67
|
+
const gitPath = path.join(cwd, '.git');
|
|
68
|
+
// Check if .git exists and is a file (worktrees have .git file, main has .git directory)
|
|
69
|
+
if (!existsSync(gitPath))
|
|
70
|
+
return null;
|
|
71
|
+
try {
|
|
72
|
+
const stats = statSync(gitPath);
|
|
73
|
+
if (stats.isFile()) {
|
|
74
|
+
// Parse .git file to verify it points to main repo's worktrees
|
|
75
|
+
const gitContent = readFileSync(gitPath, { encoding: 'utf-8' });
|
|
76
|
+
const match = gitContent.match(/^gitdir:\s*(.+)$/m);
|
|
77
|
+
if (match && match[1].includes('.git/worktrees/')) {
|
|
78
|
+
console.log(`${LOG_PREFIX.DONE} ${EMOJI.TARGET} Auto-detected worktree from process.cwd(): ${cwd}`);
|
|
79
|
+
return cwd;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
// Ignore errors, fall back to calculated path
|
|
85
|
+
console.log(`${LOG_PREFIX.DONE} ${EMOJI.WARNING} Failed to detect worktree: ${err.message}`);
|
|
86
|
+
}
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Resolve worktree path from WU YAML
|
|
91
|
+
* Originally implemented in WU-1226, extracted to validators module in WU-1215
|
|
92
|
+
* Priority:
|
|
93
|
+
* 1. Read worktree_path field (set at claim time, immune to lane field changes)
|
|
94
|
+
* 2. Fall back to calculating from lane field (for old WUs without worktree_path)
|
|
95
|
+
* 3. Use git worktree list to find actual path (defensive fallback)
|
|
96
|
+
* @param {object} doc - WU YAML document
|
|
97
|
+
* @returns {Promise<string|null>} - Worktree path or null if not found
|
|
98
|
+
*/
|
|
99
|
+
export async function defaultWorktreeFrom(doc) {
|
|
100
|
+
// Priority 1 - use recorded worktree_path if available
|
|
101
|
+
if (doc.worktree_path) {
|
|
102
|
+
return doc.worktree_path;
|
|
103
|
+
}
|
|
104
|
+
// Priority 2 - calculate from current lane field (legacy behavior)
|
|
105
|
+
const lane = (doc.lane || '').toString();
|
|
106
|
+
const laneK = toKebab(lane);
|
|
107
|
+
const idK = (doc.id || '').toLowerCase();
|
|
108
|
+
if (!laneK || !idK)
|
|
109
|
+
return null;
|
|
110
|
+
const calculated = `worktrees/${laneK}-${idK}`;
|
|
111
|
+
// Priority 3 - verify calculated path exists, or find actual path via git worktree list
|
|
112
|
+
let calculatedExists = true;
|
|
113
|
+
try {
|
|
114
|
+
await access(calculated);
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
calculatedExists = false;
|
|
118
|
+
}
|
|
119
|
+
if (!calculatedExists) {
|
|
120
|
+
try {
|
|
121
|
+
const worktreeList = await getGitForCwd().worktreeList();
|
|
122
|
+
const lines = worktreeList.split(STRING_LITERALS.NEWLINE);
|
|
123
|
+
const branch = `lane/${laneK}/${idK}`;
|
|
124
|
+
for (let i = 0; i < lines.length; i++) {
|
|
125
|
+
if (lines[i].startsWith('branch ') && lines[i].includes(branch)) {
|
|
126
|
+
// Found the branch, now get the worktree path from previous line
|
|
127
|
+
for (let j = i - 1; j >= 0; j--) {
|
|
128
|
+
if (lines[j].startsWith('worktree ')) {
|
|
129
|
+
const fullPath = lines[j].substring('worktree '.length);
|
|
130
|
+
// Convert absolute path to relative path from repo root
|
|
131
|
+
const repoRoot = process.cwd();
|
|
132
|
+
const relativePath = path.relative(repoRoot, fullPath);
|
|
133
|
+
console.log(`${LOG_PREFIX.DONE} ${EMOJI.WARNING} Worktree path mismatch detected:\n` +
|
|
134
|
+
` Expected: ${calculated}\n` +
|
|
135
|
+
` Actual: ${relativePath}\n` +
|
|
136
|
+
` Using actual path from git worktree list`);
|
|
137
|
+
return relativePath;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
catch (e) {
|
|
144
|
+
console.warn(`${LOG_PREFIX.DONE} Could not query git worktree list: ${e.message}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return calculated;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Detect workspace mode from WU YAML
|
|
151
|
+
* @param {object} doc - WU YAML document
|
|
152
|
+
* @returns {'worktree' | 'branch-only'}
|
|
153
|
+
*/
|
|
154
|
+
export function detectWorkspaceMode(doc) {
|
|
155
|
+
// Explicit mode field takes precedence
|
|
156
|
+
if (doc.claimed_mode === CLAIMED_MODES.BRANCH_ONLY)
|
|
157
|
+
return CLAIMED_MODES.BRANCH_ONLY;
|
|
158
|
+
if (doc.claimed_mode === CLAIMED_MODES.WORKTREE)
|
|
159
|
+
return CLAIMED_MODES.WORKTREE;
|
|
160
|
+
// Backward compatibility: if claimed_mode is missing, assume worktree mode
|
|
161
|
+
// (all WUs claimed before WU-510 used worktree mode)
|
|
162
|
+
return CLAIMED_MODES.WORKTREE;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Calculate lane branch name from WU YAML
|
|
166
|
+
* @param {object} doc - WU YAML document
|
|
167
|
+
* @returns {string|null} Lane branch name (e.g., lane/operations-tooling/wu-1215)
|
|
168
|
+
*/
|
|
169
|
+
export function defaultBranchFrom(doc) {
|
|
170
|
+
const lane = (doc.lane || '').toString();
|
|
171
|
+
const laneK = toKebab(lane);
|
|
172
|
+
const idK = (doc.id || '').toLowerCase();
|
|
173
|
+
if (!laneK || !idK)
|
|
174
|
+
return null;
|
|
175
|
+
return `lane/${laneK}/${idK}`;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Check if a branch exists
|
|
179
|
+
* @param {string} branch - Branch name to check
|
|
180
|
+
* @returns {Promise<boolean>} True if branch exists
|
|
181
|
+
*/
|
|
182
|
+
export async function branchExists(branch) {
|
|
183
|
+
return await getGitForCwd().branchExists(branch);
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Detect workspace mode and calculate all relevant paths
|
|
187
|
+
* @param {string} id - WU ID
|
|
188
|
+
* @param {object} args - Parsed command-line arguments
|
|
189
|
+
* @returns {Promise<object>} Object containing paths, mode info, and WU document
|
|
190
|
+
*/
|
|
191
|
+
export async function detectModeAndPaths(id, args) {
|
|
192
|
+
const WU_PATH = WU_PATHS.WU(id);
|
|
193
|
+
const STATUS_PATH = WU_PATHS.STATUS();
|
|
194
|
+
const BACKLOG_PATH = WU_PATHS.BACKLOG();
|
|
195
|
+
const STAMPS_DIR = WU_PATHS.STAMPS_DIR();
|
|
196
|
+
// Read WU YAML to detect workspace mode
|
|
197
|
+
const docMain = readWU(WU_PATH, id);
|
|
198
|
+
const workspaceMode = detectWorkspaceMode(docMain);
|
|
199
|
+
const isBranchOnly = workspaceMode === CLAIMED_MODES.BRANCH_ONLY;
|
|
200
|
+
console.log(`\n${LOG_PREFIX.DONE} Detected workspace mode: ${workspaceMode}`);
|
|
201
|
+
// Determine candidate worktree path early (only relevant for Worktree mode)
|
|
202
|
+
// Priority: 1) Auto-detect from cwd 2) Explicit --worktree arg 3) Calculate from YAML
|
|
203
|
+
const detectedWorktree = detectCurrentWorktree();
|
|
204
|
+
const worktreePathGuess = args.worktree || null;
|
|
205
|
+
// For Worktree mode: prefer auto-detected worktree, then explicit arg, then calculated path
|
|
206
|
+
// For Branch-Only mode: use main checkout version (no worktree exists)
|
|
207
|
+
const derivedWorktree = isBranchOnly
|
|
208
|
+
? null
|
|
209
|
+
: detectedWorktree || worktreePathGuess || (await defaultWorktreeFrom(docMain));
|
|
210
|
+
if (!isBranchOnly && derivedWorktree && !detectedWorktree) {
|
|
211
|
+
console.log(`${LOG_PREFIX.DONE} ${EMOJI.FOLDER} Calculated worktree path from YAML: ${derivedWorktree}`);
|
|
212
|
+
}
|
|
213
|
+
// Read the actual WU YAML for validation (prefer worktree version over main)
|
|
214
|
+
const docForValidation = isBranchOnly
|
|
215
|
+
? docMain
|
|
216
|
+
: readWUPreferWorktree(id, derivedWorktree, WU_PATH);
|
|
217
|
+
// WU-1234: Detect docs-only by type OR by code_paths
|
|
218
|
+
// Auto-detect if all code_paths are under docs/, ai/, .claude/, or are README/CLAUDE files
|
|
219
|
+
const isDocsOnlyByType = docForValidation.type === 'documentation';
|
|
220
|
+
const isDocsOnlyByPaths = detectDocsOnlyByPaths(docForValidation.code_paths);
|
|
221
|
+
const isDocsOnly = isDocsOnlyByType || isDocsOnlyByPaths;
|
|
222
|
+
if (isDocsOnlyByPaths && !isDocsOnlyByType) {
|
|
223
|
+
console.log(`${LOG_PREFIX.DONE} ${EMOJI.INFO} Auto-detected docs-only WU from code_paths (type: ${docForValidation.type || 'unset'})`);
|
|
224
|
+
}
|
|
225
|
+
return {
|
|
226
|
+
WU_PATH,
|
|
227
|
+
STATUS_PATH,
|
|
228
|
+
BACKLOG_PATH,
|
|
229
|
+
STAMPS_DIR,
|
|
230
|
+
docMain,
|
|
231
|
+
workspaceMode,
|
|
232
|
+
isBranchOnly,
|
|
233
|
+
derivedWorktree,
|
|
234
|
+
docForValidation,
|
|
235
|
+
isDocsOnly,
|
|
236
|
+
};
|
|
237
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Preflight validation helpers for wu:done.
|
|
3
|
+
*/
|
|
4
|
+
import { execSync as execSyncImport } from 'node:child_process';
|
|
5
|
+
import { validatePreflight } from './wu-preflight-validators.js';
|
|
6
|
+
/**
|
|
7
|
+
* WU-1781: Build preflight error message with actionable guidance
|
|
8
|
+
*/
|
|
9
|
+
export declare function buildPreflightErrorMessage(id: any, errors: any): string;
|
|
10
|
+
/**
|
|
11
|
+
* WU-1805: Execute preflight code_paths and test_paths validation
|
|
12
|
+
*/
|
|
13
|
+
export interface ExecutePreflightCodePathValidationOptions {
|
|
14
|
+
/** Override validatePreflight for testing */
|
|
15
|
+
validatePreflightFn?: typeof validatePreflight;
|
|
16
|
+
}
|
|
17
|
+
export declare function executePreflightCodePathValidation(id: any, paths: any, options?: ExecutePreflightCodePathValidationOptions): Promise<{
|
|
18
|
+
valid: boolean;
|
|
19
|
+
errors: any[];
|
|
20
|
+
missingCodePaths: any[];
|
|
21
|
+
missingTestPaths: any[];
|
|
22
|
+
abortedBeforeGates: boolean;
|
|
23
|
+
}>;
|
|
24
|
+
/**
|
|
25
|
+
* WU-1805: Build preflight code_paths error message with actionable guidance
|
|
26
|
+
*/
|
|
27
|
+
export declare function buildPreflightCodePathErrorMessage(id: any, result: any): string;
|
|
28
|
+
/**
|
|
29
|
+
* WU-1781: Run tasks:validate as preflight check before any git operations
|
|
30
|
+
*/
|
|
31
|
+
export interface ExecSyncOverrideOptions {
|
|
32
|
+
/** Override execSync for testing (default: child_process.execSync) */
|
|
33
|
+
execSyncFn?: typeof execSyncImport;
|
|
34
|
+
}
|
|
35
|
+
export declare function runPreflightTasksValidation(id: any, options?: ExecSyncOverrideOptions): {
|
|
36
|
+
valid: boolean;
|
|
37
|
+
errors: any;
|
|
38
|
+
abortedBeforeMerge: boolean;
|
|
39
|
+
localMainModified: boolean;
|
|
40
|
+
hasStampStatusError: any;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* WU-2308: Validate all pre-commit hooks with worktree context
|
|
44
|
+
*/
|
|
45
|
+
export declare function validateAllPreCommitHooks(id: any, worktreePath?: any, options?: ExecSyncOverrideOptions): {
|
|
46
|
+
valid: boolean;
|
|
47
|
+
errors: any[];
|
|
48
|
+
};
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Preflight validation helpers for wu:done.
|
|
3
|
+
*/
|
|
4
|
+
import { execSync as execSyncImport } from 'node:child_process';
|
|
5
|
+
import { validatePreflight } from './wu-preflight-validators.js';
|
|
6
|
+
import { LOG_PREFIX, EMOJI, STDIO } from './wu-constants.js';
|
|
7
|
+
/**
|
|
8
|
+
* WU-1781: Build preflight error message with actionable guidance
|
|
9
|
+
*/
|
|
10
|
+
export function buildPreflightErrorMessage(id, errors) {
|
|
11
|
+
const hasStampStatusError = errors.some((e) => e.includes('stamp but status is not done'));
|
|
12
|
+
let message = `
|
|
13
|
+
❌ PREFLIGHT VALIDATION FAILED
|
|
14
|
+
|
|
15
|
+
tasks:validate found errors that would block pre-push hooks.
|
|
16
|
+
Aborting wu:done BEFORE any merge operations to prevent deadlocks.
|
|
17
|
+
|
|
18
|
+
Errors:
|
|
19
|
+
${errors.map((e) => ` - ${e}`).join('\n')}
|
|
20
|
+
|
|
21
|
+
Fix options:
|
|
22
|
+
`;
|
|
23
|
+
if (hasStampStatusError) {
|
|
24
|
+
message += `
|
|
25
|
+
For stamp-status mismatch errors:
|
|
26
|
+
1. Fix the WU status to match the stamp (set status: done, locked: true)
|
|
27
|
+
2. Or add the WU ID to .lumenflow.config.yaml > exemptions > stamp_status_mismatch
|
|
28
|
+
|
|
29
|
+
`;
|
|
30
|
+
}
|
|
31
|
+
message += `
|
|
32
|
+
General fixes:
|
|
33
|
+
1. Run: pnpm tasks:validate to see full errors
|
|
34
|
+
2. Fix the validation errors
|
|
35
|
+
3. Retry: pnpm wu:done --id ${id}
|
|
36
|
+
|
|
37
|
+
This preflight check prevents wu:done from leaving main in a stuck state
|
|
38
|
+
where husky pre-push would block all further operations.
|
|
39
|
+
`;
|
|
40
|
+
return message;
|
|
41
|
+
}
|
|
42
|
+
export async function executePreflightCodePathValidation(id, paths, options = {}) {
|
|
43
|
+
// Use injected validator for testability, default to actual implementation
|
|
44
|
+
const validatePreflightFn = options.validatePreflightFn || validatePreflight;
|
|
45
|
+
console.log(`\n${LOG_PREFIX.DONE} 🔍 Preflight: validating code_paths and test paths...`);
|
|
46
|
+
const result = await validatePreflightFn(id, {
|
|
47
|
+
rootDir: paths.rootDir,
|
|
48
|
+
worktreePath: paths.worktreePath,
|
|
49
|
+
});
|
|
50
|
+
if (result.valid) {
|
|
51
|
+
console.log(`${LOG_PREFIX.DONE} ${EMOJI.SUCCESS} Preflight code_paths validation passed`);
|
|
52
|
+
return {
|
|
53
|
+
valid: true,
|
|
54
|
+
errors: [],
|
|
55
|
+
missingCodePaths: [],
|
|
56
|
+
missingTestPaths: [],
|
|
57
|
+
abortedBeforeGates: false,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
console.error(`\n${LOG_PREFIX.DONE} ${EMOJI.FAILURE} Preflight code_paths validation failed`);
|
|
61
|
+
return {
|
|
62
|
+
valid: false,
|
|
63
|
+
errors: result.errors,
|
|
64
|
+
missingCodePaths: result.missingCodePaths || [],
|
|
65
|
+
missingTestPaths: result.missingTestPaths || [],
|
|
66
|
+
abortedBeforeGates: true,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* WU-1805: Build preflight code_paths error message with actionable guidance
|
|
71
|
+
*/
|
|
72
|
+
export function buildPreflightCodePathErrorMessage(id, result) {
|
|
73
|
+
const { errors, missingCodePaths = [], missingTestPaths = [] } = result;
|
|
74
|
+
let message = `
|
|
75
|
+
❌ PREFLIGHT CODE_PATHS VALIDATION FAILED
|
|
76
|
+
|
|
77
|
+
code_paths/test_paths validation found errors that would cause gates to fail.
|
|
78
|
+
Aborting wu:done BEFORE running gates to save time.
|
|
79
|
+
|
|
80
|
+
Errors:
|
|
81
|
+
${errors.map((e) => ` ${e}`).join('\n')}
|
|
82
|
+
|
|
83
|
+
`;
|
|
84
|
+
if (missingCodePaths.length > 0) {
|
|
85
|
+
message += `
|
|
86
|
+
Fix options for missing code_paths:
|
|
87
|
+
1. Create the missing files in your worktree
|
|
88
|
+
2. Update code_paths in ${id}.yaml using: pnpm wu:edit --id ${id} --code-paths "<corrected-paths>"
|
|
89
|
+
3. Remove paths that were intentionally not created
|
|
90
|
+
|
|
91
|
+
`;
|
|
92
|
+
}
|
|
93
|
+
if (missingTestPaths.length > 0) {
|
|
94
|
+
message += `
|
|
95
|
+
Fix options for missing test_paths:
|
|
96
|
+
1. Create the missing test files
|
|
97
|
+
2. Update test paths in ${id}.yaml using wu:edit
|
|
98
|
+
3. Use tests.manual for descriptions instead of file paths
|
|
99
|
+
|
|
100
|
+
`;
|
|
101
|
+
}
|
|
102
|
+
message += `
|
|
103
|
+
After fixing, retry:
|
|
104
|
+
pnpm wu:done --id ${id}
|
|
105
|
+
|
|
106
|
+
This preflight check runs BEFORE gates to catch YAML mismatches early.
|
|
107
|
+
See: docs/04-operations/_frameworks/lumenflow/agent/onboarding/troubleshooting-wu-done.md for more recovery options.
|
|
108
|
+
`;
|
|
109
|
+
return message;
|
|
110
|
+
}
|
|
111
|
+
export function runPreflightTasksValidation(id, options = {}) {
|
|
112
|
+
// Use injected execSync for testability, default to node's child_process
|
|
113
|
+
const execSyncFn = options.execSyncFn || execSyncImport;
|
|
114
|
+
console.log(`\n${LOG_PREFIX.DONE} 🔍 Preflight: running tasks:validate...`);
|
|
115
|
+
try {
|
|
116
|
+
// Run tasks:validate with WU_ID context (single-WU validation mode)
|
|
117
|
+
execSyncFn('node tools/validate.js', {
|
|
118
|
+
stdio: STDIO.PIPE,
|
|
119
|
+
encoding: 'utf-8',
|
|
120
|
+
env: { ...process.env, WU_ID: id },
|
|
121
|
+
});
|
|
122
|
+
console.log(`${LOG_PREFIX.DONE} ${EMOJI.SUCCESS} Preflight tasks:validate passed`);
|
|
123
|
+
return {
|
|
124
|
+
valid: true,
|
|
125
|
+
errors: [],
|
|
126
|
+
abortedBeforeMerge: false,
|
|
127
|
+
localMainModified: false,
|
|
128
|
+
hasStampStatusError: false,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
catch (err) {
|
|
132
|
+
// Validation failed - extract errors from output
|
|
133
|
+
const output = err.stdout || err.message || 'Unknown validation error';
|
|
134
|
+
const errors = output
|
|
135
|
+
.split('\n')
|
|
136
|
+
.filter((line) => line.includes('[') && line.includes(']'))
|
|
137
|
+
.map((line) => line.trim());
|
|
138
|
+
const hasStampStatusError = errors.some((e) => e.includes('stamp but status is not done'));
|
|
139
|
+
console.error(`\n${LOG_PREFIX.DONE} ${EMOJI.FAILURE} Preflight tasks:validate failed`);
|
|
140
|
+
return {
|
|
141
|
+
valid: false,
|
|
142
|
+
errors: errors.length > 0 ? errors : [output],
|
|
143
|
+
abortedBeforeMerge: true,
|
|
144
|
+
localMainModified: false,
|
|
145
|
+
hasStampStatusError,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* WU-2308: Validate all pre-commit hooks with worktree context
|
|
151
|
+
*/
|
|
152
|
+
export function validateAllPreCommitHooks(id, worktreePath = null, options = {}) {
|
|
153
|
+
const execSyncFn = options.execSyncFn || execSyncImport;
|
|
154
|
+
console.log(`\n${LOG_PREFIX.DONE} 🔍 Pre-flight: validating all pre-commit hooks...`);
|
|
155
|
+
const errors = [];
|
|
156
|
+
try {
|
|
157
|
+
// WU-2308: Run from worktree context when provided to ensure audit checks
|
|
158
|
+
// the worktree's dependencies (with fixes) not main's stale dependencies
|
|
159
|
+
const execOptions = {
|
|
160
|
+
stdio: STDIO.INHERIT,
|
|
161
|
+
encoding: 'utf-8',
|
|
162
|
+
};
|
|
163
|
+
// Only set cwd when worktreePath is provided
|
|
164
|
+
if (worktreePath) {
|
|
165
|
+
execOptions.cwd = worktreePath;
|
|
166
|
+
}
|
|
167
|
+
// Run the gates-pre-commit script that contains all validation gates
|
|
168
|
+
execSyncFn('node tools/gates-pre-commit.js', execOptions);
|
|
169
|
+
console.log(`${LOG_PREFIX.DONE} ${EMOJI.SUCCESS} All pre-commit hooks passed`);
|
|
170
|
+
return { valid: true, errors: [] };
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
// Pre-commit hooks failed
|
|
174
|
+
errors.push('Pre-commit hook validation failed. Fix these issues before wu:done:');
|
|
175
|
+
errors.push('');
|
|
176
|
+
errors.push('Common fixes:');
|
|
177
|
+
errors.push(' • Formatting issues: Run pnpm format');
|
|
178
|
+
errors.push(' • Lint errors: Run pnpm lint:fix');
|
|
179
|
+
errors.push(' • Type errors: Check pnpm typecheck output');
|
|
180
|
+
errors.push(' • Audit issues: Check pnpm audit output');
|
|
181
|
+
errors.push('');
|
|
182
|
+
errors.push(`After fixing, re-run: pnpm wu:done --id ${id}`);
|
|
183
|
+
return { valid: false, errors };
|
|
184
|
+
}
|
|
185
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core validation helpers for wu:done.
|
|
3
|
+
*/
|
|
4
|
+
interface ExposureDefaultResult {
|
|
5
|
+
applied: boolean;
|
|
6
|
+
exposure?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function applyExposureDefaults(doc: any): ExposureDefaultResult;
|
|
9
|
+
/**
|
|
10
|
+
* WU-1351: Validate code_paths files exist on main branch
|
|
11
|
+
*
|
|
12
|
+
* Prevents false completions by ensuring all code_paths entries
|
|
13
|
+
* actually exist on the target branch (main or lane branch).
|
|
14
|
+
*
|
|
15
|
+
* This guards against:
|
|
16
|
+
* - Stamps being created for WUs where code never merged
|
|
17
|
+
* - Metadata becoming desynchronized from actual code
|
|
18
|
+
*/
|
|
19
|
+
export interface ValidateCodePathsExistOptions {
|
|
20
|
+
/** Branch to check files against (default: 'main') */
|
|
21
|
+
targetBranch?: string;
|
|
22
|
+
/** Worktree path for worktree mode */
|
|
23
|
+
worktreePath?: string | null;
|
|
24
|
+
}
|
|
25
|
+
export declare function validateCodePathsExist(doc: any, id: any, options?: ValidateCodePathsExistOptions): Promise<{
|
|
26
|
+
valid: boolean;
|
|
27
|
+
errors: any[];
|
|
28
|
+
missing: any[];
|
|
29
|
+
}>;
|
|
30
|
+
/**
|
|
31
|
+
* Validate WU spec completeness (WU-1162, WU-1280)
|
|
32
|
+
*
|
|
33
|
+
* Ensures WU specifications are complete before allowing wu:done to proceed.
|
|
34
|
+
* Prevents placeholder WUs from being marked as done.
|
|
35
|
+
*/
|
|
36
|
+
export declare function validateSpecCompleteness(doc: any, _id: any): {
|
|
37
|
+
valid: boolean;
|
|
38
|
+
errors: any[];
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* WU-1617: Post-mutation validation for wu:done
|
|
42
|
+
*
|
|
43
|
+
* Validates that metadata files written by tx.commit() are valid:
|
|
44
|
+
* 1. WU YAML has completed_at field with valid ISO datetime
|
|
45
|
+
* 2. WU YAML has locked: true
|
|
46
|
+
* 3. Stamp file exists
|
|
47
|
+
*/
|
|
48
|
+
export declare function validatePostMutation({ id, wuPath, stampPath }: {
|
|
49
|
+
id: any;
|
|
50
|
+
wuPath: any;
|
|
51
|
+
stampPath: any;
|
|
52
|
+
}): {
|
|
53
|
+
valid: boolean;
|
|
54
|
+
errors: any[];
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* WU-2242: Validate that test_paths is required for non-doc WUs
|
|
58
|
+
*
|
|
59
|
+
* Enforces that WUs with code changes (non-documentation types with code_paths
|
|
60
|
+
* that contain actual code) have at least one test path specified.
|
|
61
|
+
*/
|
|
62
|
+
export declare function validateTestPathsRequired(wu: any): {
|
|
63
|
+
valid: boolean;
|
|
64
|
+
error?: undefined;
|
|
65
|
+
} | {
|
|
66
|
+
valid: boolean;
|
|
67
|
+
error: string;
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* WU-2310: Validate type vs code_paths at preflight (before transaction starts).
|
|
71
|
+
*/
|
|
72
|
+
export declare function validateTypeVsCodePathsPreflight(wu: any): {
|
|
73
|
+
valid: boolean;
|
|
74
|
+
errors: any[];
|
|
75
|
+
blockedPaths: any[];
|
|
76
|
+
abortedBeforeTransaction: boolean;
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* WU-2310: Build error message for type vs code_paths preflight failure.
|
|
80
|
+
*/
|
|
81
|
+
export declare function buildTypeVsCodePathsErrorMessage(id: any, blockedPaths: any): string;
|
|
82
|
+
export {};
|