@lumenflow/core 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +190 -0
- package/README.md +119 -0
- package/dist/active-wu-detector.d.ts +33 -0
- package/dist/active-wu-detector.js +106 -0
- package/dist/adapters/filesystem-metrics.adapter.d.ts +108 -0
- package/dist/adapters/filesystem-metrics.adapter.js +519 -0
- package/dist/adapters/terminal-renderer.adapter.d.ts +106 -0
- package/dist/adapters/terminal-renderer.adapter.js +337 -0
- package/dist/arg-parser.d.ts +63 -0
- package/dist/arg-parser.js +560 -0
- package/dist/backlog-editor.d.ts +98 -0
- package/dist/backlog-editor.js +179 -0
- package/dist/backlog-generator.d.ts +111 -0
- package/dist/backlog-generator.js +381 -0
- package/dist/backlog-parser.d.ts +45 -0
- package/dist/backlog-parser.js +102 -0
- package/dist/backlog-sync-validator.d.ts +78 -0
- package/dist/backlog-sync-validator.js +294 -0
- package/dist/branch-drift.d.ts +34 -0
- package/dist/branch-drift.js +51 -0
- package/dist/cleanup-install-config.d.ts +33 -0
- package/dist/cleanup-install-config.js +37 -0
- package/dist/cleanup-lock.d.ts +139 -0
- package/dist/cleanup-lock.js +313 -0
- package/dist/code-path-validator.d.ts +146 -0
- package/dist/code-path-validator.js +537 -0
- package/dist/code-paths-overlap.d.ts +55 -0
- package/dist/code-paths-overlap.js +245 -0
- package/dist/commands-logger.d.ts +77 -0
- package/dist/commands-logger.js +254 -0
- package/dist/commit-message-utils.d.ts +25 -0
- package/dist/commit-message-utils.js +41 -0
- package/dist/compliance-parser.d.ts +150 -0
- package/dist/compliance-parser.js +507 -0
- package/dist/constants/backlog-patterns.d.ts +20 -0
- package/dist/constants/backlog-patterns.js +23 -0
- package/dist/constants/dora-constants.d.ts +49 -0
- package/dist/constants/dora-constants.js +53 -0
- package/dist/constants/gate-constants.d.ts +15 -0
- package/dist/constants/gate-constants.js +15 -0
- package/dist/constants/linter-constants.d.ts +16 -0
- package/dist/constants/linter-constants.js +16 -0
- package/dist/constants/tokenizer-constants.d.ts +15 -0
- package/dist/constants/tokenizer-constants.js +15 -0
- package/dist/core/scope-checker.d.ts +97 -0
- package/dist/core/scope-checker.js +163 -0
- package/dist/core/tool-runner.d.ts +161 -0
- package/dist/core/tool-runner.js +393 -0
- package/dist/core/tool.constants.d.ts +105 -0
- package/dist/core/tool.constants.js +101 -0
- package/dist/core/tool.schemas.d.ts +226 -0
- package/dist/core/tool.schemas.js +226 -0
- package/dist/core/worktree-guard.d.ts +130 -0
- package/dist/core/worktree-guard.js +242 -0
- package/dist/coverage-gate.d.ts +108 -0
- package/dist/coverage-gate.js +196 -0
- package/dist/date-utils.d.ts +75 -0
- package/dist/date-utils.js +140 -0
- package/dist/dependency-graph.d.ts +142 -0
- package/dist/dependency-graph.js +550 -0
- package/dist/dependency-guard.d.ts +54 -0
- package/dist/dependency-guard.js +142 -0
- package/dist/dependency-validator.d.ts +105 -0
- package/dist/dependency-validator.js +154 -0
- package/dist/docs-path-validator.d.ts +36 -0
- package/dist/docs-path-validator.js +95 -0
- package/dist/domain/orchestration.constants.d.ts +99 -0
- package/dist/domain/orchestration.constants.js +97 -0
- package/dist/domain/orchestration.schemas.d.ts +280 -0
- package/dist/domain/orchestration.schemas.js +211 -0
- package/dist/domain/orchestration.types.d.ts +133 -0
- package/dist/domain/orchestration.types.js +12 -0
- package/dist/error-handler.d.ts +116 -0
- package/dist/error-handler.js +136 -0
- package/dist/file-classifiers.d.ts +62 -0
- package/dist/file-classifiers.js +108 -0
- package/dist/gates-agent-mode.d.ts +81 -0
- package/dist/gates-agent-mode.js +94 -0
- package/dist/generate-traceability.d.ts +107 -0
- package/dist/generate-traceability.js +411 -0
- package/dist/git-adapter.d.ts +395 -0
- package/dist/git-adapter.js +649 -0
- package/dist/git-staged-validator.d.ts +32 -0
- package/dist/git-staged-validator.js +48 -0
- package/dist/hardcoded-strings.d.ts +61 -0
- package/dist/hardcoded-strings.js +270 -0
- package/dist/incremental-lint.d.ts +78 -0
- package/dist/incremental-lint.js +129 -0
- package/dist/incremental-test.d.ts +39 -0
- package/dist/incremental-test.js +61 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.js +61 -0
- package/dist/invariants/check-automated-tests.d.ts +50 -0
- package/dist/invariants/check-automated-tests.js +166 -0
- package/dist/invariants-runner.d.ts +103 -0
- package/dist/invariants-runner.js +527 -0
- package/dist/lane-checker.d.ts +50 -0
- package/dist/lane-checker.js +319 -0
- package/dist/lane-inference.d.ts +39 -0
- package/dist/lane-inference.js +195 -0
- package/dist/lane-lock.d.ts +211 -0
- package/dist/lane-lock.js +474 -0
- package/dist/lane-validator.d.ts +48 -0
- package/dist/lane-validator.js +114 -0
- package/dist/logs-lib.d.ts +104 -0
- package/dist/logs-lib.js +207 -0
- package/dist/lumenflow-config-schema.d.ts +272 -0
- package/dist/lumenflow-config-schema.js +207 -0
- package/dist/lumenflow-config.d.ts +95 -0
- package/dist/lumenflow-config.js +236 -0
- package/dist/manual-test-validator.d.ts +80 -0
- package/dist/manual-test-validator.js +200 -0
- package/dist/merge-lock.d.ts +115 -0
- package/dist/merge-lock.js +251 -0
- package/dist/micro-worktree.d.ts +159 -0
- package/dist/micro-worktree.js +427 -0
- package/dist/migration-deployer.d.ts +69 -0
- package/dist/migration-deployer.js +151 -0
- package/dist/orchestration-advisory-loader.d.ts +28 -0
- package/dist/orchestration-advisory-loader.js +87 -0
- package/dist/orchestration-advisory.d.ts +58 -0
- package/dist/orchestration-advisory.js +94 -0
- package/dist/orchestration-di.d.ts +48 -0
- package/dist/orchestration-di.js +57 -0
- package/dist/orchestration-rules.d.ts +57 -0
- package/dist/orchestration-rules.js +201 -0
- package/dist/orphan-detector.d.ts +131 -0
- package/dist/orphan-detector.js +226 -0
- package/dist/path-classifiers.d.ts +57 -0
- package/dist/path-classifiers.js +93 -0
- package/dist/piped-command-detector.d.ts +34 -0
- package/dist/piped-command-detector.js +64 -0
- package/dist/ports/dashboard-renderer.port.d.ts +112 -0
- package/dist/ports/dashboard-renderer.port.js +25 -0
- package/dist/ports/metrics-collector.port.d.ts +132 -0
- package/dist/ports/metrics-collector.port.js +26 -0
- package/dist/process-detector.d.ts +84 -0
- package/dist/process-detector.js +172 -0
- package/dist/prompt-linter.d.ts +72 -0
- package/dist/prompt-linter.js +312 -0
- package/dist/prompt-monitor.d.ts +15 -0
- package/dist/prompt-monitor.js +205 -0
- package/dist/rebase-artifact-cleanup.d.ts +145 -0
- package/dist/rebase-artifact-cleanup.js +433 -0
- package/dist/retry-strategy.d.ts +189 -0
- package/dist/retry-strategy.js +283 -0
- package/dist/risk-detector.d.ts +108 -0
- package/dist/risk-detector.js +252 -0
- package/dist/rollback-utils.d.ts +76 -0
- package/dist/rollback-utils.js +104 -0
- package/dist/section-headings.d.ts +43 -0
- package/dist/section-headings.js +49 -0
- package/dist/spawn-escalation.d.ts +90 -0
- package/dist/spawn-escalation.js +253 -0
- package/dist/spawn-monitor.d.ts +229 -0
- package/dist/spawn-monitor.js +672 -0
- package/dist/spawn-recovery.d.ts +82 -0
- package/dist/spawn-recovery.js +298 -0
- package/dist/spawn-registry-schema.d.ts +98 -0
- package/dist/spawn-registry-schema.js +108 -0
- package/dist/spawn-registry-store.d.ts +146 -0
- package/dist/spawn-registry-store.js +273 -0
- package/dist/spawn-tree.d.ts +121 -0
- package/dist/spawn-tree.js +285 -0
- package/dist/stamp-status-validator.d.ts +84 -0
- package/dist/stamp-status-validator.js +134 -0
- package/dist/stamp-utils.d.ts +100 -0
- package/dist/stamp-utils.js +229 -0
- package/dist/state-machine.d.ts +26 -0
- package/dist/state-machine.js +83 -0
- package/dist/system-map-validator.d.ts +80 -0
- package/dist/system-map-validator.js +272 -0
- package/dist/telemetry.d.ts +80 -0
- package/dist/telemetry.js +213 -0
- package/dist/token-counter.d.ts +51 -0
- package/dist/token-counter.js +145 -0
- package/dist/usecases/get-dashboard-data.usecase.d.ts +52 -0
- package/dist/usecases/get-dashboard-data.usecase.js +61 -0
- package/dist/usecases/get-suggestions.usecase.d.ts +100 -0
- package/dist/usecases/get-suggestions.usecase.js +153 -0
- package/dist/user-normalizer.d.ts +41 -0
- package/dist/user-normalizer.js +141 -0
- package/dist/validators/phi-constants.d.ts +97 -0
- package/dist/validators/phi-constants.js +152 -0
- package/dist/validators/phi-scanner.d.ts +58 -0
- package/dist/validators/phi-scanner.js +215 -0
- package/dist/worktree-ownership.d.ts +50 -0
- package/dist/worktree-ownership.js +74 -0
- package/dist/worktree-scanner.d.ts +103 -0
- package/dist/worktree-scanner.js +168 -0
- package/dist/worktree-symlink.d.ts +99 -0
- package/dist/worktree-symlink.js +359 -0
- package/dist/wu-backlog-updater.d.ts +17 -0
- package/dist/wu-backlog-updater.js +37 -0
- package/dist/wu-checkpoint.d.ts +124 -0
- package/dist/wu-checkpoint.js +233 -0
- package/dist/wu-claim-helpers.d.ts +26 -0
- package/dist/wu-claim-helpers.js +63 -0
- package/dist/wu-claim-resume.d.ts +106 -0
- package/dist/wu-claim-resume.js +276 -0
- package/dist/wu-consistency-checker.d.ts +95 -0
- package/dist/wu-consistency-checker.js +567 -0
- package/dist/wu-constants.d.ts +1275 -0
- package/dist/wu-constants.js +1382 -0
- package/dist/wu-create-validators.d.ts +42 -0
- package/dist/wu-create-validators.js +93 -0
- package/dist/wu-done-branch-only.d.ts +63 -0
- package/dist/wu-done-branch-only.js +191 -0
- package/dist/wu-done-messages.d.ts +119 -0
- package/dist/wu-done-messages.js +185 -0
- package/dist/wu-done-pr.d.ts +72 -0
- package/dist/wu-done-pr.js +174 -0
- package/dist/wu-done-retry-helpers.d.ts +85 -0
- package/dist/wu-done-retry-helpers.js +172 -0
- package/dist/wu-done-ui.d.ts +37 -0
- package/dist/wu-done-ui.js +69 -0
- package/dist/wu-done-validators.d.ts +411 -0
- package/dist/wu-done-validators.js +1229 -0
- package/dist/wu-done-worktree.d.ts +182 -0
- package/dist/wu-done-worktree.js +1097 -0
- package/dist/wu-helpers.d.ts +128 -0
- package/dist/wu-helpers.js +248 -0
- package/dist/wu-lint.d.ts +70 -0
- package/dist/wu-lint.js +234 -0
- package/dist/wu-paths.d.ts +171 -0
- package/dist/wu-paths.js +178 -0
- package/dist/wu-preflight-validators.d.ts +86 -0
- package/dist/wu-preflight-validators.js +251 -0
- package/dist/wu-recovery.d.ts +138 -0
- package/dist/wu-recovery.js +341 -0
- package/dist/wu-repair-core.d.ts +131 -0
- package/dist/wu-repair-core.js +669 -0
- package/dist/wu-schema-normalization.d.ts +17 -0
- package/dist/wu-schema-normalization.js +82 -0
- package/dist/wu-schema.d.ts +793 -0
- package/dist/wu-schema.js +881 -0
- package/dist/wu-spawn-helpers.d.ts +121 -0
- package/dist/wu-spawn-helpers.js +271 -0
- package/dist/wu-spawn.d.ts +158 -0
- package/dist/wu-spawn.js +1306 -0
- package/dist/wu-state-schema.d.ts +213 -0
- package/dist/wu-state-schema.js +156 -0
- package/dist/wu-state-store.d.ts +264 -0
- package/dist/wu-state-store.js +691 -0
- package/dist/wu-status-transition.d.ts +63 -0
- package/dist/wu-status-transition.js +382 -0
- package/dist/wu-status-updater.d.ts +25 -0
- package/dist/wu-status-updater.js +116 -0
- package/dist/wu-transaction-collectors.d.ts +116 -0
- package/dist/wu-transaction-collectors.js +272 -0
- package/dist/wu-transaction.d.ts +170 -0
- package/dist/wu-transaction.js +273 -0
- package/dist/wu-validation-constants.d.ts +60 -0
- package/dist/wu-validation-constants.js +66 -0
- package/dist/wu-validation.d.ts +118 -0
- package/dist/wu-validation.js +243 -0
- package/dist/wu-validator.d.ts +62 -0
- package/dist/wu-validator.js +325 -0
- package/dist/wu-yaml-fixer.d.ts +97 -0
- package/dist/wu-yaml-fixer.js +264 -0
- package/dist/wu-yaml.d.ts +86 -0
- package/dist/wu-yaml.js +222 -0
- package/package.json +114 -0
|
@@ -0,0 +1,537 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Unified Code Path Validator (WU-1825)
|
|
4
|
+
*
|
|
5
|
+
* Consolidates three separate code path validators into one module:
|
|
6
|
+
* - validateCodePathsExist (wu-done-validators.mjs) - file existence for wu:done
|
|
7
|
+
* - validateLaneCodePaths (lane-validator.mjs) - lane pattern matching
|
|
8
|
+
* - validateWUCodePaths (wu-validator.mjs) - code quality (TODOs, mocks)
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* import { validate } from './code-path-validator.js';
|
|
12
|
+
*
|
|
13
|
+
* // Mode: 'exist' - check file existence (wu:done workflow)
|
|
14
|
+
* const result = await validate(paths, { mode: 'exist', worktreePath, targetBranch });
|
|
15
|
+
*
|
|
16
|
+
* // Mode: 'lane' - check lane pattern matching (wu:claim workflow)
|
|
17
|
+
* const result = validate(paths, { mode: 'lane', lane: 'Operations: Tooling' });
|
|
18
|
+
*
|
|
19
|
+
* // Mode: 'quality' - check code quality (TODOs, mocks)
|
|
20
|
+
* const result = validate(paths, { mode: 'quality', worktreePath, allowTodos: false });
|
|
21
|
+
*
|
|
22
|
+
* Part of INIT-023: Workflow Integrity initiative.
|
|
23
|
+
*/
|
|
24
|
+
/* eslint-disable security/detect-non-literal-fs-filename, security/detect-object-injection */
|
|
25
|
+
import path from 'node:path';
|
|
26
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
27
|
+
import { execSync } from 'node:child_process';
|
|
28
|
+
import micromatch from 'micromatch';
|
|
29
|
+
import { getGitForCwd } from './git-adapter.js';
|
|
30
|
+
import { extractParent } from './lane-checker.js';
|
|
31
|
+
import { LANE_PATH_PATTERNS, BRANCHES, STRING_LITERALS, GIT_COMMANDS, LOG_PREFIX, EMOJI, } from './wu-constants.js';
|
|
32
|
+
// ============================================================================
|
|
33
|
+
// VALIDATION MODE CONSTANTS
|
|
34
|
+
// ============================================================================
|
|
35
|
+
/**
|
|
36
|
+
* Validation modes for the unified validator
|
|
37
|
+
* @enum {string}
|
|
38
|
+
*/
|
|
39
|
+
export const VALIDATION_MODES = Object.freeze({
|
|
40
|
+
/** Check file existence - used by wu:done */
|
|
41
|
+
EXIST: 'exist',
|
|
42
|
+
/** Check lane pattern matching - used by wu:claim */
|
|
43
|
+
LANE: 'lane',
|
|
44
|
+
/** Check code quality (TODOs, mocks) - used by wu:done */
|
|
45
|
+
QUALITY: 'quality',
|
|
46
|
+
});
|
|
47
|
+
// ============================================================================
|
|
48
|
+
// FILE EXISTENCE VALIDATION (MODE: 'exist')
|
|
49
|
+
// ============================================================================
|
|
50
|
+
/**
|
|
51
|
+
* Check if a file path is a test file
|
|
52
|
+
* @param {string} filePath - Path to check
|
|
53
|
+
* @returns {boolean} True if file is a test file
|
|
54
|
+
*/
|
|
55
|
+
function isTestFile(filePath) {
|
|
56
|
+
const normalized = filePath.replace(/\\/g, '/');
|
|
57
|
+
const testPatterns = [
|
|
58
|
+
/\.test\.(ts|tsx|js|jsx|mjs)$/,
|
|
59
|
+
/\.spec\.(ts|tsx|js|jsx|mjs)$/,
|
|
60
|
+
/__tests__\//,
|
|
61
|
+
/\.test-utils\./,
|
|
62
|
+
/\.mock\./,
|
|
63
|
+
];
|
|
64
|
+
return testPatterns.some((pattern) => pattern.test(normalized));
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Check if a file path is a markdown file
|
|
68
|
+
* @param {string} filePath - Path to check
|
|
69
|
+
* @returns {boolean} True if file is a markdown file
|
|
70
|
+
*/
|
|
71
|
+
function isMarkdownFile(filePath) {
|
|
72
|
+
const normalized = filePath.replace(/\\/g, '/');
|
|
73
|
+
return /\.md$/i.test(normalized);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Get the repo root directory
|
|
77
|
+
* @returns {string} Absolute path to repo root
|
|
78
|
+
*/
|
|
79
|
+
function getRepoRoot() {
|
|
80
|
+
try {
|
|
81
|
+
return execSync('git rev-parse --show-toplevel', {
|
|
82
|
+
encoding: 'utf-8',
|
|
83
|
+
stdio: ['pipe', 'pipe', 'ignore'],
|
|
84
|
+
}).trim();
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return process.cwd();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Validate that files exist (worktree mode)
|
|
92
|
+
* @param {string[]} codePaths - Array of file paths
|
|
93
|
+
* @param {string} worktreePath - Worktree directory path
|
|
94
|
+
* @returns {ExistValidationResult} Validation result
|
|
95
|
+
*/
|
|
96
|
+
function validateExistenceInWorktree(codePaths, worktreePath) {
|
|
97
|
+
const missing = [];
|
|
98
|
+
const errors = [];
|
|
99
|
+
for (const filePath of codePaths) {
|
|
100
|
+
const fullPath = path.join(worktreePath, filePath);
|
|
101
|
+
if (!existsSync(fullPath)) {
|
|
102
|
+
missing.push(filePath);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (missing.length > 0) {
|
|
106
|
+
errors.push(`code_paths validation failed - ${missing.length} file(s) not found in worktree:\n${missing
|
|
107
|
+
.map((p) => ` - ${p}`)
|
|
108
|
+
.join(STRING_LITERALS.NEWLINE)}\n\nEnsure all files listed in code_paths exist before running wu:done.`);
|
|
109
|
+
}
|
|
110
|
+
return { valid: errors.length === 0, errors, missing };
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Validate that files exist on a git branch (branch-only mode)
|
|
114
|
+
* @param {string[]} codePaths - Array of file paths
|
|
115
|
+
* @param {string} targetBranch - Branch to check files against
|
|
116
|
+
* @returns {Promise<ExistValidationResult>} Validation result
|
|
117
|
+
*/
|
|
118
|
+
async function validateExistenceOnBranch(codePaths, targetBranch) {
|
|
119
|
+
const missing = [];
|
|
120
|
+
const errors = [];
|
|
121
|
+
try {
|
|
122
|
+
const gitAdapter = getGitForCwd();
|
|
123
|
+
for (const filePath of codePaths) {
|
|
124
|
+
try {
|
|
125
|
+
const result = await gitAdapter.raw([GIT_COMMANDS.LS_TREE, targetBranch, '--', filePath]);
|
|
126
|
+
if (!result || result.trim() === '') {
|
|
127
|
+
missing.push(filePath);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
missing.push(filePath);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if (missing.length > 0) {
|
|
135
|
+
errors.push(`code_paths validation failed - ${missing.length} file(s) not found on ${targetBranch}:\n${missing
|
|
136
|
+
.map((p) => ` - ${p}`)
|
|
137
|
+
.join(STRING_LITERALS.NEWLINE)}\n\n❌ POTENTIAL FALSE COMPLETION DETECTED\n\n` +
|
|
138
|
+
`These files are listed in code_paths but do not exist on ${targetBranch}.\n` +
|
|
139
|
+
`This prevents creating a stamp for incomplete work.\n\n` +
|
|
140
|
+
`Fix options:\n` +
|
|
141
|
+
` 1. Ensure all code is committed and merged to ${targetBranch}\n` +
|
|
142
|
+
` 2. Update code_paths in WU YAML to match actual files\n` +
|
|
143
|
+
` 3. Remove files that were intentionally not created\n\n` +
|
|
144
|
+
`Context: WU-1351 prevents false completions from INIT-WORKFLOW-INTEGRITY`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
catch (err) {
|
|
148
|
+
const errMessage = err instanceof Error ? err.message : String(err);
|
|
149
|
+
console.warn(`${LOG_PREFIX.DONE} ${EMOJI.WARNING} Could not validate code_paths: ${errMessage}`);
|
|
150
|
+
return { valid: true, errors: [], missing: [] };
|
|
151
|
+
}
|
|
152
|
+
return { valid: errors.length === 0, errors, missing };
|
|
153
|
+
}
|
|
154
|
+
// ============================================================================
|
|
155
|
+
// LANE PATTERN VALIDATION (MODE: 'lane')
|
|
156
|
+
// ============================================================================
|
|
157
|
+
/**
|
|
158
|
+
* Validate code paths against lane patterns
|
|
159
|
+
* @param {string[]} codePaths - Array of file paths
|
|
160
|
+
* @param {string} lane - Lane name (e.g., "Operations: Tooling")
|
|
161
|
+
* @returns {LaneValidationResult} Validation result
|
|
162
|
+
*/
|
|
163
|
+
function validateLanePatterns(codePaths, lane) {
|
|
164
|
+
// Skip validation if no code_paths
|
|
165
|
+
if (!codePaths || codePaths.length === 0) {
|
|
166
|
+
return {
|
|
167
|
+
hasWarnings: false,
|
|
168
|
+
warnings: [],
|
|
169
|
+
violations: [],
|
|
170
|
+
skipped: true,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
// Extract parent lane (e.g., "Operations" from "Operations: Tooling")
|
|
174
|
+
const parentLane = extractParent(lane);
|
|
175
|
+
// Get patterns for this lane parent
|
|
176
|
+
const patterns = LANE_PATH_PATTERNS[parentLane];
|
|
177
|
+
// Skip validation if no patterns defined for this lane
|
|
178
|
+
if (!patterns) {
|
|
179
|
+
return {
|
|
180
|
+
hasWarnings: false,
|
|
181
|
+
warnings: [],
|
|
182
|
+
violations: [],
|
|
183
|
+
skipped: true,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
const { exclude = [], allowExceptions = [] } = patterns;
|
|
187
|
+
// Find violations: paths that match exclude patterns but NOT exception patterns
|
|
188
|
+
const violations = codePaths.filter((codePath) => {
|
|
189
|
+
const matchesExclude = micromatch.isMatch(codePath, exclude, { nocase: true });
|
|
190
|
+
if (!matchesExclude)
|
|
191
|
+
return false;
|
|
192
|
+
if (allowExceptions.length > 0) {
|
|
193
|
+
const matchesException = micromatch.isMatch(codePath, allowExceptions, { nocase: true });
|
|
194
|
+
if (matchesException)
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
return true;
|
|
198
|
+
});
|
|
199
|
+
if (violations.length === 0) {
|
|
200
|
+
return {
|
|
201
|
+
hasWarnings: false,
|
|
202
|
+
warnings: [],
|
|
203
|
+
violations: [],
|
|
204
|
+
skipped: false,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
// Build warning messages
|
|
208
|
+
const warnings = violations.map((violatingPath) => {
|
|
209
|
+
return `Lane "${lane}" typically doesn't include "${violatingPath}" (expected for different lane)`;
|
|
210
|
+
});
|
|
211
|
+
return {
|
|
212
|
+
hasWarnings: true,
|
|
213
|
+
warnings,
|
|
214
|
+
violations,
|
|
215
|
+
skipped: false,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
// ============================================================================
|
|
219
|
+
// CODE QUALITY VALIDATION (MODE: 'quality')
|
|
220
|
+
// ============================================================================
|
|
221
|
+
/**
|
|
222
|
+
* Scan a file for TODO/FIXME/HACK/XXX comments
|
|
223
|
+
* @param {string} filePath - Path to file to scan
|
|
224
|
+
* @returns {{found: boolean, matches: Array<{line: number, text: string, pattern: string}>}}
|
|
225
|
+
*/
|
|
226
|
+
function scanFileForTODOs(filePath) {
|
|
227
|
+
if (!existsSync(filePath)) {
|
|
228
|
+
return { found: false, matches: [] };
|
|
229
|
+
}
|
|
230
|
+
if (isTestFile(filePath)) {
|
|
231
|
+
return { found: false, matches: [] };
|
|
232
|
+
}
|
|
233
|
+
if (isMarkdownFile(filePath)) {
|
|
234
|
+
return { found: false, matches: [] };
|
|
235
|
+
}
|
|
236
|
+
try {
|
|
237
|
+
const content = readFileSync(filePath, { encoding: 'utf-8' });
|
|
238
|
+
const lines = content.split(/\r?\n/);
|
|
239
|
+
const matches = [];
|
|
240
|
+
const checkForActionableMarker = (line) => {
|
|
241
|
+
const trimmed = line.trim();
|
|
242
|
+
// Skip documentation lines
|
|
243
|
+
if (trimmed.includes('// TODO:,') || trimmed.includes('/* TODO */')) {
|
|
244
|
+
return { found: false, pattern: null };
|
|
245
|
+
}
|
|
246
|
+
if (trimmed.includes('@todo,') || trimmed.includes('@-prefixed:')) {
|
|
247
|
+
return { found: false, pattern: null };
|
|
248
|
+
}
|
|
249
|
+
// Pattern 1: @-prefixed tags at start of JSDoc comment line
|
|
250
|
+
const atTagMatch = trimmed.match(/^\*\s+@(todo|fixme|hack|xxx)\b/i);
|
|
251
|
+
if (atTagMatch) {
|
|
252
|
+
return { found: true, pattern: atTagMatch[1].toUpperCase() };
|
|
253
|
+
}
|
|
254
|
+
// Pattern 2: Keyword at start of comment content
|
|
255
|
+
const commentStartMatch = trimmed.match(/^(?:\/\/|\/\*+|\*|<!--|#)\s*(TODO|FIXME|HACK|XXX)(?::|[\s]|$)/i);
|
|
256
|
+
if (commentStartMatch) {
|
|
257
|
+
const afterKeyword = trimmed.slice(trimmed.indexOf(commentStartMatch[1]) + commentStartMatch[1].length);
|
|
258
|
+
if (!afterKeyword.startsWith('/')) {
|
|
259
|
+
return { found: true, pattern: commentStartMatch[1].toUpperCase() };
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
// Pattern 3: Keyword in inline comment after code
|
|
263
|
+
const inlineCommentMatch = line.match(/\/\/\s*(TODO|FIXME|HACK|XXX)(?::|[\s]|$)/i);
|
|
264
|
+
if (inlineCommentMatch && !line.match(/\/\/\s*(TODO|FIXME|HACK|XXX)\//i)) {
|
|
265
|
+
const doubleSlashIndex = line.indexOf('//');
|
|
266
|
+
const beforeSlash = line.slice(0, doubleSlashIndex);
|
|
267
|
+
const singleQuotes = (beforeSlash.match(/(?<!\\)'/g) || []).length;
|
|
268
|
+
const doubleQuotes = (beforeSlash.match(/(?<!\\)"/g) || []).length;
|
|
269
|
+
const backticks = (beforeSlash.match(/(?<!\\)`/g) || []).length;
|
|
270
|
+
if (singleQuotes % 2 !== 0 || doubleQuotes % 2 !== 0 || backticks % 2 !== 0) {
|
|
271
|
+
return { found: false, pattern: null };
|
|
272
|
+
}
|
|
273
|
+
const commentPart = line.slice(doubleSlashIndex);
|
|
274
|
+
const keywordIndex = commentPart.search(/\b(TODO|FIXME|HACK|XXX)\b/i);
|
|
275
|
+
if (keywordIndex >= 0 && keywordIndex <= 10) {
|
|
276
|
+
return { found: true, pattern: inlineCommentMatch[1].toUpperCase() };
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
// Exclude WU-XXX placeholders
|
|
280
|
+
if (trimmed.match(/\bWU-XXX\b/i)) {
|
|
281
|
+
return { found: false, pattern: null };
|
|
282
|
+
}
|
|
283
|
+
return { found: false, pattern: null };
|
|
284
|
+
};
|
|
285
|
+
lines.forEach((line, index) => {
|
|
286
|
+
const lineNumber = index + 1;
|
|
287
|
+
const trimmed = line.trim();
|
|
288
|
+
const isComment = /^(\/\/|\/\*|\*|<!--|#)/.test(trimmed) || line.includes('//') || line.includes('/*');
|
|
289
|
+
if (isComment) {
|
|
290
|
+
const result = checkForActionableMarker(line);
|
|
291
|
+
if (result.found) {
|
|
292
|
+
matches.push({
|
|
293
|
+
line: lineNumber,
|
|
294
|
+
text: trimmed,
|
|
295
|
+
pattern: result.pattern,
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
return { found: matches.length > 0, matches };
|
|
301
|
+
}
|
|
302
|
+
catch {
|
|
303
|
+
return { found: false, matches: [] };
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Scan a file for Mock/Stub/Fake class/function names
|
|
308
|
+
* @param {string} filePath - Path to file to scan
|
|
309
|
+
* @returns {{found: boolean, matches: Array<{line: number, text: string, type: string}>}}
|
|
310
|
+
*/
|
|
311
|
+
function scanFileForMocks(filePath) {
|
|
312
|
+
if (!existsSync(filePath)) {
|
|
313
|
+
return { found: false, matches: [] };
|
|
314
|
+
}
|
|
315
|
+
if (isTestFile(filePath)) {
|
|
316
|
+
return { found: false, matches: [] };
|
|
317
|
+
}
|
|
318
|
+
try {
|
|
319
|
+
const content = readFileSync(filePath, { encoding: 'utf-8' });
|
|
320
|
+
const lines = content.split(/\r?\n/);
|
|
321
|
+
const matches = [];
|
|
322
|
+
const mockPatterns = [
|
|
323
|
+
{ name: 'Mock', regex: /\b(class|export\s+class)\s+(\w*Mock\w*)/i },
|
|
324
|
+
{ name: 'Stub', regex: /\b(class|export\s+class)\s+(\w*Stub\w*)/i },
|
|
325
|
+
{ name: 'Fake', regex: /\b(class|export\s+class)\s+(\w*Fake\w*)/i },
|
|
326
|
+
{ name: 'Placeholder', regex: /\b(class|export\s+class)\s+(\w*Placeholder\w*)/i },
|
|
327
|
+
{ name: 'Mock', regex: /\b(function|const|let|var)\s+(\w*mock\w*)/i },
|
|
328
|
+
{ name: 'Stub', regex: /\b(function|const|let|var)\s+(\w*stub\w*)/i },
|
|
329
|
+
{ name: 'Fake', regex: /\b(function|const|let|var)\s+(\w*fake\w*)/i },
|
|
330
|
+
{ name: 'Placeholder', regex: /\b(function|const|let|var)\s+(\w*placeholder\w*)/i },
|
|
331
|
+
];
|
|
332
|
+
lines.forEach((line, index) => {
|
|
333
|
+
const lineNumber = index + 1;
|
|
334
|
+
mockPatterns.forEach(({ name, regex }) => {
|
|
335
|
+
const match = regex.exec(line);
|
|
336
|
+
if (match) {
|
|
337
|
+
matches.push({
|
|
338
|
+
line: lineNumber,
|
|
339
|
+
text: line.trim(),
|
|
340
|
+
type: name,
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
});
|
|
345
|
+
return { found: matches.length > 0, matches };
|
|
346
|
+
}
|
|
347
|
+
catch {
|
|
348
|
+
return { found: false, matches: [] };
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Format TODO findings for display
|
|
353
|
+
* @param {Array} findings - TODO findings
|
|
354
|
+
* @returns {string} Formatted message
|
|
355
|
+
*/
|
|
356
|
+
function formatTODOFindings(findings) {
|
|
357
|
+
let msg = '\n❌ TODO/FIXME/HACK/XXX comments found in production code:\n';
|
|
358
|
+
findings.forEach(({ path: filePath, matches }) => {
|
|
359
|
+
msg += `\n ${filePath}:\n`;
|
|
360
|
+
matches.forEach(({ line, text }) => {
|
|
361
|
+
msg += ` Line ${line}: ${text}\n`;
|
|
362
|
+
});
|
|
363
|
+
});
|
|
364
|
+
msg += '\nThese indicate incomplete work and must be resolved before WU completion.';
|
|
365
|
+
msg += '\nEither complete the work or use --allow-todo with justification in WU notes.';
|
|
366
|
+
return msg;
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Format Mock findings for display
|
|
370
|
+
* @param {Array} findings - Mock findings
|
|
371
|
+
* @returns {string} Formatted message
|
|
372
|
+
*/
|
|
373
|
+
function formatMockFindings(findings) {
|
|
374
|
+
let msg = '\n⚠️ Mock/Stub/Fake/Placeholder classes found in production code:\n';
|
|
375
|
+
findings.forEach(({ path: filePath, matches }) => {
|
|
376
|
+
msg += `\n ${filePath}:\n`;
|
|
377
|
+
matches.forEach(({ line, text }) => {
|
|
378
|
+
msg += ` Line ${line}: ${text}\n`;
|
|
379
|
+
});
|
|
380
|
+
});
|
|
381
|
+
msg += '\nThese suggest incomplete implementation (interface ≠ implementation).';
|
|
382
|
+
msg += '\nVerify these are actual implementations, not placeholder code.';
|
|
383
|
+
return msg;
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Validate code quality (TODOs, mocks)
|
|
387
|
+
* @param {string[]} codePaths - Array of file paths
|
|
388
|
+
* @param {object} options - Options
|
|
389
|
+
* @param {boolean} options.allowTodos - Allow TODO comments (with warning)
|
|
390
|
+
* @param {string} options.worktreePath - Worktree path for file lookups
|
|
391
|
+
* @returns {QualityValidationResult} Validation result
|
|
392
|
+
*/
|
|
393
|
+
function validateCodeQuality(codePaths, options = {}) {
|
|
394
|
+
const { allowTodos = false, worktreePath = null } = options;
|
|
395
|
+
const errors = [];
|
|
396
|
+
const warnings = [];
|
|
397
|
+
const repoRoot = worktreePath || getRepoRoot();
|
|
398
|
+
if (!codePaths || codePaths.length === 0) {
|
|
399
|
+
return { valid: true, errors, warnings };
|
|
400
|
+
}
|
|
401
|
+
const todoFindings = [];
|
|
402
|
+
const mockFindings = [];
|
|
403
|
+
for (const codePath of codePaths) {
|
|
404
|
+
const absolutePath = path.join(repoRoot, codePath);
|
|
405
|
+
if (!existsSync(absolutePath)) {
|
|
406
|
+
errors.push(`\n❌ Code path validation failed: File does not exist: ${codePath}\n\n` +
|
|
407
|
+
`This indicates the WU claims to have created/modified a file that doesn't exist.\n` +
|
|
408
|
+
`Either create the file, or remove it from code_paths in the WU YAML.\n`);
|
|
409
|
+
continue;
|
|
410
|
+
}
|
|
411
|
+
const todoResult = scanFileForTODOs(absolutePath);
|
|
412
|
+
if (todoResult.found) {
|
|
413
|
+
todoFindings.push({ path: codePath, ...todoResult });
|
|
414
|
+
}
|
|
415
|
+
const mockResult = scanFileForMocks(absolutePath);
|
|
416
|
+
if (mockResult.found) {
|
|
417
|
+
mockFindings.push({ path: codePath, ...mockResult });
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
if (todoFindings.length > 0) {
|
|
421
|
+
const message = formatTODOFindings(todoFindings);
|
|
422
|
+
if (allowTodos) {
|
|
423
|
+
warnings.push(message);
|
|
424
|
+
}
|
|
425
|
+
else {
|
|
426
|
+
errors.push(message);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
if (mockFindings.length > 0) {
|
|
430
|
+
warnings.push(formatMockFindings(mockFindings));
|
|
431
|
+
}
|
|
432
|
+
return { valid: errors.length === 0, errors, warnings };
|
|
433
|
+
}
|
|
434
|
+
// ============================================================================
|
|
435
|
+
// UNIFIED VALIDATE API
|
|
436
|
+
// ============================================================================
|
|
437
|
+
/**
|
|
438
|
+
* Unified code path validation function
|
|
439
|
+
*
|
|
440
|
+
* @param {string[]} codePaths - Array of file paths to validate
|
|
441
|
+
* @param {object} options - Validation options
|
|
442
|
+
* @param {string} options.mode - Validation mode: 'exist', 'lane', or 'quality'
|
|
443
|
+
* @param {string} [options.worktreePath] - Worktree path (for 'exist' and 'quality' modes)
|
|
444
|
+
* @param {string} [options.targetBranch] - Target branch (for 'exist' mode, branch-only)
|
|
445
|
+
* @param {string} [options.lane] - Lane name (for 'lane' mode)
|
|
446
|
+
* @param {boolean} [options.allowTodos] - Allow TODO comments (for 'quality' mode)
|
|
447
|
+
* @returns {Promise<ExistValidationResult|LaneValidationResult|QualityValidationResult>}
|
|
448
|
+
*/
|
|
449
|
+
export async function validate(codePaths, options = {}) {
|
|
450
|
+
const { mode = VALIDATION_MODES.EXIST } = options;
|
|
451
|
+
switch (mode) {
|
|
452
|
+
case VALIDATION_MODES.EXIST: {
|
|
453
|
+
const { worktreePath, targetBranch = BRANCHES.MAIN } = options;
|
|
454
|
+
if (!codePaths || codePaths.length === 0) {
|
|
455
|
+
return { valid: true, errors: [], missing: [] };
|
|
456
|
+
}
|
|
457
|
+
if (worktreePath && existsSync(worktreePath)) {
|
|
458
|
+
return validateExistenceInWorktree(codePaths, worktreePath);
|
|
459
|
+
}
|
|
460
|
+
return validateExistenceOnBranch(codePaths, targetBranch);
|
|
461
|
+
}
|
|
462
|
+
case VALIDATION_MODES.LANE: {
|
|
463
|
+
const { lane } = options;
|
|
464
|
+
if (!lane) {
|
|
465
|
+
throw new Error('Lane name is required for lane validation mode');
|
|
466
|
+
}
|
|
467
|
+
return validateLanePatterns(codePaths, lane);
|
|
468
|
+
}
|
|
469
|
+
case VALIDATION_MODES.QUALITY: {
|
|
470
|
+
const { worktreePath, allowTodos } = options;
|
|
471
|
+
return validateCodeQuality(codePaths, { worktreePath, allowTodos });
|
|
472
|
+
}
|
|
473
|
+
default:
|
|
474
|
+
throw new Error(`Unknown validation mode: ${mode}`);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
/**
|
|
478
|
+
* @deprecated Use validate(paths, { mode: 'exist' }) instead
|
|
479
|
+
* Backward-compatible wrapper for validateCodePathsExist
|
|
480
|
+
*/
|
|
481
|
+
export async function validateCodePathsExist(doc, _id, options = {}) {
|
|
482
|
+
const codePaths = doc.code_paths || [];
|
|
483
|
+
const { targetBranch = BRANCHES.MAIN, worktreePath = null } = options;
|
|
484
|
+
if (codePaths.length === 0) {
|
|
485
|
+
console.log(`${LOG_PREFIX.DONE} ${EMOJI.INFO} No code_paths to validate for ${_id}`);
|
|
486
|
+
return { valid: true, errors: [], missing: [] };
|
|
487
|
+
}
|
|
488
|
+
console.log(`${LOG_PREFIX.DONE} Validating ${codePaths.length} code_paths exist...`);
|
|
489
|
+
const result = (await validate(codePaths, {
|
|
490
|
+
mode: VALIDATION_MODES.EXIST,
|
|
491
|
+
worktreePath: worktreePath ?? undefined,
|
|
492
|
+
targetBranch,
|
|
493
|
+
}));
|
|
494
|
+
if (result.valid) {
|
|
495
|
+
console.log(`${LOG_PREFIX.DONE} ${EMOJI.SUCCESS} All ${codePaths.length} code_paths verified`);
|
|
496
|
+
}
|
|
497
|
+
return result;
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* @deprecated Use validate(paths, { mode: 'lane', lane }) instead
|
|
501
|
+
* Backward-compatible wrapper for validateLaneCodePaths
|
|
502
|
+
* NOTE: This must remain SYNCHRONOUS for backward compatibility
|
|
503
|
+
*/
|
|
504
|
+
export function validateLaneCodePaths(doc, lane) {
|
|
505
|
+
const codePaths = doc.code_paths || [];
|
|
506
|
+
// Call the sync internal function directly to maintain sync behavior
|
|
507
|
+
return validateLanePatterns(codePaths, lane);
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* @deprecated Use validate(paths, { mode: 'quality' }) instead
|
|
511
|
+
* Backward-compatible wrapper for validateWUCodePaths
|
|
512
|
+
* NOTE: This must remain SYNCHRONOUS for backward compatibility
|
|
513
|
+
*/
|
|
514
|
+
export function validateWUCodePaths(codePaths, options = {}) {
|
|
515
|
+
const { allowTodos = false, worktreePath = null } = options;
|
|
516
|
+
// Call the sync internal function directly to maintain sync behavior
|
|
517
|
+
return validateCodeQuality(codePaths, { worktreePath, allowTodos });
|
|
518
|
+
}
|
|
519
|
+
/**
|
|
520
|
+
* Log lane validation warnings to console.
|
|
521
|
+
* Helper function to format and display warnings consistently.
|
|
522
|
+
*
|
|
523
|
+
* @param {LaneValidationResult} result - Result from validateLaneCodePaths
|
|
524
|
+
* @param {string} logPrefix - Log prefix (e.g., "[wu-claim]")
|
|
525
|
+
*/
|
|
526
|
+
export function logLaneValidationWarnings(result, logPrefix = '[wu-claim]') {
|
|
527
|
+
if (!result.hasWarnings) {
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
console.warn(`${logPrefix} Lane/code_paths mismatch detected (advisory only):`);
|
|
531
|
+
for (const warning of result.warnings) {
|
|
532
|
+
console.warn(`${logPrefix} ${warning}`);
|
|
533
|
+
}
|
|
534
|
+
console.warn(`${logPrefix} This is a warning only - proceeding with claim.`);
|
|
535
|
+
}
|
|
536
|
+
// Re-export helper functions for consumers that need them
|
|
537
|
+
export { isTestFile, isMarkdownFile, scanFileForTODOs, scanFileForMocks };
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code Paths Overlap Detection
|
|
3
|
+
*
|
|
4
|
+
* Two-step algorithm for detecting code path conflicts between Work Units:
|
|
5
|
+
* 1. Static glob containment check (fast pre-filter using pattern matching)
|
|
6
|
+
* 2. Concrete file intersection (authoritative using filesystem)
|
|
7
|
+
*
|
|
8
|
+
* @module code-paths-overlap
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Check for code path overlap between two sets of glob patterns
|
|
12
|
+
*
|
|
13
|
+
* Uses two-step algorithm:
|
|
14
|
+
* - Static check: Fast pattern analysis to detect obvious containment
|
|
15
|
+
* - Concrete check: Filesystem-based expansion to find actual file intersection
|
|
16
|
+
*
|
|
17
|
+
* @param {string[]} claimingPaths - Glob patterns from WU being claimed
|
|
18
|
+
* @param {string[]} existingPaths - Glob patterns from in-progress WU
|
|
19
|
+
* @returns {{
|
|
20
|
+
* overlaps: boolean,
|
|
21
|
+
* type: 'none'|'concrete'|'ambiguous',
|
|
22
|
+
* files: string[]
|
|
23
|
+
* }} Overlap detection result
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* checkOverlap(['apps/web/**'], ['apps/web/prompts/**'])
|
|
27
|
+
* // => { overlaps: true, type: 'concrete', files: [...] }
|
|
28
|
+
*/
|
|
29
|
+
export declare function checkOverlap(claimingPaths: any, existingPaths: any): {
|
|
30
|
+
overlaps: boolean;
|
|
31
|
+
type: string;
|
|
32
|
+
files: unknown[];
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Find all in-progress WUs with overlapping code paths
|
|
36
|
+
*
|
|
37
|
+
* Reads status.md to find in-progress WUs, loads their code_paths,
|
|
38
|
+
* and checks for overlaps with the claiming WU's paths.
|
|
39
|
+
*
|
|
40
|
+
* @param {string} statusPath - Path to status.md file
|
|
41
|
+
* @param {string[]} claimingPaths - Glob patterns from WU being claimed
|
|
42
|
+
* @param {string} claimingWU - WU ID being claimed (excluded from check)
|
|
43
|
+
* @returns {{
|
|
44
|
+
* conflicts: Array<{wuid: string, overlaps: string[]}>,
|
|
45
|
+
* hasBlocker: boolean
|
|
46
|
+
* }} List of conflicting WUs and whether to block claim
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* detectConflicts('docs/04-operations/tasks/status.md', ['apps/**'], 'WU-901')
|
|
50
|
+
* // => { conflicts: [{wuid: 'WU-900', overlaps: ['apps/web/foo.ts']}], hasBlocker: true }
|
|
51
|
+
*/
|
|
52
|
+
export declare function detectConflicts(statusPath: any, claimingPaths: any, claimingWU: any): {
|
|
53
|
+
conflicts: any[];
|
|
54
|
+
hasBlocker: boolean;
|
|
55
|
+
};
|