@lumenflow/cli 5.4.0 → 5.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.
Files changed (31) hide show
  1. package/dist/gate-defaults.js +154 -9
  2. package/dist/gate-defaults.js.map +1 -1
  3. package/dist/gate-registry.js.map +1 -1
  4. package/dist/gates-runners.js +358 -0
  5. package/dist/gates-runners.js.map +1 -1
  6. package/dist/gates.js +347 -3
  7. package/dist/gates.js.map +1 -1
  8. package/dist/metrics-cli.js +19 -2
  9. package/dist/metrics-cli.js.map +1 -1
  10. package/dist/metrics-snapshot.js +25 -2
  11. package/dist/metrics-snapshot.js.map +1 -1
  12. package/dist/wu-done-gates.js +112 -3
  13. package/dist/wu-done-gates.js.map +1 -1
  14. package/dist/wu-done.js +30 -6
  15. package/dist/wu-done.js.map +1 -1
  16. package/dist/wu-prep.js +91 -1
  17. package/dist/wu-prep.js.map +1 -1
  18. package/package.json +11 -11
  19. package/packs/agent-runtime/package.json +1 -1
  20. package/packs/sidekick/package.json +1 -1
  21. package/packs/software-delivery/package.json +1 -1
  22. package/packs/software-delivery/src/constants/wu-cli-constants.ts +12 -0
  23. package/templates/core/AGENTS.md.template +97 -25
  24. package/templates/core/LUMENFLOW.md.template +189 -28
  25. package/templates/core/ai/onboarding/agent-invocation-guide.md.template +0 -5
  26. package/templates/core/ai/onboarding/agent-safety-card.md.template +63 -17
  27. package/templates/core/ai/onboarding/initiative-orchestration.md.template +4 -0
  28. package/templates/core/ai/onboarding/release-process.md.template +7 -7
  29. package/templates/core/ai/onboarding/vendor-support.md.template +74 -10
  30. package/templates/vendors/claude/.claude/skills/frontend-design/SKILL.md.template +1 -1
  31. package/templates/vendors/claude/.claude/skills/wu-lifecycle/SKILL.md.template +28 -0
@@ -1,7 +1,75 @@
1
1
  // Copyright (c) 2026 Hellmai Ltd
2
2
  // SPDX-License-Identifier: LicenseRef-LumenFlow-Proprietary
3
3
  import { GATE_NAMES, GATE_COMMANDS, SCRIPTS } from '@lumenflow/core/wu-constants';
4
- import { runCoChangeGate } from './gates-runners.js';
4
+ import { runCoChangeGate,
5
+ // WU-2926: per-gate applicability + precondition checks
6
+ checkSafetyCriticalApplicability, checkMigrationVerifyApplicability, checkMigrationVerifyPreconditions, checkInvariantsPreconditions, checkLaneHealthApplicability, checkLaneHealthPreconditions, checkCoChangeApplicability, checkClaimValidationApplicability,
7
+ // WU-2927: opt-in local-prep gate checks
8
+ checkBuildApplicability, checkBuildPreconditions, checkIntegrationTestApplicability, checkIntegrationTestPreconditions, checkE2eSmokeApplicability, checkE2eSmokePreconditions, } from './gates-runners.js';
9
+ /**
10
+ * WU-2925: Framework gate skippability allow-list.
11
+ *
12
+ * Fail-closed default: every gate is `skippable: false` unless explicitly listed
13
+ * here. Per WU-2925 acceptance, only environmental or per-WU policy gates are
14
+ * skippable by default:
15
+ *
16
+ * - `migration-verify`: opt-in DB verification, often absent in agent worktrees
17
+ * - `lane-health`: file-pattern coverage gap, tracked separately
18
+ * - `tdd-diff-evidence`: per-WU type policy (also config-driven via
19
+ * `software_delivery.gates.tdd_diff_evidence`)
20
+ *
21
+ * All other framework gates (format, lint, typecheck, invariants,
22
+ * safety-critical-test, backlog-sync, claim-validation, co-change, spec:linter,
23
+ * integration-test, onboarding-smoke-test, incremental-test, coverage-gate,
24
+ * tiered-test, unread-directed-signal) default to `skippable: false` and require
25
+ * a per-repo override at `software_delivery.gates.overrides.<gate_id>.skippable=true`
26
+ * to be skipped.
27
+ *
28
+ * Repo-specific gates not present in this map default to `false` unless the
29
+ * repo declares an override.
30
+ *
31
+ * Resolution helper: see `getEffectiveSkippable()`.
32
+ */
33
+ export const FRAMEWORK_GATE_SKIPPABLE_DEFAULTS = Object.freeze({
34
+ [GATE_NAMES.MIGRATION_VERIFY]: true,
35
+ [GATE_NAMES.LANE_HEALTH]: true,
36
+ // 'tdd-diff-evidence' is enforced through a different surface (config-driven),
37
+ // but its skippable default is recorded here for completeness when consulted
38
+ // via getEffectiveSkippable().
39
+ 'tdd-diff-evidence': true,
40
+ // WU-2927: opt-in local-prep gates (extends the WU-2925 allow-list).
41
+ // Rationale: these are opt-in environmental gates; agents need --skip-gate
42
+ // for legitimate hotfix scenarios where the gate is broken/irrelevant for
43
+ // that WU. Skips captured in audit per WU-2925 semantics with source: 'agent'.
44
+ [GATE_NAMES.BUILD]: true,
45
+ [GATE_NAMES.INTEGRATION_TEST]: true,
46
+ [GATE_NAMES.E2E_SMOKE]: true,
47
+ });
48
+ /**
49
+ * WU-2925: Resolve effective skippability for a gate.
50
+ *
51
+ * Resolution order:
52
+ * 1. Repo override at `software_delivery.gates.overrides.<gate_id>.skippable`
53
+ * 2. GateDefinition.skippable_default (when registered)
54
+ * 3. FRAMEWORK_GATE_SKIPPABLE_DEFAULTS map
55
+ * 4. Fail-closed default: false
56
+ *
57
+ * @param gateName - Gate identifier (e.g. 'lane-health')
58
+ * @param overrides - The `software_delivery.gates.overrides` map from workspace.yaml (may be undefined)
59
+ * @param registeredDefault - The `skippable_default` from the registered GateDefinition (may be undefined)
60
+ * @returns true if `--skip-gate <gateName>` is permitted; false otherwise
61
+ */
62
+ export function getEffectiveSkippable(gateName, overrides, registeredDefault) {
63
+ const override = overrides?.[gateName]?.skippable;
64
+ if (typeof override === 'boolean')
65
+ return override;
66
+ if (typeof registeredDefault === 'boolean')
67
+ return registeredDefault;
68
+ const baked = FRAMEWORK_GATE_SKIPPABLE_DEFAULTS[gateName];
69
+ if (typeof baked === 'boolean')
70
+ return baked;
71
+ return false;
72
+ }
5
73
  /**
6
74
  * Register the default docs-only gates into the registry.
7
75
  *
@@ -14,9 +82,11 @@ import { runCoChangeGate } from './gates-runners.js';
14
82
  export function registerDocsOnlyGates(registry, options) {
15
83
  const { laneHealthMode, testsRequired, docsOnlyTestPlan, enableDeliveryReview = false } = options;
16
84
  // WU-2252: Invariants check runs first (non-bypassable)
85
+ // WU-2926: invariants is always applicable; preconditions = config present
17
86
  registry.register({
18
87
  name: GATE_NAMES.INVARIANTS,
19
88
  cmd: GATE_COMMANDS.INVARIANTS,
89
+ checkPreconditions: checkInvariantsPreconditions,
20
90
  });
21
91
  registry.register({
22
92
  name: GATE_NAMES.FORMAT_CHECK,
@@ -34,13 +104,20 @@ export function registerDocsOnlyGates(registry, options) {
34
104
  registry.register({
35
105
  name: GATE_NAMES.BACKLOG_SYNC,
36
106
  });
107
+ // WU-2926: claim-validation is applicable only when a WU is claimed
37
108
  registry.register({
38
109
  name: GATE_NAMES.CLAIM_VALIDATION,
110
+ checkApplicability: checkClaimValidationApplicability,
39
111
  });
40
112
  // WU-1191: Lane health check (configurable: warn/error/off)
113
+ // WU-2925: skippable_default: true (allow-list — file-pattern coverage gap)
114
+ // WU-2926: applicable when lane-state.json exists; preconditions = state writable
41
115
  registry.register({
42
116
  name: GATE_NAMES.LANE_HEALTH,
43
117
  warnOnly: laneHealthMode !== 'error',
118
+ skippable_default: true,
119
+ checkApplicability: checkLaneHealthApplicability,
120
+ checkPreconditions: checkLaneHealthPreconditions,
44
121
  });
45
122
  // WU-1315: Onboarding smoke test
46
123
  registry.register({
@@ -72,9 +149,11 @@ export function registerDocsOnlyGates(registry, options) {
72
149
  export function registerCodeGates(registry, options) {
73
150
  const { isFullLint, isFullTests, isFullCoverage, laneHealthMode, testsRequired, shouldRunIntegration, configuredTestFullCmd, enableDeliveryReview = false, } = options;
74
151
  // WU-2252: Invariants check runs first (non-bypassable)
152
+ // WU-2926: invariants is always applicable; preconditions = config present
75
153
  registry.register({
76
154
  name: GATE_NAMES.INVARIANTS,
77
155
  cmd: GATE_COMMANDS.INVARIANTS,
156
+ checkPreconditions: checkInvariantsPreconditions,
78
157
  });
79
158
  registry.register({
80
159
  name: GATE_NAMES.FORMAT_CHECK,
@@ -85,12 +164,23 @@ export function registerCodeGates(registry, options) {
85
164
  cmd: isFullLint ? `pnpm ${SCRIPTS.LINT}` : GATE_COMMANDS.INCREMENTAL,
86
165
  scriptName: SCRIPTS.LINT,
87
166
  });
167
+ // WU-2926: co-change is applicable only when workspace.yaml has co-change rules
88
168
  registry.register({
89
169
  name: GATE_NAMES.CO_CHANGE,
90
170
  run: runCoChangeGate,
171
+ checkApplicability: checkCoChangeApplicability,
91
172
  });
173
+ // WU-2380: Optional migration-state verification gate
174
+ // WU-2925: skippable_default: true (allow-list — environmental DB infra)
175
+ // WU-2926: applicability + DB-prereq detection so non-migration WUs auto-skip
176
+ // not-applicable, and migration WUs without DB infra auto-skip missing-prereq
177
+ // (or BLOCK when override flips skippable=false).
92
178
  registry.register({
93
179
  name: GATE_NAMES.MIGRATION_VERIFY,
180
+ skippable_default: true,
181
+ checkApplicability: checkMigrationVerifyApplicability,
182
+ checkPreconditions: checkMigrationVerifyPreconditions,
183
+ prereq_strategy: 'auto-skip',
94
184
  });
95
185
  registry.register({
96
186
  name: GATE_NAMES.TYPECHECK,
@@ -105,16 +195,23 @@ export function registerCodeGates(registry, options) {
105
195
  registry.register({
106
196
  name: GATE_NAMES.BACKLOG_SYNC,
107
197
  });
198
+ // WU-2926: claim-validation is applicable only when a WU is claimed
108
199
  registry.register({
109
200
  name: GATE_NAMES.CLAIM_VALIDATION,
201
+ checkApplicability: checkClaimValidationApplicability,
110
202
  });
111
203
  registry.register({
112
204
  name: GATE_NAMES.SUPABASE_DOCS_LINTER,
113
205
  });
114
206
  // WU-1191: Lane health check
207
+ // WU-2925: skippable_default: true (allow-list — file-pattern coverage gap)
208
+ // WU-2926: applicable when lane-state.json exists; preconditions = state writable
115
209
  registry.register({
116
210
  name: GATE_NAMES.LANE_HEALTH,
117
211
  warnOnly: laneHealthMode !== 'error',
212
+ skippable_default: true,
213
+ checkApplicability: checkLaneHealthApplicability,
214
+ checkPreconditions: checkLaneHealthPreconditions,
118
215
  });
119
216
  // WU-1315: Onboarding smoke test
120
217
  registry.register({
@@ -122,10 +219,18 @@ export function registerCodeGates(registry, options) {
122
219
  cmd: GATE_COMMANDS.ONBOARDING_SMOKE_TEST,
123
220
  });
124
221
  // WU-2062: Safety-critical tests ALWAYS run
222
+ // WU-2926: ships with skippable_default: false (explicit) and a default
223
+ // checkApplicability that returns 'applicable' for every change set,
224
+ // preserving today's gate-defaults.ts:195 / gates-runners.ts:743 behaviour.
225
+ // Per-repo override at
226
+ // software_delivery.gates.overrides.safety-critical-test.applicability_paths
227
+ // narrows applicability without flipping the default.
125
228
  registry.register({
126
229
  name: GATE_NAMES.SAFETY_CRITICAL_TEST,
127
230
  cmd: GATE_COMMANDS.SAFETY_CRITICAL_TEST,
128
231
  warnOnly: !testsRequired,
232
+ skippable_default: false,
233
+ checkApplicability: (ctx) => checkSafetyCriticalApplicability(ctx),
129
234
  });
130
235
  // WU-1920: Changed tests by default, full suite with --full-tests
131
236
  registry.register({
@@ -133,14 +238,54 @@ export function registerCodeGates(registry, options) {
133
238
  cmd: isFullTests || isFullCoverage ? configuredTestFullCmd : GATE_COMMANDS.INCREMENTAL_TEST,
134
239
  warnOnly: !testsRequired,
135
240
  });
136
- // WU-2062: Integration tests only for high-risk changes
137
- if (shouldRunIntegration) {
138
- registry.register({
139
- name: GATE_NAMES.INTEGRATION_TEST,
140
- cmd: GATE_COMMANDS.TIERED_TEST,
141
- warnOnly: !testsRequired,
142
- });
143
- }
241
+ // WU-2927: Integration-test gate is now UNCONDITIONALLY registered.
242
+ // Pre-WU-2927: registered conditionally behind `shouldRunIntegration`
243
+ // (hardcoded false at gates.ts:450/486). That made
244
+ // `software_delivery.gates.local_prep.integration_test.enabled` ineffective
245
+ // for opting in. Now: always registered, applicability is the only knob.
246
+ //
247
+ // Decision matrix (WU-2926 + WU-2927):
248
+ // local_prep.integration_test.enabled = false (default) → not-applicable → auto-skip
249
+ // enabled = true + script present → run
250
+ // enabled = true + script missing → 'failed' (prereq_strategy: 'fail')
251
+ // enabled = true + script missing + override skippable=false → blocked-missing-prereq
252
+ //
253
+ // The legacy `shouldRunIntegration` flag is intentionally read but no longer
254
+ // gates registration — it is preserved as a compatibility no-op so callers
255
+ // (and the WU-1550 registry-defaults tests) continue to compile unchanged.
256
+ void shouldRunIntegration;
257
+ registry.register({
258
+ name: GATE_NAMES.INTEGRATION_TEST,
259
+ cmd: GATE_COMMANDS.TIERED_TEST,
260
+ warnOnly: !testsRequired,
261
+ skippable_default: true,
262
+ prereq_strategy: 'fail',
263
+ checkApplicability: checkIntegrationTestApplicability,
264
+ checkPreconditions: (ctx) => Promise.resolve(checkIntegrationTestPreconditions(ctx)),
265
+ });
266
+ // WU-2927: Opt-in local-prep build gate. Registered always; applicable only
267
+ // when local_prep.build.enabled = true. Wraps SCRIPTS.BUILD via a sentinel
268
+ // dispatched in gates.ts.
269
+ registry.register({
270
+ name: GATE_NAMES.BUILD,
271
+ cmd: GATE_COMMANDS.BUILD,
272
+ scriptName: SCRIPTS.BUILD,
273
+ skippable_default: true,
274
+ prereq_strategy: 'fail',
275
+ checkApplicability: checkBuildApplicability,
276
+ checkPreconditions: (ctx) => Promise.resolve(checkBuildPreconditions(ctx)),
277
+ });
278
+ // WU-2927: Opt-in local-prep e2e-smoke gate. Applicable only when
279
+ // local_prep.e2e_smoke.enabled = true AND playwright.config.{ts,js,mjs}
280
+ // exists at repo root (absence of Playwright is a clean opt-out).
281
+ registry.register({
282
+ name: GATE_NAMES.E2E_SMOKE,
283
+ cmd: GATE_COMMANDS.E2E_SMOKE,
284
+ skippable_default: true,
285
+ prereq_strategy: 'fail',
286
+ checkApplicability: checkE2eSmokeApplicability,
287
+ checkPreconditions: (ctx) => Promise.resolve(checkE2eSmokePreconditions(ctx)),
288
+ });
144
289
  if (enableDeliveryReview) {
145
290
  registry.register({
146
291
  name: GATE_NAMES.DELIVERY_REVIEW,
@@ -1 +1 @@
1
- {"version":3,"file":"gate-defaults.js","sourceRoot":"","sources":["../src/gate-defaults.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gate-defaults.js","sourceRoot":"","sources":["../src/gate-defaults.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"gate-registry.js","sourceRoot":"","sources":["../src/gate-registry.ts"],"names":[],"mappings":"AAAA,iCAAiC;AACjC,4DAA4D;AAE5D;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AA+B1D;;;;;;GAMG;AACH,MAAM,OAAO,YAAY;IACN,KAAK,GAAqB,EAAE,CAAC;IAC7B,SAAS,GAAG,IAAI,GAAG,EAAkB,CAAC;IAEvD;;;;;OAKG;IACH,QAAQ,CAAC,IAAoB;QAC3B,IAAI,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAClC,MAAM,WAAW,CACf,UAAU,CAAC,uBAAuB,EAClC,SAAS,IAAI,CAAC,IAAI,yBAAyB,CAC5C,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACjD,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC;IAED;;;;OAIG;IACH,WAAW,CAAC,KAAuB;QACjC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QACtB,CAAC;IACH,CAAC;IAED;;;;OAIG;IACH,MAAM;QACJ,OAAO,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;IACzB,CAAC;IAED;;;;;OAKG;IACH,GAAG,CAAC,IAAY;QACd,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACvC,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO,SAAS,CAAC;QAC1C,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC3B,CAAC;IAED;;;;;OAKG;IACH,GAAG,CAAC,IAAY;QACd,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAClC,CAAC;IAED;;OAEG;IACH,KAAK;QACH,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;QACtB,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;IACzB,CAAC;CACF"}
1
+ {"version":3,"file":"gate-registry.js","sourceRoot":"","sources":["../src/gate-registry.ts"],"names":[],"mappings":"AAAA,iCAAiC;AACjC,4DAA4D;AAE5D;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAyJ1D;;;;;;GAMG;AACH,MAAM,OAAO,YAAY;IACN,KAAK,GAAqB,EAAE,CAAC;IAC7B,SAAS,GAAG,IAAI,GAAG,EAAkB,CAAC;IAEvD;;;;;OAKG;IACH,QAAQ,CAAC,IAAoB;QAC3B,IAAI,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAClC,MAAM,WAAW,CACf,UAAU,CAAC,uBAAuB,EAClC,SAAS,IAAI,CAAC,IAAI,yBAAyB,CAC5C,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACjD,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC;IAED;;;;OAIG;IACH,WAAW,CAAC,KAAuB;QACjC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QACtB,CAAC;IACH,CAAC;IAED;;;;OAIG;IACH,MAAM;QACJ,OAAO,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;IACzB,CAAC;IAED;;;;;OAKG;IACH,GAAG,CAAC,IAAY;QACd,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACvC,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO,SAAS,CAAC;QAC1C,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC3B,CAAC;IAED;;;;;OAKG;IACH,GAAG,CAAC,IAAY;QACd,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAClC,CAAC;IAED;;OAEG;IACH,KAAK;QACH,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;QACtB,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;IACzB,CAAC;CACF"}
@@ -585,6 +585,181 @@ export async function runIntegrationTests({ agentLog, cwd, }) {
585
585
  return { ok: false, duration: Date.now() - start };
586
586
  }
587
587
  }
588
+ // ── WU-2927: Opt-in local-prep gates (build / integration-test / e2e-smoke) ──
589
+ /**
590
+ * WU-2927: Resolve `software_delivery.gates.local_prep` from workspace.yaml.
591
+ *
592
+ * Returns the typed sub-shape (or undefined when the section is missing or
593
+ * malformed). The runtime callers fall through to "not applicable" semantics
594
+ * when this returns undefined or `{ enabled: false }` — backward-compat for
595
+ * repos that have not opted in.
596
+ */
597
+ function readLocalPrepConfig(cwd) {
598
+ try {
599
+ const section = getGatesSection(cwd);
600
+ const lp = section?.local_prep;
601
+ if (lp && typeof lp === 'object' && !Array.isArray(lp)) {
602
+ return lp;
603
+ }
604
+ return undefined;
605
+ }
606
+ catch {
607
+ return undefined;
608
+ }
609
+ }
610
+ function readPackageScripts(cwd) {
611
+ try {
612
+ const pkgPath = path.join(cwd, 'package.json');
613
+ if (!existsSync(pkgPath))
614
+ return undefined;
615
+ const raw = readFileSync(pkgPath, 'utf8');
616
+ const parsed = JSON.parse(raw);
617
+ return parsed.scripts;
618
+ }
619
+ catch {
620
+ return undefined;
621
+ }
622
+ }
623
+ /**
624
+ * WU-2927: build gate applicability — only when the local-prep profile opts in
625
+ * (`local_prep.build.enabled = true`). Default false → not-applicable.
626
+ */
627
+ export function checkBuildApplicability(ctx) {
628
+ const cwd = ctx.cwd ?? process.cwd();
629
+ const lp = readLocalPrepConfig(cwd);
630
+ return Promise.resolve(lp?.build?.enabled === true ? 'applicable' : 'not-applicable');
631
+ }
632
+ /**
633
+ * WU-2927: build gate preconditions — `build` script must exist in
634
+ * package.json. With `prereq_strategy: 'fail'` a missing script is treated as
635
+ * a misconfiguration, not an environmental gap.
636
+ */
637
+ export function checkBuildPreconditions(ctx) {
638
+ const cwd = ctx.cwd ?? process.cwd();
639
+ const scripts = readPackageScripts(cwd);
640
+ return scripts && Object.prototype.hasOwnProperty.call(scripts, SCRIPTS.BUILD)
641
+ ? 'satisfied'
642
+ : 'missing';
643
+ }
644
+ /**
645
+ * WU-2927: integration-test gate applicability — opt-in via
646
+ * `local_prep.integration_test.enabled = true`. Replaces the pre-WU-2927
647
+ * conditional registration (gate-defaults.ts:209 / gates.ts:450/486) so that
648
+ * the gate is unconditionally registered and applicability is the only knob.
649
+ */
650
+ export function checkIntegrationTestApplicability(ctx) {
651
+ const cwd = ctx.cwd ?? process.cwd();
652
+ const lp = readLocalPrepConfig(cwd);
653
+ return Promise.resolve(lp?.integration_test?.enabled === true ? 'applicable' : 'not-applicable');
654
+ }
655
+ /**
656
+ * WU-2927: integration-test gate preconditions — `test:integration` script
657
+ * must exist. Same fail-on-missing semantics as build (misconfig).
658
+ */
659
+ export function checkIntegrationTestPreconditions(ctx) {
660
+ const cwd = ctx.cwd ?? process.cwd();
661
+ const scripts = readPackageScripts(cwd);
662
+ return scripts && Object.prototype.hasOwnProperty.call(scripts, SCRIPTS.TEST_INTEGRATION)
663
+ ? 'satisfied'
664
+ : 'missing';
665
+ }
666
+ /**
667
+ * WU-2927: e2e-smoke gate applicability — applicable iff
668
+ * (a) `local_prep.e2e_smoke.enabled = true`, AND
669
+ * (b) `playwright.config.{ts,js,mjs}` exists at repo root.
670
+ *
671
+ * Per stated policy: the absence of a Playwright config is a clean opt-out,
672
+ * not a missing precondition (no explicit disable required for repos that do
673
+ * not use Playwright).
674
+ */
675
+ export function checkE2eSmokeApplicability(ctx) {
676
+ const cwd = ctx.cwd ?? process.cwd();
677
+ const lp = readLocalPrepConfig(cwd);
678
+ if (lp?.e2e_smoke?.enabled !== true)
679
+ return Promise.resolve('not-applicable');
680
+ for (const ext of ['ts', 'js', 'mjs']) {
681
+ if (existsSync(path.join(cwd, `playwright.config.${ext}`))) {
682
+ return Promise.resolve('applicable');
683
+ }
684
+ }
685
+ return Promise.resolve('not-applicable');
686
+ }
687
+ /**
688
+ * WU-2927: e2e-smoke gate preconditions — once applicable (Playwright config
689
+ * present + opted in), the `test:e2e` script must exist. Missing = misconfig
690
+ * → 'failed' under `prereq_strategy: 'fail'`.
691
+ */
692
+ export function checkE2eSmokePreconditions(ctx) {
693
+ const cwd = ctx.cwd ?? process.cwd();
694
+ const scripts = readPackageScripts(cwd);
695
+ return scripts && Object.prototype.hasOwnProperty.call(scripts, SCRIPTS.TEST_E2E)
696
+ ? 'satisfied'
697
+ : 'missing';
698
+ }
699
+ /**
700
+ * WU-2927: Read the configured e2e_smoke.tag (default 'smoke').
701
+ */
702
+ export function getE2eSmokeTag(cwd) {
703
+ const lp = readLocalPrepConfig(cwd);
704
+ const tag = lp?.e2e_smoke?.tag;
705
+ return typeof tag === 'string' && tag.length > 0 ? tag : 'smoke';
706
+ }
707
+ /**
708
+ * WU-2927: Read the configured latency_budget_warn_ms (default 180_000).
709
+ */
710
+ export function getLocalPrepLatencyBudgetMs(cwd) {
711
+ const lp = readLocalPrepConfig(cwd);
712
+ const budget = lp?.latency_budget_warn_ms;
713
+ return typeof budget === 'number' && budget >= 0 ? budget : 180_000;
714
+ }
715
+ /**
716
+ * WU-2927: Run the opt-in build gate (`pnpm build`). Wraps SCRIPTS.BUILD.
717
+ */
718
+ export async function runBuildGate({ agentLog, cwd, }) {
719
+ const start = Date.now();
720
+ const logLine = (line) => {
721
+ if (!agentLog) {
722
+ console.log(line);
723
+ return;
724
+ }
725
+ writeSync(agentLog.logFd, `${line}\n`);
726
+ };
727
+ try {
728
+ logLine('\n> Build gate (local_prep)\n');
729
+ const result = await run(pnpmRun(SCRIPTS.BUILD), { agentLog, cwd });
730
+ return { ok: result.ok, duration: Date.now() - start };
731
+ }
732
+ catch (error) {
733
+ const message = error instanceof Error ? error.message : String(error);
734
+ logLine(`⚠️ Build gate failed: ${message}`);
735
+ return { ok: false, duration: Date.now() - start };
736
+ }
737
+ }
738
+ /**
739
+ * WU-2927: Run the opt-in e2e-smoke gate. Executes
740
+ * `pnpm test:e2e --tag <local_prep.e2e_smoke.tag>` (default tag 'smoke').
741
+ */
742
+ export async function runE2eSmokeGate({ agentLog, cwd, }) {
743
+ const start = Date.now();
744
+ const logLine = (line) => {
745
+ if (!agentLog) {
746
+ console.log(line);
747
+ return;
748
+ }
749
+ writeSync(agentLog.logFd, `${line}\n`);
750
+ };
751
+ try {
752
+ const tag = getE2eSmokeTag(cwd);
753
+ logLine(`\n> E2E smoke gate (local_prep, tag=${tag})\n`);
754
+ const result = await run(pnpmRun(SCRIPTS.TEST_E2E, '--tag', tag), { agentLog, cwd });
755
+ return { ok: result.ok, duration: Date.now() - start };
756
+ }
757
+ catch (error) {
758
+ const message = error instanceof Error ? error.message : String(error);
759
+ logLine(`⚠️ E2E smoke gate failed: ${message}`);
760
+ return { ok: false, duration: Date.now() - start };
761
+ }
762
+ }
588
763
  // ── Co-change gate ────────────────────────────────────────────────────
589
764
  const CoChangeRulesConfigSchema = CoChangeRuleConfigSchema.array();
590
765
  const ConditionalCommandsConfigSchema = ConditionalCommandConfigSchema.array();
@@ -1099,4 +1274,187 @@ export async function runDocsOnlyFilteredTests({ packages, agentLog, cwd = proce
1099
1274
  const result = await run(filteredCmd, { agentLog });
1100
1275
  return { ok: result.ok, duration: Date.now() - start };
1101
1276
  }
1277
+ // ── WU-2926: Per-gate applicability + precondition helpers ─────────────
1278
+ /**
1279
+ * WU-2926: Test whether any changed file matches one of the
1280
+ * `applicability_paths` patterns. Returns `true` (applicable) when the override
1281
+ * is undefined or empty so safety-critical-test preserves today's "always
1282
+ * runs" behaviour at gate-defaults.ts:195.
1283
+ */
1284
+ export function applicabilityPathsMatch(input) {
1285
+ const { applicabilityPaths } = input;
1286
+ if (!applicabilityPaths || applicabilityPaths.length === 0) {
1287
+ // Default applicable — preserves backward-compat (no override, no narrowing).
1288
+ return true;
1289
+ }
1290
+ return micromatch.isMatch.length >= 0
1291
+ ? input.changedFiles.some((file) => micromatch.isMatch(file, applicabilityPaths))
1292
+ : false;
1293
+ }
1294
+ /**
1295
+ * WU-2926: Default safety-critical applicability check.
1296
+ *
1297
+ * Backward-compat preservation (load-bearing): with no per-repo
1298
+ * `applicability_paths` override, returns 'applicable' for every change set —
1299
+ * matching the always-runs behaviour at gate-defaults.ts:195 and
1300
+ * gates-runners.ts:743 (runSafetyCriticalTests).
1301
+ *
1302
+ * With an override (e.g. `['app/security/**', 'lib/auth/**']`), returns
1303
+ * 'applicable' only when at least one changed file matches.
1304
+ */
1305
+ export async function checkSafetyCriticalApplicability(input) {
1306
+ // Resolve applicability_paths: explicit input wins; else read from
1307
+ // software_delivery.gates.overrides.safety-critical-test.applicability_paths.
1308
+ let paths = input.applicabilityPaths;
1309
+ if (paths === undefined) {
1310
+ paths = readApplicabilityPathsOverride('safety-critical-test', input.cwd ?? process.cwd());
1311
+ }
1312
+ if (!paths || paths.length === 0) {
1313
+ return 'applicable';
1314
+ }
1315
+ let files = input.changedFiles;
1316
+ if (!files) {
1317
+ try {
1318
+ const git = createGitForPath(input.cwd ?? process.cwd());
1319
+ files = await getChangedFilesForIncremental({ git });
1320
+ }
1321
+ catch {
1322
+ files = [];
1323
+ }
1324
+ }
1325
+ return applicabilityPathsMatch({ changedFiles: files, applicabilityPaths: paths })
1326
+ ? 'applicable'
1327
+ : 'not-applicable';
1328
+ }
1329
+ /**
1330
+ * WU-2926: Read `software_delivery.gates.overrides.<gate_id>.applicability_paths`
1331
+ * from workspace.yaml. Returns undefined when no override is set so callers
1332
+ * can fall through to default-applicable behaviour.
1333
+ */
1334
+ function readApplicabilityPathsOverride(gateId, cwd) {
1335
+ try {
1336
+ const required = require;
1337
+ const cfgMod = required('@lumenflow/core/config');
1338
+ if (!cfgMod || typeof cfgMod.getConfig !== 'function')
1339
+ return undefined;
1340
+ const cfg = cfgMod.getConfig();
1341
+ const sd = cfg &&
1342
+ cfg.software_delivery;
1343
+ const gates = sd?.gates;
1344
+ const overrides = gates?.overrides;
1345
+ const entry = overrides?.[gateId];
1346
+ const ap = entry?.applicability_paths;
1347
+ if (Array.isArray(ap) && ap.every((s) => typeof s === 'string')) {
1348
+ return ap;
1349
+ }
1350
+ return undefined;
1351
+ }
1352
+ catch {
1353
+ void cwd;
1354
+ return undefined;
1355
+ }
1356
+ }
1357
+ /**
1358
+ * WU-2926: migration-verify applicability — only when migrations/.sql or
1359
+ * schema files have changed.
1360
+ */
1361
+ export async function checkMigrationVerifyApplicability(ctx) {
1362
+ try {
1363
+ const git = createGitForPath(ctx.cwd ?? process.cwd());
1364
+ const changed = await getChangedFilesForIncremental({ git });
1365
+ return shouldRunMigrationVerifyForChanges({ changedFiles: changed })
1366
+ ? 'applicable'
1367
+ : 'not-applicable';
1368
+ }
1369
+ catch {
1370
+ return 'not-applicable';
1371
+ }
1372
+ }
1373
+ /**
1374
+ * WU-2926: migration-verify preconditions — DATABASE_URL set OR PGlite available.
1375
+ *
1376
+ * PGlite presence is approximated by node_modules/@electric-sql/pglite, the
1377
+ * canonical package the migration toolchain uses for in-process Postgres in
1378
+ * agent worktrees.
1379
+ */
1380
+ export function checkMigrationVerifyPreconditions(ctx) {
1381
+ if (process.env.DATABASE_URL && process.env.DATABASE_URL.trim().length > 0) {
1382
+ return Promise.resolve('satisfied');
1383
+ }
1384
+ const cwd = ctx.cwd ?? process.cwd();
1385
+ const pgliteDir = path.join(cwd, 'node_modules', '@electric-sql', 'pglite');
1386
+ return Promise.resolve(existsSync(pgliteDir) ? 'satisfied' : 'missing');
1387
+ }
1388
+ /**
1389
+ * WU-2926: invariants gate — always applicable; preconditions satisfied when
1390
+ * an invariants config is present (best-effort detection at standard paths).
1391
+ */
1392
+ export function checkInvariantsPreconditions(ctx) {
1393
+ const cwd = ctx.cwd ?? process.cwd();
1394
+ const candidates = [
1395
+ 'invariants.yaml',
1396
+ 'invariants.yml',
1397
+ '.lumenflow/invariants.yaml',
1398
+ 'docs/operations/_frameworks/lumenflow/invariants.yaml',
1399
+ ];
1400
+ for (const rel of candidates) {
1401
+ if (existsSync(path.join(cwd, rel)))
1402
+ return Promise.resolve('satisfied');
1403
+ }
1404
+ // Fallback: invariants runner has internal defaults (the existing gate
1405
+ // already runs without explicit config). Return 'satisfied' to preserve
1406
+ // today's behaviour.
1407
+ return Promise.resolve('satisfied');
1408
+ }
1409
+ /**
1410
+ * WU-2926: lane-health applicability — only when `.lumenflow/state/lane-state.json`
1411
+ * exists. Preconditions = state path is writable.
1412
+ */
1413
+ export function checkLaneHealthApplicability(ctx) {
1414
+ const cwd = ctx.cwd ?? process.cwd();
1415
+ return Promise.resolve(existsSync(path.join(cwd, '.lumenflow', 'state', 'lane-state.json'))
1416
+ ? 'applicable'
1417
+ : 'not-applicable');
1418
+ }
1419
+ export async function checkLaneHealthPreconditions(ctx) {
1420
+ const cwd = ctx.cwd ?? process.cwd();
1421
+ const stateDir = path.join(cwd, '.lumenflow', 'state');
1422
+ try {
1423
+ await access(stateDir);
1424
+ return 'satisfied';
1425
+ }
1426
+ catch {
1427
+ return 'missing';
1428
+ }
1429
+ }
1430
+ /**
1431
+ * WU-2926: co-change applicability — only when workspace.yaml has co-change
1432
+ * rules (otherwise the gate has nothing to evaluate).
1433
+ */
1434
+ export function checkCoChangeApplicability(ctx) {
1435
+ const cwd = ctx.cwd ?? process.cwd();
1436
+ try {
1437
+ const section = getGatesSection(cwd);
1438
+ const rules = section?.co_change?.rules;
1439
+ return Promise.resolve(Array.isArray(rules) && rules.length > 0 ? 'applicable' : 'not-applicable');
1440
+ }
1441
+ catch {
1442
+ return Promise.resolve('not-applicable');
1443
+ }
1444
+ }
1445
+ /**
1446
+ * WU-2926: claim-validation applicability — only when a claimed WU YAML
1447
+ * exists for the current worktree. Without a claim, the gate has nothing to
1448
+ * validate.
1449
+ */
1450
+ export async function checkClaimValidationApplicability(ctx) {
1451
+ const cwd = ctx.cwd ?? process.cwd();
1452
+ try {
1453
+ const wuId = await detectCurrentWUForCwd(cwd);
1454
+ return wuId ? 'applicable' : 'not-applicable';
1455
+ }
1456
+ catch {
1457
+ return 'not-applicable';
1458
+ }
1459
+ }
1102
1460
  //# sourceMappingURL=gates-runners.js.map