@lumenflow/cli 3.12.4 → 3.12.6

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 (125) hide show
  1. package/dist/chunk-2D2VOCA4.js +37 -0
  2. package/dist/chunk-2D5KFYGX.js +284 -0
  3. package/dist/chunk-2GXVIN57.js +14072 -0
  4. package/dist/chunk-2MQ7HZWZ.js +26 -0
  5. package/dist/chunk-2UFQ3A3C.js +643 -0
  6. package/dist/chunk-3RG5ZIWI.js +10 -0
  7. package/dist/chunk-4N74J3UT.js +15 -0
  8. package/dist/chunk-5GTOXFYR.js +392 -0
  9. package/dist/chunk-5VY6MQMC.js +240 -0
  10. package/dist/chunk-67XVPMRY.js +1297 -0
  11. package/dist/chunk-6HO4GWJE.js +164 -0
  12. package/dist/chunk-6W5XHWYV.js +1890 -0
  13. package/dist/chunk-6X4EMYJQ.js +64 -0
  14. package/dist/chunk-6XYXI2NQ.js +772 -0
  15. package/dist/chunk-7ANSOV6Q.js +285 -0
  16. package/dist/chunk-A624LFLB.js +1380 -0
  17. package/dist/chunk-ADN5NHG4.js +126 -0
  18. package/dist/chunk-B7YJYJKG.js +33 -0
  19. package/dist/chunk-CCLHCPKG.js +210 -0
  20. package/dist/chunk-CK36VROC.js +1584 -0
  21. package/dist/chunk-D3UOFRSB.js +81 -0
  22. package/dist/chunk-DFR4DJBM.js +230 -0
  23. package/dist/chunk-DSYBDHYH.js +79 -0
  24. package/dist/chunk-DWMLTXKQ.js +1176 -0
  25. package/dist/chunk-E3REJTAJ.js +28 -0
  26. package/dist/chunk-EA3IVO64.js +633 -0
  27. package/dist/chunk-EK2AKZKD.js +55 -0
  28. package/dist/chunk-ELD7JTTT.js +343 -0
  29. package/dist/chunk-EX6TT2XI.js +195 -0
  30. package/dist/chunk-EXINSFZE.js +82 -0
  31. package/dist/chunk-EZ6ZBYBM.js +510 -0
  32. package/dist/chunk-FBKAPTJ2.js +16 -0
  33. package/dist/chunk-FVLV5RYH.js +1118 -0
  34. package/dist/chunk-GDNSBQVK.js +2485 -0
  35. package/dist/chunk-GPQHMBNN.js +278 -0
  36. package/dist/chunk-GTFJB67L.js +68 -0
  37. package/dist/chunk-HANJXVKW.js +1127 -0
  38. package/dist/chunk-HEVS5YLD.js +269 -0
  39. package/dist/chunk-HMEVZKPQ.js +9 -0
  40. package/dist/chunk-HRGSYNLM.js +3511 -0
  41. package/dist/chunk-ISZR5N4K.js +60 -0
  42. package/dist/chunk-J6SUPR2C.js +226 -0
  43. package/dist/chunk-JERYVEIZ.js +244 -0
  44. package/dist/chunk-JHHWGL2N.js +87 -0
  45. package/dist/chunk-JONWQUB5.js +775 -0
  46. package/dist/chunk-K2DIWWDM.js +1766 -0
  47. package/dist/chunk-KY4PGL5V.js +969 -0
  48. package/dist/chunk-L737LQ4C.js +1285 -0
  49. package/dist/chunk-LFTWYIB2.js +497 -0
  50. package/dist/chunk-LV47RFNJ.js +41 -0
  51. package/dist/chunk-MKSAITI7.js +15 -0
  52. package/dist/chunk-MZ7RKIX4.js +212 -0
  53. package/dist/chunk-NAP6CFSO.js +84 -0
  54. package/dist/chunk-ND6MY37M.js +16 -0
  55. package/dist/chunk-NMG736UR.js +683 -0
  56. package/dist/chunk-NRAXROED.js +32 -0
  57. package/dist/chunk-NRIZR3A7.js +690 -0
  58. package/dist/chunk-NX43BG3M.js +233 -0
  59. package/dist/chunk-O645XLSI.js +297 -0
  60. package/dist/chunk-OMJD6A3S.js +235 -0
  61. package/dist/chunk-QB6SJD4T.js +430 -0
  62. package/dist/chunk-QFSTL4J3.js +276 -0
  63. package/dist/chunk-QLGDFMFX.js +212 -0
  64. package/dist/chunk-RIAAGL2E.js +13 -0
  65. package/dist/chunk-RWO5XMZ6.js +86 -0
  66. package/dist/chunk-RXRKBBSM.js +149 -0
  67. package/dist/chunk-RZOZMML6.js +363 -0
  68. package/dist/chunk-U7I7FS7T.js +113 -0
  69. package/dist/chunk-UI42RODY.js +717 -0
  70. package/dist/chunk-UTVMVSCO.js +519 -0
  71. package/dist/chunk-V6OJGLBA.js +1746 -0
  72. package/dist/chunk-W2JHVH7D.js +152 -0
  73. package/dist/chunk-WD3Y7VQN.js +280 -0
  74. package/dist/chunk-WOCTQ5MS.js +303 -0
  75. package/dist/chunk-WZR3ZUNN.js +696 -0
  76. package/dist/chunk-XGI665H7.js +150 -0
  77. package/dist/chunk-XKY65P2T.js +304 -0
  78. package/dist/chunk-Y4CQZY65.js +57 -0
  79. package/dist/chunk-YFEXKLVE.js +194 -0
  80. package/dist/chunk-YHO3HS5X.js +287 -0
  81. package/dist/chunk-YLS7AZSX.js +738 -0
  82. package/dist/chunk-ZE473AO6.js +49 -0
  83. package/dist/chunk-ZF747T3O.js +644 -0
  84. package/dist/chunk-ZHCZHZH3.js +43 -0
  85. package/dist/chunk-ZZNZX2XY.js +87 -0
  86. package/dist/constants-7QAP3VQ4.js +23 -0
  87. package/dist/dist-IY3UUMWK.js +33 -0
  88. package/dist/init-templates.js +9 -9
  89. package/dist/invariants-runner-W5RGHCSU.js +27 -0
  90. package/dist/lane-lock-6J36HD5O.js +35 -0
  91. package/dist/mem-checkpoint-core-EANG2GVN.js +14 -0
  92. package/dist/mem-signal-core-2LZ2WYHW.js +19 -0
  93. package/dist/memory-store-OLB5FO7K.js +18 -0
  94. package/dist/service-6BYCOCO5.js +13 -0
  95. package/dist/spawn-policy-resolver-NTSZYQ6R.js +17 -0
  96. package/dist/spawn-task-builder-R4E2BHSW.js +22 -0
  97. package/dist/wu-claim.js +2 -2
  98. package/dist/wu-claim.js.map +1 -1
  99. package/dist/wu-done-already-merged.js +12 -5
  100. package/dist/wu-done-already-merged.js.map +1 -1
  101. package/dist/wu-done-gates.js +25 -4
  102. package/dist/wu-done-gates.js.map +1 -1
  103. package/dist/wu-done-ownership.js +6 -1
  104. package/dist/wu-done-ownership.js.map +1 -1
  105. package/dist/wu-done-pr-WLFFFEPJ.js +25 -0
  106. package/dist/wu-done-validation-3J5E36FE.js +30 -0
  107. package/dist/wu-done.js +6 -6
  108. package/dist/wu-done.js.map +1 -1
  109. package/dist/wu-duplicate-id-detector-5S7JHELK.js +232 -0
  110. package/dist/wu-edit-operations.js +58 -17
  111. package/dist/wu-edit-operations.js.map +1 -1
  112. package/dist/wu-edit-validators.js +104 -28
  113. package/dist/wu-edit-validators.js.map +1 -1
  114. package/dist/wu-edit.js +1 -1
  115. package/dist/wu-edit.js.map +1 -1
  116. package/dist/wu-spawn-prompt-builders.js +8 -7
  117. package/dist/wu-spawn-prompt-builders.js.map +1 -1
  118. package/package.json +8 -8
  119. package/packs/sidekick/.turbo/turbo-build.log +1 -1
  120. package/packs/sidekick/package.json +1 -1
  121. package/packs/software-delivery/.turbo/turbo-build.log +1 -1
  122. package/packs/software-delivery/package.json +1 -1
  123. package/templates/core/LUMENFLOW.md.template +1 -1
  124. package/templates/core/ai/onboarding/quick-ref-commands.md.template +3 -3
  125. package/templates/core/ai/onboarding/starting-prompt.md.template +11 -11
@@ -0,0 +1,683 @@
1
+ import {
2
+ DIRECTORIES
3
+ } from "./chunk-DWMLTXKQ.js";
4
+
5
+ // ../core/dist/work-classifier.js
6
+ import { minimatch } from "minimatch";
7
+ var WORK_DOMAINS = {
8
+ UI: "ui",
9
+ BACKEND: "backend",
10
+ DOCS: "docs",
11
+ INFRA: "infra",
12
+ MIXED: "mixed"
13
+ };
14
+ var TEST_METHODOLOGY_HINTS = {
15
+ SMOKE_TEST: "smoke-test",
16
+ STRUCTURED_CONTENT: "structured-content"
17
+ };
18
+ var SIGNAL_WEIGHTS = {
19
+ CODE_PATHS: 1,
20
+ LANE: 0.6,
21
+ TYPE: 0.3,
22
+ DESCRIPTION: 0.2
23
+ };
24
+ var CONFIDENCE_THRESHOLD = 0.3;
25
+ var SMOKE_TEST_THRESHOLD = 0.5;
26
+ var STRUCTURED_CONTENT_CODE_PATH_PATTERNS = Object.freeze([
27
+ "**/*.yaml",
28
+ "**/*.yml",
29
+ "**/*.json",
30
+ "**/*.md",
31
+ "**/*.mdx"
32
+ ]);
33
+ var DEFAULT_UI_CODE_PATH_PATTERNS = Object.freeze([
34
+ // Stylesheets
35
+ "**/*.css",
36
+ "**/*.scss",
37
+ "**/*.less",
38
+ "**/*.module.css",
39
+ "**/*.module.scss",
40
+ // Styled components
41
+ "**/*.styled.ts",
42
+ "**/*.styled.tsx",
43
+ "**/*.styled.js",
44
+ "**/*.styled.jsx",
45
+ // Component / page directories
46
+ "**/components/**",
47
+ "**/pages/**",
48
+ // Next.js / app router conventions
49
+ "**/app/**/page.tsx",
50
+ "**/app/**/page.ts",
51
+ "**/app/**/page.jsx",
52
+ "**/app/**/page.js",
53
+ "**/app/**/layout.tsx",
54
+ "**/app/**/layout.ts",
55
+ "**/app/**/layout.jsx",
56
+ "**/app/**/layout.js",
57
+ "**/app/page.tsx",
58
+ "**/app/page.ts",
59
+ "**/app/layout.tsx",
60
+ "**/app/layout.ts"
61
+ ]);
62
+ var DEFAULT_UI_LANE_HINTS = Object.freeze([
63
+ "Experience",
64
+ "Frontend",
65
+ "UI",
66
+ "Design"
67
+ ]);
68
+ var DEFAULT_DOCS_CODE_PATH_PATTERNS = Object.freeze([
69
+ "docs/**",
70
+ "**/*.md",
71
+ "**/*.mdx",
72
+ "README*",
73
+ "CHANGELOG*"
74
+ ]);
75
+ var DEFAULT_DOCS_LANE_HINTS = Object.freeze([
76
+ "Content",
77
+ "Documentation",
78
+ "Docs"
79
+ ]);
80
+ var DEFAULT_INFRA_CODE_PATH_PATTERNS = Object.freeze([
81
+ ".github/**",
82
+ "Dockerfile*",
83
+ "docker-compose*",
84
+ "**/terraform/**",
85
+ "**/k8s/**",
86
+ "**/kubernetes/**",
87
+ "infrastructure/**"
88
+ ]);
89
+ var DEFAULT_INFRA_LANE_HINTS = Object.freeze([
90
+ "Operations",
91
+ "Infrastructure",
92
+ "DevOps",
93
+ "Platform"
94
+ ]);
95
+ var UI_DESCRIPTION_KEYWORDS = Object.freeze([
96
+ "css",
97
+ "scss",
98
+ "less",
99
+ "stylesheet",
100
+ "component",
101
+ "layout",
102
+ "responsive",
103
+ "ui",
104
+ "frontend",
105
+ "styled",
106
+ "animation",
107
+ "theme"
108
+ ]);
109
+ var DOCS_DESCRIPTION_KEYWORDS = Object.freeze([
110
+ "documentation",
111
+ "readme",
112
+ "changelog",
113
+ "docs",
114
+ "guide",
115
+ "tutorial"
116
+ ]);
117
+ var INFRA_DESCRIPTION_KEYWORDS = Object.freeze([
118
+ "docker",
119
+ "kubernetes",
120
+ "terraform",
121
+ "ci/cd",
122
+ "pipeline",
123
+ "deploy",
124
+ "infrastructure"
125
+ ]);
126
+ function createDomainSignals(domain) {
127
+ return { domain, maxWeight: 0, signals: [] };
128
+ }
129
+ function addSignal(ds, signal) {
130
+ ds.signals.push(signal);
131
+ if (signal.weight > ds.maxWeight) {
132
+ ds.maxWeight = signal.weight;
133
+ }
134
+ }
135
+ function matchCodePaths(codePaths, patterns) {
136
+ for (const path of codePaths) {
137
+ for (const pattern of patterns) {
138
+ if (minimatch(path, pattern)) {
139
+ return pattern;
140
+ }
141
+ }
142
+ }
143
+ return void 0;
144
+ }
145
+ function isStructuredContentOnly(codePaths) {
146
+ return codePaths.length > 0 && codePaths.every((path) => STRUCTURED_CONTENT_CODE_PATH_PATTERNS.some((pattern) => minimatch(path, pattern, { nocase: true })));
147
+ }
148
+ function matchLaneHint(lane, hints) {
149
+ const parts = lane.split(":");
150
+ const laneParent = (parts[0] ?? "").trim().toLowerCase();
151
+ const laneSublane = parts.length > 1 ? (parts[1] ?? "").trim().toLowerCase() : "";
152
+ for (const hint of hints) {
153
+ const hintLower = hint.toLowerCase();
154
+ if (laneParent === hintLower || laneSublane === hintLower) {
155
+ return hint;
156
+ }
157
+ }
158
+ return void 0;
159
+ }
160
+ function matchDescriptionKeywords(description, keywords) {
161
+ const descLower = description.toLowerCase();
162
+ for (const keyword of keywords) {
163
+ if (descLower.includes(keyword.toLowerCase())) {
164
+ return keyword;
165
+ }
166
+ }
167
+ return void 0;
168
+ }
169
+ var DOMAIN_CAPABILITIES = {
170
+ [WORK_DOMAINS.UI]: ["ui-design-awareness", "component-reuse-check"],
171
+ [WORK_DOMAINS.DOCS]: ["documentation-structure", "link-validation"],
172
+ [WORK_DOMAINS.INFRA]: ["infrastructure-review", "security-check"],
173
+ [WORK_DOMAINS.MIXED]: ["cross-domain-awareness"],
174
+ [WORK_DOMAINS.BACKEND]: []
175
+ };
176
+ function getCapabilities(domain) {
177
+ return DOMAIN_CAPABILITIES[domain] ?? [];
178
+ }
179
+ function classifyWork(doc, config) {
180
+ const codePaths = doc.code_paths ?? [];
181
+ const lane = doc.lane ?? "";
182
+ const type = doc.type ?? "";
183
+ const description = doc.description ?? "";
184
+ const structuredContentOnly = isStructuredContentOnly(codePaths);
185
+ const uiCodePathPatterns = [
186
+ ...DEFAULT_UI_CODE_PATH_PATTERNS,
187
+ ...config?.ui?.code_path_patterns ?? []
188
+ ];
189
+ const uiLaneHints = [...DEFAULT_UI_LANE_HINTS, ...config?.ui?.lane_hints ?? []];
190
+ const ui = createDomainSignals(WORK_DOMAINS.UI);
191
+ const docs = createDomainSignals(WORK_DOMAINS.DOCS);
192
+ const infra = createDomainSignals(WORK_DOMAINS.INFRA);
193
+ if (codePaths.length > 0) {
194
+ const uiMatch = matchCodePaths(codePaths, uiCodePathPatterns);
195
+ if (uiMatch) {
196
+ addSignal(ui, {
197
+ source: "code_paths",
198
+ domain: WORK_DOMAINS.UI,
199
+ weight: SIGNAL_WEIGHTS.CODE_PATHS,
200
+ match: uiMatch
201
+ });
202
+ }
203
+ const docsMatch = matchCodePaths(codePaths, DEFAULT_DOCS_CODE_PATH_PATTERNS);
204
+ if (docsMatch) {
205
+ addSignal(docs, {
206
+ source: "code_paths",
207
+ domain: WORK_DOMAINS.DOCS,
208
+ weight: SIGNAL_WEIGHTS.CODE_PATHS,
209
+ match: docsMatch
210
+ });
211
+ }
212
+ const infraMatch = matchCodePaths(codePaths, DEFAULT_INFRA_CODE_PATH_PATTERNS);
213
+ if (infraMatch) {
214
+ addSignal(infra, {
215
+ source: "code_paths",
216
+ domain: WORK_DOMAINS.INFRA,
217
+ weight: SIGNAL_WEIGHTS.CODE_PATHS,
218
+ match: infraMatch
219
+ });
220
+ }
221
+ }
222
+ if (lane) {
223
+ const uiLaneMatch = matchLaneHint(lane, uiLaneHints);
224
+ if (uiLaneMatch) {
225
+ addSignal(ui, {
226
+ source: "lane",
227
+ domain: WORK_DOMAINS.UI,
228
+ weight: SIGNAL_WEIGHTS.LANE,
229
+ match: uiLaneMatch
230
+ });
231
+ }
232
+ const docsLaneMatch = matchLaneHint(lane, DEFAULT_DOCS_LANE_HINTS);
233
+ if (docsLaneMatch) {
234
+ addSignal(docs, {
235
+ source: "lane",
236
+ domain: WORK_DOMAINS.DOCS,
237
+ weight: SIGNAL_WEIGHTS.LANE,
238
+ match: docsLaneMatch
239
+ });
240
+ }
241
+ const infraLaneMatch = matchLaneHint(lane, DEFAULT_INFRA_LANE_HINTS);
242
+ if (infraLaneMatch) {
243
+ addSignal(infra, {
244
+ source: "lane",
245
+ domain: WORK_DOMAINS.INFRA,
246
+ weight: SIGNAL_WEIGHTS.LANE,
247
+ match: infraLaneMatch
248
+ });
249
+ }
250
+ }
251
+ if (type === "documentation") {
252
+ addSignal(docs, {
253
+ source: "type",
254
+ domain: WORK_DOMAINS.DOCS,
255
+ weight: SIGNAL_WEIGHTS.TYPE,
256
+ match: "documentation"
257
+ });
258
+ }
259
+ if (description) {
260
+ const uiKeywordMatch = matchDescriptionKeywords(description, UI_DESCRIPTION_KEYWORDS);
261
+ if (uiKeywordMatch) {
262
+ addSignal(ui, {
263
+ source: "description",
264
+ domain: WORK_DOMAINS.UI,
265
+ weight: SIGNAL_WEIGHTS.DESCRIPTION,
266
+ match: uiKeywordMatch
267
+ });
268
+ }
269
+ const docsKeywordMatch = matchDescriptionKeywords(description, DOCS_DESCRIPTION_KEYWORDS);
270
+ if (docsKeywordMatch) {
271
+ addSignal(docs, {
272
+ source: "description",
273
+ domain: WORK_DOMAINS.DOCS,
274
+ weight: SIGNAL_WEIGHTS.DESCRIPTION,
275
+ match: docsKeywordMatch
276
+ });
277
+ }
278
+ const infraKeywordMatch = matchDescriptionKeywords(description, INFRA_DESCRIPTION_KEYWORDS);
279
+ if (infraKeywordMatch) {
280
+ addSignal(infra, {
281
+ source: "description",
282
+ domain: WORK_DOMAINS.INFRA,
283
+ weight: SIGNAL_WEIGHTS.DESCRIPTION,
284
+ match: infraKeywordMatch
285
+ });
286
+ }
287
+ }
288
+ const candidates = [ui, docs, infra].filter((d) => d.maxWeight >= CONFIDENCE_THRESHOLD);
289
+ const strongCodePathDomains = [ui, docs, infra].filter((d) => d.signals.some((s) => s.source === "code_paths"));
290
+ if (strongCodePathDomains.length > 1) {
291
+ const allSignals = strongCodePathDomains.flatMap((d) => d.signals);
292
+ return {
293
+ domain: WORK_DOMAINS.MIXED,
294
+ confidence: Math.max(...strongCodePathDomains.map((d) => d.maxWeight)),
295
+ signals: allSignals,
296
+ capabilities: getCapabilities(WORK_DOMAINS.MIXED),
297
+ testMethodologyHint: void 0
298
+ };
299
+ }
300
+ if (candidates.length === 0) {
301
+ return {
302
+ domain: WORK_DOMAINS.BACKEND,
303
+ confidence: 0,
304
+ signals: [],
305
+ capabilities: [],
306
+ testMethodologyHint: structuredContentOnly ? TEST_METHODOLOGY_HINTS.STRUCTURED_CONTENT : void 0
307
+ };
308
+ }
309
+ candidates.sort((a, b) => b.maxWeight - a.maxWeight);
310
+ const winner = candidates[0];
311
+ const testMethodologyHint = winner.domain === WORK_DOMAINS.UI && winner.maxWeight >= SMOKE_TEST_THRESHOLD ? TEST_METHODOLOGY_HINTS.SMOKE_TEST : structuredContentOnly ? TEST_METHODOLOGY_HINTS.STRUCTURED_CONTENT : void 0;
312
+ return {
313
+ domain: winner.domain,
314
+ confidence: winner.maxWeight,
315
+ signals: winner.signals,
316
+ capabilities: getCapabilities(winner.domain),
317
+ testMethodologyHint
318
+ };
319
+ }
320
+
321
+ // ../core/dist/spawn-guidance-generators.js
322
+ var DEFAULT_WORKTREES_DIR_SEGMENT = DIRECTORIES.WORKTREES.replace(/\/+$/g, "");
323
+ var EXISTING_TESTS_TYPES = ["refactor"];
324
+ var SMOKE_TEST_TYPES = ["visual", "design", "ui"];
325
+ var DOCS_ONLY_TYPES = ["documentation", "docs", "config"];
326
+ function generateTDDDirective() {
327
+ return `## \u26D4 TDD DIRECTIVE \u2014 READ BEFORE CODING
328
+
329
+ **IF YOU WRITE IMPLEMENTATION CODE BEFORE A FAILING TEST, YOU HAVE FAILED THIS WU.**
330
+
331
+ ### Test-First Workflow (MANDATORY)
332
+
333
+ 1. Write a failing test for the acceptance criteria
334
+ 2. Run the test to confirm it fails (RED)
335
+ 3. Implement the minimum code to pass the test
336
+ 4. Run the test to confirm it passes (GREEN)
337
+ 5. Refactor if needed, keeping tests green
338
+
339
+ ### Test Ratchet Rule (WU-1253)
340
+
341
+ Gates compare test results against \`.lumenflow/test-baseline.json\`:
342
+
343
+ - **NEW failures** (not in baseline) **BLOCK** gates - you must fix them
344
+ - **Pre-existing failures** (in baseline) show **WARNING** - do not block your WU
345
+ - When tests are **fixed**, baseline auto-updates (ratchet forward)
346
+
347
+ If gates fail due to test failures:
348
+ 1. Check if failure is in baseline: \`cat .lumenflow/test-baseline.json\`
349
+ 2. If pre-existing: continue, it will warn but not block
350
+ 3. If NEW: fix the test or add to baseline with reason and fix-wu
351
+
352
+ ### Why This Matters
353
+
354
+ - Tests document expected behavior BEFORE implementation
355
+ - Prevents scope creep and over-engineering
356
+ - Ensures every feature has verification
357
+ - Failing tests prove the test actually tests something
358
+ - Ratchet pattern prevents being blocked by unrelated failures`;
359
+ }
360
+ function generateDocsGuidance() {
361
+ return `## Documentation Standards
362
+
363
+ **Format check only** - No TDD required for documentation WUs.
364
+
365
+ ### Requirements
366
+
367
+ 1. Run \`pnpm gates --docs-only\` before completion
368
+ 2. Ensure markdown formatting is correct
369
+ 3. Verify links are valid
370
+ 4. Check spelling and grammar`;
371
+ }
372
+ function generateSmokeTestGuidance() {
373
+ return `## UI/Visual Verification Strategy
374
+
375
+ **Prefer user-outcome verification over brittle DOM assertions** for UI-classified work.
376
+
377
+ ### Recommended Order
378
+
379
+ 1. Cover critical user flows with integration or E2E tests when behavior crosses component boundaries
380
+ 2. Add smoke/render coverage for crash-prone states, loading states, and empty states
381
+ 3. Use unit tests for pure logic only (formatters, reducers, validators, accessibility helpers, slug builders)
382
+ 4. Verify responsive behavior and accessibility manually or with automation
383
+ 5. Avoid tests that only snapshot CSS classes or markup shape unless that contract is intentional`;
384
+ }
385
+ function generateRefactorGuidance() {
386
+ return `## Refactor Testing
387
+
388
+ **Existing tests must pass** - Refactoring must not break current behavior.
389
+
390
+ ### Requirements
391
+
392
+ 1. Run all existing tests BEFORE refactoring
393
+ 2. Run all existing tests AFTER refactoring
394
+ 3. No new tests required unless behavior changes
395
+ 4. If tests fail after refactor, the refactor introduced a bug`;
396
+ }
397
+ function usesNonTddSummaryHint(options) {
398
+ return options?.testMethodologyHint === TEST_METHODOLOGY_HINTS.SMOKE_TEST || options?.testMethodologyHint === TEST_METHODOLOGY_HINTS.STRUCTURED_CONTENT;
399
+ }
400
+ function generateStructuredContentGuidance() {
401
+ return `## Structured Content Testing
402
+
403
+ **Content/config validation over TDD** - Structured content changes should be verified with parsing, linting, and domain evaluation.
404
+
405
+ ### Requirements
406
+
407
+ 1. Validate file parseability and schema compatibility (where schema exists)
408
+ 2. Run relevant lint/format checks for content/config files
409
+ 3. Run project/domain evaluators for prompt or policy content when available
410
+ 4. Execute at least one manual/automated smoke check for changed content path
411
+ 5. Document evidence/results in completion notes`;
412
+ }
413
+ function generateCodeCraftGuidance() {
414
+ return `## Code Craft
415
+
416
+ Use these implementation standards while you write code:
417
+
418
+ 1. **Extract repeated literals to named constants**. Use your project's duplicate-string threshold (configured in \`eslint.config.mjs\`) and extract meaningful numbers.
419
+ \`\`\`ts
420
+ // Before
421
+ if (status === 'in_progress') notify('in_progress');
422
+ if (nextStatus === 'in_progress') log('in_progress');
423
+ if (prevStatus === 'in_progress') audit('in_progress');
424
+
425
+ // After
426
+ const STATUS_IN_PROGRESS = 'in_progress';
427
+ if (status === STATUS_IN_PROGRESS) notify(STATUS_IN_PROGRESS);
428
+ \`\`\`
429
+
430
+ 2. **Write error messages with context**: what failed, why, and next action.
431
+ \`\`\`ts
432
+ // Before
433
+ die('failed');
434
+
435
+ // After
436
+ die('Failed to parse workspace.yaml: missing lane field. Run pnpm lane:setup to regenerate.');
437
+ \`\`\`
438
+
439
+ 3. **Prefer existing libraries for common problems** (parsing, validation, dates, schema, paths) before custom implementations.
440
+ \`\`\`ts
441
+ // Before
442
+ function parseDate(input: string) { /* custom parser */ }
443
+
444
+ // After
445
+ import { parseISO } from 'date-fns';
446
+ const parsed = parseISO(input);
447
+ \`\`\`
448
+
449
+ 4. **Use type narrowing instead of unsafe casts**.
450
+ \`\`\`ts
451
+ // Before
452
+ const id = (payload as { id: string }).id;
453
+
454
+ // After
455
+ if (payload && typeof payload === 'object' && 'id' in payload) {
456
+ const id = String(payload.id);
457
+ }
458
+ \`\`\`
459
+
460
+ 5. **Extract duplicated logic when it appears at your project's extraction threshold**.
461
+ \`\`\`ts
462
+ // Before: same branch checks repeated in multiple files
463
+ if (mode === 'branch-pr') { /* ... */ }
464
+
465
+ // After
466
+ function isBranchPrMode(mode: string): boolean {
467
+ return mode === 'branch-pr';
468
+ }
469
+ \`\`\`
470
+
471
+ 6. **Keep functions focused**. One function should do one clear job.
472
+ \`\`\`ts
473
+ // Before
474
+ function completeWuAndNotifyAndWriteAudit() { /* many responsibilities */ }
475
+
476
+ // After
477
+ function completeWu() { /* completion only */ }
478
+ function notifyCompletion() { /* notification only */ }
479
+ \`\`\`
480
+
481
+ 7. **Inject infrastructure dependencies into application logic** instead of importing them directly.
482
+ \`\`\`ts
483
+ // Before
484
+ import { readFileSync } from 'node:fs';
485
+ function loadSpec(path: string) { return readFileSync(path, 'utf8'); }
486
+
487
+ // After
488
+ function loadSpec(path: string, readText: (p: string) => string) {
489
+ return readText(path);
490
+ }
491
+ \`\`\``;
492
+ }
493
+ function generateReadBeforeWriteDiscipline() {
494
+ return `## Read Before Write
495
+
496
+ Understand the change before touching code:
497
+
498
+ 1. Read each file you plan to edit from top to bottom.
499
+ 2. Read adjacent files that define related types, helpers, and boundaries.
500
+ 3. Find callers and dependents (for example with \`rg\`) before changing behavior.
501
+ 4. Confirm acceptance criteria and code_paths still match your intended scope.
502
+ 5. Never edit a file you have not read first.
503
+ 6. If you cannot determine impact from reading alone, ask before proceeding.`;
504
+ }
505
+ function generateSelfReviewDirective(id) {
506
+ return `## Self-Review Before Completion
507
+
508
+ Before you finish ${id}, review your own diff against gate and craft checks:
509
+
510
+ 1. [GATE] Repeated strings at your project threshold are extracted to named constants.
511
+ 2. [STANDARD] Semantic numbers use named constants (except simple loop indexes and 0/1 guards).
512
+ 3. [STANDARD] Error messages explain what failed, why, and how to fix.
513
+ 4. [STANDARD] No \`as\` casts without preceding type narrowing or guards.
514
+ 5. [GATE] No \`TODO\` or \`FIXME\` markers in production changes.
515
+ 6. [GATE] Architecture boundaries are respected (no forbidden cross-layer imports).
516
+
517
+ Fix any failed checks before running completion commands.`;
518
+ }
519
+ function generateMandatoryStandards(policy, options) {
520
+ const lines = ["## Mandatory Standards", ""];
521
+ lines.push("- **LumenFlow**: Follow trunk-based flow, WIP=1, worktree discipline");
522
+ if (options?.testMethodologyHint === TEST_METHODOLOGY_HINTS.SMOKE_TEST) {
523
+ lines.push("- **Verification Strategy**: Prefer user-outcome verification over brittle DOM assertions. Use integration/E2E, smoke coverage, and manual or visual checks as appropriate.");
524
+ } else if (options?.testMethodologyHint === TEST_METHODOLOGY_HINTS.STRUCTURED_CONTENT) {
525
+ lines.push("- **Verification Strategy**: Verify structured content with fixtures, schema checks, or targeted integration coverage. Do not force runtime TDD for content-only changes.");
526
+ } else if (policy.testing === "tdd") {
527
+ lines.push(`- **TDD**: Failing test first, then implementation, then passing test. ${policy.coverage_threshold}%+ coverage on new application code`);
528
+ } else if (policy.testing === "test-after") {
529
+ lines.push(`- **Test-After**: Write implementation first, then add tests. ${policy.coverage_threshold}%+ coverage on new application code`);
530
+ } else if (policy.testing === "none") {
531
+ lines.push("- **Testing**: Tests are optional for this project");
532
+ }
533
+ if (policy.architecture === "hexagonal") {
534
+ lines.push("- **Hexagonal Architecture**: Ports-first design. No application -> infrastructure imports");
535
+ } else if (policy.architecture === "layered") {
536
+ lines.push("- **Layered Architecture**: Clear layer separation. Each layer can only depend on layers below it");
537
+ }
538
+ lines.push("- **Code Craft**: See Code Craft section below for constants, DRY, SRP, DI, error handling, and type safety rules with examples");
539
+ lines.push("- **KISS/YAGNI**: Solve the stated problem with the minimum code. Do not add features, parameters, abstractions, or configurability beyond what acceptance criteria require");
540
+ lines.push("- **Worktree Discipline**: ALWAYS use `pnpm wu:claim` to create worktrees (never `git worktree add` directly). Work ONLY in the worktree, never edit main");
541
+ lines.push("- **Documentation**: Update tooling docs if changing tools. Keep docs in sync with code");
542
+ lines.push("- **Sub-agents**: Use Explore agent for codebase investigation. Activate mandatory agents as configured for your project");
543
+ return lines.join("\n");
544
+ }
545
+ function generatePolicyBasedTestGuidance(wuType, policy, options) {
546
+ const type = (wuType || "feature").toLowerCase();
547
+ if (DOCS_ONLY_TYPES.includes(type)) {
548
+ return generateDocsGuidance();
549
+ }
550
+ if (SMOKE_TEST_TYPES.includes(type)) {
551
+ return generateSmokeTestGuidance();
552
+ }
553
+ if (options?.testMethodologyHint === TEST_METHODOLOGY_HINTS.STRUCTURED_CONTENT) {
554
+ return generateStructuredContentGuidance();
555
+ }
556
+ if (options?.testMethodologyHint === TEST_METHODOLOGY_HINTS.SMOKE_TEST) {
557
+ return generateSmokeTestGuidance();
558
+ }
559
+ if (EXISTING_TESTS_TYPES.includes(type)) {
560
+ return generateRefactorGuidance();
561
+ }
562
+ switch (policy.testing) {
563
+ case "test-after":
564
+ return generateTestAfterDirective(policy.coverage_threshold);
565
+ case "none":
566
+ return generateTestingOptionalDirective();
567
+ case "tdd":
568
+ default:
569
+ return generateTDDDirective();
570
+ }
571
+ }
572
+ function generateTestAfterDirective(coverageThreshold) {
573
+ return `## Test-After Methodology
574
+
575
+ **Write implementation first, then add tests** - Focus on solving the problem, then verify.
576
+
577
+ ### Test-After Workflow
578
+
579
+ 1. Understand the acceptance criteria
580
+ 2. Write implementation first
581
+ 3. Add tests to verify behavior
582
+ 4. Aim for ${coverageThreshold}% coverage on new code
583
+
584
+ ### When This Works
585
+
586
+ - Exploratory prototyping where requirements are unclear
587
+ - Quick iterations where test-first slows discovery
588
+ - Projects configured with \`methodology.testing: 'test-after'\`
589
+
590
+ ### Requirements
591
+
592
+ - Tests must be added before \`wu:done\`
593
+ - Coverage target: ${coverageThreshold}%+ on new application code
594
+ - All existing tests must still pass`;
595
+ }
596
+ function generateTestingOptionalDirective() {
597
+ return `## Testing Optional
598
+
599
+ **Tests are not required** - Project is configured without test requirements.
600
+
601
+ ### Focus
602
+
603
+ - Code quality and functionality
604
+ - Run \`pnpm gates\` before completion (will skip coverage checks)
605
+
606
+ ### If You Want to Add Tests
607
+
608
+ You can still add tests for critical functionality:
609
+ \`\`\`bash
610
+ pnpm test -- --coverage
611
+ \`\`\`
612
+
613
+ But they are not required for WU completion.`;
614
+ }
615
+ function generateDesignContextSection(classification) {
616
+ if (classification.domain !== "ui") {
617
+ return "";
618
+ }
619
+ return `## Design Context
620
+
621
+ This work involves UI components or styling. Follow these guidelines:
622
+
623
+ ### Pattern Check
624
+
625
+ - Before creating new components, check for existing patterns in the codebase
626
+ - Search for similar components that may already solve the problem
627
+ - Verify design system tokens and variables are used instead of hardcoded values
628
+
629
+ ### Viewport Verification
630
+
631
+ - Test across common viewport sizes (mobile: 375px, tablet: 768px, desktop: 1280px)
632
+ - Verify responsive behavior at breakpoints
633
+ - Check for overflow, text truncation, and layout shifts
634
+
635
+ ### Accessibility
636
+
637
+ - Verify keyboard navigation works for interactive elements
638
+ - Ensure sufficient color contrast (WCAG 2.1 AA minimum)
639
+ - Add appropriate ARIA attributes where needed
640
+ - Test with screen reader if applicable
641
+
642
+ ### Codebase Exploration
643
+
644
+ - Check the project's design system or component library before building custom
645
+ - Look for CSS variables, theme tokens, or shared style utilities
646
+ - Review existing component patterns for consistency`;
647
+ }
648
+ function generateEnforcementSummary(policy, options) {
649
+ const lines = ["## You will be judged by", ""];
650
+ if (options?.testMethodologyHint === TEST_METHODOLOGY_HINTS.SMOKE_TEST) {
651
+ lines.push("- **Testing**: fit-for-surface UI verification (integration/E2E, smoke, manual, or visual checks as appropriate)");
652
+ } else if (options?.testMethodologyHint === TEST_METHODOLOGY_HINTS.STRUCTURED_CONTENT) {
653
+ lines.push("- **Testing**: structured-content verification (fixtures, schema, golden data, or integration checks as appropriate)");
654
+ } else {
655
+ const testingStatus = policy.tests_required ? "required" : "optional";
656
+ lines.push(`- **Testing**: ${policy.testing} (tests ${testingStatus})`);
657
+ }
658
+ if (usesNonTddSummaryHint(options)) {
659
+ const modeLabel = policy.coverage_mode === "block" ? "blocking" : policy.coverage_mode === "warn" ? "warn only" : "disabled";
660
+ lines.push(`- **Coverage**: ${policy.coverage_threshold}% policy target (${modeLabel}) when automated application tests apply`);
661
+ } else if (policy.coverage_mode === "off") {
662
+ lines.push("- **Coverage**: disabled");
663
+ } else {
664
+ const modeLabel = policy.coverage_mode === "block" ? "blocking" : "warn only";
665
+ lines.push(`- **Coverage**: ${policy.coverage_threshold}% (${modeLabel})`);
666
+ }
667
+ if (policy.architecture !== "none") {
668
+ lines.push(`- **Architecture**: ${policy.architecture}`);
669
+ }
670
+ return lines.join("\n");
671
+ }
672
+
673
+ export {
674
+ TEST_METHODOLOGY_HINTS,
675
+ classifyWork,
676
+ generateCodeCraftGuidance,
677
+ generateReadBeforeWriteDiscipline,
678
+ generateSelfReviewDirective,
679
+ generateMandatoryStandards,
680
+ generatePolicyBasedTestGuidance,
681
+ generateDesignContextSection,
682
+ generateEnforcementSummary
683
+ };