@lumenflow/cli 2.4.0 → 2.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -8
- package/dist/__tests__/init-config-lanes.test.js +131 -0
- package/dist/__tests__/init-docs-structure.test.js +119 -0
- package/dist/__tests__/init-lane-inference.test.js +125 -0
- package/dist/__tests__/init-onboarding-docs.test.js +132 -0
- package/dist/__tests__/init-quick-ref.test.js +145 -0
- package/dist/__tests__/init-scripts.test.js +207 -0
- package/dist/__tests__/init-template-portability.test.js +97 -0
- package/dist/__tests__/init.test.js +7 -2
- package/dist/__tests__/initiative-add-wu.test.js +420 -0
- package/dist/__tests__/initiative-plan-replacement.test.js +162 -0
- package/dist/__tests__/initiative-remove-wu.test.js +458 -0
- package/dist/__tests__/onboarding-smoke-test.test.js +211 -0
- package/dist/__tests__/path-centralization-cli.test.js +234 -0
- package/dist/__tests__/plan-create.test.js +126 -0
- package/dist/__tests__/plan-edit.test.js +157 -0
- package/dist/__tests__/plan-link.test.js +239 -0
- package/dist/__tests__/plan-promote.test.js +181 -0
- package/dist/__tests__/templates-sync.test.js +219 -0
- package/dist/__tests__/wu-create-strict.test.js +118 -0
- package/dist/__tests__/wu-edit-strict.test.js +109 -0
- package/dist/__tests__/wu-validate-strict.test.js +113 -0
- package/dist/flow-bottlenecks.js +4 -2
- package/dist/gates.js +22 -0
- package/dist/init.js +670 -87
- package/dist/initiative-add-wu.js +112 -16
- package/dist/initiative-remove-wu.js +248 -0
- package/dist/onboarding-smoke-test.js +400 -0
- package/dist/orchestrate-init-status.js +37 -9
- package/dist/orchestrate-initiative.js +10 -4
- package/dist/plan-create.js +199 -0
- package/dist/plan-edit.js +235 -0
- package/dist/plan-link.js +233 -0
- package/dist/plan-promote.js +231 -0
- package/dist/sync-templates.js +137 -5
- package/dist/wu-block.js +16 -5
- package/dist/wu-claim.js +15 -9
- package/dist/wu-create.js +50 -2
- package/dist/wu-deps.js +3 -1
- package/dist/wu-done.js +14 -5
- package/dist/wu-edit.js +35 -0
- package/dist/wu-prep.js +131 -8
- package/dist/wu-spawn.js +14 -1
- package/dist/wu-unblock.js +34 -2
- package/dist/wu-validate.js +25 -17
- package/package.json +11 -7
- package/templates/core/.lumenflow/constraints.md.template +61 -3
- package/templates/core/AGENTS.md.template +2 -2
- package/templates/core/LUMENFLOW.md.template +85 -23
- package/templates/core/ai/onboarding/agent-invocation-guide.md.template +157 -0
- package/templates/core/ai/onboarding/agent-safety-card.md.template +227 -0
- package/templates/core/ai/onboarding/docs-generation.md.template +277 -0
- package/templates/core/ai/onboarding/first-wu-mistakes.md.template +49 -7
- package/templates/core/ai/onboarding/quick-ref-commands.md.template +343 -110
- package/templates/core/ai/onboarding/release-process.md.template +8 -2
- package/templates/core/ai/onboarding/starting-prompt.md.template +407 -0
- package/templates/core/ai/onboarding/test-ratchet.md.template +131 -0
- package/templates/core/ai/onboarding/troubleshooting-wu-done.md.template +91 -38
- package/templates/core/ai/onboarding/vendor-support.md.template +219 -0
- package/templates/vendors/claude/.claude/skills/context-management/SKILL.md.template +13 -1
- package/templates/vendors/claude/.claude/skills/execution-memory/SKILL.md.template +14 -16
- package/templates/vendors/claude/.claude/skills/orchestration/SKILL.md.template +48 -4
- package/templates/vendors/claude/.claude/skills/worktree-discipline/SKILL.md.template +5 -1
- package/templates/vendors/claude/.claude/skills/wu-lifecycle/SKILL.md.template +19 -8
- package/dist/__tests__/init-plan.test.js +0 -340
- package/dist/agent-issues-query.d.ts +0 -16
- package/dist/agent-log-issue.d.ts +0 -10
- package/dist/agent-session-end.d.ts +0 -10
- package/dist/agent-session.d.ts +0 -10
- package/dist/backlog-prune.d.ts +0 -84
- package/dist/cli-entry-point.d.ts +0 -8
- package/dist/deps-add.d.ts +0 -91
- package/dist/deps-remove.d.ts +0 -17
- package/dist/docs-sync.d.ts +0 -50
- package/dist/file-delete.d.ts +0 -84
- package/dist/file-edit.d.ts +0 -82
- package/dist/file-read.d.ts +0 -92
- package/dist/file-write.d.ts +0 -90
- package/dist/flow-bottlenecks.d.ts +0 -16
- package/dist/flow-report.d.ts +0 -16
- package/dist/gates.d.ts +0 -94
- package/dist/git-branch.d.ts +0 -65
- package/dist/git-diff.d.ts +0 -58
- package/dist/git-log.d.ts +0 -69
- package/dist/git-status.d.ts +0 -58
- package/dist/guard-locked.d.ts +0 -62
- package/dist/guard-main-branch.d.ts +0 -50
- package/dist/guard-worktree-commit.d.ts +0 -59
- package/dist/index.d.ts +0 -10
- package/dist/init-plan.d.ts +0 -80
- package/dist/init-plan.js +0 -337
- package/dist/init.d.ts +0 -46
- package/dist/initiative-add-wu.d.ts +0 -22
- package/dist/initiative-bulk-assign-wus.d.ts +0 -16
- package/dist/initiative-create.d.ts +0 -28
- package/dist/initiative-edit.d.ts +0 -34
- package/dist/initiative-list.d.ts +0 -12
- package/dist/initiative-status.d.ts +0 -11
- package/dist/lumenflow-upgrade.d.ts +0 -103
- package/dist/mem-checkpoint.d.ts +0 -16
- package/dist/mem-cleanup.d.ts +0 -29
- package/dist/mem-create.d.ts +0 -17
- package/dist/mem-export.d.ts +0 -10
- package/dist/mem-inbox.d.ts +0 -35
- package/dist/mem-init.d.ts +0 -15
- package/dist/mem-ready.d.ts +0 -16
- package/dist/mem-signal.d.ts +0 -16
- package/dist/mem-start.d.ts +0 -16
- package/dist/mem-summarize.d.ts +0 -22
- package/dist/mem-triage.d.ts +0 -22
- package/dist/metrics-cli.d.ts +0 -90
- package/dist/metrics-snapshot.d.ts +0 -18
- package/dist/orchestrate-init-status.d.ts +0 -11
- package/dist/orchestrate-initiative.d.ts +0 -12
- package/dist/orchestrate-monitor.d.ts +0 -11
- package/dist/release.d.ts +0 -117
- package/dist/rotate-progress.d.ts +0 -48
- package/dist/session-coordinator.d.ts +0 -74
- package/dist/spawn-list.d.ts +0 -16
- package/dist/state-bootstrap.d.ts +0 -92
- package/dist/sync-templates.d.ts +0 -52
- package/dist/trace-gen.d.ts +0 -84
- package/dist/validate-agent-skills.d.ts +0 -50
- package/dist/validate-agent-sync.d.ts +0 -36
- package/dist/validate-backlog-sync.d.ts +0 -37
- package/dist/validate-skills-spec.d.ts +0 -40
- package/dist/validate.d.ts +0 -60
- package/dist/wu-block.d.ts +0 -16
- package/dist/wu-claim.d.ts +0 -74
- package/dist/wu-cleanup.d.ts +0 -35
- package/dist/wu-create.d.ts +0 -69
- package/dist/wu-delete.d.ts +0 -21
- package/dist/wu-deps.d.ts +0 -13
- package/dist/wu-done.d.ts +0 -225
- package/dist/wu-edit.d.ts +0 -63
- package/dist/wu-infer-lane.d.ts +0 -17
- package/dist/wu-preflight.d.ts +0 -47
- package/dist/wu-prune.d.ts +0 -16
- package/dist/wu-recover.d.ts +0 -37
- package/dist/wu-release.d.ts +0 -19
- package/dist/wu-repair.d.ts +0 -60
- package/dist/wu-spawn-completion.d.ts +0 -10
- package/dist/wu-spawn.d.ts +0 -192
- package/dist/wu-status.d.ts +0 -25
- package/dist/wu-unblock.d.ts +0 -16
- package/dist/wu-unlock-lane.d.ts +0 -19
- package/dist/wu-validate.d.ts +0 -16
package/dist/wu-done.js
CHANGED
|
@@ -65,6 +65,7 @@ CONTEXT_VALIDATION, } from '@lumenflow/core/dist/wu-constants.js';
|
|
|
65
65
|
import { printGateFailureBox, printStatusPreview } from '@lumenflow/core/dist/wu-done-ui.js';
|
|
66
66
|
import { ensureOnMain } from '@lumenflow/core/dist/wu-helpers.js';
|
|
67
67
|
import { WU_PATHS } from '@lumenflow/core/dist/wu-paths.js';
|
|
68
|
+
import { getConfig } from '@lumenflow/core/dist/lumenflow-config.js';
|
|
68
69
|
import { writeWU, appendNote, parseYAML } from '@lumenflow/core/dist/wu-yaml.js';
|
|
69
70
|
import { PLACEHOLDER_SENTINEL, validateWU, validateDoneWU, validateApprovalGates, } from '@lumenflow/core/dist/wu-schema.js';
|
|
70
71
|
import { validateBacklogSync } from '@lumenflow/core/dist/backlog-sync-validator.js';
|
|
@@ -969,11 +970,14 @@ async function runGatesInWorktree(worktreePath, id, options = {}) {
|
|
|
969
970
|
}
|
|
970
971
|
async function validateStagedFiles(id, isDocsOnly = false) {
|
|
971
972
|
const staged = await listStaged();
|
|
973
|
+
// WU-1311: Use config-based paths instead of hardcoded docs/04-operations paths
|
|
974
|
+
const config = getConfig();
|
|
975
|
+
const wuPath = `${config.directories.wuDir}/${id}.yaml`;
|
|
972
976
|
// WU-1740: Include wu-events.jsonl to persist state store events
|
|
973
977
|
const whitelist = [
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
978
|
+
wuPath,
|
|
979
|
+
config.directories.statusPath,
|
|
980
|
+
config.directories.backlogPath,
|
|
977
981
|
WU_EVENTS_PATH,
|
|
978
982
|
];
|
|
979
983
|
if (isDocsOnly) {
|
|
@@ -998,7 +1002,10 @@ async function validateStagedFiles(id, isDocsOnly = false) {
|
|
|
998
1002
|
return true;
|
|
999
1003
|
});
|
|
1000
1004
|
if (unexpected.length > 0) {
|
|
1001
|
-
|
|
1005
|
+
// WU-1311: Use config-based pattern for WU YAML detection
|
|
1006
|
+
const wuDirPattern = config.directories.wuDir.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
1007
|
+
const wuYamlRegex = new RegExp(`^${wuDirPattern}/WU-\\d+\\.yaml$`);
|
|
1008
|
+
const otherWuYamlOnly = unexpected.every((f) => wuYamlRegex.test(f));
|
|
1002
1009
|
if (otherWuYamlOnly) {
|
|
1003
1010
|
console.warn(`${LOG_PREFIX.DONE} Warning: other WU YAMLs are staged; proceeding and committing only current WU files.`);
|
|
1004
1011
|
}
|
|
@@ -1620,8 +1627,10 @@ async function executePreFlightChecks({ id, args, isBranchOnly, isDocsOnly, docM
|
|
|
1620
1627
|
if (!specResult.valid) {
|
|
1621
1628
|
console.error(`\n❌ Spec completeness validation failed for ${id}:\n`);
|
|
1622
1629
|
specResult.errors.forEach((err) => console.error(` - ${err}`));
|
|
1630
|
+
// WU-1311: Use config-based path in error message
|
|
1631
|
+
const specConfig = getConfig();
|
|
1623
1632
|
console.error(`\nFix these issues before running wu:done:\n` +
|
|
1624
|
-
` 1. Update
|
|
1633
|
+
` 1. Update ${specConfig.directories.wuDir}/${id}.yaml\n` +
|
|
1625
1634
|
` 2. Fill description with Context/Problem/Solution\n` +
|
|
1626
1635
|
` 3. Replace ${PLACEHOLDER_SENTINEL} text with specific criteria\n` +
|
|
1627
1636
|
` 4. List all modified files in code_paths\n` +
|
package/dist/wu-edit.js
CHANGED
|
@@ -57,6 +57,8 @@ import { readInitiative, writeInitiative } from '@lumenflow/initiatives/dist/ini
|
|
|
57
57
|
import { normalizeWUSchema } from '@lumenflow/core/dist/wu-schema-normalization.js';
|
|
58
58
|
// WU-2253: Import WU spec linter for acceptance/code_paths validation
|
|
59
59
|
import { lintWUSpec, formatLintErrors } from '@lumenflow/core/dist/wu-lint.js';
|
|
60
|
+
// WU-1329: Import path existence validators for strict validation
|
|
61
|
+
import { validateCodePathsExistence, validateTestPathsExistence, } from '@lumenflow/core/dist/wu-preflight-validators.js';
|
|
60
62
|
/* eslint-disable security/detect-object-injection */
|
|
61
63
|
const PREFIX = LOG_PREFIX.EDIT;
|
|
62
64
|
/**
|
|
@@ -377,6 +379,8 @@ function parseArgs() {
|
|
|
377
379
|
EDIT_OPTIONS.replaceDependencies,
|
|
378
380
|
// WU-1039: Add exposure for done WU metadata updates
|
|
379
381
|
WU_OPTIONS.exposure,
|
|
382
|
+
// WU-1329: Strict validation is default, --no-strict bypasses
|
|
383
|
+
WU_OPTIONS.noStrict,
|
|
380
384
|
],
|
|
381
385
|
required: ['id'],
|
|
382
386
|
allowPositionalId: true,
|
|
@@ -921,6 +925,37 @@ async function main() {
|
|
|
921
925
|
// WU-1750: CRITICAL - Use transformed data for all subsequent operations
|
|
922
926
|
// This ensures embedded newlines are normalized before YAML output
|
|
923
927
|
const normalizedWU = validationResult.data;
|
|
928
|
+
// WU-1329: Strict validation of path existence (default behavior)
|
|
929
|
+
// --no-strict bypasses these checks
|
|
930
|
+
const strict = !opts.noStrict;
|
|
931
|
+
if (!strict) {
|
|
932
|
+
console.warn(`${PREFIX} WARNING: strict validation bypassed (--no-strict). Path existence checks skipped.`);
|
|
933
|
+
}
|
|
934
|
+
if (strict) {
|
|
935
|
+
const rootDir = process.cwd();
|
|
936
|
+
const strictErrors = [];
|
|
937
|
+
// Validate code_paths exist
|
|
938
|
+
if (normalizedWU.code_paths && normalizedWU.code_paths.length > 0) {
|
|
939
|
+
const codePathsResult = validateCodePathsExistence(normalizedWU.code_paths, rootDir);
|
|
940
|
+
if (!codePathsResult.valid) {
|
|
941
|
+
strictErrors.push(...codePathsResult.errors);
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
// Validate test_paths exist (unit, e2e - not manual)
|
|
945
|
+
if (normalizedWU.tests) {
|
|
946
|
+
const testPathsResult = validateTestPathsExistence(normalizedWU.tests, rootDir);
|
|
947
|
+
if (!testPathsResult.valid) {
|
|
948
|
+
strictErrors.push(...testPathsResult.errors);
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
if (strictErrors.length > 0) {
|
|
952
|
+
const errorList = strictErrors.map((e) => ` • ${e}`).join('\n');
|
|
953
|
+
die(`${PREFIX} ❌ Strict validation failed:\n\n${errorList}\n\n` +
|
|
954
|
+
`Options:\n` +
|
|
955
|
+
` 1. Fix the paths in the WU spec to match actual files\n` +
|
|
956
|
+
` 2. Use --no-strict to bypass path existence checks (not recommended)`);
|
|
957
|
+
}
|
|
958
|
+
}
|
|
924
959
|
// Validate lane format if present (WU-923: block parent-only lanes with taxonomy)
|
|
925
960
|
if (normalizedWU.lane) {
|
|
926
961
|
validateLaneFormat(normalizedWU.lane);
|
package/dist/wu-prep.js
CHANGED
|
@@ -1,21 +1,28 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
/* eslint-disable no-console -- CLI command uses console for status output */
|
|
2
3
|
/**
|
|
3
4
|
* WU Prep Helper (WU-1223)
|
|
4
5
|
*
|
|
5
6
|
* Prepares a WU for completion by running gates and generating docs in the worktree.
|
|
6
7
|
* After successful prep, prints copy-paste instruction to run wu:done from main.
|
|
7
8
|
*
|
|
9
|
+
* WU-1344: When gates fail on spec:linter due to pre-existing WU validation errors
|
|
10
|
+
* (not caused by the current WU), prints a ready-to-copy wu:done --skip-gates
|
|
11
|
+
* command with reason and fix-wu placeholders.
|
|
12
|
+
*
|
|
8
13
|
* Workflow:
|
|
9
14
|
* 1. Verify we're in a worktree (error if in main checkout)
|
|
10
15
|
* 2. Run gates in the worktree
|
|
11
|
-
* 3.
|
|
12
|
-
* 4.
|
|
16
|
+
* 3. If gates fail, check if failures are pre-existing on main
|
|
17
|
+
* 4. If pre-existing, print skip-gates command; otherwise, print fix guidance
|
|
18
|
+
* 5. On success, print copy-paste instruction for wu:done from main
|
|
13
19
|
*
|
|
14
20
|
* Usage:
|
|
15
21
|
* pnpm wu:prep --id WU-XXX [--docs-only]
|
|
16
22
|
*
|
|
17
23
|
* @module
|
|
18
24
|
*/
|
|
25
|
+
import { spawnSync } from 'node:child_process';
|
|
19
26
|
import { createWUParser, WU_OPTIONS } from '@lumenflow/core/dist/arg-parser.js';
|
|
20
27
|
import { die } from '@lumenflow/core/dist/error-handler.js';
|
|
21
28
|
import { resolveLocation } from '@lumenflow/core/dist/context/location-resolver.js';
|
|
@@ -39,6 +46,84 @@ const PREP_OPTIONS = {
|
|
|
39
46
|
description: 'Run docs-only gates (format, spec-linter)',
|
|
40
47
|
},
|
|
41
48
|
};
|
|
49
|
+
/**
|
|
50
|
+
* WU-1344: Check if a gate name is the spec:linter gate.
|
|
51
|
+
* Used to identify when spec:linter fails so we can check for pre-existing failures.
|
|
52
|
+
*
|
|
53
|
+
* @param gateName - Name of the gate that failed
|
|
54
|
+
* @returns true if this is the spec:linter gate
|
|
55
|
+
*/
|
|
56
|
+
export function isPreExistingSpecLinterFailure(gateName) {
|
|
57
|
+
const normalizedName = gateName.toLowerCase().replace(/[:-]/g, '');
|
|
58
|
+
return normalizedName === 'speclinter';
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* WU-1344: Format a skip-gates command for wu:done.
|
|
62
|
+
* Includes --reason and --fix-wu placeholders.
|
|
63
|
+
*
|
|
64
|
+
* @param options - Configuration options
|
|
65
|
+
* @param options.wuId - The WU ID being completed
|
|
66
|
+
* @param options.mainCheckout - Path to main checkout
|
|
67
|
+
* @returns Formatted command string ready to copy-paste
|
|
68
|
+
*/
|
|
69
|
+
export function formatSkipGatesCommand(options) {
|
|
70
|
+
const { wuId, mainCheckout } = options;
|
|
71
|
+
return `cd ${mainCheckout} && pnpm wu:done --id ${wuId} --skip-gates --reason "pre-existing on main" --fix-wu WU-XXXX`;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* WU-1344: Default implementation of execOnMain using spawnSync.
|
|
75
|
+
* Uses spawnSync with pnpm executable for safety (no shell injection risk).
|
|
76
|
+
*/
|
|
77
|
+
function defaultExecOnMain(mainCheckout) {
|
|
78
|
+
return async (cmd) => {
|
|
79
|
+
// Parse command to extract pnpm script name and args
|
|
80
|
+
// Expected format: "pnpm spec:linter" or similar
|
|
81
|
+
const parts = cmd.split(/\s+/);
|
|
82
|
+
const executable = parts[0];
|
|
83
|
+
const args = parts.slice(1);
|
|
84
|
+
const result = spawnSync(executable, args, {
|
|
85
|
+
cwd: mainCheckout,
|
|
86
|
+
encoding: 'utf-8',
|
|
87
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
88
|
+
});
|
|
89
|
+
return {
|
|
90
|
+
exitCode: result.status ?? 1,
|
|
91
|
+
stdout: result.stdout ?? '',
|
|
92
|
+
stderr: result.stderr ?? '',
|
|
93
|
+
};
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* WU-1344: Check if spec:linter failures are pre-existing on main branch.
|
|
98
|
+
*
|
|
99
|
+
* Runs spec:linter on the main checkout to determine if the failures
|
|
100
|
+
* already exist there (i.e., not caused by the current WU).
|
|
101
|
+
*
|
|
102
|
+
* @param options - Configuration options
|
|
103
|
+
* @param options.mainCheckout - Path to main checkout
|
|
104
|
+
* @param options.execOnMain - Optional function to execute commands on main (for testing)
|
|
105
|
+
* @returns Result indicating whether failures are pre-existing
|
|
106
|
+
*/
|
|
107
|
+
export async function checkPreExistingFailures(options) {
|
|
108
|
+
const { mainCheckout, execOnMain = defaultExecOnMain(mainCheckout) } = options;
|
|
109
|
+
try {
|
|
110
|
+
// Run spec:linter on main checkout
|
|
111
|
+
const result = await execOnMain('pnpm spec:linter');
|
|
112
|
+
// If spec:linter fails on main, the failures are pre-existing
|
|
113
|
+
if (result.exitCode !== 0) {
|
|
114
|
+
return { hasPreExisting: true };
|
|
115
|
+
}
|
|
116
|
+
return { hasPreExisting: false };
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
// If we can't check main, assume failures are NOT pre-existing
|
|
120
|
+
// (safer to require fixing rather than skipping)
|
|
121
|
+
return {
|
|
122
|
+
hasPreExisting: false,
|
|
123
|
+
error: error.message,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
}
|
|
42
127
|
/**
|
|
43
128
|
* Print success message with copy-paste instruction.
|
|
44
129
|
*/
|
|
@@ -106,6 +191,34 @@ async function main() {
|
|
|
106
191
|
console.log(`${PREP_PREFIX} Location: ${location.cwd}`);
|
|
107
192
|
console.log(`${PREP_PREFIX} Main checkout: ${location.mainCheckout}`);
|
|
108
193
|
console.log('');
|
|
194
|
+
// WU-1344: Check for pre-existing spec:linter failures on main BEFORE running gates.
|
|
195
|
+
// We do this first because runGates() calls die() on failure, which exits the process
|
|
196
|
+
// before we can check. By checking first, we can set up an exit handler to show
|
|
197
|
+
// the skip-gates command if gates fail.
|
|
198
|
+
console.log(`${PREP_PREFIX} Checking for pre-existing spec:linter failures on main...`);
|
|
199
|
+
const preExistingCheck = await checkPreExistingFailures({
|
|
200
|
+
mainCheckout: location.mainCheckout,
|
|
201
|
+
});
|
|
202
|
+
if (preExistingCheck.hasPreExisting) {
|
|
203
|
+
console.log(`${PREP_PREFIX} ${EMOJI.WARNING} Pre-existing failures detected on main.`);
|
|
204
|
+
// Set up an exit handler to print the skip-gates command when gates fail
|
|
205
|
+
// This runs before process.exit() fully terminates the process
|
|
206
|
+
process.on('exit', (code) => {
|
|
207
|
+
if (code !== EXIT_CODES.SUCCESS) {
|
|
208
|
+
console.log('');
|
|
209
|
+
console.log(`${PREP_PREFIX} ${EMOJI.WARNING} Since failures are pre-existing on main, you can skip gates:`);
|
|
210
|
+
console.log('');
|
|
211
|
+
console.log(` ${formatSkipGatesCommand({ wuId: id, mainCheckout: location.mainCheckout })}`);
|
|
212
|
+
console.log('');
|
|
213
|
+
console.log(`${PREP_PREFIX} Replace WU-XXXX with the WU that will fix these spec issues.`);
|
|
214
|
+
console.log('');
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
console.log(`${PREP_PREFIX} No pre-existing failures on main.`);
|
|
220
|
+
}
|
|
221
|
+
console.log('');
|
|
109
222
|
// Run gates in the worktree
|
|
110
223
|
console.log(`${PREP_PREFIX} Running gates in worktree...`);
|
|
111
224
|
const gatesResult = await runGates({
|
|
@@ -113,13 +226,23 @@ async function main() {
|
|
|
113
226
|
docsOnly: args.docsOnly,
|
|
114
227
|
});
|
|
115
228
|
if (!gatesResult) {
|
|
116
|
-
|
|
117
|
-
|
|
229
|
+
// Gates failed - if pre-existing check was already done and showed failures,
|
|
230
|
+
// the exit handler above will print the skip-gates command.
|
|
231
|
+
// Otherwise, tell the user to fix the failures.
|
|
232
|
+
if (!preExistingCheck.hasPreExisting) {
|
|
233
|
+
die(`${EMOJI.FAILURE} Gates failed in worktree.\n\n` +
|
|
234
|
+
`Fix the gate failures and run wu:prep again.`);
|
|
235
|
+
}
|
|
236
|
+
// Pre-existing failures - exit with error, handler will print skip-gates command
|
|
237
|
+
process.exit(EXIT_CODES.ERROR);
|
|
118
238
|
}
|
|
119
239
|
// Success - print copy-paste instruction
|
|
120
240
|
printSuccessMessage(id, location.mainCheckout);
|
|
121
241
|
}
|
|
122
|
-
main
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
}
|
|
242
|
+
// WU-1181: Use import.meta.main instead of process.argv[1] comparison
|
|
243
|
+
// The old pattern fails with pnpm symlinks because process.argv[1] is the symlink
|
|
244
|
+
// path but import.meta.url resolves to the real path - they never match
|
|
245
|
+
import { runCLI } from './cli-entry-point.js';
|
|
246
|
+
if (import.meta.main) {
|
|
247
|
+
void runCLI(main);
|
|
248
|
+
}
|
package/dist/wu-spawn.js
CHANGED
|
@@ -36,11 +36,14 @@ import { parseYAML } from '@lumenflow/core/dist/wu-yaml.js';
|
|
|
36
36
|
import { die } from '@lumenflow/core/dist/error-handler.js';
|
|
37
37
|
import { WU_STATUS, PATTERNS, FILE_SYSTEM, EMOJI } from '@lumenflow/core/dist/wu-constants.js';
|
|
38
38
|
// WU-1603: Check lane lock status before spawning
|
|
39
|
+
// WU-1325: Import lock policy getter for lane availability check
|
|
39
40
|
import { checkLaneLock } from '@lumenflow/core/dist/lane-lock.js';
|
|
41
|
+
import { getLockPolicyForLane, getWipLimitForLane } from '@lumenflow/core/dist/lane-checker.js';
|
|
40
42
|
import { minimatch } from 'minimatch';
|
|
41
43
|
// WU-2252: Import invariants loader for spawn output injection
|
|
42
44
|
import { loadInvariants, INVARIANT_TYPES } from '@lumenflow/core/dist/invariants-runner.js';
|
|
43
45
|
import { validateSpawnArgs, generateExecutionModeSection, generateThinkToolGuidance, recordSpawnToRegistry, formatSpawnRecordedMessage, } from '@lumenflow/core/dist/wu-spawn-helpers.js';
|
|
46
|
+
// eslint-disable-next-line sonarjs/deprecation -- legacy factory used by CLI spawns
|
|
44
47
|
import { SpawnStrategyFactory } from '@lumenflow/core/dist/spawn-strategy.js';
|
|
45
48
|
import { getConfig } from '@lumenflow/core/dist/lumenflow-config.js';
|
|
46
49
|
import { generateClientSkillsGuidance, generateSkillsSelectionSection, resolveClientConfig, } from '@lumenflow/core/dist/wu-spawn-skills.js';
|
|
@@ -1146,11 +1149,17 @@ ${SPAWN_END_SENTINEL}
|
|
|
1146
1149
|
}
|
|
1147
1150
|
/**
|
|
1148
1151
|
* WU-1603: Check if a lane is currently occupied by another WU
|
|
1152
|
+
* WU-1325: Now considers lock_policy - lanes with policy=none are never occupied
|
|
1149
1153
|
*
|
|
1150
1154
|
* @param {string} lane - Lane name (e.g., "Operations: Tooling")
|
|
1151
1155
|
* @returns {import('@lumenflow/core/dist/lane-lock.js').LockMetadata|null} Lock metadata if occupied, null otherwise
|
|
1152
1156
|
*/
|
|
1153
1157
|
export function checkLaneOccupation(lane) {
|
|
1158
|
+
// WU-1325: Lanes with lock_policy=none never report as occupied
|
|
1159
|
+
const lockPolicy = getLockPolicyForLane(lane);
|
|
1160
|
+
if (lockPolicy === 'none') {
|
|
1161
|
+
return null;
|
|
1162
|
+
}
|
|
1154
1163
|
const lockStatus = checkLaneLock(lane);
|
|
1155
1164
|
if (lockStatus.locked && lockStatus.metadata) {
|
|
1156
1165
|
return lockStatus.metadata;
|
|
@@ -1160,7 +1169,10 @@ export function checkLaneOccupation(lane) {
|
|
|
1160
1169
|
export function generateLaneOccupationWarning(lockMetadata, targetWuId, options = {}) {
|
|
1161
1170
|
const { isStale = false } = options;
|
|
1162
1171
|
let warning = `⚠️ Lane "${lockMetadata.lane}" is occupied by ${lockMetadata.wuId}\n`;
|
|
1163
|
-
|
|
1172
|
+
// WU-1346: Use injected values if provided, otherwise look up from config
|
|
1173
|
+
const lockPolicy = options.lockPolicy ?? getLockPolicyForLane(lockMetadata.lane);
|
|
1174
|
+
const wipLimit = options.wipLimit ?? getWipLimitForLane(lockMetadata.lane);
|
|
1175
|
+
warning += ` This violates WIP=${wipLimit} (lock_policy=${lockPolicy}).\n\n`;
|
|
1164
1176
|
if (isStale) {
|
|
1165
1177
|
warning += ` ⏰ This lock is STALE (>24 hours old) - the WU may be abandoned.\n`;
|
|
1166
1178
|
warning += ` Consider using pnpm wu:block --id ${lockMetadata.wuId} if work is stalled.\n\n`;
|
|
@@ -1319,6 +1331,7 @@ async function main() {
|
|
|
1319
1331
|
}
|
|
1320
1332
|
}
|
|
1321
1333
|
// Create strategy
|
|
1334
|
+
// eslint-disable-next-line sonarjs/deprecation -- legacy factory used by CLI spawns
|
|
1322
1335
|
const strategy = SpawnStrategyFactory.create(clientName);
|
|
1323
1336
|
const clientContext = { name: clientName, config: resolveClientConfig(config, clientName) };
|
|
1324
1337
|
if (clientName === 'codex-cli' || args.codex) {
|
package/dist/wu-unblock.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
/* eslint-disable no-console -- CLI tool requires console output */
|
|
2
3
|
/**
|
|
3
4
|
* WU Unblock Helper
|
|
4
5
|
*
|
|
@@ -16,12 +17,15 @@
|
|
|
16
17
|
import { existsSync, writeFileSync } from 'node:fs';
|
|
17
18
|
import path from 'node:path';
|
|
18
19
|
import { assertTransition } from '@lumenflow/core/dist/state-machine.js';
|
|
19
|
-
import { checkLaneFree } from '@lumenflow/core/dist/lane-checker.js';
|
|
20
|
+
import { checkLaneFree, getLockPolicyForLane } from '@lumenflow/core/dist/lane-checker.js';
|
|
21
|
+
// WU-1325: Import lane lock functions for policy-based lock acquisition on unblock
|
|
22
|
+
import { acquireLaneLock } from '@lumenflow/core/dist/lane-lock.js';
|
|
20
23
|
import { getGitForCwd } from '@lumenflow/core/dist/git-adapter.js';
|
|
21
24
|
import { die } from '@lumenflow/core/dist/error-handler.js';
|
|
22
25
|
import { todayISO } from '@lumenflow/core/dist/date-utils.js';
|
|
23
26
|
import { createWUParser, WU_OPTIONS } from '@lumenflow/core/dist/arg-parser.js';
|
|
24
27
|
import { WU_PATHS, defaultWorktreeFrom } from '@lumenflow/core/dist/wu-paths.js';
|
|
28
|
+
import { getConfig } from '@lumenflow/core/dist/lumenflow-config.js';
|
|
25
29
|
import { readWU, writeWU, appendNote } from '@lumenflow/core/dist/wu-yaml.js';
|
|
26
30
|
import { STATUS_SECTIONS, PATTERNS, LOG_PREFIX, WU_STATUS, REMOTES, BRANCHES, GIT_REFS, FILE_SYSTEM, EXIT_CODES, MICRO_WORKTREE_OPERATIONS, } from '@lumenflow/core/dist/wu-constants.js';
|
|
27
31
|
import { defaultBranchFrom } from '@lumenflow/core/dist/wu-done-validators.js';
|
|
@@ -88,7 +92,8 @@ function handleLaneOccupancy(laneCheck, lane, id, force) {
|
|
|
88
92
|
` 1. Wait for ${laneCheck.occupiedBy} to complete or block\n` +
|
|
89
93
|
` 2. Move ${id} to a different lane\n` +
|
|
90
94
|
` 3. Use --force to override (P0 emergencies only)\n\n` +
|
|
91
|
-
|
|
95
|
+
// WU-1311: Use config-based status path
|
|
96
|
+
`To check lane status: grep "${STATUS_SECTIONS.IN_PROGRESS}" ${getConfig().directories.statusPath}`);
|
|
92
97
|
}
|
|
93
98
|
/**
|
|
94
99
|
* Handle optional worktree creation after unblock
|
|
@@ -221,6 +226,33 @@ async function main() {
|
|
|
221
226
|
getGitForCwd().run(`git push ${REMOTES.ORIGIN} ${BRANCHES.MAIN}`);
|
|
222
227
|
}
|
|
223
228
|
handleWorktreeCreation(args, doc);
|
|
229
|
+
// WU-1325: Re-acquire lane lock when WU is unblocked (only for lock_policy=active)
|
|
230
|
+
// For policy=all, lock was retained through the block cycle
|
|
231
|
+
// For policy=none, no lock exists to acquire
|
|
232
|
+
try {
|
|
233
|
+
if (lane && lane !== 'Unknown') {
|
|
234
|
+
const lockPolicy = getLockPolicyForLane(lane);
|
|
235
|
+
if (lockPolicy === 'active') {
|
|
236
|
+
const lockResult = acquireLaneLock(lane, id);
|
|
237
|
+
if (lockResult.acquired && !lockResult.skipped) {
|
|
238
|
+
console.log(`${PREFIX} Lane lock re-acquired for "${lane}" (lock_policy=active)`);
|
|
239
|
+
}
|
|
240
|
+
else if (!lockResult.acquired) {
|
|
241
|
+
// Lock acquisition failed - another WU claimed the lane while we were blocked
|
|
242
|
+
console.warn(`${PREFIX} Warning: Could not re-acquire lane lock: ${lockResult.error}`);
|
|
243
|
+
console.warn(`${PREFIX} Another WU may have claimed lane "${lane}" while this WU was blocked.`);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
else if (lockPolicy === 'all') {
|
|
247
|
+
console.log(`${PREFIX} Lane lock retained for "${lane}" (lock_policy=all)`);
|
|
248
|
+
}
|
|
249
|
+
// For policy=none, no lock exists - nothing to do
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
catch (err) {
|
|
253
|
+
// Non-blocking: lock acquisition failure should not block the unblocking operation
|
|
254
|
+
console.warn(`${PREFIX} Warning: Could not acquire lane lock: ${err.message}`);
|
|
255
|
+
}
|
|
224
256
|
console.log(`\n${PREFIX} Marked in progress and pushed.`);
|
|
225
257
|
console.log(`- WU: ${id} — ${title}`);
|
|
226
258
|
if (args.reason)
|
package/dist/wu-validate.js
CHANGED
|
@@ -3,13 +3,15 @@
|
|
|
3
3
|
* WU Validation Tool
|
|
4
4
|
*
|
|
5
5
|
* Validates WU YAML files against schema and checks for quality warnings.
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
*
|
|
7
|
+
* WU-1329: Strict mode is now the DEFAULT behavior.
|
|
8
|
+
* - Warnings are treated as errors by default
|
|
9
|
+
* - Use --no-strict to restore legacy advisory-only warnings behavior
|
|
8
10
|
*
|
|
9
11
|
* Usage:
|
|
10
|
-
* pnpm wu:validate --id WU-123 # Validate
|
|
11
|
-
* pnpm wu:validate --all # Validate all WUs
|
|
12
|
-
* pnpm wu:validate --all --strict
|
|
12
|
+
* pnpm wu:validate --id WU-123 # Validate with strict mode (default)
|
|
13
|
+
* pnpm wu:validate --all # Validate all WUs with strict mode
|
|
14
|
+
* pnpm wu:validate --all --no-strict # Warnings are advisory (legacy behavior)
|
|
13
15
|
*
|
|
14
16
|
* @see {@link packages/@lumenflow/cli/src/lib/wu-schema.ts} - Schema definitions
|
|
15
17
|
*/
|
|
@@ -27,12 +29,14 @@ const LOG_PREFIX = '[wu:validate]';
|
|
|
27
29
|
/**
|
|
28
30
|
* Validate a single WU file
|
|
29
31
|
*
|
|
32
|
+
* WU-1329: strict defaults to true (warnings treated as errors)
|
|
33
|
+
*
|
|
30
34
|
* @param {string} wuPath - Path to WU YAML file
|
|
31
35
|
* @param {object} options - Validation options
|
|
32
|
-
* @param {boolean} options.strict - Treat warnings as errors
|
|
36
|
+
* @param {boolean} options.strict - Treat warnings as errors (default: true)
|
|
33
37
|
* @returns {{valid: boolean, warnings: string[], errors: string[]}}
|
|
34
38
|
*/
|
|
35
|
-
function validateSingleWU(wuPath, { strict =
|
|
39
|
+
function validateSingleWU(wuPath, { strict = true } = {}) {
|
|
36
40
|
const errors = [];
|
|
37
41
|
const warnings = [];
|
|
38
42
|
// Read and parse YAML
|
|
@@ -74,11 +78,13 @@ function validateSingleWU(wuPath, { strict = false } = {}) {
|
|
|
74
78
|
/**
|
|
75
79
|
* Validate all WU files
|
|
76
80
|
*
|
|
81
|
+
* WU-1329: strict defaults to true (warnings treated as errors)
|
|
82
|
+
*
|
|
77
83
|
* @param {object} options - Validation options
|
|
78
|
-
* @param {boolean} options.strict - Treat warnings as errors
|
|
84
|
+
* @param {boolean} options.strict - Treat warnings as errors (default: true)
|
|
79
85
|
* @returns {{totalValid: number, totalInvalid: number, totalWarnings: number, results: object[]}}
|
|
80
86
|
*/
|
|
81
|
-
function validateAllWUs({ strict =
|
|
87
|
+
function validateAllWUs({ strict = true } = {}) {
|
|
82
88
|
const wuDir = WU_PATHS.WU_DIR();
|
|
83
89
|
if (!existsSync(wuDir)) {
|
|
84
90
|
die(`WU directory not found: ${wuDir}`);
|
|
@@ -110,7 +116,7 @@ function validateAllWUs({ strict = false } = {}) {
|
|
|
110
116
|
async function main() {
|
|
111
117
|
const args = createWUParser({
|
|
112
118
|
name: 'wu-validate',
|
|
113
|
-
description: 'Validate WU YAML files against schema',
|
|
119
|
+
description: 'Validate WU YAML files against schema (strict mode by default, WU-1329)',
|
|
114
120
|
options: [
|
|
115
121
|
WU_OPTIONS.id,
|
|
116
122
|
{
|
|
@@ -119,17 +125,19 @@ async function main() {
|
|
|
119
125
|
type: 'boolean',
|
|
120
126
|
description: 'Validate all WUs',
|
|
121
127
|
},
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
flags: '-s, --strict',
|
|
125
|
-
type: 'boolean',
|
|
126
|
-
description: 'Treat warnings as errors',
|
|
127
|
-
},
|
|
128
|
+
// WU-1329: Change from --strict to --no-strict (strict is now default)
|
|
129
|
+
WU_OPTIONS.noStrict,
|
|
128
130
|
],
|
|
129
131
|
required: [],
|
|
130
132
|
allowPositionalId: true,
|
|
131
133
|
});
|
|
132
|
-
const { id, all,
|
|
134
|
+
const { id, all, noStrict } = args;
|
|
135
|
+
// WU-1329: Strict mode is the default, --no-strict opts out
|
|
136
|
+
const strict = !noStrict;
|
|
137
|
+
// WU-1329: Log when strict validation is bypassed
|
|
138
|
+
if (noStrict) {
|
|
139
|
+
console.warn(`${LOG_PREFIX} WARNING: strict validation bypassed (--no-strict). Warnings will be advisory only.`);
|
|
140
|
+
}
|
|
133
141
|
if (!id && !all) {
|
|
134
142
|
die('Must specify --id WU-XXX or --all');
|
|
135
143
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lumenflow/cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.1",
|
|
4
4
|
"description": "Command-line interface for LumenFlow workflow framework",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"lumenflow",
|
|
@@ -74,7 +74,11 @@
|
|
|
74
74
|
"initiative-status": "./dist/initiative-status.js",
|
|
75
75
|
"initiative-add-wu": "./dist/initiative-add-wu.js",
|
|
76
76
|
"initiative-plan": "./dist/initiative-plan.js",
|
|
77
|
-
"init-plan": "./dist/
|
|
77
|
+
"init-plan": "./dist/initiative-plan.js",
|
|
78
|
+
"plan-create": "./dist/plan-create.js",
|
|
79
|
+
"plan-link": "./dist/plan-link.js",
|
|
80
|
+
"plan-edit": "./dist/plan-edit.js",
|
|
81
|
+
"plan-promote": "./dist/plan-promote.js",
|
|
78
82
|
"agent-session": "./dist/agent-session.js",
|
|
79
83
|
"agent-session-end": "./dist/agent-session-end.js",
|
|
80
84
|
"agent-log-issue": "./dist/agent-log-issue.js",
|
|
@@ -144,11 +148,11 @@
|
|
|
144
148
|
"pretty-ms": "^9.2.0",
|
|
145
149
|
"simple-git": "^3.30.0",
|
|
146
150
|
"yaml": "^2.8.2",
|
|
147
|
-
"@lumenflow/
|
|
148
|
-
"@lumenflow/
|
|
149
|
-
"@lumenflow/
|
|
150
|
-
"@lumenflow/
|
|
151
|
-
"@lumenflow/
|
|
151
|
+
"@lumenflow/metrics": "2.5.1",
|
|
152
|
+
"@lumenflow/core": "2.5.1",
|
|
153
|
+
"@lumenflow/initiatives": "2.5.1",
|
|
154
|
+
"@lumenflow/agent": "2.5.1",
|
|
155
|
+
"@lumenflow/memory": "2.5.1"
|
|
152
156
|
},
|
|
153
157
|
"devDependencies": {
|
|
154
158
|
"@vitest/coverage-v8": "^4.0.17",
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# LumenFlow Constraints Capsule
|
|
2
2
|
|
|
3
|
-
**Version:** 1.
|
|
3
|
+
**Version:** 1.1
|
|
4
4
|
**Last updated:** {{DATE}}
|
|
5
5
|
|
|
6
|
-
This document contains the
|
|
6
|
+
This document contains the 7 non-negotiable constraints that every agent must keep "in working memory" from first plan through `wu:done`.
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
-
## The
|
|
10
|
+
## The 7 Non-Negotiable Constraints
|
|
11
11
|
|
|
12
12
|
### 1. Worktree Discipline and Git Safety
|
|
13
13
|
|
|
@@ -19,8 +19,40 @@ This document contains the 6 non-negotiable constraints that every agent must ke
|
|
|
19
19
|
- Hooks block WU commits from main checkout
|
|
20
20
|
- Forbidden commands on main: `git reset --hard`, `git stash`, `git clean -fd`, `git push --force`
|
|
21
21
|
|
|
22
|
+
**MANDATORY PRE-WRITE CHECK**
|
|
23
|
+
|
|
24
|
+
Before ANY Write/Edit/Read operation:
|
|
25
|
+
|
|
26
|
+
1. **Verify worktree location**:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pwd
|
|
30
|
+
# Must show: .../worktrees/<lane>-wu-xxx
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
2. **Verify relative paths only**:
|
|
34
|
+
- ✅ `docs/...`, `packages/...`, `apps/...`, `./...`
|
|
35
|
+
- ❌ `/home/...`, `/Users/...`, or full repo paths
|
|
36
|
+
|
|
37
|
+
3. **Docs-only exception (documentation WUs)**:
|
|
38
|
+
- You may run **read-only** commands from main (e.g., `pnpm gates --docs-only`).
|
|
39
|
+
- **All file writes still require a worktree**. If no worktree exists, claim one first.
|
|
40
|
+
|
|
22
41
|
**Why:** Worktree isolation prevents cross-contamination between parallel WUs and protects the main branch.
|
|
23
42
|
|
|
43
|
+
**NEVER "QUICK FIX" ON MAIN**
|
|
44
|
+
|
|
45
|
+
If you see something broken on main (failing gates, format issues, typos, lint errors):
|
|
46
|
+
|
|
47
|
+
- ❌ **Don't** fix it directly, even if it seems small or helpful
|
|
48
|
+
- ❌ **Don't** run `prettier --write`, `pnpm lint --fix`, or any command that modifies files
|
|
49
|
+
- ✅ **Do** report the issue to the user
|
|
50
|
+
- ✅ **Do** create a WU if a fix is needed
|
|
51
|
+
|
|
52
|
+
**Why this matters:** The "helpful fix" instinct causes agents to modify files on main without a worktree. While commits are blocked by hooks, the files are still modified, requiring manual cleanup. Every change—no matter how small—needs a worktree.
|
|
53
|
+
|
|
54
|
+
**If you're on main and want to change something: STOP. Create a WU first.**
|
|
55
|
+
|
|
24
56
|
---
|
|
25
57
|
|
|
26
58
|
### 2. WUs Are Specs, Not Code
|
|
@@ -94,6 +126,32 @@ This document contains the 6 non-negotiable constraints that every agent must ke
|
|
|
94
126
|
|
|
95
127
|
---
|
|
96
128
|
|
|
129
|
+
### 7. Test Ratchet Pattern (WU-1253)
|
|
130
|
+
|
|
131
|
+
**Rule:** NEW test failures block gates; pre-existing failures (in baseline) warn but do not block.
|
|
132
|
+
|
|
133
|
+
**Baseline File:** `.lumenflow/test-baseline.json`
|
|
134
|
+
|
|
135
|
+
**Enforcement:**
|
|
136
|
+
|
|
137
|
+
- Gates compare current test results against baseline
|
|
138
|
+
- NEW failures (not in baseline) = **BLOCK** (must fix or add to baseline)
|
|
139
|
+
- Pre-existing failures (in baseline) = **WARNING** (continue, do not block WU)
|
|
140
|
+
- Fixed tests auto-removed from baseline (ratchet forward)
|
|
141
|
+
|
|
142
|
+
**When gates fail due to tests:**
|
|
143
|
+
|
|
144
|
+
1. Check baseline: `cat .lumenflow/test-baseline.json`
|
|
145
|
+
2. If failure is pre-existing: warning shown, WU proceeds
|
|
146
|
+
3. If failure is NEW: fix the test or add to baseline with:
|
|
147
|
+
```bash
|
|
148
|
+
pnpm baseline:add --test "<test_name>" --reason "<why>" --fix-wu WU-XXXX
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**Why:** Agents should not be blocked by unrelated failures. The ratchet ensures quality improves over time (failures can only be removed, never added without justification).
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
97
155
|
## Mini Audit Checklist
|
|
98
156
|
|
|
99
157
|
Before running `wu:done`, verify:
|
|
@@ -17,11 +17,11 @@ cd worktrees/<lane>-wu-xxxx
|
|
|
17
17
|
pnpm gates
|
|
18
18
|
|
|
19
19
|
# 3. Complete (ALWAYS run this!)
|
|
20
|
-
cd
|
|
20
|
+
cd <project-root>
|
|
21
21
|
pnpm wu:done --id WU-XXXX
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
> **Complete CLI reference:** See [quick-ref-commands.md](
|
|
24
|
+
> **Complete CLI reference:** See [quick-ref-commands.md]({{QUICK_REF_LINK}})
|
|
25
25
|
|
|
26
26
|
---
|
|
27
27
|
|