@lumenflow/cli 2.3.2 → 2.5.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/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 +96 -0
- package/dist/__tests__/init-template-portability.test.js +97 -0
- package/dist/__tests__/init.test.js +199 -3
- 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__/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/flow-report.js +3 -2
- package/dist/gates.js +202 -2
- package/dist/init.js +720 -40
- package/dist/initiative-add-wu.js +112 -16
- package/dist/initiative-plan.js +3 -2
- package/dist/initiative-remove-wu.js +248 -0
- package/dist/mem-context.js +0 -0
- package/dist/metrics-snapshot.js +3 -2
- package/dist/onboarding-smoke-test.js +400 -0
- 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/rotate-progress.js +8 -5
- package/dist/spawn-list.js +4 -3
- package/dist/state-bootstrap.js +6 -4
- package/dist/state-doctor-fix.js +5 -4
- package/dist/state-doctor.js +32 -2
- package/dist/trace-gen.js +6 -3
- 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-infer-lane.js +3 -1
- package/dist/wu-spawn.js +8 -0
- package/dist/wu-unblock.js +34 -2
- package/dist/wu-validate.js +25 -17
- package/package.json +12 -6
- package/templates/core/AGENTS.md.template +2 -2
- 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
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file wu-validate-strict.test.ts
|
|
3
|
+
* Test suite for wu:validate strict validation behavior (WU-1329)
|
|
4
|
+
*
|
|
5
|
+
* WU-1329: Make wu:validate treat warnings as errors by default
|
|
6
|
+
*
|
|
7
|
+
* Tests:
|
|
8
|
+
* - Default strict mode behavior (warnings treated as errors)
|
|
9
|
+
* - --no-strict flag restores original behavior (warnings advisory)
|
|
10
|
+
* - Help text documents strict default
|
|
11
|
+
*/
|
|
12
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
13
|
+
import { WU_OPTIONS, NEGATED_OPTIONS } from '@lumenflow/core/dist/arg-parser.js';
|
|
14
|
+
describe('wu:validate strict validation (WU-1329)', () => {
|
|
15
|
+
describe('WU_OPTIONS.noStrict configuration', () => {
|
|
16
|
+
// WU-1329: Verify the noStrict option is properly configured
|
|
17
|
+
it('should have noStrict option defined in WU_OPTIONS', () => {
|
|
18
|
+
expect(WU_OPTIONS.noStrict).toBeDefined();
|
|
19
|
+
expect(WU_OPTIONS.noStrict.name).toBe('noStrict');
|
|
20
|
+
expect(WU_OPTIONS.noStrict.flags).toBe('--no-strict');
|
|
21
|
+
expect(WU_OPTIONS.noStrict.isNegated).toBe(true);
|
|
22
|
+
});
|
|
23
|
+
it('should include description about bypassing strict validation', () => {
|
|
24
|
+
expect(WU_OPTIONS.noStrict.description).toContain('Bypass strict validation');
|
|
25
|
+
});
|
|
26
|
+
// WU-1329: Verify 'strict' is in NEGATED_OPTIONS array
|
|
27
|
+
it('should include strict in NEGATED_OPTIONS array', () => {
|
|
28
|
+
expect(NEGATED_OPTIONS).toContain('strict');
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
describe('strict mode logic', () => {
|
|
32
|
+
// WU-1329: Verify the strict mode conversion pattern
|
|
33
|
+
it('should default to strict=true when noStrict is undefined', () => {
|
|
34
|
+
const args = { noStrict: undefined };
|
|
35
|
+
const strict = !args.noStrict;
|
|
36
|
+
expect(strict).toBe(true);
|
|
37
|
+
});
|
|
38
|
+
it('should set strict=false when noStrict is true (--no-strict flag)', () => {
|
|
39
|
+
const args = { noStrict: true };
|
|
40
|
+
const strict = !args.noStrict;
|
|
41
|
+
expect(strict).toBe(false);
|
|
42
|
+
});
|
|
43
|
+
it('should set strict=true when noStrict is false (explicit)', () => {
|
|
44
|
+
const args = { noStrict: false };
|
|
45
|
+
const strict = !args.noStrict;
|
|
46
|
+
expect(strict).toBe(true);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
describe('--no-strict logging behavior', () => {
|
|
50
|
+
let consoleSpy;
|
|
51
|
+
beforeEach(() => {
|
|
52
|
+
consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => { });
|
|
53
|
+
});
|
|
54
|
+
afterEach(() => {
|
|
55
|
+
consoleSpy.mockRestore();
|
|
56
|
+
});
|
|
57
|
+
// WU-1329: The logging behavior is implemented in main() functions
|
|
58
|
+
// This test documents the expected behavior pattern using the spy
|
|
59
|
+
it('should log when --no-strict bypass is used', () => {
|
|
60
|
+
// Simulate the logging pattern from wu-validate.ts main()
|
|
61
|
+
const noStrict = true;
|
|
62
|
+
const LOG_PREFIX = '[wu:validate]';
|
|
63
|
+
const message = `${LOG_PREFIX} WARNING: strict validation bypassed (--no-strict). Warnings will be advisory only.`;
|
|
64
|
+
if (noStrict) {
|
|
65
|
+
// Use the spy to simulate logging
|
|
66
|
+
consoleSpy(message);
|
|
67
|
+
}
|
|
68
|
+
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('strict validation bypassed'));
|
|
69
|
+
});
|
|
70
|
+
it('should not log when strict mode is active', () => {
|
|
71
|
+
// When noStrict is false, no logging should occur
|
|
72
|
+
const noStrict = false;
|
|
73
|
+
if (noStrict) {
|
|
74
|
+
consoleSpy('This should not be called');
|
|
75
|
+
}
|
|
76
|
+
expect(consoleSpy).not.toHaveBeenCalled();
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
describe('validateSingleWU strict mode behavior', () => {
|
|
80
|
+
// WU-1329: These tests document the expected behavior
|
|
81
|
+
// The actual validation is done by calling the CLI command
|
|
82
|
+
it('should treat warnings as errors by default (strict=true)', () => {
|
|
83
|
+
// In strict mode, any warnings from completeness validation
|
|
84
|
+
// should become errors and cause validation to fail
|
|
85
|
+
const strict = true;
|
|
86
|
+
const warnings = ['Missing recommended field: notes'];
|
|
87
|
+
const errors = [];
|
|
88
|
+
if (strict && warnings.length > 0) {
|
|
89
|
+
errors.push(...warnings.map((w) => `[STRICT] ${w}`));
|
|
90
|
+
}
|
|
91
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
92
|
+
expect(errors[0]).toContain('[STRICT]');
|
|
93
|
+
});
|
|
94
|
+
it('should allow warnings when strict=false (--no-strict)', () => {
|
|
95
|
+
// In non-strict mode, warnings should remain warnings
|
|
96
|
+
// and validation should pass
|
|
97
|
+
const strict = false;
|
|
98
|
+
const warnings = ['Missing recommended field: notes'];
|
|
99
|
+
const errors = [];
|
|
100
|
+
if (strict && warnings.length > 0) {
|
|
101
|
+
errors.push(...warnings.map((w) => `[STRICT] ${w}`));
|
|
102
|
+
}
|
|
103
|
+
expect(errors.length).toBe(0);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
describe('help text documentation', () => {
|
|
107
|
+
// WU-1329: Verify help text documents strict default
|
|
108
|
+
it('should document that --no-strict bypasses strict validation', () => {
|
|
109
|
+
const expectedDescription = 'Bypass strict validation';
|
|
110
|
+
expect(WU_OPTIONS.noStrict.description).toContain(expectedDescription);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
});
|
package/dist/flow-bottlenecks.js
CHANGED
|
@@ -15,8 +15,9 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import { Command } from 'commander';
|
|
17
17
|
import { getBottleneckAnalysis, } from '@lumenflow/metrics';
|
|
18
|
-
import { buildDependencyGraphAsync, renderMermaid
|
|
18
|
+
import { buildDependencyGraphAsync, renderMermaid } from '@lumenflow/core/dist/dependency-graph.js';
|
|
19
19
|
import { die } from '@lumenflow/core/dist/error-handler.js';
|
|
20
|
+
import { getConfig } from '@lumenflow/core/dist/lumenflow-config.js';
|
|
20
21
|
/** Log prefix for console output */
|
|
21
22
|
const LOG_PREFIX = '[flow:bottlenecks]';
|
|
22
23
|
/** Default bottleneck limit */
|
|
@@ -147,7 +148,8 @@ async function main() {
|
|
|
147
148
|
const coreGraph = await buildDependencyGraphAsync();
|
|
148
149
|
if (coreGraph.size === 0) {
|
|
149
150
|
console.log(`${LOG_PREFIX} No WUs found in dependency graph.`);
|
|
150
|
-
|
|
151
|
+
// WU-1311: Use config-based WU directory path
|
|
152
|
+
console.log(`${LOG_PREFIX} Ensure WU YAML files exist in ${getConfig().directories.wuDir}/ with blocked_by/blocks fields.`);
|
|
151
153
|
return;
|
|
152
154
|
}
|
|
153
155
|
console.log(`${LOG_PREFIX} Found ${coreGraph.size} WUs in graph`);
|
package/dist/flow-report.js
CHANGED
|
@@ -21,6 +21,7 @@ import { parse as parseYaml } from 'yaml';
|
|
|
21
21
|
import { Command } from 'commander';
|
|
22
22
|
import { generateFlowReport, TELEMETRY_PATHS, } from '@lumenflow/metrics';
|
|
23
23
|
import { die } from '@lumenflow/core/dist/error-handler.js';
|
|
24
|
+
import { WU_PATHS } from '@lumenflow/core/dist/wu-paths.js';
|
|
24
25
|
/** Log prefix for console output */
|
|
25
26
|
const LOG_PREFIX = '[flow:report]';
|
|
26
27
|
/** Default report window in days */
|
|
@@ -30,8 +31,8 @@ const OUTPUT_FORMATS = {
|
|
|
30
31
|
JSON: 'json',
|
|
31
32
|
TABLE: 'table',
|
|
32
33
|
};
|
|
33
|
-
/** WU directory relative to repo root */
|
|
34
|
-
const WU_DIR =
|
|
34
|
+
/** WU directory relative to repo root (WU-1301: uses config-based paths) */
|
|
35
|
+
const WU_DIR = WU_PATHS.WU_DIR();
|
|
35
36
|
/**
|
|
36
37
|
* Parse command line arguments
|
|
37
38
|
*/
|
package/dist/gates.js
CHANGED
|
@@ -45,6 +45,9 @@ import { access } from 'node:fs/promises';
|
|
|
45
45
|
import path from 'node:path';
|
|
46
46
|
import { emitGateEvent, getCurrentWU, getCurrentLane } from '@lumenflow/core/dist/telemetry.js';
|
|
47
47
|
import { die } from '@lumenflow/core/dist/error-handler.js';
|
|
48
|
+
// WU-1299: Import WU YAML reader to get code_paths for docs-only filtering
|
|
49
|
+
import { readWURaw } from '@lumenflow/core/dist/wu-yaml.js';
|
|
50
|
+
import { createWuPaths } from '@lumenflow/core/dist/wu-paths.js';
|
|
48
51
|
import { getChangedLintableFiles, isLintableFile } from '@lumenflow/core/dist/incremental-lint.js';
|
|
49
52
|
import { buildVitestChangedArgs, isCodeFilePath } from '@lumenflow/core/dist/incremental-test.js';
|
|
50
53
|
import { getGitForCwd } from '@lumenflow/core/dist/git-adapter.js';
|
|
@@ -65,6 +68,8 @@ import { runSystemMapValidation } from '@lumenflow/core/dist/system-map-validato
|
|
|
65
68
|
import { loadLaneHealthConfig, resolveTestPolicy, } from '@lumenflow/core/dist/gates-config.js';
|
|
66
69
|
// WU-1191: Lane health check
|
|
67
70
|
import { runLaneHealthCheck } from './lane-health.js';
|
|
71
|
+
// WU-1315: Onboarding smoke test
|
|
72
|
+
import { runOnboardingSmokeTestGate } from './onboarding-smoke-test.js';
|
|
68
73
|
import { BRANCHES, PACKAGES, PKG_MANAGER, ESLINT_FLAGS, ESLINT_COMMANDS, ESLINT_DEFAULTS, SCRIPTS, CACHE_STRATEGIES, DIRECTORIES, GATE_NAMES, GATE_COMMANDS, CLI_MODES, EXIT_CODES, FILE_SYSTEM, PRETTIER_ARGS, PRETTIER_FLAGS, } from '@lumenflow/core/dist/wu-constants.js';
|
|
69
74
|
/**
|
|
70
75
|
* WU-1087: Gates-specific option definitions for createWUParser.
|
|
@@ -250,6 +255,152 @@ export function resolveTestPlan({ isMainBranch, hasUntrackedCode, hasConfigChang
|
|
|
250
255
|
}
|
|
251
256
|
return { mode: 'incremental' };
|
|
252
257
|
}
|
|
258
|
+
/**
|
|
259
|
+
* WU-1299: Extract package name from a single code path
|
|
260
|
+
*
|
|
261
|
+
* @param codePath - Single code path to parse
|
|
262
|
+
* @returns Package name or null if not a package/app path
|
|
263
|
+
*/
|
|
264
|
+
function extractPackageFromPath(codePath) {
|
|
265
|
+
if (!codePath || typeof codePath !== 'string') {
|
|
266
|
+
return null;
|
|
267
|
+
}
|
|
268
|
+
const normalized = codePath.replace(/\\/g, '/');
|
|
269
|
+
// Handle packages/@scope/name/... or packages/name/...
|
|
270
|
+
if (normalized.startsWith('packages/')) {
|
|
271
|
+
const parts = normalized.slice('packages/'.length).split('/');
|
|
272
|
+
// Scoped package (@scope/name)
|
|
273
|
+
if (parts[0]?.startsWith('@') && parts[1]) {
|
|
274
|
+
return `${parts[0]}/${parts[1]}`;
|
|
275
|
+
}
|
|
276
|
+
// Unscoped package
|
|
277
|
+
if (parts[0]) {
|
|
278
|
+
return parts[0];
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
// Handle apps/name/...
|
|
282
|
+
if (normalized.startsWith('apps/')) {
|
|
283
|
+
const parts = normalized.slice('apps/'.length).split('/');
|
|
284
|
+
if (parts[0]) {
|
|
285
|
+
return parts[0];
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return null;
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* WU-1299: Extract package/app names from code_paths
|
|
292
|
+
*
|
|
293
|
+
* Parses paths like:
|
|
294
|
+
* - packages/@lumenflow/cli/src/file.ts -> @lumenflow/cli
|
|
295
|
+
* - apps/web/src/file.ts -> web
|
|
296
|
+
*
|
|
297
|
+
* @param codePaths - Array of code paths from WU YAML
|
|
298
|
+
* @returns Array of unique package/app names
|
|
299
|
+
*/
|
|
300
|
+
export function extractPackagesFromCodePaths(codePaths) {
|
|
301
|
+
if (!codePaths || !Array.isArray(codePaths) || codePaths.length === 0) {
|
|
302
|
+
return [];
|
|
303
|
+
}
|
|
304
|
+
const packages = new Set();
|
|
305
|
+
for (const codePath of codePaths) {
|
|
306
|
+
const pkg = extractPackageFromPath(codePath);
|
|
307
|
+
if (pkg) {
|
|
308
|
+
packages.add(pkg);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
return Array.from(packages);
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* WU-1299: Resolve test plan for docs-only mode
|
|
315
|
+
*
|
|
316
|
+
* When --docs-only is passed, this determines whether to:
|
|
317
|
+
* - Skip tests entirely (no code packages in code_paths)
|
|
318
|
+
* - Run tests only for packages mentioned in code_paths
|
|
319
|
+
*
|
|
320
|
+
* @param options - Options including code_paths from WU YAML
|
|
321
|
+
* @returns DocsOnlyTestPlan indicating how to handle tests
|
|
322
|
+
*/
|
|
323
|
+
export function resolveDocsOnlyTestPlan({ codePaths }) {
|
|
324
|
+
const packages = extractPackagesFromCodePaths(codePaths);
|
|
325
|
+
if (packages.length === 0) {
|
|
326
|
+
return {
|
|
327
|
+
mode: 'skip',
|
|
328
|
+
packages: [],
|
|
329
|
+
reason: 'no-code-packages',
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
return {
|
|
333
|
+
mode: 'filtered',
|
|
334
|
+
packages,
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* WU-1299: Format message for docs-only test skipping/filtering
|
|
339
|
+
*
|
|
340
|
+
* Provides clear messaging when tests are skipped or filtered in docs-only mode.
|
|
341
|
+
*
|
|
342
|
+
* @param plan - The docs-only test plan
|
|
343
|
+
* @returns Human-readable message explaining what's happening
|
|
344
|
+
*/
|
|
345
|
+
export function formatDocsOnlySkipMessage(plan) {
|
|
346
|
+
if (plan.mode === 'skip') {
|
|
347
|
+
return '📝 docs-only mode: skipping all tests (no code packages in code_paths)';
|
|
348
|
+
}
|
|
349
|
+
const packageList = plan.packages.join(', ');
|
|
350
|
+
return `📝 docs-only mode: running tests only for packages in code_paths: ${packageList}`;
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* WU-1299: Load code_paths from current WU YAML
|
|
354
|
+
*
|
|
355
|
+
* Attempts to read the WU YAML file for the current WU (detected from git branch)
|
|
356
|
+
* and return its code_paths. Returns empty array if WU cannot be determined or
|
|
357
|
+
* YAML file doesn't exist.
|
|
358
|
+
*
|
|
359
|
+
* @param options - Options including optional cwd
|
|
360
|
+
* @returns Array of code_paths from WU YAML, or empty array if unavailable
|
|
361
|
+
*/
|
|
362
|
+
export function loadCurrentWUCodePaths(options = {}) {
|
|
363
|
+
const cwd = options.cwd ?? process.cwd();
|
|
364
|
+
const wuId = getCurrentWU();
|
|
365
|
+
if (!wuId) {
|
|
366
|
+
return [];
|
|
367
|
+
}
|
|
368
|
+
try {
|
|
369
|
+
const wuPaths = createWuPaths({ projectRoot: cwd });
|
|
370
|
+
const wuYamlPath = wuPaths.WU(wuId);
|
|
371
|
+
const wuDoc = readWURaw(wuYamlPath);
|
|
372
|
+
if (wuDoc && Array.isArray(wuDoc.code_paths)) {
|
|
373
|
+
return wuDoc.code_paths.filter((p) => typeof p === 'string');
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
catch {
|
|
377
|
+
// WU YAML not found or unreadable - return empty array
|
|
378
|
+
}
|
|
379
|
+
return [];
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* WU-1299: Run filtered tests for docs-only mode
|
|
383
|
+
*
|
|
384
|
+
* When --docs-only is passed and code_paths contains packages, this runs tests
|
|
385
|
+
* only for those specific packages using turbo's --filter flag.
|
|
386
|
+
*
|
|
387
|
+
* @param options - Options including packages to test and agent log context
|
|
388
|
+
* @returns Result object with ok status and duration
|
|
389
|
+
*/
|
|
390
|
+
async function runDocsOnlyFilteredTests({ packages, agentLog, }) {
|
|
391
|
+
const start = Date.now();
|
|
392
|
+
const logLine = makeGateLogger({ agentLog, useAgentMode: !!agentLog });
|
|
393
|
+
if (packages.length === 0) {
|
|
394
|
+
logLine('📝 docs-only mode: no packages to test, skipping');
|
|
395
|
+
return { ok: true, duration: Date.now() - start };
|
|
396
|
+
}
|
|
397
|
+
logLine(`\n> Tests (docs-only filtered: ${packages.join(', ')})\n`);
|
|
398
|
+
// Build turbo filter args for each package
|
|
399
|
+
// turbo supports --filter=@scope/package or --filter=package
|
|
400
|
+
const filterArgs = packages.map((pkg) => `--filter=${pkg}`);
|
|
401
|
+
const result = run(pnpmCmd('turbo', 'run', 'test', ...filterArgs), { agentLog });
|
|
402
|
+
return { ok: result.ok, duration: Date.now() - start };
|
|
403
|
+
}
|
|
253
404
|
export function parsePrettierListOutput(output) {
|
|
254
405
|
if (!output)
|
|
255
406
|
return [];
|
|
@@ -935,6 +1086,12 @@ async function executeGates(opts) {
|
|
|
935
1086
|
}
|
|
936
1087
|
// Determine effective docs-only mode (explicit flag OR detected from changed files)
|
|
937
1088
|
const effectiveDocsOnly = isDocsOnly || (riskTier && riskTier.isDocsOnly);
|
|
1089
|
+
// WU-1299: Load code_paths and compute docs-only test plan
|
|
1090
|
+
let docsOnlyTestPlan = null;
|
|
1091
|
+
if (effectiveDocsOnly) {
|
|
1092
|
+
const codePaths = loadCurrentWUCodePaths({ cwd: process.cwd() });
|
|
1093
|
+
docsOnlyTestPlan = resolveDocsOnlyTestPlan({ codePaths });
|
|
1094
|
+
}
|
|
938
1095
|
// Determine which gates to run
|
|
939
1096
|
// WU-2252: Invariants gate runs FIRST and is included in both docs-only and regular modes
|
|
940
1097
|
const gates = effectiveDocsOnly
|
|
@@ -960,6 +1117,30 @@ async function executeGates(opts) {
|
|
|
960
1117
|
run: (ctx) => runLaneHealthGate({ ...ctx, mode: laneHealthMode }),
|
|
961
1118
|
warnOnly: laneHealthMode !== 'error',
|
|
962
1119
|
},
|
|
1120
|
+
// WU-1315: Onboarding smoke test (init + wu:create validation)
|
|
1121
|
+
{
|
|
1122
|
+
name: GATE_NAMES.ONBOARDING_SMOKE_TEST,
|
|
1123
|
+
cmd: GATE_COMMANDS.ONBOARDING_SMOKE_TEST,
|
|
1124
|
+
},
|
|
1125
|
+
// WU-1299: Filtered tests for packages in code_paths (if any)
|
|
1126
|
+
// When docs-only mode has packages in code_paths, run tests only for those packages
|
|
1127
|
+
// This prevents pre-existing failures in unrelated packages from blocking
|
|
1128
|
+
...(docsOnlyTestPlan && docsOnlyTestPlan.mode === 'filtered'
|
|
1129
|
+
? [
|
|
1130
|
+
{
|
|
1131
|
+
name: GATE_NAMES.TEST,
|
|
1132
|
+
run: (ctx) => {
|
|
1133
|
+
// Safe access: docsOnlyTestPlan is guaranteed non-null by the outer conditional
|
|
1134
|
+
const pkgs = docsOnlyTestPlan.packages;
|
|
1135
|
+
return runDocsOnlyFilteredTests({
|
|
1136
|
+
packages: pkgs,
|
|
1137
|
+
agentLog: ctx.agentLog,
|
|
1138
|
+
});
|
|
1139
|
+
},
|
|
1140
|
+
warnOnly: !testsRequired,
|
|
1141
|
+
},
|
|
1142
|
+
]
|
|
1143
|
+
: []),
|
|
963
1144
|
]
|
|
964
1145
|
: [
|
|
965
1146
|
// WU-2252: Invariants check runs first (non-bypassable)
|
|
@@ -989,6 +1170,11 @@ async function executeGates(opts) {
|
|
|
989
1170
|
run: (ctx) => runLaneHealthGate({ ...ctx, mode: laneHealthMode }),
|
|
990
1171
|
warnOnly: laneHealthMode !== 'error',
|
|
991
1172
|
},
|
|
1173
|
+
// WU-1315: Onboarding smoke test (init + wu:create validation)
|
|
1174
|
+
{
|
|
1175
|
+
name: GATE_NAMES.ONBOARDING_SMOKE_TEST,
|
|
1176
|
+
cmd: GATE_COMMANDS.ONBOARDING_SMOKE_TEST,
|
|
1177
|
+
},
|
|
992
1178
|
// WU-2062: Safety-critical tests ALWAYS run
|
|
993
1179
|
// WU-1280: When tests_required=false (methodology.testing: none), failures only warn
|
|
994
1180
|
{
|
|
@@ -1021,11 +1207,15 @@ async function executeGates(opts) {
|
|
|
1021
1207
|
{ name: GATE_NAMES.COVERAGE, cmd: GATE_COMMANDS.COVERAGE_GATE },
|
|
1022
1208
|
];
|
|
1023
1209
|
if (effectiveDocsOnly) {
|
|
1210
|
+
// WU-1299: Show clear messaging about what's being skipped/run in docs-only mode
|
|
1211
|
+
const docsOnlyMessage = docsOnlyTestPlan && docsOnlyTestPlan.mode === 'filtered'
|
|
1212
|
+
? formatDocsOnlySkipMessage(docsOnlyTestPlan)
|
|
1213
|
+
: '📝 Docs-only mode: skipping lint, typecheck, and all tests (no code packages in code_paths)';
|
|
1024
1214
|
if (!useAgentMode) {
|
|
1025
|
-
console.log(
|
|
1215
|
+
console.log(`${docsOnlyMessage}\n`);
|
|
1026
1216
|
}
|
|
1027
1217
|
else {
|
|
1028
|
-
writeSync(agentLog.logFd,
|
|
1218
|
+
writeSync(agentLog.logFd, `${docsOnlyMessage}\n`);
|
|
1029
1219
|
}
|
|
1030
1220
|
}
|
|
1031
1221
|
// Run all gates sequentially
|
|
@@ -1107,6 +1297,16 @@ async function executeGates(opts) {
|
|
|
1107
1297
|
: console,
|
|
1108
1298
|
});
|
|
1109
1299
|
}
|
|
1300
|
+
else if (gate.cmd === GATE_COMMANDS.ONBOARDING_SMOKE_TEST) {
|
|
1301
|
+
// WU-1315: Onboarding smoke test (init + wu:create validation)
|
|
1302
|
+
const logLine = useAgentMode
|
|
1303
|
+
? (line) => writeSync(agentLog.logFd, `${line}\n`)
|
|
1304
|
+
: (line) => console.log(line);
|
|
1305
|
+
logLine('\n> Onboarding smoke test\n');
|
|
1306
|
+
result = await runOnboardingSmokeTestGate({
|
|
1307
|
+
logger: { log: logLine },
|
|
1308
|
+
});
|
|
1309
|
+
}
|
|
1110
1310
|
else {
|
|
1111
1311
|
result = run(gate.cmd, { agentLog });
|
|
1112
1312
|
}
|