@lumenflow/cli 5.4.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 (227) 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 +191 -9
  7. package/dist/gate-defaults.js.map +1 -1
  8. package/dist/gate-registry.js.map +1 -1
  9. package/dist/gates/monolithic-file-contention-guard.js +167 -0
  10. package/dist/gates/monolithic-file-contention-guard.js.map +1 -0
  11. package/dist/gates/prod-migration-drift.js +207 -0
  12. package/dist/gates/prod-migration-drift.js.map +1 -0
  13. package/dist/gates/test-over-deletion-guard.js +255 -0
  14. package/dist/gates/test-over-deletion-guard.js.map +1 -0
  15. package/dist/gates-runners.js +401 -2
  16. package/dist/gates-runners.js.map +1 -1
  17. package/dist/gates.js +349 -4
  18. package/dist/gates.js.map +1 -1
  19. package/dist/lumenflow-setup.js +144 -0
  20. package/dist/lumenflow-setup.js.map +1 -0
  21. package/dist/lumenflow-upgrade.js +2 -1
  22. package/dist/lumenflow-upgrade.js.map +1 -1
  23. package/dist/mem-create.js +10 -1
  24. package/dist/mem-create.js.map +1 -1
  25. package/dist/mem-signal.js +21 -4
  26. package/dist/mem-signal.js.map +1 -1
  27. package/dist/metrics-cli.js +19 -2
  28. package/dist/metrics-cli.js.map +1 -1
  29. package/dist/metrics-snapshot.js +25 -2
  30. package/dist/metrics-snapshot.js.map +1 -1
  31. package/dist/orchestrate-initiative.js +28 -3
  32. package/dist/orchestrate-initiative.js.map +1 -1
  33. package/dist/public-manifest.js +17 -0
  34. package/dist/public-manifest.js.map +1 -1
  35. package/dist/release.js +53 -18
  36. package/dist/release.js.map +1 -1
  37. package/dist/wu-done-gates.js +121 -8
  38. package/dist/wu-done-gates.js.map +1 -1
  39. package/dist/wu-done.js +30 -6
  40. package/dist/wu-done.js.map +1 -1
  41. package/dist/wu-edit-operations.js +74 -0
  42. package/dist/wu-edit-operations.js.map +1 -1
  43. package/dist/wu-edit-validators.js +58 -0
  44. package/dist/wu-edit-validators.js.map +1 -1
  45. package/dist/wu-edit.js +106 -4
  46. package/dist/wu-edit.js.map +1 -1
  47. package/dist/wu-prep.js +132 -8
  48. package/dist/wu-prep.js.map +1 -1
  49. package/dist/wu-recover.js +6 -0
  50. package/dist/wu-recover.js.map +1 -1
  51. package/dist/wu-release.js +120 -2
  52. package/dist/wu-release.js.map +1 -1
  53. package/dist/wu-sizing-validation.js +47 -17
  54. package/dist/wu-sizing-validation.js.map +1 -1
  55. package/dist/wu-status.js +33 -0
  56. package/dist/wu-status.js.map +1 -1
  57. package/package.json +13 -11
  58. package/packs/agent-runtime/package.json +1 -1
  59. package/packs/sidekick/package.json +1 -1
  60. package/packs/software-delivery/package.json +1 -1
  61. package/templates/core/AGENTS.md.template +162 -26
  62. package/templates/core/LUMENFLOW.md.template +381 -70
  63. package/templates/core/ai/onboarding/agent-invocation-guide.md.template +0 -5
  64. package/templates/core/ai/onboarding/agent-safety-card.md.template +63 -17
  65. package/templates/core/ai/onboarding/initiative-orchestration.md.template +4 -0
  66. package/templates/core/ai/onboarding/release-process.md.template +7 -7
  67. package/templates/core/ai/onboarding/vendor-support.md.template +74 -10
  68. package/templates/vendors/claude/.claude/skills/frontend-design/SKILL.md.template +1 -1
  69. package/templates/vendors/claude/.claude/skills/wu-lifecycle/SKILL.md.template +28 -0
  70. package/packs/agent-runtime/agent-heartbeat.ts +0 -163
  71. package/packs/agent-runtime/auto-session-integration.ts +0 -888
  72. package/packs/agent-runtime/capability-factory.ts +0 -104
  73. package/packs/agent-runtime/constants.ts +0 -21
  74. package/packs/agent-runtime/delegation-registry-schema.ts +0 -220
  75. package/packs/agent-runtime/delegation-registry-store.ts +0 -269
  76. package/packs/agent-runtime/delegation-tree.ts +0 -328
  77. package/packs/agent-runtime/index.ts +0 -20
  78. package/packs/agent-runtime/manifest.ts +0 -348
  79. package/packs/agent-runtime/memory-coordination-contract.ts +0 -86
  80. package/packs/agent-runtime/orchestration.ts +0 -2027
  81. package/packs/agent-runtime/pack-registration.ts +0 -110
  82. package/packs/agent-runtime/policy-factory.ts +0 -165
  83. package/packs/agent-runtime/remote-controls/index.ts +0 -7
  84. package/packs/agent-runtime/remote-controls/operations.ts +0 -405
  85. package/packs/agent-runtime/remote-controls/port.ts +0 -48
  86. package/packs/agent-runtime/remote-controls/state-store.ts +0 -258
  87. package/packs/agent-runtime/remote-controls/types.ts +0 -105
  88. package/packs/agent-runtime/session-schema.ts +0 -467
  89. package/packs/agent-runtime/tool-impl/agent-turn-tools.ts +0 -793
  90. package/packs/agent-runtime/tool-impl/index.ts +0 -6
  91. package/packs/agent-runtime/tool-impl/provider-adapters.ts +0 -1245
  92. package/packs/agent-runtime/tool-impl/remote-controls.mock.ts +0 -256
  93. package/packs/agent-runtime/tool-impl/remote-controls.ts +0 -273
  94. package/packs/agent-runtime/tools/index.ts +0 -4
  95. package/packs/agent-runtime/tools/types.ts +0 -47
  96. package/packs/agent-runtime/turn-lifecycle-events.ts +0 -590
  97. package/packs/agent-runtime/types.ts +0 -128
  98. package/packs/agent-runtime/vitest.config.ts +0 -11
  99. package/packs/sidekick/channel-ingress.ts +0 -137
  100. package/packs/sidekick/constants.ts +0 -10
  101. package/packs/sidekick/index.ts +0 -8
  102. package/packs/sidekick/manifest-schema.ts +0 -49
  103. package/packs/sidekick/manifest.ts +0 -512
  104. package/packs/sidekick/pack-registration.ts +0 -110
  105. package/packs/sidekick/policy-factory.ts +0 -38
  106. package/packs/sidekick/sidekick-events.ts +0 -694
  107. package/packs/sidekick/src/adapters/cloud-queue.ts +0 -101
  108. package/packs/sidekick/src/adapters/control-plane-bridge.adapter.ts +0 -386
  109. package/packs/sidekick/src/adapters/filesystem-bridge.adapter.ts +0 -228
  110. package/packs/sidekick/src/domain/channel.types.ts +0 -64
  111. package/packs/sidekick/src/ports/channel-bridge.port.ts +0 -92
  112. package/packs/sidekick/src/routines/commit.ts +0 -74
  113. package/packs/sidekick/tool-impl/channel-tools.ts +0 -577
  114. package/packs/sidekick/tool-impl/channel-transports.ts +0 -75
  115. package/packs/sidekick/tool-impl/index.ts +0 -29
  116. package/packs/sidekick/tool-impl/memory-tools.ts +0 -290
  117. package/packs/sidekick/tool-impl/routine-commit.ts +0 -102
  118. package/packs/sidekick/tool-impl/routine-tools.ts +0 -440
  119. package/packs/sidekick/tool-impl/runtime-context.ts +0 -28
  120. package/packs/sidekick/tool-impl/shared.ts +0 -125
  121. package/packs/sidekick/tool-impl/storage.ts +0 -325
  122. package/packs/sidekick/tool-impl/system-tools.ts +0 -160
  123. package/packs/sidekick/tool-impl/task-tools.ts +0 -506
  124. package/packs/sidekick/tools/channel-tools.ts +0 -53
  125. package/packs/sidekick/tools/index.ts +0 -9
  126. package/packs/sidekick/tools/memory-tools.ts +0 -53
  127. package/packs/sidekick/tools/routine-tools.ts +0 -53
  128. package/packs/sidekick/tools/system-tools.ts +0 -47
  129. package/packs/sidekick/tools/task-tools.ts +0 -61
  130. package/packs/sidekick/tools/types.ts +0 -57
  131. package/packs/sidekick/vitest.config.ts +0 -11
  132. package/packs/software-delivery/constants.ts +0 -10
  133. package/packs/software-delivery/extensions.ts +0 -140
  134. package/packs/software-delivery/gate-policies.ts +0 -134
  135. package/packs/software-delivery/index.ts +0 -8
  136. package/packs/software-delivery/manifest-schema.ts +0 -268
  137. package/packs/software-delivery/manifest.ts +0 -657
  138. package/packs/software-delivery/pack-registration.ts +0 -113
  139. package/packs/software-delivery/src/commands/index.ts +0 -5
  140. package/packs/software-delivery/src/config/delivery-review-contract.ts +0 -256
  141. package/packs/software-delivery/src/config/env-accessors.ts +0 -66
  142. package/packs/software-delivery/src/config/index.ts +0 -8
  143. package/packs/software-delivery/src/config/normalize-config-keys.ts +0 -9
  144. package/packs/software-delivery/src/config/schemas/lumenflow-config-schema-types.ts +0 -460
  145. package/packs/software-delivery/src/config/workspace-reader.ts +0 -375
  146. package/packs/software-delivery/src/constants/backlog-patterns.ts +0 -31
  147. package/packs/software-delivery/src/constants/client-ids.ts +0 -19
  148. package/packs/software-delivery/src/constants/config-contract.ts +0 -7
  149. package/packs/software-delivery/src/constants/docs-layout-presets.ts +0 -50
  150. package/packs/software-delivery/src/constants/duration-constants.ts +0 -20
  151. package/packs/software-delivery/src/constants/gate-constants.ts +0 -32
  152. package/packs/software-delivery/src/constants/index.ts +0 -29
  153. package/packs/software-delivery/src/constants/lock-constants.ts +0 -35
  154. package/packs/software-delivery/src/constants/object-guards.ts +0 -12
  155. package/packs/software-delivery/src/constants/section-headings.ts +0 -107
  156. package/packs/software-delivery/src/constants/wu-cli-constants.ts +0 -488
  157. package/packs/software-delivery/src/constants/wu-domain-constants.ts +0 -466
  158. package/packs/software-delivery/src/constants/wu-git-constants.ts +0 -7
  159. package/packs/software-delivery/src/constants/wu-id-format.ts +0 -327
  160. package/packs/software-delivery/src/constants/wu-paths-constants.ts +0 -384
  161. package/packs/software-delivery/src/constants/wu-statuses.ts +0 -287
  162. package/packs/software-delivery/src/constants/wu-type-helpers.ts +0 -67
  163. package/packs/software-delivery/src/constants/wu-ui-constants.ts +0 -267
  164. package/packs/software-delivery/src/constants/wu-validation-constants.ts +0 -73
  165. package/packs/software-delivery/src/domain/index.ts +0 -5
  166. package/packs/software-delivery/src/domain/orchestration.constants.ts +0 -166
  167. package/packs/software-delivery/src/domain/orchestration.schemas.ts +0 -238
  168. package/packs/software-delivery/src/domain/orchestration.types.ts +0 -176
  169. package/packs/software-delivery/src/methodology/incremental-test.ts +0 -122
  170. package/packs/software-delivery/src/methodology/index.ts +0 -6
  171. package/packs/software-delivery/src/methodology/manual-test-validator.ts +0 -292
  172. package/packs/software-delivery/src/policy/coverage-gate.ts +0 -270
  173. package/packs/software-delivery/src/policy/gates-agent-mode.ts +0 -223
  174. package/packs/software-delivery/src/policy/gates-config-internal.ts +0 -121
  175. package/packs/software-delivery/src/policy/gates-config.ts +0 -300
  176. package/packs/software-delivery/src/policy/gates-coverage.ts +0 -356
  177. package/packs/software-delivery/src/policy/gates-presets.ts +0 -134
  178. package/packs/software-delivery/src/policy/gates-schemas.ts +0 -173
  179. package/packs/software-delivery/src/policy/index.ts +0 -22
  180. package/packs/software-delivery/src/policy/package-manager-resolver.ts +0 -319
  181. package/packs/software-delivery/src/policy/resolve-policy.ts +0 -601
  182. package/packs/software-delivery/src/ports/config.ports.ts +0 -90
  183. package/packs/software-delivery/src/ports/dashboard-renderer.port.ts +0 -125
  184. package/packs/software-delivery/src/ports/index.ts +0 -10
  185. package/packs/software-delivery/src/ports/sync-validator.ports.ts +0 -59
  186. package/packs/software-delivery/src/ports/wu-helpers.ports.ts +0 -168
  187. package/packs/software-delivery/src/ports/wu-state.ports.ts +0 -241
  188. package/packs/software-delivery/src/primitives/index.ts +0 -5
  189. package/packs/software-delivery/src/runtime/index.ts +0 -6
  190. package/packs/software-delivery/src/runtime/work-classifier.ts +0 -561
  191. package/packs/software-delivery/src/sandbox/index.ts +0 -10
  192. package/packs/software-delivery/src/sandbox/sandbox-allowlist.ts +0 -118
  193. package/packs/software-delivery/src/sandbox/sandbox-backend-linux.ts +0 -88
  194. package/packs/software-delivery/src/sandbox/sandbox-backend-macos.ts +0 -154
  195. package/packs/software-delivery/src/sandbox/sandbox-backend-windows.ts +0 -47
  196. package/packs/software-delivery/src/sandbox/sandbox-profile.ts +0 -153
  197. package/packs/software-delivery/src/schemas/index.ts +0 -5
  198. package/packs/software-delivery/src/state/date-utils.ts +0 -158
  199. package/packs/software-delivery/src/state/index.ts +0 -15
  200. package/packs/software-delivery/src/state/state-machine.ts +0 -119
  201. package/packs/software-delivery/src/state/wu-doc-types.ts +0 -51
  202. package/packs/software-delivery/src/state/wu-paths.ts +0 -381
  203. package/packs/software-delivery/src/state/wu-schema.ts +0 -1139
  204. package/packs/software-delivery/src/state/wu-state-schema.ts +0 -255
  205. package/packs/software-delivery/src/state/wu-yaml.ts +0 -338
  206. package/packs/software-delivery/tool-impl/agent-tools.ts +0 -263
  207. package/packs/software-delivery/tool-impl/delegation-tools.ts +0 -66
  208. package/packs/software-delivery/tool-impl/flow-metrics-tools.ts +0 -219
  209. package/packs/software-delivery/tool-impl/git-runner.ts +0 -113
  210. package/packs/software-delivery/tool-impl/git-tools.ts +0 -316
  211. package/packs/software-delivery/tool-impl/index.ts +0 -15
  212. package/packs/software-delivery/tool-impl/initiative-orchestration-tools.ts +0 -720
  213. package/packs/software-delivery/tool-impl/lane-lock.ts +0 -246
  214. package/packs/software-delivery/tool-impl/memory-tools.ts +0 -470
  215. package/packs/software-delivery/tool-impl/pending-runtime-tools.ts +0 -21
  216. package/packs/software-delivery/tool-impl/runtime-cli-adapter.ts +0 -329
  217. package/packs/software-delivery/tool-impl/runtime-native-tools.ts +0 -687
  218. package/packs/software-delivery/tool-impl/worker-loader.ts +0 -52
  219. package/packs/software-delivery/tool-impl/worktree-tools.ts +0 -46
  220. package/packs/software-delivery/tool-impl/wu-lifecycle-tools.ts +0 -807
  221. package/packs/software-delivery/tools/delegation-tools.ts +0 -23
  222. package/packs/software-delivery/tools/git-tools.ts +0 -55
  223. package/packs/software-delivery/tools/index.ts +0 -8
  224. package/packs/software-delivery/tools/lane-lock-tool.ts +0 -37
  225. package/packs/software-delivery/tools/types.ts +0 -71
  226. package/packs/software-delivery/tools/worktree-tools.ts +0 -49
  227. 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
- }