@lumenflow/cli 5.5.0 → 5.7.12

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 (213) hide show
  1. package/README.md +42 -40
  2. package/dist/db-journal-recover.js +400 -0
  3. package/dist/db-journal-recover.js.map +1 -0
  4. package/dist/docs-sync.js +8 -3
  5. package/dist/docs-sync.js.map +1 -1
  6. package/dist/gate-defaults.js +37 -0
  7. package/dist/gate-defaults.js.map +1 -1
  8. package/dist/gates/monolithic-file-contention-guard.js +167 -0
  9. package/dist/gates/monolithic-file-contention-guard.js.map +1 -0
  10. package/dist/gates/prod-migration-drift.js +207 -0
  11. package/dist/gates/prod-migration-drift.js.map +1 -0
  12. package/dist/gates/test-over-deletion-guard.js +255 -0
  13. package/dist/gates/test-over-deletion-guard.js.map +1 -0
  14. package/dist/gates-runners.js +44 -3
  15. package/dist/gates-runners.js.map +1 -1
  16. package/dist/gates.js +3 -2
  17. package/dist/gates.js.map +1 -1
  18. package/dist/lumenflow-setup.js +144 -0
  19. package/dist/lumenflow-setup.js.map +1 -0
  20. package/dist/lumenflow-upgrade.js +2 -1
  21. package/dist/lumenflow-upgrade.js.map +1 -1
  22. package/dist/mem-create.js +10 -1
  23. package/dist/mem-create.js.map +1 -1
  24. package/dist/mem-signal.js +21 -4
  25. package/dist/mem-signal.js.map +1 -1
  26. package/dist/orchestrate-initiative.js +28 -3
  27. package/dist/orchestrate-initiative.js.map +1 -1
  28. package/dist/public-manifest.js +17 -0
  29. package/dist/public-manifest.js.map +1 -1
  30. package/dist/release.js +53 -18
  31. package/dist/release.js.map +1 -1
  32. package/dist/wu-done-gates.js +13 -9
  33. package/dist/wu-done-gates.js.map +1 -1
  34. package/dist/wu-edit-operations.js +74 -0
  35. package/dist/wu-edit-operations.js.map +1 -1
  36. package/dist/wu-edit-validators.js +58 -0
  37. package/dist/wu-edit-validators.js.map +1 -1
  38. package/dist/wu-edit.js +106 -4
  39. package/dist/wu-edit.js.map +1 -1
  40. package/dist/wu-prep.js +41 -7
  41. package/dist/wu-prep.js.map +1 -1
  42. package/dist/wu-recover.js +6 -0
  43. package/dist/wu-recover.js.map +1 -1
  44. package/dist/wu-release.js +120 -2
  45. package/dist/wu-release.js.map +1 -1
  46. package/dist/wu-sizing-validation.js +47 -17
  47. package/dist/wu-sizing-validation.js.map +1 -1
  48. package/dist/wu-status.js +33 -0
  49. package/dist/wu-status.js.map +1 -1
  50. package/package.json +13 -11
  51. package/packs/agent-runtime/package.json +1 -1
  52. package/packs/sidekick/package.json +1 -1
  53. package/packs/software-delivery/package.json +1 -1
  54. package/templates/core/AGENTS.md.template +67 -3
  55. package/templates/core/LUMENFLOW.md.template +197 -47
  56. package/packs/agent-runtime/agent-heartbeat.ts +0 -163
  57. package/packs/agent-runtime/auto-session-integration.ts +0 -888
  58. package/packs/agent-runtime/capability-factory.ts +0 -104
  59. package/packs/agent-runtime/constants.ts +0 -21
  60. package/packs/agent-runtime/delegation-registry-schema.ts +0 -220
  61. package/packs/agent-runtime/delegation-registry-store.ts +0 -269
  62. package/packs/agent-runtime/delegation-tree.ts +0 -328
  63. package/packs/agent-runtime/index.ts +0 -20
  64. package/packs/agent-runtime/manifest.ts +0 -348
  65. package/packs/agent-runtime/memory-coordination-contract.ts +0 -86
  66. package/packs/agent-runtime/orchestration.ts +0 -2027
  67. package/packs/agent-runtime/pack-registration.ts +0 -110
  68. package/packs/agent-runtime/policy-factory.ts +0 -165
  69. package/packs/agent-runtime/remote-controls/index.ts +0 -7
  70. package/packs/agent-runtime/remote-controls/operations.ts +0 -405
  71. package/packs/agent-runtime/remote-controls/port.ts +0 -48
  72. package/packs/agent-runtime/remote-controls/state-store.ts +0 -258
  73. package/packs/agent-runtime/remote-controls/types.ts +0 -105
  74. package/packs/agent-runtime/session-schema.ts +0 -467
  75. package/packs/agent-runtime/tool-impl/agent-turn-tools.ts +0 -793
  76. package/packs/agent-runtime/tool-impl/index.ts +0 -6
  77. package/packs/agent-runtime/tool-impl/provider-adapters.ts +0 -1245
  78. package/packs/agent-runtime/tool-impl/remote-controls.mock.ts +0 -256
  79. package/packs/agent-runtime/tool-impl/remote-controls.ts +0 -273
  80. package/packs/agent-runtime/tools/index.ts +0 -4
  81. package/packs/agent-runtime/tools/types.ts +0 -47
  82. package/packs/agent-runtime/turn-lifecycle-events.ts +0 -590
  83. package/packs/agent-runtime/types.ts +0 -128
  84. package/packs/agent-runtime/vitest.config.ts +0 -11
  85. package/packs/sidekick/channel-ingress.ts +0 -137
  86. package/packs/sidekick/constants.ts +0 -10
  87. package/packs/sidekick/index.ts +0 -8
  88. package/packs/sidekick/manifest-schema.ts +0 -49
  89. package/packs/sidekick/manifest.ts +0 -512
  90. package/packs/sidekick/pack-registration.ts +0 -110
  91. package/packs/sidekick/policy-factory.ts +0 -38
  92. package/packs/sidekick/sidekick-events.ts +0 -694
  93. package/packs/sidekick/src/adapters/cloud-queue.ts +0 -101
  94. package/packs/sidekick/src/adapters/control-plane-bridge.adapter.ts +0 -386
  95. package/packs/sidekick/src/adapters/filesystem-bridge.adapter.ts +0 -228
  96. package/packs/sidekick/src/domain/channel.types.ts +0 -64
  97. package/packs/sidekick/src/ports/channel-bridge.port.ts +0 -92
  98. package/packs/sidekick/src/routines/commit.ts +0 -74
  99. package/packs/sidekick/tool-impl/channel-tools.ts +0 -577
  100. package/packs/sidekick/tool-impl/channel-transports.ts +0 -75
  101. package/packs/sidekick/tool-impl/index.ts +0 -29
  102. package/packs/sidekick/tool-impl/memory-tools.ts +0 -290
  103. package/packs/sidekick/tool-impl/routine-commit.ts +0 -102
  104. package/packs/sidekick/tool-impl/routine-tools.ts +0 -440
  105. package/packs/sidekick/tool-impl/runtime-context.ts +0 -28
  106. package/packs/sidekick/tool-impl/shared.ts +0 -125
  107. package/packs/sidekick/tool-impl/storage.ts +0 -325
  108. package/packs/sidekick/tool-impl/system-tools.ts +0 -160
  109. package/packs/sidekick/tool-impl/task-tools.ts +0 -506
  110. package/packs/sidekick/tools/channel-tools.ts +0 -53
  111. package/packs/sidekick/tools/index.ts +0 -9
  112. package/packs/sidekick/tools/memory-tools.ts +0 -53
  113. package/packs/sidekick/tools/routine-tools.ts +0 -53
  114. package/packs/sidekick/tools/system-tools.ts +0 -47
  115. package/packs/sidekick/tools/task-tools.ts +0 -61
  116. package/packs/sidekick/tools/types.ts +0 -57
  117. package/packs/sidekick/vitest.config.ts +0 -11
  118. package/packs/software-delivery/constants.ts +0 -10
  119. package/packs/software-delivery/extensions.ts +0 -140
  120. package/packs/software-delivery/gate-policies.ts +0 -134
  121. package/packs/software-delivery/index.ts +0 -8
  122. package/packs/software-delivery/manifest-schema.ts +0 -268
  123. package/packs/software-delivery/manifest.ts +0 -657
  124. package/packs/software-delivery/pack-registration.ts +0 -113
  125. package/packs/software-delivery/src/commands/index.ts +0 -5
  126. package/packs/software-delivery/src/config/delivery-review-contract.ts +0 -256
  127. package/packs/software-delivery/src/config/env-accessors.ts +0 -66
  128. package/packs/software-delivery/src/config/index.ts +0 -8
  129. package/packs/software-delivery/src/config/normalize-config-keys.ts +0 -9
  130. package/packs/software-delivery/src/config/schemas/lumenflow-config-schema-types.ts +0 -460
  131. package/packs/software-delivery/src/config/workspace-reader.ts +0 -375
  132. package/packs/software-delivery/src/constants/backlog-patterns.ts +0 -31
  133. package/packs/software-delivery/src/constants/client-ids.ts +0 -19
  134. package/packs/software-delivery/src/constants/config-contract.ts +0 -7
  135. package/packs/software-delivery/src/constants/docs-layout-presets.ts +0 -50
  136. package/packs/software-delivery/src/constants/duration-constants.ts +0 -20
  137. package/packs/software-delivery/src/constants/gate-constants.ts +0 -32
  138. package/packs/software-delivery/src/constants/index.ts +0 -29
  139. package/packs/software-delivery/src/constants/lock-constants.ts +0 -35
  140. package/packs/software-delivery/src/constants/object-guards.ts +0 -12
  141. package/packs/software-delivery/src/constants/section-headings.ts +0 -107
  142. package/packs/software-delivery/src/constants/wu-cli-constants.ts +0 -500
  143. package/packs/software-delivery/src/constants/wu-domain-constants.ts +0 -466
  144. package/packs/software-delivery/src/constants/wu-git-constants.ts +0 -7
  145. package/packs/software-delivery/src/constants/wu-id-format.ts +0 -327
  146. package/packs/software-delivery/src/constants/wu-paths-constants.ts +0 -384
  147. package/packs/software-delivery/src/constants/wu-statuses.ts +0 -287
  148. package/packs/software-delivery/src/constants/wu-type-helpers.ts +0 -67
  149. package/packs/software-delivery/src/constants/wu-ui-constants.ts +0 -267
  150. package/packs/software-delivery/src/constants/wu-validation-constants.ts +0 -73
  151. package/packs/software-delivery/src/domain/index.ts +0 -5
  152. package/packs/software-delivery/src/domain/orchestration.constants.ts +0 -166
  153. package/packs/software-delivery/src/domain/orchestration.schemas.ts +0 -238
  154. package/packs/software-delivery/src/domain/orchestration.types.ts +0 -176
  155. package/packs/software-delivery/src/methodology/incremental-test.ts +0 -122
  156. package/packs/software-delivery/src/methodology/index.ts +0 -6
  157. package/packs/software-delivery/src/methodology/manual-test-validator.ts +0 -292
  158. package/packs/software-delivery/src/policy/coverage-gate.ts +0 -270
  159. package/packs/software-delivery/src/policy/gates-agent-mode.ts +0 -223
  160. package/packs/software-delivery/src/policy/gates-config-internal.ts +0 -121
  161. package/packs/software-delivery/src/policy/gates-config.ts +0 -300
  162. package/packs/software-delivery/src/policy/gates-coverage.ts +0 -356
  163. package/packs/software-delivery/src/policy/gates-presets.ts +0 -134
  164. package/packs/software-delivery/src/policy/gates-schemas.ts +0 -173
  165. package/packs/software-delivery/src/policy/index.ts +0 -22
  166. package/packs/software-delivery/src/policy/package-manager-resolver.ts +0 -319
  167. package/packs/software-delivery/src/policy/resolve-policy.ts +0 -601
  168. package/packs/software-delivery/src/ports/config.ports.ts +0 -90
  169. package/packs/software-delivery/src/ports/dashboard-renderer.port.ts +0 -125
  170. package/packs/software-delivery/src/ports/index.ts +0 -10
  171. package/packs/software-delivery/src/ports/sync-validator.ports.ts +0 -59
  172. package/packs/software-delivery/src/ports/wu-helpers.ports.ts +0 -168
  173. package/packs/software-delivery/src/ports/wu-state.ports.ts +0 -241
  174. package/packs/software-delivery/src/primitives/index.ts +0 -5
  175. package/packs/software-delivery/src/runtime/index.ts +0 -6
  176. package/packs/software-delivery/src/runtime/work-classifier.ts +0 -561
  177. package/packs/software-delivery/src/sandbox/index.ts +0 -10
  178. package/packs/software-delivery/src/sandbox/sandbox-allowlist.ts +0 -118
  179. package/packs/software-delivery/src/sandbox/sandbox-backend-linux.ts +0 -88
  180. package/packs/software-delivery/src/sandbox/sandbox-backend-macos.ts +0 -154
  181. package/packs/software-delivery/src/sandbox/sandbox-backend-windows.ts +0 -47
  182. package/packs/software-delivery/src/sandbox/sandbox-profile.ts +0 -153
  183. package/packs/software-delivery/src/schemas/index.ts +0 -5
  184. package/packs/software-delivery/src/state/date-utils.ts +0 -158
  185. package/packs/software-delivery/src/state/index.ts +0 -15
  186. package/packs/software-delivery/src/state/state-machine.ts +0 -119
  187. package/packs/software-delivery/src/state/wu-doc-types.ts +0 -51
  188. package/packs/software-delivery/src/state/wu-paths.ts +0 -381
  189. package/packs/software-delivery/src/state/wu-schema.ts +0 -1139
  190. package/packs/software-delivery/src/state/wu-state-schema.ts +0 -255
  191. package/packs/software-delivery/src/state/wu-yaml.ts +0 -338
  192. package/packs/software-delivery/tool-impl/agent-tools.ts +0 -263
  193. package/packs/software-delivery/tool-impl/delegation-tools.ts +0 -66
  194. package/packs/software-delivery/tool-impl/flow-metrics-tools.ts +0 -219
  195. package/packs/software-delivery/tool-impl/git-runner.ts +0 -113
  196. package/packs/software-delivery/tool-impl/git-tools.ts +0 -316
  197. package/packs/software-delivery/tool-impl/index.ts +0 -15
  198. package/packs/software-delivery/tool-impl/initiative-orchestration-tools.ts +0 -720
  199. package/packs/software-delivery/tool-impl/lane-lock.ts +0 -246
  200. package/packs/software-delivery/tool-impl/memory-tools.ts +0 -470
  201. package/packs/software-delivery/tool-impl/pending-runtime-tools.ts +0 -21
  202. package/packs/software-delivery/tool-impl/runtime-cli-adapter.ts +0 -329
  203. package/packs/software-delivery/tool-impl/runtime-native-tools.ts +0 -687
  204. package/packs/software-delivery/tool-impl/worker-loader.ts +0 -52
  205. package/packs/software-delivery/tool-impl/worktree-tools.ts +0 -46
  206. package/packs/software-delivery/tool-impl/wu-lifecycle-tools.ts +0 -807
  207. package/packs/software-delivery/tools/delegation-tools.ts +0 -23
  208. package/packs/software-delivery/tools/git-tools.ts +0 -55
  209. package/packs/software-delivery/tools/index.ts +0 -8
  210. package/packs/software-delivery/tools/lane-lock-tool.ts +0 -37
  211. package/packs/software-delivery/tools/types.ts +0 -71
  212. package/packs/software-delivery/tools/worktree-tools.ts +0 -49
  213. package/packs/software-delivery/vitest.config.ts +0 -11
@@ -1,292 +0,0 @@
1
- #!/usr/bin/env node
2
- // Copyright (c) 2026 Hellmai Ltd
3
- // SPDX-License-Identifier: LicenseRef-LumenFlow-Proprietary
4
- /**
5
- * Manual Test Escape Hatch Validator
6
- *
7
- * WU-1433: Restricts manual-only tests for WUs touching hex core code.
8
- * WU-2332: Require automated tests for all code files, remove lane exemptions.
9
- *
10
- * Implementation WUs must have at least one automated test (unit/e2e/integration).
11
- *
12
- * Exemptions:
13
- * - type: documentation only
14
- * - code_paths containing only documentation/config files (no code files)
15
- * - WU-2273: notes containing tdd-exception: marker (explicit opt-out)
16
- *
17
- * @see {@link packages/@lumenflow/cli/src/lib/wu-done-validators.ts} - Integration point
18
- * @see {@link config.directories.completeGuidePath} - TDD requirements
19
- */
20
-
21
- import path from 'node:path';
22
- import { TEST_TYPES, WU_TYPES } from '../constants/wu-statuses.js';
23
- import { isDocumentationType } from '../constants/wu-type-helpers.js';
24
-
25
- /** Minimal WU document shape for test validation */
26
- interface WuDocForTests {
27
- type?: string;
28
- code_paths?: unknown;
29
- tests?: unknown;
30
- test_paths?: unknown;
31
- notes?: unknown;
32
- }
33
-
34
- /**
35
- * Code file extensions that require automated tests.
36
- * @constant {string[]}
37
- */
38
- const CODE_EXTENSIONS = Object.freeze(['.js', '.ts', '.tsx', '.ts']);
39
-
40
- /**
41
- * Non-code file extensions (documentation, data, config).
42
- * @constant {string[]}
43
- */
44
- const NON_CODE_EXTENSIONS = Object.freeze(['.md', '.yaml', '.yml', '.json']);
45
-
46
- /**
47
- * Patterns that indicate a config file (even with code extensions).
48
- * @constant {RegExp[]}
49
- */
50
- const CONFIG_PATTERNS = Object.freeze([
51
- /config\./i, // vitest.config.ts, eslint.config.js
52
- /\.config\./i, // *.config.ts, *.config.js
53
- /rc\.[jt]s$/i, // .eslintrc.js, .prettierrc.ts
54
- /^\.[a-z]+rc\./i, // .eslintrc.*, .prettierrc.*
55
- ]);
56
-
57
- /**
58
- * Path prefixes for hex core code requiring automated tests.
59
- * These are the critical application layer paths.
60
- *
61
- * WU-1068: Changed from @exampleapp to @lumenflow for framework reusability.
62
- * Project-specific patterns should be configured in workspace.yaml.
63
- *
64
- * @constant {string[]}
65
- */
66
- export const HEX_CORE_CODE_PATTERNS = Object.freeze([
67
- 'packages/@lumenflow/core/',
68
- 'packages/@lumenflow/cli/',
69
- 'packages/@lumenflow/agent/',
70
- ]);
71
-
72
- /**
73
- * @deprecated Lane-based exemptions removed in WU-2332.
74
- * Test requirements are now based on file types, not lanes.
75
- * Kept for backward compatibility but no longer used.
76
- * @constant {string[]}
77
- */
78
- export const EXEMPT_LANES = Object.freeze([]);
79
-
80
- /**
81
- * WU types exempt from automated test requirement.
82
- * Only 'documentation' type is exempt - actual code changes require automated tests.
83
- *
84
- * @constant {string[]}
85
- */
86
- export const EXEMPT_TYPES = Object.freeze([WU_TYPES.DOCUMENTATION]);
87
-
88
- /**
89
- * Marker prefix in WU notes that explicitly opts out of automated test requirement.
90
- * Must match the marker used by wu:prep (packages/@lumenflow/cli/src/wu-prep.ts).
91
- *
92
- * WU-2273: Ensures wu:done and wu:prep agree on tdd-exception behavior.
93
- *
94
- * @constant {string}
95
- */
96
- const TDD_EXCEPTION_MARKER = 'tdd-exception:';
97
-
98
- /**
99
- * Determine if a file path represents a code file requiring automated tests.
100
- *
101
- * Code files are those with extensions like .ts, .ts, .tsx, .js
102
- * EXCEPT config files (vitest.config.ts, .eslintrc.js, etc.)
103
- *
104
- * @param {string} filePath - File path to check
105
- * @returns {boolean} True if the file is a code file requiring tests
106
- */
107
- export function isCodeFile(filePath: string) {
108
- if (!filePath || typeof filePath !== 'string') {
109
- return false;
110
- }
111
-
112
- // Get the filename from the path
113
- const fileName = path.basename(filePath);
114
- const ext = path.extname(filePath).toLowerCase();
115
-
116
- // Check if it's a non-code extension (docs, data)
117
- if (NON_CODE_EXTENSIONS.includes(ext)) {
118
- return false;
119
- }
120
-
121
- // Check if it's a code extension
122
- if (!CODE_EXTENSIONS.includes(ext)) {
123
- return false;
124
- }
125
-
126
- // Check if it's a config file (even with code extension)
127
- const isConfig = CONFIG_PATTERNS.some((pattern) => pattern.test(fileName));
128
- if (isConfig) {
129
- return false;
130
- }
131
-
132
- return true;
133
- }
134
-
135
- /**
136
- * Check if code_paths contains any hex core code.
137
- *
138
- * @param {string[]|null|undefined} codePaths - Array of file paths from WU YAML
139
- * @returns {boolean} True if any path is in hex core layer
140
- */
141
- export function containsHexCoreCode(codePaths: unknown) {
142
- if (!codePaths || !Array.isArray(codePaths) || codePaths.length === 0) {
143
- return false;
144
- }
145
-
146
- return codePaths.some((path) => {
147
- if (!path || typeof path !== 'string') return false;
148
- return HEX_CORE_CODE_PATTERNS.some((pattern) => path.startsWith(pattern));
149
- });
150
- }
151
-
152
- /**
153
- * Check if WU notes contain an explicit tdd-exception marker.
154
- *
155
- * Supports both string and array-of-strings notes formats.
156
- * Uses case-insensitive matching consistent with wu:prep behavior.
157
- *
158
- * WU-2273: Extracted to share logic with isExemptFromAutomatedTests.
159
- *
160
- * @param {unknown} notes - WU notes field (string, string[], or other)
161
- * @returns {boolean} True if notes contain the tdd-exception marker
162
- */
163
- function hasDocumentedTddException(notes: unknown): boolean {
164
- if (typeof notes === 'string') {
165
- return notes.toLowerCase().includes(TDD_EXCEPTION_MARKER);
166
- }
167
-
168
- if (Array.isArray(notes)) {
169
- return notes.some((entry) => typeof entry === 'string' && hasDocumentedTddException(entry));
170
- }
171
-
172
- return false;
173
- }
174
-
175
- /**
176
- * Check if a WU is exempt from automated test requirement.
177
- *
178
- * WU-2332: Only type: 'documentation' is exempt.
179
- * WU-2273: Also exempt if notes contain tdd-exception: marker,
180
- * aligning wu:done with wu:prep behavior.
181
- *
182
- * Lane-based exemptions have been removed - test requirements
183
- * are now based on file types in code_paths.
184
- *
185
- * @param {object} doc - WU YAML document
186
- * @returns {boolean} True if WU is exempt
187
- */
188
- export function isExemptFromAutomatedTests(doc: WuDocForTests | null | undefined) {
189
- if (!doc) return false;
190
-
191
- // type: documentation is exempt
192
- const type = doc.type || '';
193
- if (isDocumentationType(type)) {
194
- return true;
195
- }
196
-
197
- // WU-2273: tdd-exception: in notes is an explicit opt-out
198
- if (hasDocumentedTddException(doc.notes)) {
199
- return true;
200
- }
201
-
202
- return false;
203
- }
204
-
205
- /**
206
- * Check if WU has at least one automated test.
207
- *
208
- * @param {object} tests - Tests object from WU YAML
209
- * @returns {boolean} True if has at least one automated test
210
- */
211
- function hasAutomatedTest(tests: unknown): boolean {
212
- if (!tests || typeof tests !== 'object') return false;
213
-
214
- const hasItems = (arr: unknown) => Array.isArray(arr) && arr.length > 0;
215
- const t = tests as Record<string, unknown>;
216
-
217
- return (
218
- hasItems(t[TEST_TYPES.UNIT]) ||
219
- hasItems(t[TEST_TYPES.E2E]) ||
220
- hasItems(t[TEST_TYPES.INTEGRATION])
221
- );
222
- }
223
-
224
- /**
225
- * Check if any code_paths contain actual code files (not docs/config).
226
- *
227
- * @param {string[]} codePaths - Array of file paths from WU YAML
228
- * @returns {{ hasCodeFiles: boolean, codeFiles: string[] }} Result with list of code files
229
- */
230
- function analyzeCodePaths(codePaths: unknown) {
231
- if (!codePaths || !Array.isArray(codePaths) || codePaths.length === 0) {
232
- return { hasCodeFiles: false, codeFiles: [] };
233
- }
234
-
235
- const codeFiles = codePaths.filter((p) => isCodeFile(p));
236
- return {
237
- hasCodeFiles: codeFiles.length > 0,
238
- codeFiles,
239
- };
240
- }
241
-
242
- /**
243
- * Validate automated test requirement for WU.
244
- *
245
- * WU-2332: Requirements are based on file types, not lanes.
246
- * - WUs with ANY code files in code_paths must have automated tests
247
- * - WUs with only docs/config files can use manual-only tests
248
- * - type: 'documentation' is always exempt
249
- *
250
- * @param {object} doc - WU YAML document
251
- * @returns {{ valid: boolean, errors: string[] }} Validation result
252
- */
253
- export function validateAutomatedTestRequirement(doc: WuDocForTests | null | undefined) {
254
- const errors = [];
255
-
256
- if (!doc) {
257
- return { valid: true, errors: [] };
258
- }
259
-
260
- // Check if WU is exempt by type
261
- if (isExemptFromAutomatedTests(doc)) {
262
- return { valid: true, errors: [] };
263
- }
264
-
265
- // Analyze code_paths for actual code files
266
- const codePaths = doc.code_paths || [];
267
- const { hasCodeFiles, codeFiles } = analyzeCodePaths(codePaths);
268
-
269
- // If no code files, manual tests are fine
270
- if (!hasCodeFiles) {
271
- return { valid: true, errors: [] };
272
- }
273
-
274
- // WU has code files - require automated tests
275
- // Support both tests: (current) and test_paths: (legacy)
276
- const tests = doc.tests || doc.test_paths || {};
277
- if (!hasAutomatedTest(tests)) {
278
- errors.push(
279
- `WU modifies code files but has no automated tests.\n` +
280
- ` Code files: ${codeFiles.join(', ')}\n` +
281
- ` Required: At least one automated test (unit, e2e, or integration)\n` +
282
- ` Manual-only tests are not allowed for code changes.\n\n` +
283
- ` Fix options:\n` +
284
- ` 1. Add tests to tests.unit, tests.e2e, or tests.integration in WU YAML\n` +
285
- ` 2. If this WU legitimately does not need automated tests (UI, config, templates),\n` +
286
- ` add tdd-exception: <reason> to the WU notes field`,
287
- );
288
- return { valid: false, errors };
289
- }
290
-
291
- return { valid: true, errors: [] };
292
- }
@@ -1,270 +0,0 @@
1
- #!/usr/bin/env node
2
- // Copyright (c) 2026 Hellmai Ltd
3
- // SPDX-License-Identifier: LicenseRef-LumenFlow-Proprietary
4
- /**
5
- * Coverage Gate for Quality Gates
6
- *
7
- * WU-1433: Adds coverage checking to gates with configurable mode (warn/block).
8
- * Enforces ≥90% coverage on hex core files (application layer).
9
- *
10
- * Mode flag allows gradual rollout:
11
- * - warn: Log failures but don't block (default)
12
- * - block: Fail the gate if thresholds not met
13
- *
14
- * @see {@link packages/@lumenflow/cli/src/gates.ts} - Integration point
15
- * @see {@link vitest.config.ts} - Coverage thresholds
16
- */
17
-
18
- /* eslint-disable security/detect-non-literal-fs-filename, security/detect-object-injection */
19
- import { readFileSync, existsSync } from 'node:fs';
20
- import { EMOJI, STRING_LITERALS } from '../constants/wu-ui-constants.js';
21
- import { DEFAULT_MIN_COVERAGE } from '../constants/gate-constants.js';
22
-
23
- /**
24
- * Coverage gate modes
25
- * @constant
26
- */
27
- export const COVERAGE_GATE_MODES = Object.freeze({
28
- /** Log warnings but don't fail the gate */
29
- WARN: 'warn',
30
- /** Fail the gate if thresholds not met */
31
- BLOCK: 'block',
32
- });
33
-
34
- /**
35
- * Glob patterns for hex core files that require ≥90% coverage.
36
- * These are the critical application layer files.
37
- *
38
- * WU-1068: Changed from @exampleapp to @lumenflow for framework reusability.
39
- * Project-specific patterns should be configured in workspace.yaml.
40
- *
41
- * @constant {string[]}
42
- */
43
- export const HEX_CORE_PATTERNS = Object.freeze([
44
- 'packages/@lumenflow/core/',
45
- 'packages/@lumenflow/cli/',
46
- ]);
47
-
48
- /**
49
- * Default coverage threshold for hex core files (percentage)
50
- * WU-1262: Kept for backwards compatibility.
51
- * WU-2044: Now delegates to DEFAULT_MIN_COVERAGE from gate-constants.ts (DRY).
52
- * The actual threshold is determined by resolveCoverageConfig() based on methodology.
53
- * @constant {number}
54
- */
55
- export const COVERAGE_THRESHOLD = DEFAULT_MIN_COVERAGE;
56
-
57
- /**
58
- * Default path to coverage summary JSON
59
- * @constant {string}
60
- */
61
- export const DEFAULT_COVERAGE_PATH = 'coverage/coverage-summary.json';
62
-
63
- /**
64
- * Check if a file path is in the hex core layer.
65
- *
66
- * WU-2448: Coverage reporters may emit absolute paths (e.g., <repo-root>/packages/...)
67
- * or file:// URLs; use substring matching so hex-core checks still apply.
68
- *
69
- * @param {string|null|undefined} filePath - File path to check
70
- * @returns {boolean} True if file is in hex core layer
71
- */
72
- export function isHexCoreFile(filePath: UnsafeAny) {
73
- if (!filePath || typeof filePath !== 'string') {
74
- return false;
75
- }
76
-
77
- // Normalize backslashes to forward slashes for cross-platform compatibility
78
- const normalizedPath = filePath.replace(/\\/g, '/');
79
- return HEX_CORE_PATTERNS.some((pattern) => normalizedPath.includes(pattern));
80
- }
81
-
82
- /**
83
- * Parse coverage JSON file.
84
- *
85
- * @param {string} coveragePath - Path to coverage-summary.json
86
- * @returns {object|null} Parsed coverage data or null if invalid
87
- */
88
- export function parseCoverageJson(coveragePath: UnsafeAny) {
89
- if (!existsSync(coveragePath)) {
90
- return null;
91
- }
92
-
93
- try {
94
- const content = readFileSync(coveragePath, { encoding: 'utf-8' });
95
- const data = JSON.parse(content);
96
-
97
- // Transform to consistent format
98
- const files: Record<string, unknown> = {};
99
- for (const [key, value] of Object.entries(data)) {
100
- if (key === 'total') continue;
101
- files[key] = value;
102
- }
103
-
104
- return {
105
- total: data.total,
106
- files,
107
- };
108
- } catch {
109
- return null;
110
- }
111
- }
112
-
113
- /**
114
- * Check if coverage meets thresholds for hex core files.
115
- *
116
- * @param {object|null} coverageData - Parsed coverage data
117
- * @param {number} [threshold] - Coverage threshold to use (defaults to COVERAGE_THRESHOLD)
118
- * @returns {{ pass: boolean, failures: Array<{ file: string, actual: number, threshold: number, metric: string }> }}
119
- */
120
- export function checkCoverageThresholds(coverageData: UnsafeAny, threshold?: number) {
121
- if (!coverageData || !coverageData.files) {
122
- return { pass: true, failures: [] };
123
- }
124
-
125
- // WU-1262: Use provided threshold or fall back to constant
126
- const effectiveThreshold = threshold ?? COVERAGE_THRESHOLD;
127
- const failures = [];
128
-
129
- for (const [file, metricsValue] of Object.entries(coverageData.files)) {
130
- if (!isHexCoreFile(file)) {
131
- continue;
132
- }
133
-
134
- // Check lines coverage (primary metric)
135
- const metrics = metricsValue as { lines?: { pct: number } };
136
- const linesCoverage = metrics.lines?.pct ?? 0;
137
- if (linesCoverage < effectiveThreshold) {
138
- failures.push({
139
- file,
140
- actual: linesCoverage,
141
- threshold: effectiveThreshold,
142
- metric: 'lines',
143
- });
144
- }
145
- }
146
-
147
- return {
148
- pass: failures.length === 0,
149
- failures,
150
- };
151
- }
152
-
153
- /**
154
- * Format coverage data for display.
155
- *
156
- * @param {object|null} coverageData - Parsed coverage data
157
- * @returns {string} Formatted output string
158
- */
159
- export function formatCoverageDelta(coverageData: UnsafeAny) {
160
- if (!coverageData) {
161
- return '';
162
- }
163
-
164
- const lines = [];
165
- const totalPct = coverageData.total?.lines?.pct ?? 0;
166
-
167
- lines.push(
168
- `${STRING_LITERALS.NEWLINE}Coverage Summary: ${totalPct.toFixed(1)}% lines${STRING_LITERALS.NEWLINE}`,
169
- );
170
-
171
- // Show hex core files
172
- const hexCoreFiles = Object.entries(coverageData.files || {}).filter(([file]) =>
173
- isHexCoreFile(file),
174
- );
175
-
176
- if (hexCoreFiles.length > 0) {
177
- lines.push('Hex Core Files:');
178
- for (const [file, metricsValue] of hexCoreFiles) {
179
- const metrics = metricsValue as { lines?: { pct: number } };
180
- const pct = metrics.lines?.pct ?? 0;
181
- const status = pct >= COVERAGE_THRESHOLD ? EMOJI.SUCCESS : EMOJI.FAILURE;
182
- const shortFile = file.replace('packages/@lumenflow/', '');
183
- lines.push(` ${status} ${shortFile}: ${pct.toFixed(1)}%`);
184
- }
185
- }
186
-
187
- return lines.join(STRING_LITERALS.NEWLINE);
188
- }
189
-
190
- /**
191
- * Logger interface for coverage gate output
192
- */
193
- interface CoverageGateLogger {
194
- log: (...args: unknown[]) => void;
195
- }
196
-
197
- /**
198
- * Options for running coverage gate
199
- */
200
- export interface CoverageGateOptions {
201
- /** Gate mode ('warn' or 'block') */
202
- mode?: string;
203
- /** Path to coverage JSON */
204
- coveragePath?: string;
205
- /** Logger for output */
206
- logger?: CoverageGateLogger;
207
- /**
208
- * WU-1262: Coverage threshold override (0-100).
209
- * When provided, overrides the default COVERAGE_THRESHOLD constant.
210
- * This is typically populated from resolveCoverageConfig().
211
- */
212
- threshold?: number;
213
- }
214
-
215
- /**
216
- * Run coverage gate.
217
- *
218
- * @param {CoverageGateOptions} options - Gate options
219
- * @returns {Promise<{ ok: boolean, mode: string, duration: number, message: string }>}
220
- */
221
- export async function runCoverageGate(options: CoverageGateOptions = {}) {
222
- const start = Date.now();
223
- const mode = options.mode || COVERAGE_GATE_MODES.WARN;
224
- const coveragePath = options.coveragePath || DEFAULT_COVERAGE_PATH;
225
- const logger =
226
- options.logger && typeof options.logger.log === 'function' ? options.logger : console;
227
- // WU-1262: Use provided threshold or default constant
228
- const threshold = options.threshold ?? COVERAGE_THRESHOLD;
229
-
230
- // Parse coverage data
231
- const coverageData = parseCoverageJson(coveragePath);
232
-
233
- if (!coverageData) {
234
- const duration = Date.now() - start;
235
- logger.log(`\n${EMOJI.WARNING} Coverage gate: No coverage data found at ${coveragePath}`);
236
- logger.log(' Run tests with coverage first: pnpm test:coverage\n');
237
- return { ok: true, mode, duration, message: 'No coverage data' };
238
- }
239
-
240
- // Check thresholds (WU-1262: pass resolved threshold)
241
- const { pass, failures } = checkCoverageThresholds(coverageData, threshold);
242
-
243
- // Format and display
244
- const output = formatCoverageDelta(coverageData);
245
- logger.log(output);
246
-
247
- const duration = Date.now() - start;
248
-
249
- if (!pass) {
250
- logger.log(`\n${EMOJI.FAILURE} Coverage below ${threshold}% for hex core files:`);
251
- for (const failure of failures) {
252
- const shortFile = failure.file.replace('packages/@lumenflow/', '');
253
- logger.log(
254
- ` - ${shortFile}: ${failure.actual.toFixed(1)}% (requires ${failure.threshold}%)`,
255
- );
256
- }
257
-
258
- if (mode === COVERAGE_GATE_MODES.BLOCK) {
259
- logger.log(`\n${EMOJI.FAILURE} Coverage gate FAILED (mode: block)\n`);
260
- return { ok: false, mode, duration, message: 'Coverage threshold not met' };
261
- } else {
262
- logger.log(`\n${EMOJI.WARNING} Coverage gate WARNING (mode: warn)\n`);
263
- logger.log(' Note: This will become blocking in future. Fix coverage now.\n');
264
- return { ok: true, mode, duration, message: 'Coverage warning' };
265
- }
266
- }
267
-
268
- logger.log(`\n${EMOJI.SUCCESS} Coverage gate passed\n`);
269
- return { ok: true, mode, duration, message: 'Coverage OK' };
270
- }