@lumenflow/cli 2.20.1 → 2.21.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 +8 -4
- package/dist/hooks/enforcement-checks.js +120 -0
- package/dist/hooks/enforcement-checks.js.map +1 -1
- package/dist/init-lane-validation.js +141 -0
- package/dist/init-lane-validation.js.map +1 -0
- package/dist/init-templates.js +36 -8
- package/dist/init-templates.js.map +1 -1
- package/dist/init.js +27 -58
- package/dist/init.js.map +1 -1
- package/dist/initiative-create.js +35 -4
- package/dist/initiative-create.js.map +1 -1
- package/dist/lane-lifecycle-process.js +364 -0
- package/dist/lane-lifecycle-process.js.map +1 -0
- package/dist/lane-lock.js +41 -0
- package/dist/lane-lock.js.map +1 -0
- package/dist/lane-setup.js +55 -0
- package/dist/lane-setup.js.map +1 -0
- package/dist/lane-status.js +38 -0
- package/dist/lane-status.js.map +1 -0
- package/dist/lane-validate.js +43 -0
- package/dist/lane-validate.js.map +1 -0
- package/dist/onboarding-smoke-test.js +17 -0
- package/dist/onboarding-smoke-test.js.map +1 -1
- package/dist/public-manifest.js +28 -0
- package/dist/public-manifest.js.map +1 -1
- package/dist/wu-claim-cloud.js +16 -0
- package/dist/wu-claim-cloud.js.map +1 -1
- package/dist/wu-claim.js +12 -2
- package/dist/wu-claim.js.map +1 -1
- package/dist/wu-create-content.js +8 -2
- package/dist/wu-create-content.js.map +1 -1
- package/dist/wu-create-validation.js +5 -3
- package/dist/wu-create-validation.js.map +1 -1
- package/dist/wu-create.js +21 -1
- package/dist/wu-create.js.map +1 -1
- package/dist/wu-done.js +57 -8
- package/dist/wu-done.js.map +1 -1
- package/dist/wu-prep.js +22 -0
- package/dist/wu-prep.js.map +1 -1
- package/package.json +15 -11
- package/dist/__tests__/agent-log-issue.test.js +0 -56
- package/dist/__tests__/agent-spawn-coordination.test.js +0 -451
- package/dist/__tests__/backlog-prune.test.js +0 -478
- package/dist/__tests__/cli-entry-point.test.js +0 -160
- package/dist/__tests__/cli-subprocess.test.js +0 -89
- package/dist/__tests__/commands/integrate.test.js +0 -165
- package/dist/__tests__/commands.test.js +0 -271
- package/dist/__tests__/deps-operations.test.js +0 -206
- package/dist/__tests__/doctor.test.js +0 -510
- package/dist/__tests__/file-operations.test.js +0 -906
- package/dist/__tests__/flow-report.test.js +0 -24
- package/dist/__tests__/gates-config.test.js +0 -303
- package/dist/__tests__/gates-integration-tests.test.js +0 -112
- package/dist/__tests__/git-operations.test.js +0 -668
- package/dist/__tests__/guard-main-branch.test.js +0 -79
- package/dist/__tests__/guards-validation.test.js +0 -416
- package/dist/__tests__/hooks/enforcement.test.js +0 -279
- package/dist/__tests__/init-config-lanes.test.js +0 -131
- package/dist/__tests__/init-docs-structure.test.js +0 -152
- package/dist/__tests__/init-greenfield.test.js +0 -247
- package/dist/__tests__/init-lane-inference.test.js +0 -125
- package/dist/__tests__/init-onboarding-docs.test.js +0 -132
- package/dist/__tests__/init-quick-ref.test.js +0 -144
- package/dist/__tests__/init-scripts.test.js +0 -207
- package/dist/__tests__/init-template-portability.test.js +0 -96
- package/dist/__tests__/init.test.js +0 -968
- package/dist/__tests__/initiative-add-wu.test.js +0 -490
- package/dist/__tests__/initiative-e2e.test.js +0 -442
- package/dist/__tests__/initiative-plan-replacement.test.js +0 -161
- package/dist/__tests__/initiative-plan.test.js +0 -340
- package/dist/__tests__/initiative-remove-wu.test.js +0 -458
- package/dist/__tests__/lumenflow-upgrade.test.js +0 -260
- package/dist/__tests__/mem-cleanup-execution.test.js +0 -19
- package/dist/__tests__/memory-integration.test.js +0 -333
- package/dist/__tests__/merge-block.test.js +0 -220
- package/dist/__tests__/metrics-cli.test.js +0 -619
- package/dist/__tests__/metrics-snapshot.test.js +0 -24
- package/dist/__tests__/no-beacon-references-docs.test.js +0 -30
- package/dist/__tests__/no-beacon-references.test.js +0 -39
- package/dist/__tests__/onboarding-smoke-test.test.js +0 -211
- package/dist/__tests__/path-centralization-cli.test.js +0 -234
- package/dist/__tests__/plan-create.test.js +0 -126
- package/dist/__tests__/plan-edit.test.js +0 -157
- package/dist/__tests__/plan-link.test.js +0 -239
- package/dist/__tests__/plan-promote.test.js +0 -181
- package/dist/__tests__/release.test.js +0 -372
- package/dist/__tests__/rotate-progress.test.js +0 -127
- package/dist/__tests__/safe-git.test.js +0 -190
- package/dist/__tests__/session-coordinator.test.js +0 -109
- package/dist/__tests__/state-bootstrap.test.js +0 -432
- package/dist/__tests__/state-doctor.test.js +0 -328
- package/dist/__tests__/sync-templates.test.js +0 -255
- package/dist/__tests__/templates-sync.test.js +0 -219
- package/dist/__tests__/trace-gen.test.js +0 -115
- package/dist/__tests__/wu-create-required-fields.test.js +0 -143
- package/dist/__tests__/wu-create-strict.test.js +0 -118
- package/dist/__tests__/wu-create.test.js +0 -121
- package/dist/__tests__/wu-done-auto-cleanup.test.js +0 -135
- package/dist/__tests__/wu-done-docs-only-policy.test.js +0 -20
- package/dist/__tests__/wu-done-staging-whitelist.test.js +0 -35
- package/dist/__tests__/wu-done.test.js +0 -36
- package/dist/__tests__/wu-edit-strict.test.js +0 -109
- package/dist/__tests__/wu-edit.test.js +0 -119
- package/dist/__tests__/wu-lifecycle-integration.test.js +0 -388
- package/dist/__tests__/wu-prep-default-exec.test.js +0 -35
- package/dist/__tests__/wu-prep.test.js +0 -140
- package/dist/__tests__/wu-proto.test.js +0 -97
- package/dist/__tests__/wu-validate-strict.test.js +0 -113
- package/dist/__tests__/wu-validate.test.js +0 -36
- package/dist/spawn-list.js +0 -143
- package/dist/spawn-list.js.map +0 -1
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file wu-create.test.ts
|
|
3
|
-
* Tests for wu:create helpers and warnings (WU-1429)
|
|
4
|
-
*/
|
|
5
|
-
import { describe, it, expect } from 'vitest';
|
|
6
|
-
import { buildWUContent, collectInitiativeWarnings, validateCreateSpec } from '../wu-create.js';
|
|
7
|
-
const BASE_WU = {
|
|
8
|
-
id: 'WU-1429',
|
|
9
|
-
lane: 'Framework: CLI',
|
|
10
|
-
title: 'Test WU',
|
|
11
|
-
priority: 'P2',
|
|
12
|
-
type: 'feature',
|
|
13
|
-
created: '2026-02-04',
|
|
14
|
-
opts: {
|
|
15
|
-
description: 'Context: test context.\nProblem: test problem.\nSolution: test solution that exceeds minimum.',
|
|
16
|
-
acceptance: ['Acceptance criterion'],
|
|
17
|
-
exposure: 'backend-only',
|
|
18
|
-
codePaths: ['packages/@lumenflow/cli/src/wu-create.ts'],
|
|
19
|
-
testPathsUnit: ['packages/@lumenflow/cli/src/__tests__/wu-create.test.ts'],
|
|
20
|
-
specRefs: ['lumenflow://plans/WU-1429-plan.md'],
|
|
21
|
-
},
|
|
22
|
-
};
|
|
23
|
-
describe('wu:create helpers (WU-1429)', () => {
|
|
24
|
-
it('should default notes to non-empty placeholder when not provided', () => {
|
|
25
|
-
const wu = buildWUContent({
|
|
26
|
-
...BASE_WU,
|
|
27
|
-
opts: {
|
|
28
|
-
...BASE_WU.opts,
|
|
29
|
-
// Intentionally omit notes
|
|
30
|
-
notes: undefined,
|
|
31
|
-
},
|
|
32
|
-
});
|
|
33
|
-
expect(typeof wu.notes).toBe('string');
|
|
34
|
-
expect(wu.notes.trim().length).toBeGreaterThan(0);
|
|
35
|
-
expect(wu.notes).toContain('(auto)');
|
|
36
|
-
});
|
|
37
|
-
it('should persist notes when provided', () => {
|
|
38
|
-
const wu = buildWUContent({
|
|
39
|
-
...BASE_WU,
|
|
40
|
-
opts: {
|
|
41
|
-
...BASE_WU.opts,
|
|
42
|
-
notes: 'Implementation notes for test',
|
|
43
|
-
},
|
|
44
|
-
});
|
|
45
|
-
expect(wu.notes).toBe('Implementation notes for test');
|
|
46
|
-
});
|
|
47
|
-
it('should allow creating a plan-first WU without explicit test flags when code_paths are non-code', () => {
|
|
48
|
-
const validation = validateCreateSpec({
|
|
49
|
-
id: 'WU-2000',
|
|
50
|
-
lane: 'Framework: CLI',
|
|
51
|
-
title: 'Plan-only spec creation',
|
|
52
|
-
priority: 'P2',
|
|
53
|
-
type: 'feature',
|
|
54
|
-
opts: {
|
|
55
|
-
description: 'Context: test context.\nProblem: test problem.\nSolution: test solution that exceeds minimum.',
|
|
56
|
-
acceptance: ['Acceptance criterion'],
|
|
57
|
-
exposure: 'backend-only',
|
|
58
|
-
// Non-code file path: manual-only tests are acceptable.
|
|
59
|
-
codePaths: ['docs/README.md'],
|
|
60
|
-
// No testPathsManual/unit/e2e provided - should auto-default manual stub.
|
|
61
|
-
specRefs: ['lumenflow://plans/WU-2000-plan.md'],
|
|
62
|
-
strict: false,
|
|
63
|
-
},
|
|
64
|
-
});
|
|
65
|
-
expect(validation.valid).toBe(true);
|
|
66
|
-
const wu = buildWUContent({
|
|
67
|
-
id: 'WU-2000',
|
|
68
|
-
lane: 'Framework: CLI',
|
|
69
|
-
title: 'Plan-only spec creation',
|
|
70
|
-
priority: 'P2',
|
|
71
|
-
type: 'feature',
|
|
72
|
-
created: '2026-02-05',
|
|
73
|
-
opts: {
|
|
74
|
-
description: 'Context: test context.\nProblem: test problem.\nSolution: test solution that exceeds minimum.',
|
|
75
|
-
acceptance: ['Acceptance criterion'],
|
|
76
|
-
exposure: 'backend-only',
|
|
77
|
-
codePaths: ['docs/README.md'],
|
|
78
|
-
specRefs: ['lumenflow://plans/WU-2000-plan.md'],
|
|
79
|
-
},
|
|
80
|
-
});
|
|
81
|
-
expect(wu.tests?.manual?.length).toBeGreaterThan(0);
|
|
82
|
-
});
|
|
83
|
-
it('should warn when initiative has phases but no --phase is provided', () => {
|
|
84
|
-
const warnings = collectInitiativeWarnings({
|
|
85
|
-
initiativeId: 'INIT-TEST',
|
|
86
|
-
initiativeDoc: {
|
|
87
|
-
phases: [{ id: 1, title: 'Phase 1' }],
|
|
88
|
-
},
|
|
89
|
-
phase: undefined,
|
|
90
|
-
specRefs: ['lumenflow://plans/WU-1429-plan.md'],
|
|
91
|
-
});
|
|
92
|
-
expect(warnings).toEqual(expect.arrayContaining([
|
|
93
|
-
'Initiative INIT-TEST has phases defined. Consider adding --phase to link this WU to a phase.',
|
|
94
|
-
]));
|
|
95
|
-
});
|
|
96
|
-
it('should warn when initiative has related_plan but no spec_refs', () => {
|
|
97
|
-
const warnings = collectInitiativeWarnings({
|
|
98
|
-
initiativeId: 'INIT-TEST',
|
|
99
|
-
initiativeDoc: {
|
|
100
|
-
related_plan: 'lumenflow://plans/INIT-TEST-plan.md',
|
|
101
|
-
},
|
|
102
|
-
phase: '1',
|
|
103
|
-
specRefs: [],
|
|
104
|
-
});
|
|
105
|
-
expect(warnings).toEqual(expect.arrayContaining([
|
|
106
|
-
'Initiative INIT-TEST has related_plan (lumenflow://plans/INIT-TEST-plan.md). Consider adding --spec-refs to link this WU to the plan.',
|
|
107
|
-
]));
|
|
108
|
-
});
|
|
109
|
-
it('should not warn when phase and spec_refs are provided', () => {
|
|
110
|
-
const warnings = collectInitiativeWarnings({
|
|
111
|
-
initiativeId: 'INIT-TEST',
|
|
112
|
-
initiativeDoc: {
|
|
113
|
-
phases: [{ id: 1, title: 'Phase 1' }],
|
|
114
|
-
related_plan: 'lumenflow://plans/INIT-TEST-plan.md',
|
|
115
|
-
},
|
|
116
|
-
phase: '1',
|
|
117
|
-
specRefs: ['lumenflow://plans/WU-1429-plan.md'],
|
|
118
|
-
});
|
|
119
|
-
expect(warnings).toEqual([]);
|
|
120
|
-
});
|
|
121
|
-
});
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file wu-done-auto-cleanup.test.ts
|
|
3
|
-
* Test suite for wu:done auto cleanup on success (WU-1366)
|
|
4
|
-
*
|
|
5
|
-
* WU-1366: State cleanup runs automatically after wu:done success (non-fatal)
|
|
6
|
-
*
|
|
7
|
-
* Tests:
|
|
8
|
-
* - shouldRunAutoCleanup respects config.cleanup.trigger setting
|
|
9
|
-
* - runAutoCleanupAfterDone is non-fatal (logs errors but doesn't throw)
|
|
10
|
-
*/
|
|
11
|
-
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
12
|
-
/** Common mock path for lumenflow config module */
|
|
13
|
-
const CONFIG_MODULE_PATH = '@lumenflow/core/dist/lumenflow-config.js';
|
|
14
|
-
// Test the exported functions directly with minimal mocking
|
|
15
|
-
describe('wu:done auto cleanup (WU-1366)', () => {
|
|
16
|
-
let consoleLogSpy;
|
|
17
|
-
let consoleWarnSpy;
|
|
18
|
-
beforeEach(() => {
|
|
19
|
-
consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
20
|
-
consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => { });
|
|
21
|
-
});
|
|
22
|
-
afterEach(() => {
|
|
23
|
-
consoleLogSpy.mockRestore();
|
|
24
|
-
consoleWarnSpy.mockRestore();
|
|
25
|
-
vi.resetModules();
|
|
26
|
-
});
|
|
27
|
-
describe('shouldRunAutoCleanup', () => {
|
|
28
|
-
it('should return true when config.cleanup.trigger is on_done', async () => {
|
|
29
|
-
// Mock getConfig to return on_done trigger
|
|
30
|
-
vi.doMock(CONFIG_MODULE_PATH, () => ({
|
|
31
|
-
getConfig: vi.fn().mockReturnValue({
|
|
32
|
-
cleanup: { trigger: 'on_done' },
|
|
33
|
-
}),
|
|
34
|
-
}));
|
|
35
|
-
const { shouldRunAutoCleanup } = await import('../wu-done-auto-cleanup.js');
|
|
36
|
-
const result = shouldRunAutoCleanup();
|
|
37
|
-
expect(result).toBe(true);
|
|
38
|
-
});
|
|
39
|
-
it('should return false when config.cleanup.trigger is manual', async () => {
|
|
40
|
-
vi.doMock(CONFIG_MODULE_PATH, () => ({
|
|
41
|
-
getConfig: vi.fn().mockReturnValue({
|
|
42
|
-
cleanup: { trigger: 'manual' },
|
|
43
|
-
}),
|
|
44
|
-
}));
|
|
45
|
-
const { shouldRunAutoCleanup } = await import('../wu-done-auto-cleanup.js');
|
|
46
|
-
const result = shouldRunAutoCleanup();
|
|
47
|
-
expect(result).toBe(false);
|
|
48
|
-
});
|
|
49
|
-
it('should return false when config.cleanup.trigger is on_init', async () => {
|
|
50
|
-
vi.doMock(CONFIG_MODULE_PATH, () => ({
|
|
51
|
-
getConfig: vi.fn().mockReturnValue({
|
|
52
|
-
cleanup: { trigger: 'on_init' },
|
|
53
|
-
}),
|
|
54
|
-
}));
|
|
55
|
-
const { shouldRunAutoCleanup } = await import('../wu-done-auto-cleanup.js');
|
|
56
|
-
const result = shouldRunAutoCleanup();
|
|
57
|
-
expect(result).toBe(false);
|
|
58
|
-
});
|
|
59
|
-
it('should return true when cleanup config is missing (default behavior)', async () => {
|
|
60
|
-
vi.doMock(CONFIG_MODULE_PATH, () => ({
|
|
61
|
-
getConfig: vi.fn().mockReturnValue({}),
|
|
62
|
-
}));
|
|
63
|
-
const { shouldRunAutoCleanup } = await import('../wu-done-auto-cleanup.js');
|
|
64
|
-
const result = shouldRunAutoCleanup();
|
|
65
|
-
expect(result).toBe(true);
|
|
66
|
-
});
|
|
67
|
-
});
|
|
68
|
-
describe('runAutoCleanupAfterDone non-fatal behavior', () => {
|
|
69
|
-
it('should not throw when cleanup throws an error', async () => {
|
|
70
|
-
// Mock config to enable cleanup
|
|
71
|
-
vi.doMock(CONFIG_MODULE_PATH, () => ({
|
|
72
|
-
getConfig: vi.fn().mockReturnValue({
|
|
73
|
-
cleanup: { trigger: 'on_done' },
|
|
74
|
-
directories: { wuDir: 'docs/tasks/wu' },
|
|
75
|
-
}),
|
|
76
|
-
}));
|
|
77
|
-
// Mock cleanupState to throw
|
|
78
|
-
vi.doMock('@lumenflow/core/dist/state-cleanup-core.js', () => ({
|
|
79
|
-
cleanupState: vi.fn().mockRejectedValue(new Error('Cleanup failed')),
|
|
80
|
-
}));
|
|
81
|
-
// Mock the memory functions to avoid actual file operations
|
|
82
|
-
vi.doMock('@lumenflow/memory/dist/signal-cleanup-core.js', () => ({
|
|
83
|
-
cleanupSignals: vi.fn().mockResolvedValue({
|
|
84
|
-
success: true,
|
|
85
|
-
removedIds: [],
|
|
86
|
-
retainedIds: [],
|
|
87
|
-
bytesFreed: 0,
|
|
88
|
-
compactionRatio: 0,
|
|
89
|
-
breakdown: {},
|
|
90
|
-
}),
|
|
91
|
-
}));
|
|
92
|
-
vi.doMock('@lumenflow/memory/dist/mem-cleanup-core.js', () => ({
|
|
93
|
-
cleanupMemory: vi.fn().mockResolvedValue({
|
|
94
|
-
success: true,
|
|
95
|
-
removedIds: [],
|
|
96
|
-
retainedIds: [],
|
|
97
|
-
bytesFreed: 0,
|
|
98
|
-
compactionRatio: 0,
|
|
99
|
-
breakdown: {},
|
|
100
|
-
}),
|
|
101
|
-
}));
|
|
102
|
-
vi.doMock('@lumenflow/core/dist/wu-events-cleanup.js', () => ({
|
|
103
|
-
archiveWuEvents: vi.fn().mockResolvedValue({
|
|
104
|
-
success: true,
|
|
105
|
-
archivedWuIds: [],
|
|
106
|
-
retainedWuIds: [],
|
|
107
|
-
bytesArchived: 0,
|
|
108
|
-
archivedEventCount: 0,
|
|
109
|
-
retainedEventCount: 0,
|
|
110
|
-
breakdown: {},
|
|
111
|
-
}),
|
|
112
|
-
}));
|
|
113
|
-
const { runAutoCleanupAfterDone } = await import('../wu-done-auto-cleanup.js');
|
|
114
|
-
// Should not throw - cleanup errors are non-fatal
|
|
115
|
-
await expect(runAutoCleanupAfterDone('/test/dir')).resolves.not.toThrow();
|
|
116
|
-
// Should log warning about the error
|
|
117
|
-
expect(consoleWarnSpy).toHaveBeenCalled();
|
|
118
|
-
});
|
|
119
|
-
it('should skip cleanup when trigger is manual', async () => {
|
|
120
|
-
vi.doMock(CONFIG_MODULE_PATH, () => ({
|
|
121
|
-
getConfig: vi.fn().mockReturnValue({
|
|
122
|
-
cleanup: { trigger: 'manual' },
|
|
123
|
-
}),
|
|
124
|
-
}));
|
|
125
|
-
const mockCleanupState = vi.fn();
|
|
126
|
-
vi.doMock('@lumenflow/core/dist/state-cleanup-core.js', () => ({
|
|
127
|
-
cleanupState: mockCleanupState,
|
|
128
|
-
}));
|
|
129
|
-
const { runAutoCleanupAfterDone } = await import('../wu-done-auto-cleanup.js');
|
|
130
|
-
await runAutoCleanupAfterDone('/test/dir');
|
|
131
|
-
// Cleanup should not be called when trigger is manual
|
|
132
|
-
expect(mockCleanupState).not.toHaveBeenCalled();
|
|
133
|
-
});
|
|
134
|
-
});
|
|
135
|
-
});
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file wu-done-docs-only-policy.test.ts
|
|
3
|
-
* Guardrail test: docs-only eligibility checks must not use raw type/exposure string literals (WU-1446).
|
|
4
|
-
*
|
|
5
|
-
* This keeps CLI policy logic DRY and aligned with core constants/helpers.
|
|
6
|
-
*/
|
|
7
|
-
import { describe, it, expect } from 'vitest';
|
|
8
|
-
import { readFileSync } from 'node:fs';
|
|
9
|
-
import path from 'node:path';
|
|
10
|
-
import { fileURLToPath } from 'node:url';
|
|
11
|
-
describe('wu:done docs-only policy (WU-1446)', () => {
|
|
12
|
-
it('should not use raw documentation string comparisons for type/exposure checks', () => {
|
|
13
|
-
const thisDir = path.dirname(fileURLToPath(import.meta.url));
|
|
14
|
-
const filePath = path.join(thisDir, '..', 'wu-done.ts');
|
|
15
|
-
const content = readFileSync(filePath, 'utf-8');
|
|
16
|
-
// These comparisons should use core constants/helpers instead.
|
|
17
|
-
expect(content).not.toContain("exposure === 'documentation'");
|
|
18
|
-
expect(content).not.toContain("type === 'documentation'");
|
|
19
|
-
});
|
|
20
|
-
});
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file Tests for wu-done staging whitelist patterns
|
|
3
|
-
* @see WU-1072: Fix staging whitelist for auto-generated docs
|
|
4
|
-
*/
|
|
5
|
-
import { describe, it, expect } from 'vitest';
|
|
6
|
-
/**
|
|
7
|
-
* Pattern matching logic extracted from validateStagedFiles in wu-done.ts
|
|
8
|
-
* This tests the MDX whitelist pattern added in WU-1072
|
|
9
|
-
*/
|
|
10
|
-
function isWhitelistedAutoGeneratedDoc(file) {
|
|
11
|
-
return file.startsWith('apps/docs/') && file.endsWith('.mdx');
|
|
12
|
-
}
|
|
13
|
-
describe('wu-done staging whitelist', () => {
|
|
14
|
-
describe('auto-generated docs pattern (WU-1072)', () => {
|
|
15
|
-
it('should whitelist MDX files in apps/docs/', () => {
|
|
16
|
-
expect(isWhitelistedAutoGeneratedDoc('apps/docs/api/wu-create.mdx')).toBe(true);
|
|
17
|
-
expect(isWhitelistedAutoGeneratedDoc('apps/docs/commands/gates.mdx')).toBe(true);
|
|
18
|
-
expect(isWhitelistedAutoGeneratedDoc('apps/docs/nested/deep/file.mdx')).toBe(true);
|
|
19
|
-
});
|
|
20
|
-
it('should not whitelist MDX files outside apps/docs/', () => {
|
|
21
|
-
expect(isWhitelistedAutoGeneratedDoc('docs/something.mdx')).toBe(false);
|
|
22
|
-
expect(isWhitelistedAutoGeneratedDoc('packages/cli/README.mdx')).toBe(false);
|
|
23
|
-
expect(isWhitelistedAutoGeneratedDoc('src/docs/file.mdx')).toBe(false);
|
|
24
|
-
});
|
|
25
|
-
it('should not whitelist non-MDX files in apps/docs/', () => {
|
|
26
|
-
expect(isWhitelistedAutoGeneratedDoc('apps/docs/config.ts')).toBe(false);
|
|
27
|
-
expect(isWhitelistedAutoGeneratedDoc('apps/docs/package.json')).toBe(false);
|
|
28
|
-
expect(isWhitelistedAutoGeneratedDoc('apps/docs/styles.css')).toBe(false);
|
|
29
|
-
});
|
|
30
|
-
it('should not whitelist files with mdx in the path but not as extension', () => {
|
|
31
|
-
expect(isWhitelistedAutoGeneratedDoc('apps/docs/mdx-utils/helper.ts')).toBe(false);
|
|
32
|
-
expect(isWhitelistedAutoGeneratedDoc('apps/docs/mdx')).toBe(false);
|
|
33
|
-
});
|
|
34
|
-
});
|
|
35
|
-
});
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
-
import { ensureCleanWorktree } from '../wu-done-check.js';
|
|
3
|
-
import * as gitAdapter from '@lumenflow/core/git-adapter';
|
|
4
|
-
import * as errorHandler from '@lumenflow/core/error-handler';
|
|
5
|
-
// Mock dependencies
|
|
6
|
-
vi.mock('@lumenflow/core/git-adapter');
|
|
7
|
-
vi.mock('@lumenflow/core/error-handler');
|
|
8
|
-
describe('wu-done', () => {
|
|
9
|
-
describe('ensureCleanWorktree', () => {
|
|
10
|
-
let mockGit;
|
|
11
|
-
beforeEach(() => {
|
|
12
|
-
vi.resetAllMocks();
|
|
13
|
-
mockGit = {
|
|
14
|
-
getStatus: vi.fn(),
|
|
15
|
-
};
|
|
16
|
-
vi.mocked(gitAdapter.createGitForPath).mockReturnValue(mockGit);
|
|
17
|
-
});
|
|
18
|
-
it('should pass if worktree is clean', async () => {
|
|
19
|
-
mockGit.getStatus.mockResolvedValue(''); // Clean status
|
|
20
|
-
await ensureCleanWorktree('/path/to/worktree');
|
|
21
|
-
expect(mockGit.getStatus).toHaveBeenCalled();
|
|
22
|
-
expect(errorHandler.die).not.toHaveBeenCalled();
|
|
23
|
-
});
|
|
24
|
-
it('should die if worktree has uncommitted changes', async () => {
|
|
25
|
-
mockGit.getStatus.mockResolvedValue('M file.ts\n?? new-file.ts'); // Dirty status
|
|
26
|
-
await ensureCleanWorktree('/path/to/worktree');
|
|
27
|
-
expect(mockGit.getStatus).toHaveBeenCalled();
|
|
28
|
-
expect(errorHandler.die).toHaveBeenCalledWith(expect.stringContaining('Worktree has uncommitted changes'));
|
|
29
|
-
});
|
|
30
|
-
it('should use the correct worktree path', async () => {
|
|
31
|
-
mockGit.getStatus.mockResolvedValue('');
|
|
32
|
-
await ensureCleanWorktree('/custom/worktree/path');
|
|
33
|
-
expect(gitAdapter.createGitForPath).toHaveBeenCalledWith('/custom/worktree/path');
|
|
34
|
-
});
|
|
35
|
-
});
|
|
36
|
-
});
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file wu-edit-strict.test.ts
|
|
3
|
-
* Test suite for wu:edit strict validation behavior (WU-1329)
|
|
4
|
-
*
|
|
5
|
-
* WU-1329: Make wu:edit run strict validation by default
|
|
6
|
-
*
|
|
7
|
-
* Tests:
|
|
8
|
-
* - applyEdits function works correctly (unit test for edit logic)
|
|
9
|
-
* - validateDoneWUEdits properly validates done WU edits
|
|
10
|
-
* - The main() function validates paths in strict mode (covered by e2e tests)
|
|
11
|
-
*/
|
|
12
|
-
import { describe, it, expect } from 'vitest';
|
|
13
|
-
// Import test utilities (WU-1329)
|
|
14
|
-
import { applyEdits, validateDoneWUEdits, validateExposureValue } from '../wu-edit.js';
|
|
15
|
-
// WU-1329: Constants for test file paths
|
|
16
|
-
const TEST_NEW_FILE_PATH = 'new/file.ts';
|
|
17
|
-
const TEST_NEW_TEST_PATH = 'new/test.test.ts';
|
|
18
|
-
describe('wu:edit strict validation (WU-1329)', () => {
|
|
19
|
-
describe('applyEdits code_paths handling', () => {
|
|
20
|
-
const baseWU = {
|
|
21
|
-
id: 'WU-9999',
|
|
22
|
-
title: 'Test WU',
|
|
23
|
-
lane: 'Framework: CLI',
|
|
24
|
-
type: 'feature',
|
|
25
|
-
status: 'ready',
|
|
26
|
-
priority: 'P2',
|
|
27
|
-
description: 'Context: test context.\nProblem: test problem.\nSolution: test solution that exceeds minimum length.',
|
|
28
|
-
acceptance: ['Existing criterion'],
|
|
29
|
-
code_paths: ['packages/@lumenflow/cli/src/wu-edit.ts'],
|
|
30
|
-
tests: {
|
|
31
|
-
unit: ['packages/@lumenflow/cli/src/__tests__/wu-edit-strict.test.ts'],
|
|
32
|
-
manual: [],
|
|
33
|
-
e2e: [],
|
|
34
|
-
},
|
|
35
|
-
};
|
|
36
|
-
// WU-1329: applyEdits transforms WU object - path validation is done separately
|
|
37
|
-
it('should apply code_paths edits correctly', () => {
|
|
38
|
-
const result = applyEdits(baseWU, {
|
|
39
|
-
codePaths: [TEST_NEW_FILE_PATH],
|
|
40
|
-
replaceCodePaths: true,
|
|
41
|
-
});
|
|
42
|
-
// applyEdits transforms the WU, validation happens later in main()
|
|
43
|
-
expect(result.code_paths).toContain(TEST_NEW_FILE_PATH);
|
|
44
|
-
expect(result.code_paths).toHaveLength(1);
|
|
45
|
-
});
|
|
46
|
-
it('should append code_paths by default', () => {
|
|
47
|
-
const result = applyEdits(baseWU, {
|
|
48
|
-
codePaths: [TEST_NEW_FILE_PATH],
|
|
49
|
-
});
|
|
50
|
-
expect(result.code_paths).toContain('packages/@lumenflow/cli/src/wu-edit.ts');
|
|
51
|
-
expect(result.code_paths).toContain(TEST_NEW_FILE_PATH);
|
|
52
|
-
expect(result.code_paths).toHaveLength(2);
|
|
53
|
-
});
|
|
54
|
-
it('should apply test_paths edits correctly', () => {
|
|
55
|
-
const result = applyEdits(baseWU, {
|
|
56
|
-
testPathsUnit: [TEST_NEW_TEST_PATH],
|
|
57
|
-
});
|
|
58
|
-
// Should append test paths
|
|
59
|
-
expect(result.tests.unit).toContain('packages/@lumenflow/cli/src/__tests__/wu-edit-strict.test.ts');
|
|
60
|
-
expect(result.tests.unit).toContain(TEST_NEW_TEST_PATH);
|
|
61
|
-
});
|
|
62
|
-
});
|
|
63
|
-
describe('validateDoneWUEdits', () => {
|
|
64
|
-
// WU-1329: Done WUs only allow initiative/phase/exposure edits
|
|
65
|
-
it('should allow exposure edits on done WUs', () => {
|
|
66
|
-
const result = validateDoneWUEdits({ exposure: 'backend-only' });
|
|
67
|
-
expect(result.valid).toBe(true);
|
|
68
|
-
expect(result.disallowedEdits).toHaveLength(0);
|
|
69
|
-
});
|
|
70
|
-
it('should disallow code_paths edits on done WUs', () => {
|
|
71
|
-
const result = validateDoneWUEdits({ codePaths: [TEST_NEW_FILE_PATH] });
|
|
72
|
-
expect(result.valid).toBe(false);
|
|
73
|
-
expect(result.disallowedEdits).toContain('--code-paths');
|
|
74
|
-
});
|
|
75
|
-
it('should disallow description edits on done WUs', () => {
|
|
76
|
-
const result = validateDoneWUEdits({ description: 'new description' });
|
|
77
|
-
expect(result.valid).toBe(false);
|
|
78
|
-
expect(result.disallowedEdits).toContain('--description');
|
|
79
|
-
});
|
|
80
|
-
});
|
|
81
|
-
describe('validateExposureValue', () => {
|
|
82
|
-
it('should accept valid exposure values', () => {
|
|
83
|
-
expect(validateExposureValue('ui').valid).toBe(true);
|
|
84
|
-
expect(validateExposureValue('api').valid).toBe(true);
|
|
85
|
-
expect(validateExposureValue('backend-only').valid).toBe(true);
|
|
86
|
-
expect(validateExposureValue('documentation').valid).toBe(true);
|
|
87
|
-
});
|
|
88
|
-
it('should reject invalid exposure values', () => {
|
|
89
|
-
const result = validateExposureValue('invalid-exposure');
|
|
90
|
-
expect(result.valid).toBe(false);
|
|
91
|
-
expect(result.error).toContain('Invalid exposure value');
|
|
92
|
-
});
|
|
93
|
-
});
|
|
94
|
-
describe('noStrict option support', () => {
|
|
95
|
-
// WU-1329: Verify CLI option parsing pattern
|
|
96
|
-
it('should support noStrict option in CLI argument pattern', () => {
|
|
97
|
-
// CLI uses --no-strict which Commander.js parses as noStrict
|
|
98
|
-
// The main() function converts this to strict: !noStrict
|
|
99
|
-
const cliArgs = { noStrict: true };
|
|
100
|
-
const strict = !cliArgs.noStrict;
|
|
101
|
-
expect(strict).toBe(false);
|
|
102
|
-
});
|
|
103
|
-
it('should default to strict=true when noStrict is undefined', () => {
|
|
104
|
-
const cliArgs = { noStrict: undefined };
|
|
105
|
-
const strict = !cliArgs.noStrict;
|
|
106
|
-
expect(strict).toBe(true);
|
|
107
|
-
});
|
|
108
|
-
});
|
|
109
|
-
});
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* WU-1225: Tests for wu-edit append-by-default behavior
|
|
3
|
-
*
|
|
4
|
-
* Validates that array fields (code_paths, risks, acceptance, etc.)
|
|
5
|
-
* now append by default instead of replacing, making behavior consistent
|
|
6
|
-
* across all array options.
|
|
7
|
-
*/
|
|
8
|
-
import { describe, it, expect } from 'vitest';
|
|
9
|
-
import { applyEdits, mergeStringField } from '../wu-edit.js';
|
|
10
|
-
describe('wu-edit applyEdits', () => {
|
|
11
|
-
describe('WU-1225: code_paths append-by-default', () => {
|
|
12
|
-
const baseWU = {
|
|
13
|
-
id: 'WU-1225',
|
|
14
|
-
status: 'ready',
|
|
15
|
-
code_paths: ['existing/path.ts'],
|
|
16
|
-
};
|
|
17
|
-
it('appends code_paths by default (no flags)', () => {
|
|
18
|
-
const opts = { codePaths: ['new/path.ts'] };
|
|
19
|
-
const result = applyEdits(baseWU, opts);
|
|
20
|
-
expect(result.code_paths).toEqual(['existing/path.ts', 'new/path.ts']);
|
|
21
|
-
});
|
|
22
|
-
it('appends code_paths when --append is set (backwards compat)', () => {
|
|
23
|
-
const opts = { codePaths: ['new/path.ts'], append: true };
|
|
24
|
-
const result = applyEdits(baseWU, opts);
|
|
25
|
-
expect(result.code_paths).toEqual(['existing/path.ts', 'new/path.ts']);
|
|
26
|
-
});
|
|
27
|
-
it('replaces code_paths when --replace-code-paths is set', () => {
|
|
28
|
-
const opts = { codePaths: ['new/path.ts'], replaceCodePaths: true };
|
|
29
|
-
const result = applyEdits(baseWU, opts);
|
|
30
|
-
expect(result.code_paths).toEqual(['new/path.ts']);
|
|
31
|
-
});
|
|
32
|
-
});
|
|
33
|
-
describe('WU-1225: risks append-by-default', () => {
|
|
34
|
-
const baseWU = {
|
|
35
|
-
id: 'WU-1225',
|
|
36
|
-
status: 'ready',
|
|
37
|
-
risks: ['existing risk'],
|
|
38
|
-
};
|
|
39
|
-
it('appends risks by default', () => {
|
|
40
|
-
const opts = { risks: ['new risk'] };
|
|
41
|
-
const result = applyEdits(baseWU, opts);
|
|
42
|
-
expect(result.risks).toEqual(['existing risk', 'new risk']);
|
|
43
|
-
});
|
|
44
|
-
it('replaces risks when --replace-risks is set', () => {
|
|
45
|
-
const opts = { risks: ['new risk'], replaceRisks: true };
|
|
46
|
-
const result = applyEdits(baseWU, opts);
|
|
47
|
-
expect(result.risks).toEqual(['new risk']);
|
|
48
|
-
});
|
|
49
|
-
});
|
|
50
|
-
describe('WU-1225: blocked_by append-by-default', () => {
|
|
51
|
-
const baseWU = {
|
|
52
|
-
id: 'WU-1225',
|
|
53
|
-
status: 'ready',
|
|
54
|
-
blocked_by: ['WU-100'],
|
|
55
|
-
};
|
|
56
|
-
it('appends blocked_by by default', () => {
|
|
57
|
-
const opts = { blockedBy: 'WU-200' };
|
|
58
|
-
const result = applyEdits(baseWU, opts);
|
|
59
|
-
expect(result.blocked_by).toEqual(['WU-100', 'WU-200']);
|
|
60
|
-
});
|
|
61
|
-
it('replaces blocked_by when --replace-blocked-by is set', () => {
|
|
62
|
-
const opts = { blockedBy: 'WU-200', replaceBlockedBy: true };
|
|
63
|
-
const result = applyEdits(baseWU, opts);
|
|
64
|
-
expect(result.blocked_by).toEqual(['WU-200']);
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
describe('WU-1225: dependencies append-by-default', () => {
|
|
68
|
-
const baseWU = {
|
|
69
|
-
id: 'WU-1225',
|
|
70
|
-
status: 'ready',
|
|
71
|
-
dependencies: ['WU-50'],
|
|
72
|
-
};
|
|
73
|
-
it('appends dependencies by default', () => {
|
|
74
|
-
const opts = { addDep: 'WU-60' };
|
|
75
|
-
const result = applyEdits(baseWU, opts);
|
|
76
|
-
expect(result.dependencies).toEqual(['WU-50', 'WU-60']);
|
|
77
|
-
});
|
|
78
|
-
it('replaces dependencies when --replace-dependencies is set', () => {
|
|
79
|
-
const opts = { addDep: 'WU-60', replaceDependencies: true };
|
|
80
|
-
const result = applyEdits(baseWU, opts);
|
|
81
|
-
expect(result.dependencies).toEqual(['WU-60']);
|
|
82
|
-
});
|
|
83
|
-
});
|
|
84
|
-
describe('WU-1144: acceptance already appends by default', () => {
|
|
85
|
-
const baseWU = {
|
|
86
|
-
id: 'WU-1225',
|
|
87
|
-
status: 'ready',
|
|
88
|
-
acceptance: ['existing criterion'],
|
|
89
|
-
};
|
|
90
|
-
it('appends acceptance by default', () => {
|
|
91
|
-
const opts = { acceptance: ['new criterion'] };
|
|
92
|
-
const result = applyEdits(baseWU, opts);
|
|
93
|
-
expect(result.acceptance).toEqual(['existing criterion', 'new criterion']);
|
|
94
|
-
});
|
|
95
|
-
it('replaces acceptance when --replace-acceptance is set', () => {
|
|
96
|
-
const opts = { acceptance: ['new criterion'], replaceAcceptance: true };
|
|
97
|
-
const result = applyEdits(baseWU, opts);
|
|
98
|
-
expect(result.acceptance).toEqual(['new criterion']);
|
|
99
|
-
});
|
|
100
|
-
});
|
|
101
|
-
});
|
|
102
|
-
describe('wu-edit mergeStringField', () => {
|
|
103
|
-
it('appends by default', () => {
|
|
104
|
-
const result = mergeStringField('existing', 'new', false);
|
|
105
|
-
expect(result).toBe('existing\n\nnew');
|
|
106
|
-
});
|
|
107
|
-
it('replaces when shouldReplace is true', () => {
|
|
108
|
-
const result = mergeStringField('existing', 'new', true);
|
|
109
|
-
expect(result).toBe('new');
|
|
110
|
-
});
|
|
111
|
-
it('returns new value if existing is empty', () => {
|
|
112
|
-
const result = mergeStringField('', 'new', false);
|
|
113
|
-
expect(result).toBe('new');
|
|
114
|
-
});
|
|
115
|
-
it('returns new value if existing is undefined', () => {
|
|
116
|
-
const result = mergeStringField(undefined, 'new', false);
|
|
117
|
-
expect(result).toBe('new');
|
|
118
|
-
});
|
|
119
|
-
});
|