@soleri/core 9.2.0 → 9.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (298) hide show
  1. package/data/flows/build.flow.yaml +8 -9
  2. package/data/flows/deliver.flow.yaml +9 -10
  3. package/data/flows/design.flow.yaml +3 -4
  4. package/data/flows/enhance.flow.yaml +5 -6
  5. package/data/flows/explore.flow.yaml +3 -4
  6. package/data/flows/fix.flow.yaml +5 -6
  7. package/data/flows/plan.flow.yaml +4 -5
  8. package/data/flows/review.flow.yaml +3 -4
  9. package/dist/curator/curator.d.ts.map +1 -1
  10. package/dist/curator/curator.js +98 -22
  11. package/dist/curator/curator.js.map +1 -1
  12. package/dist/engine/bin/soleri-engine.js.map +1 -1
  13. package/dist/engine/module-manifest.d.ts.map +1 -1
  14. package/dist/engine/module-manifest.js +21 -1
  15. package/dist/engine/module-manifest.js.map +1 -1
  16. package/dist/engine/register-engine.d.ts.map +1 -1
  17. package/dist/engine/register-engine.js +25 -1
  18. package/dist/engine/register-engine.js.map +1 -1
  19. package/dist/flows/gate-evaluator.js.map +1 -1
  20. package/dist/operator/operator-profile.d.ts.map +1 -1
  21. package/dist/operator/operator-profile.js +11 -5
  22. package/dist/operator/operator-profile.js.map +1 -1
  23. package/dist/operator/operator-signals.d.ts.map +1 -1
  24. package/dist/operator/operator-signals.js.map +1 -1
  25. package/dist/planning/evidence-collector.js.map +1 -1
  26. package/dist/planning/gap-passes.d.ts.map +1 -1
  27. package/dist/planning/gap-passes.js +23 -6
  28. package/dist/planning/gap-passes.js.map +1 -1
  29. package/dist/planning/gap-patterns.d.ts.map +1 -1
  30. package/dist/planning/gap-patterns.js +57 -11
  31. package/dist/planning/gap-patterns.js.map +1 -1
  32. package/dist/planning/github-projection.d.ts.map +1 -1
  33. package/dist/planning/github-projection.js +39 -20
  34. package/dist/planning/github-projection.js.map +1 -1
  35. package/dist/planning/impact-analyzer.d.ts.map +1 -1
  36. package/dist/planning/impact-analyzer.js +20 -18
  37. package/dist/planning/impact-analyzer.js.map +1 -1
  38. package/dist/planning/plan-lifecycle.d.ts.map +1 -1
  39. package/dist/planning/plan-lifecycle.js +22 -9
  40. package/dist/planning/plan-lifecycle.js.map +1 -1
  41. package/dist/planning/planner.d.ts.map +1 -1
  42. package/dist/planning/planner.js +60 -17
  43. package/dist/planning/planner.js.map +1 -1
  44. package/dist/planning/rationalization-detector.d.ts.map +1 -1
  45. package/dist/planning/rationalization-detector.js.map +1 -1
  46. package/dist/planning/reconciliation-engine.d.ts.map +1 -1
  47. package/dist/planning/reconciliation-engine.js.map +1 -1
  48. package/dist/planning/task-verifier.d.ts.map +1 -1
  49. package/dist/planning/task-verifier.js +14 -6
  50. package/dist/planning/task-verifier.js.map +1 -1
  51. package/dist/runtime/admin-setup-ops.d.ts.map +1 -1
  52. package/dist/runtime/admin-setup-ops.js +2 -1
  53. package/dist/runtime/admin-setup-ops.js.map +1 -1
  54. package/dist/runtime/branching-ops.d.ts +12 -0
  55. package/dist/runtime/branching-ops.d.ts.map +1 -0
  56. package/dist/runtime/branching-ops.js +100 -0
  57. package/dist/runtime/branching-ops.js.map +1 -0
  58. package/dist/runtime/context-health.d.ts.map +1 -1
  59. package/dist/runtime/context-health.js.map +1 -1
  60. package/dist/runtime/facades/branching-facade.d.ts +7 -0
  61. package/dist/runtime/facades/branching-facade.d.ts.map +1 -0
  62. package/dist/runtime/facades/branching-facade.js +8 -0
  63. package/dist/runtime/facades/branching-facade.js.map +1 -0
  64. package/dist/runtime/facades/chat-service-ops.d.ts.map +1 -1
  65. package/dist/runtime/facades/chat-service-ops.js +3 -1
  66. package/dist/runtime/facades/chat-service-ops.js.map +1 -1
  67. package/dist/runtime/facades/chat-transport-ops.d.ts.map +1 -1
  68. package/dist/runtime/facades/chat-transport-ops.js.map +1 -1
  69. package/dist/runtime/facades/index.d.ts.map +1 -1
  70. package/dist/runtime/facades/index.js +42 -0
  71. package/dist/runtime/facades/index.js.map +1 -1
  72. package/dist/runtime/facades/intake-facade.d.ts +9 -0
  73. package/dist/runtime/facades/intake-facade.d.ts.map +1 -0
  74. package/dist/runtime/facades/intake-facade.js +11 -0
  75. package/dist/runtime/facades/intake-facade.js.map +1 -0
  76. package/dist/runtime/facades/links-facade.d.ts +9 -0
  77. package/dist/runtime/facades/links-facade.d.ts.map +1 -0
  78. package/dist/runtime/facades/links-facade.js +10 -0
  79. package/dist/runtime/facades/links-facade.js.map +1 -0
  80. package/dist/runtime/facades/operator-facade.d.ts.map +1 -1
  81. package/dist/runtime/facades/operator-facade.js.map +1 -1
  82. package/dist/runtime/facades/plan-facade.d.ts.map +1 -1
  83. package/dist/runtime/facades/plan-facade.js +4 -1
  84. package/dist/runtime/facades/plan-facade.js.map +1 -1
  85. package/dist/runtime/facades/tier-facade.d.ts +7 -0
  86. package/dist/runtime/facades/tier-facade.d.ts.map +1 -0
  87. package/dist/runtime/facades/tier-facade.js +8 -0
  88. package/dist/runtime/facades/tier-facade.js.map +1 -0
  89. package/dist/runtime/facades/vault-facade.d.ts +9 -1
  90. package/dist/runtime/facades/vault-facade.d.ts.map +1 -1
  91. package/dist/runtime/facades/vault-facade.js +44 -187
  92. package/dist/runtime/facades/vault-facade.js.map +1 -1
  93. package/dist/runtime/github-integration.d.ts.map +1 -1
  94. package/dist/runtime/github-integration.js +11 -4
  95. package/dist/runtime/github-integration.js.map +1 -1
  96. package/dist/runtime/orchestrate-ops.d.ts.map +1 -1
  97. package/dist/runtime/orchestrate-ops.js +32 -10
  98. package/dist/runtime/orchestrate-ops.js.map +1 -1
  99. package/dist/runtime/planning-extra-ops.d.ts.map +1 -1
  100. package/dist/runtime/planning-extra-ops.js.map +1 -1
  101. package/dist/runtime/runtime.d.ts.map +1 -1
  102. package/dist/runtime/runtime.js +3 -1
  103. package/dist/runtime/runtime.js.map +1 -1
  104. package/dist/runtime/session-briefing.d.ts.map +1 -1
  105. package/dist/runtime/session-briefing.js +5 -1
  106. package/dist/runtime/session-briefing.js.map +1 -1
  107. package/dist/runtime/tier-ops.d.ts +13 -0
  108. package/dist/runtime/tier-ops.d.ts.map +1 -0
  109. package/dist/runtime/tier-ops.js +110 -0
  110. package/dist/runtime/tier-ops.js.map +1 -0
  111. package/dist/skills/sync-skills.d.ts.map +1 -1
  112. package/dist/skills/sync-skills.js +1 -1
  113. package/dist/skills/sync-skills.js.map +1 -1
  114. package/dist/vault/linking.d.ts.map +1 -1
  115. package/dist/vault/linking.js +41 -5
  116. package/dist/vault/linking.js.map +1 -1
  117. package/dist/vault/vault-entries.d.ts.map +1 -1
  118. package/dist/vault/vault-entries.js +68 -26
  119. package/dist/vault/vault-entries.js.map +1 -1
  120. package/dist/vault/vault-maintenance.d.ts.map +1 -1
  121. package/dist/vault/vault-maintenance.js +6 -2
  122. package/dist/vault/vault-maintenance.js.map +1 -1
  123. package/dist/vault/vault-markdown-sync.d.ts.map +1 -1
  124. package/dist/vault/vault-markdown-sync.js.map +1 -1
  125. package/dist/vault/vault-memories.d.ts.map +1 -1
  126. package/dist/vault/vault-memories.js +3 -1
  127. package/dist/vault/vault-memories.js.map +1 -1
  128. package/dist/vault/vault-schema.js +36 -10
  129. package/dist/vault/vault-schema.js.map +1 -1
  130. package/dist/vault/vault.d.ts.map +1 -1
  131. package/dist/vault/vault.js +5 -1
  132. package/dist/vault/vault.js.map +1 -1
  133. package/package.json +7 -7
  134. package/src/agency/agency-manager.test.ts +60 -40
  135. package/src/agency/default-rules.test.ts +17 -9
  136. package/src/capabilities/registry.test.ts +2 -12
  137. package/src/chat/agent-loop.test.ts +33 -43
  138. package/src/chat/mcp-bridge.test.ts +7 -2
  139. package/src/claudemd/inject.test.ts +2 -12
  140. package/src/context/context-engine.test.ts +96 -51
  141. package/src/control/intent-router.test.ts +3 -3
  142. package/src/curator/classifier.test.ts +14 -8
  143. package/src/curator/contradiction-detector.test.ts +30 -5
  144. package/src/curator/curator.ts +278 -56
  145. package/src/curator/duplicate-detector.test.ts +77 -15
  146. package/src/curator/quality-gate.test.ts +71 -31
  147. package/src/curator/tag-manager.test.ts +12 -4
  148. package/src/domain-packs/knowledge-installer.test.ts +2 -10
  149. package/src/domain-packs/token-resolver.test.ts +1 -3
  150. package/src/domain-packs/types.test.ts +16 -2
  151. package/src/enforcement/registry.test.ts +2 -8
  152. package/src/engine/bin/soleri-engine.ts +3 -1
  153. package/src/engine/module-manifest.test.ts +5 -4
  154. package/src/engine/module-manifest.ts +21 -1
  155. package/src/engine/register-engine.test.ts +6 -1
  156. package/src/engine/register-engine.ts +26 -3
  157. package/src/errors/classify.test.ts +6 -2
  158. package/src/errors/retry.test.ts +1 -4
  159. package/src/facades/facade-factory.test.ts +110 -64
  160. package/src/flows/epilogue.test.ts +16 -10
  161. package/src/flows/gate-evaluator.test.ts +12 -6
  162. package/src/flows/gate-evaluator.ts +1 -3
  163. package/src/governance/governance.test.ts +137 -21
  164. package/src/health/health-registry.test.ts +8 -1
  165. package/src/intake/content-classifier.test.ts +121 -51
  166. package/src/intake/dedup-gate.test.ts +38 -22
  167. package/src/intake/intake-pipeline.test.ts +5 -3
  168. package/src/intake/text-ingester.test.ts +26 -20
  169. package/src/llm/key-pool.test.ts +1 -3
  170. package/src/llm/llm-client.test.ts +1 -4
  171. package/src/llm/oauth-discovery.test.ts +16 -16
  172. package/src/llm/utils.test.ts +62 -18
  173. package/src/logging/logger.test.ts +4 -1
  174. package/src/loop/loop-manager.test.ts +2 -6
  175. package/src/migrations/migration-runner.edge-cases.test.ts +2 -7
  176. package/src/operator/operator-profile-extended.test.ts +15 -5
  177. package/src/operator/operator-profile.test.ts +26 -8
  178. package/src/operator/operator-profile.ts +38 -22
  179. package/src/operator/operator-signals-extended.test.ts +35 -23
  180. package/src/operator/operator-signals.test.ts +6 -10
  181. package/src/operator/operator-signals.ts +2 -1
  182. package/src/operator/prompts/hook-precompact-operator-dispatch.md +10 -6
  183. package/src/operator/prompts/subagent-soft-signal-extractor.md +5 -0
  184. package/src/operator/prompts/subagent-synthesis-cognition.md +19 -10
  185. package/src/operator/prompts/subagent-synthesis-communication.md +13 -7
  186. package/src/operator/prompts/subagent-synthesis-technical.md +19 -9
  187. package/src/operator/prompts/subagent-synthesis-trust.md +27 -21
  188. package/src/persona/defaults.test.ts +1 -5
  189. package/src/planning/evidence-collector.test.ts +147 -38
  190. package/src/planning/evidence-collector.ts +1 -4
  191. package/src/planning/gap-analysis-alternatives.test.ts +41 -11
  192. package/src/planning/gap-passes.test.ts +215 -33
  193. package/src/planning/gap-passes.ts +115 -46
  194. package/src/planning/gap-patterns.test.ts +87 -13
  195. package/src/planning/gap-patterns.ts +114 -31
  196. package/src/planning/github-projection.test.ts +6 -1
  197. package/src/planning/github-projection.ts +41 -20
  198. package/src/planning/impact-analyzer.test.ts +10 -23
  199. package/src/planning/impact-analyzer.ts +33 -46
  200. package/src/planning/plan-lifecycle.test.ts +103 -36
  201. package/src/planning/plan-lifecycle.ts +49 -18
  202. package/src/planning/planner.test.ts +12 -2
  203. package/src/planning/planner.ts +198 -58
  204. package/src/planning/rationalization-detector.test.ts +5 -20
  205. package/src/planning/rationalization-detector.ts +14 -16
  206. package/src/planning/reconciliation-engine.test.ts +20 -3
  207. package/src/planning/reconciliation-engine.ts +1 -2
  208. package/src/planning/task-verifier.test.ts +59 -27
  209. package/src/planning/task-verifier.ts +15 -9
  210. package/src/playbooks/playbook-executor.test.ts +1 -3
  211. package/src/plugins/plugin-loader.test.ts +19 -14
  212. package/src/plugins/plugin-registry.test.ts +45 -33
  213. package/src/project/project-registry.test.ts +23 -12
  214. package/src/prompts/template-manager.test.ts +4 -1
  215. package/src/queue/job-queue.test.ts +10 -14
  216. package/src/runtime/admin-extra-ops.test.ts +5 -19
  217. package/src/runtime/admin-ops.test.ts +1 -3
  218. package/src/runtime/admin-setup-ops.test.ts +3 -4
  219. package/src/runtime/admin-setup-ops.ts +9 -2
  220. package/src/runtime/archive-ops.test.ts +4 -1
  221. package/src/runtime/branching-ops.test.ts +144 -0
  222. package/src/runtime/branching-ops.ts +107 -0
  223. package/src/runtime/capture-ops.test.ts +7 -21
  224. package/src/runtime/chain-ops.test.ts +16 -6
  225. package/src/runtime/claude-md-helpers.test.ts +1 -3
  226. package/src/runtime/context-health.test.ts +1 -3
  227. package/src/runtime/context-health.ts +1 -3
  228. package/src/runtime/curator-extra-ops.test.ts +3 -1
  229. package/src/runtime/domain-ops.test.ts +46 -36
  230. package/src/runtime/facades/admin-facade.test.ts +1 -4
  231. package/src/runtime/facades/archive-facade.test.ts +21 -7
  232. package/src/runtime/facades/brain-facade.test.ts +176 -72
  233. package/src/runtime/facades/branching-facade.test.ts +43 -0
  234. package/src/runtime/facades/branching-facade.ts +11 -0
  235. package/src/runtime/facades/chat-facade.test.ts +81 -28
  236. package/src/runtime/facades/chat-service-ops.test.ts +178 -73
  237. package/src/runtime/facades/chat-service-ops.ts +3 -1
  238. package/src/runtime/facades/chat-session-ops.test.ts +25 -10
  239. package/src/runtime/facades/chat-transport-ops.test.ts +101 -34
  240. package/src/runtime/facades/chat-transport-ops.ts +0 -1
  241. package/src/runtime/facades/context-facade.test.ts +19 -4
  242. package/src/runtime/facades/control-facade.test.ts +3 -3
  243. package/src/runtime/facades/index.ts +42 -0
  244. package/src/runtime/facades/intake-facade.test.ts +215 -0
  245. package/src/runtime/facades/intake-facade.ts +14 -0
  246. package/src/runtime/facades/links-facade.test.ts +203 -0
  247. package/src/runtime/facades/links-facade.ts +13 -0
  248. package/src/runtime/facades/loop-facade.test.ts +22 -5
  249. package/src/runtime/facades/memory-facade.test.ts +19 -5
  250. package/src/runtime/facades/operator-facade.test.ts +17 -4
  251. package/src/runtime/facades/operator-facade.ts +11 -3
  252. package/src/runtime/facades/orchestrate-facade.test.ts +7 -1
  253. package/src/runtime/facades/plan-facade.test.ts +29 -12
  254. package/src/runtime/facades/plan-facade.ts +7 -2
  255. package/src/runtime/facades/tier-facade.test.ts +47 -0
  256. package/src/runtime/facades/tier-facade.ts +11 -0
  257. package/src/runtime/facades/vault-facade.test.ts +174 -242
  258. package/src/runtime/facades/vault-facade.ts +55 -199
  259. package/src/runtime/github-integration.ts +11 -8
  260. package/src/runtime/grading-ops.test.ts +39 -8
  261. package/src/runtime/intake-ops.test.ts +69 -16
  262. package/src/runtime/loop-ops.test.ts +16 -6
  263. package/src/runtime/memory-cross-project-ops.test.ts +25 -14
  264. package/src/runtime/orchestrate-ops.ts +54 -27
  265. package/src/runtime/pack-ops.test.ts +23 -6
  266. package/src/runtime/planning-extra-ops.test.ts +17 -7
  267. package/src/runtime/planning-extra-ops.ts +3 -1
  268. package/src/runtime/playbook-ops.test.ts +26 -3
  269. package/src/runtime/plugin-ops.test.ts +83 -25
  270. package/src/runtime/project-ops.test.ts +26 -6
  271. package/src/runtime/runtime.ts +3 -1
  272. package/src/runtime/session-briefing.test.ts +183 -54
  273. package/src/runtime/session-briefing.ts +8 -2
  274. package/src/runtime/sync-ops.test.ts +3 -12
  275. package/src/runtime/telemetry-ops.test.ts +31 -6
  276. package/src/runtime/tier-ops.test.ts +159 -0
  277. package/src/runtime/tier-ops.ts +119 -0
  278. package/src/runtime/vault-extra-ops.test.ts +32 -8
  279. package/src/runtime/vault-sharing-ops.test.ts +1 -4
  280. package/src/skills/sync-skills.ts +2 -12
  281. package/src/transport/ws-server.test.ts +7 -4
  282. package/src/vault/__tests__/vault-characterization.test.ts +492 -81
  283. package/src/vault/linking.test.ts +50 -17
  284. package/src/vault/linking.ts +48 -7
  285. package/src/vault/obsidian-sync.test.ts +6 -3
  286. package/src/vault/scope-detector.test.ts +1 -3
  287. package/src/vault/vault-branching.test.ts +9 -7
  288. package/src/vault/vault-entries.ts +209 -65
  289. package/src/vault/vault-maintenance.ts +7 -12
  290. package/src/vault/vault-manager.test.ts +10 -10
  291. package/src/vault/vault-markdown-sync.ts +4 -1
  292. package/src/vault/vault-memories.ts +7 -7
  293. package/src/vault/vault-schema.ts +72 -15
  294. package/src/vault/vault.ts +55 -9
  295. package/src/brain/strength-scorer.ts +0 -404
  296. package/src/engine/index.ts +0 -21
  297. package/src/persona/index.ts +0 -9
  298. package/src/vault/vault-interfaces.ts +0 -56
@@ -25,13 +25,40 @@ function makePlan(overrides: Partial<Plan> = {}): Plan {
25
25
  scope: 'Auth module, middleware, and user service. Does not include OAuth providers.',
26
26
  status: 'draft',
27
27
  decisions: [
28
- { decision: 'Use JWT for stateless auth', rationale: 'Because it scales horizontally without shared session store' },
28
+ {
29
+ decision: 'Use JWT for stateless auth',
30
+ rationale: 'Because it scales horizontally without shared session store',
31
+ },
29
32
  ],
30
33
  tasks: [
31
- { id: 'task-1', title: 'Add JWT signing', description: 'Implement JWT sign/verify using built-in crypto module', status: 'pending', updatedAt: Date.now() },
32
- { id: 'task-2', title: 'Add auth middleware', description: 'Create Express middleware that validates JWT from Authorization header', status: 'pending', updatedAt: Date.now() },
33
- { id: 'task-3', title: 'Add login endpoint', description: 'POST /auth/login returns JWT after verifying credentials', status: 'pending', updatedAt: Date.now() },
34
- { id: 'task-4', title: 'Add test coverage', description: 'Test JWT signing, middleware rejection, and login flow end-to-end', status: 'pending', updatedAt: Date.now() },
34
+ {
35
+ id: 'task-1',
36
+ title: 'Add JWT signing',
37
+ description: 'Implement JWT sign/verify using built-in crypto module',
38
+ status: 'pending',
39
+ updatedAt: Date.now(),
40
+ },
41
+ {
42
+ id: 'task-2',
43
+ title: 'Add auth middleware',
44
+ description: 'Create Express middleware that validates JWT from Authorization header',
45
+ status: 'pending',
46
+ updatedAt: Date.now(),
47
+ },
48
+ {
49
+ id: 'task-3',
50
+ title: 'Add login endpoint',
51
+ description: 'POST /auth/login returns JWT after verifying credentials',
52
+ status: 'pending',
53
+ updatedAt: Date.now(),
54
+ },
55
+ {
56
+ id: 'task-4',
57
+ title: 'Add test coverage',
58
+ description: 'Test JWT signing, middleware rejection, and login flow end-to-end',
59
+ status: 'pending',
60
+ updatedAt: Date.now(),
61
+ },
35
62
  ],
36
63
  checks: [],
37
64
  createdAt: Date.now(),
@@ -63,7 +90,9 @@ describe('Pattern constants (passes 5-8)', () => {
63
90
  });
64
91
 
65
92
  it('GENERIC_OBJECTIVE_PATTERNS does not match detailed objectives', () => {
66
- expect(GENERIC_OBJECTIVE_PATTERNS.some((p) => p.test('Create a user auth module with JWT'))).toBe(false);
93
+ expect(
94
+ GENERIC_OBJECTIVE_PATTERNS.some((p) => p.test('Create a user auth module with JWT')),
95
+ ).toBe(false);
67
96
  });
68
97
 
69
98
  it('RATIONALE_INDICATORS contains reasoning words', () => {
@@ -107,7 +136,13 @@ describe('Pass 5: Clarity', () => {
107
136
  it('flags tasks with very short descriptions', () => {
108
137
  const plan = makePlan({
109
138
  tasks: [
110
- { id: 't1', title: 'Do thing', description: 'Short', status: 'pending', updatedAt: Date.now() },
139
+ {
140
+ id: 't1',
141
+ title: 'Do thing',
142
+ description: 'Short',
143
+ status: 'pending',
144
+ updatedAt: Date.now(),
145
+ },
111
146
  { id: 't2', title: 'Do other', description: '', status: 'pending', updatedAt: Date.now() },
112
147
  ],
113
148
  });
@@ -123,7 +158,8 @@ describe('Pass 5: Clarity', () => {
123
158
 
124
159
  it('limits ambiguous words shown to 5', () => {
125
160
  const plan = makePlan({
126
- objective: 'Maybe perhaps we might could possibly somehow probably do various several things soon with some easy simple appropriate changes etc',
161
+ objective:
162
+ 'Maybe perhaps we might could possibly somehow probably do various several things soon with some easy simple appropriate changes etc',
127
163
  scope: 'Everything. Not limited.',
128
164
  });
129
165
  const gaps = analyzeClarity(plan);
@@ -158,7 +194,13 @@ describe('Pass 6: Semantic Quality', () => {
158
194
  it('flags too few tasks', () => {
159
195
  const plan = makePlan({
160
196
  tasks: [
161
- { id: 't1', title: 'Single task', description: 'Do everything in one task', status: 'pending', updatedAt: Date.now() },
197
+ {
198
+ id: 't1',
199
+ title: 'Single task',
200
+ description: 'Do everything in one task',
201
+ status: 'pending',
202
+ updatedAt: Date.now(),
203
+ },
162
204
  ],
163
205
  });
164
206
  const gaps = analyzeSemanticQuality(plan);
@@ -167,7 +209,11 @@ describe('Pass 6: Semantic Quality', () => {
167
209
 
168
210
  it('flags too many tasks (> 20)', () => {
169
211
  const tasks = Array.from({ length: 21 }, (_, i) => ({
170
- id: `t${i}`, title: `Task ${i}`, description: `Description for task ${i} with enough detail`, status: 'pending' as const, updatedAt: Date.now(),
212
+ id: `t${i}`,
213
+ title: `Task ${i}`,
214
+ description: `Description for task ${i} with enough detail`,
215
+ status: 'pending' as const,
216
+ updatedAt: Date.now(),
171
217
  }));
172
218
  const plan = makePlan({ tasks });
173
219
  const gaps = analyzeSemanticQuality(plan);
@@ -184,7 +230,9 @@ describe('Pass 6: Semantic Quality', () => {
184
230
 
185
231
  it('does not flag decisions with proper rationale', () => {
186
232
  const plan = makePlan({
187
- decisions: [{ decision: 'Use JWT', rationale: 'This is better because it scales horizontally' }],
233
+ decisions: [
234
+ { decision: 'Use JWT', rationale: 'This is better because it scales horizontally' },
235
+ ],
188
236
  });
189
237
  const gaps = analyzeSemanticQuality(plan);
190
238
  expect(gaps.some((g) => g._trigger === 'shallow_rationale')).toBe(false);
@@ -193,9 +241,27 @@ describe('Pass 6: Semantic Quality', () => {
193
241
  it('flags duplicate task titles', () => {
194
242
  const plan = makePlan({
195
243
  tasks: [
196
- { id: 't1', title: 'Implement feature', description: 'First implementation', status: 'pending', updatedAt: Date.now() },
197
- { id: 't2', title: 'Implement feature', description: 'Duplicate title', status: 'pending', updatedAt: Date.now() },
198
- { id: 't3', title: 'Test feature', description: 'Test the feature with assertions', status: 'pending', updatedAt: Date.now() },
244
+ {
245
+ id: 't1',
246
+ title: 'Implement feature',
247
+ description: 'First implementation',
248
+ status: 'pending',
249
+ updatedAt: Date.now(),
250
+ },
251
+ {
252
+ id: 't2',
253
+ title: 'Implement feature',
254
+ description: 'Duplicate title',
255
+ status: 'pending',
256
+ updatedAt: Date.now(),
257
+ },
258
+ {
259
+ id: 't3',
260
+ title: 'Test feature',
261
+ description: 'Test the feature with assertions',
262
+ status: 'pending',
263
+ updatedAt: Date.now(),
264
+ },
199
265
  ],
200
266
  });
201
267
  const gaps = analyzeSemanticQuality(plan);
@@ -212,7 +278,13 @@ describe('Pass 6: Semantic Quality', () => {
212
278
  const plan = makePlan({
213
279
  decisions: [],
214
280
  tasks: [
215
- { id: 't1', title: 'Single task', description: 'Do the thing', status: 'pending', updatedAt: Date.now() },
281
+ {
282
+ id: 't1',
283
+ title: 'Single task',
284
+ description: 'Do the thing',
285
+ status: 'pending',
286
+ updatedAt: Date.now(),
287
+ },
216
288
  ],
217
289
  });
218
290
  const gaps = analyzeSemanticQuality(plan);
@@ -224,9 +296,28 @@ describe('Pass 7: Knowledge Depth', () => {
224
296
  it('awards bonus for 5+ vault pattern references', () => {
225
297
  const plan = makePlan({
226
298
  tasks: [
227
- { id: 't1', title: 'Apply patterns', description: 'Use zod-form-validation and react-query-caching and error-boundary-pattern', status: 'pending', updatedAt: Date.now() },
228
- { id: 't2', title: 'More patterns', description: 'Use accessibility-focus-ring and semantic-token-usage and component-variant-pattern', status: 'pending', updatedAt: Date.now() },
229
- { id: 't3', title: 'Testing', description: 'Test with vitest-snapshot-testing approach', status: 'pending', updatedAt: Date.now() },
299
+ {
300
+ id: 't1',
301
+ title: 'Apply patterns',
302
+ description: 'Use zod-form-validation and react-query-caching and error-boundary-pattern',
303
+ status: 'pending',
304
+ updatedAt: Date.now(),
305
+ },
306
+ {
307
+ id: 't2',
308
+ title: 'More patterns',
309
+ description:
310
+ 'Use accessibility-focus-ring and semantic-token-usage and component-variant-pattern',
311
+ status: 'pending',
312
+ updatedAt: Date.now(),
313
+ },
314
+ {
315
+ id: 't3',
316
+ title: 'Testing',
317
+ description: 'Test with vitest-snapshot-testing approach',
318
+ status: 'pending',
319
+ updatedAt: Date.now(),
320
+ },
230
321
  ],
231
322
  });
232
323
  const gaps = analyzeKnowledgeDepth(plan);
@@ -237,9 +328,27 @@ describe('Pass 7: Knowledge Depth', () => {
237
328
  it('awards bonus for 2-4 vault pattern references', () => {
238
329
  const plan = makePlan({
239
330
  tasks: [
240
- { id: 't1', title: 'Apply patterns', description: 'Use zod-form-validation and react-query-caching', status: 'pending', updatedAt: Date.now() },
241
- { id: 't2', title: 'Build', description: 'Build the component', status: 'pending', updatedAt: Date.now() },
242
- { id: 't3', title: 'Test', description: 'Run the test suite', status: 'pending', updatedAt: Date.now() },
331
+ {
332
+ id: 't1',
333
+ title: 'Apply patterns',
334
+ description: 'Use zod-form-validation and react-query-caching',
335
+ status: 'pending',
336
+ updatedAt: Date.now(),
337
+ },
338
+ {
339
+ id: 't2',
340
+ title: 'Build',
341
+ description: 'Build the component',
342
+ status: 'pending',
343
+ updatedAt: Date.now(),
344
+ },
345
+ {
346
+ id: 't3',
347
+ title: 'Test',
348
+ description: 'Run the test suite',
349
+ status: 'pending',
350
+ updatedAt: Date.now(),
351
+ },
243
352
  ],
244
353
  });
245
354
  const gaps = analyzeKnowledgeDepth(plan);
@@ -249,11 +358,46 @@ describe('Pass 7: Knowledge Depth', () => {
249
358
  it('awards bonus for high acceptance criteria coverage', () => {
250
359
  const plan = makePlan({
251
360
  tasks: [
252
- { id: 't1', title: 'T1', description: 'Desc 1', status: 'pending', updatedAt: Date.now(), acceptanceCriteria: ['Criteria A'] },
253
- { id: 't2', title: 'T2', description: 'Desc 2', status: 'pending', updatedAt: Date.now(), acceptanceCriteria: ['Criteria B'] },
254
- { id: 't3', title: 'T3', description: 'Desc 3', status: 'pending', updatedAt: Date.now(), acceptanceCriteria: ['Criteria C'] },
255
- { id: 't4', title: 'T4', description: 'Desc 4', status: 'pending', updatedAt: Date.now(), acceptanceCriteria: ['Criteria D'] },
256
- { id: 't5', title: 'T5', description: 'Desc 5', status: 'pending', updatedAt: Date.now(), acceptanceCriteria: ['Criteria E'] },
361
+ {
362
+ id: 't1',
363
+ title: 'T1',
364
+ description: 'Desc 1',
365
+ status: 'pending',
366
+ updatedAt: Date.now(),
367
+ acceptanceCriteria: ['Criteria A'],
368
+ },
369
+ {
370
+ id: 't2',
371
+ title: 'T2',
372
+ description: 'Desc 2',
373
+ status: 'pending',
374
+ updatedAt: Date.now(),
375
+ acceptanceCriteria: ['Criteria B'],
376
+ },
377
+ {
378
+ id: 't3',
379
+ title: 'T3',
380
+ description: 'Desc 3',
381
+ status: 'pending',
382
+ updatedAt: Date.now(),
383
+ acceptanceCriteria: ['Criteria C'],
384
+ },
385
+ {
386
+ id: 't4',
387
+ title: 'T4',
388
+ description: 'Desc 4',
389
+ status: 'pending',
390
+ updatedAt: Date.now(),
391
+ acceptanceCriteria: ['Criteria D'],
392
+ },
393
+ {
394
+ id: 't5',
395
+ title: 'T5',
396
+ description: 'Desc 5',
397
+ status: 'pending',
398
+ updatedAt: Date.now(),
399
+ acceptanceCriteria: ['Criteria E'],
400
+ },
257
401
  ],
258
402
  });
259
403
  const gaps = analyzeKnowledgeDepth(plan);
@@ -263,7 +407,14 @@ describe('Pass 7: Knowledge Depth', () => {
263
407
  it('does not award acceptance criteria bonus below 80% threshold', () => {
264
408
  const plan = makePlan({
265
409
  tasks: [
266
- { id: 't1', title: 'T1', description: 'Desc', status: 'pending', updatedAt: Date.now(), acceptanceCriteria: ['A'] },
410
+ {
411
+ id: 't1',
412
+ title: 'T1',
413
+ description: 'Desc',
414
+ status: 'pending',
415
+ updatedAt: Date.now(),
416
+ acceptanceCriteria: ['A'],
417
+ },
267
418
  { id: 't2', title: 'T2', description: 'Desc', status: 'pending', updatedAt: Date.now() },
268
419
  { id: 't3', title: 'T3', description: 'Desc', status: 'pending', updatedAt: Date.now() },
269
420
  ],
@@ -273,7 +424,8 @@ describe('Pass 7: Knowledge Depth', () => {
273
424
  });
274
425
 
275
426
  it('awards bonus for rich task descriptions (avg >= 80 chars)', () => {
276
- const longDesc = 'This is a very detailed task description that provides specific technical context about what needs to be implemented.';
427
+ const longDesc =
428
+ 'This is a very detailed task description that provides specific technical context about what needs to be implemented.';
277
429
  const plan = makePlan({
278
430
  tasks: [
279
431
  { id: 't1', title: 'T1', description: longDesc, status: 'pending', updatedAt: Date.now() },
@@ -288,9 +440,27 @@ describe('Pass 7: Knowledge Depth', () => {
288
440
  it('awards bonus for domain knowledge indicators', () => {
289
441
  const plan = makePlan({
290
442
  tasks: [
291
- { id: 't1', title: 'A11y audit', description: 'Check WCAG 2.1 compliance and aria-label usage with vault patterns', status: 'pending', updatedAt: Date.now() },
292
- { id: 't2', title: 'Contrast', description: '4.5:1 contrast ratio for all text, anti-pattern detection', status: 'pending', updatedAt: Date.now() },
293
- { id: 't3', title: 'Touch', description: '44px touch target minimum, acceptance criteria for all buttons', status: 'pending', updatedAt: Date.now() },
443
+ {
444
+ id: 't1',
445
+ title: 'A11y audit',
446
+ description: 'Check WCAG 2.1 compliance and aria-label usage with vault patterns',
447
+ status: 'pending',
448
+ updatedAt: Date.now(),
449
+ },
450
+ {
451
+ id: 't2',
452
+ title: 'Contrast',
453
+ description: '4.5:1 contrast ratio for all text, anti-pattern detection',
454
+ status: 'pending',
455
+ updatedAt: Date.now(),
456
+ },
457
+ {
458
+ id: 't3',
459
+ title: 'Touch',
460
+ description: '44px touch target minimum, acceptance criteria for all buttons',
461
+ status: 'pending',
462
+ updatedAt: Date.now(),
463
+ },
294
464
  ],
295
465
  });
296
466
  const gaps = analyzeKnowledgeDepth(plan);
@@ -300,7 +470,13 @@ describe('Pass 7: Knowledge Depth', () => {
300
470
  it('returns no bonuses for basic plan', () => {
301
471
  const plan = makePlan({
302
472
  tasks: [
303
- { id: 't1', title: 'Do thing', description: 'Do it', status: 'pending', updatedAt: Date.now() },
473
+ {
474
+ id: 't1',
475
+ title: 'Do thing',
476
+ description: 'Do it',
477
+ status: 'pending',
478
+ updatedAt: Date.now(),
479
+ },
304
480
  ],
305
481
  });
306
482
  const gaps = analyzeKnowledgeDepth(plan);
@@ -311,7 +487,13 @@ describe('Pass 7: Knowledge Depth', () => {
311
487
  it('excludes common hyphenated words from pattern refs', () => {
312
488
  const plan = makePlan({
313
489
  tasks: [
314
- { id: 't1', title: 'T1', description: 'Use front-end and back-end and real-time and client-side and server-side', status: 'pending', updatedAt: Date.now() },
490
+ {
491
+ id: 't1',
492
+ title: 'T1',
493
+ description: 'Use front-end and back-end and real-time and client-side and server-side',
494
+ status: 'pending',
495
+ updatedAt: Date.now(),
496
+ },
315
497
  ],
316
498
  });
317
499
  const gaps = analyzeKnowledgeDepth(plan);
@@ -6,20 +6,26 @@
6
6
 
7
7
  import type { Plan } from './planner.js';
8
8
  import type { PlanGap } from './gap-types.js';
9
- import {
10
- gap,
11
- taskText,
12
- decisionText,
13
- decisionsText,
14
- containsAny,
15
- } from './gap-patterns.js';
9
+ import { gap, taskText, decisionText, decisionsText, containsAny } from './gap-patterns.js';
16
10
 
17
11
  // ─── Pattern Constants (Passes 5-8) ─────────────────────────────
18
12
 
19
13
  export const AMBIGUOUS_WORDS = [
20
- 'maybe', 'perhaps', 'might', 'could', 'some', 'etc', 'soon',
21
- 'simple', 'easy', 'appropriate', 'various', 'several',
22
- 'probably', 'possibly', 'somehow',
14
+ 'maybe',
15
+ 'perhaps',
16
+ 'might',
17
+ 'could',
18
+ 'some',
19
+ 'etc',
20
+ 'soon',
21
+ 'simple',
22
+ 'easy',
23
+ 'appropriate',
24
+ 'various',
25
+ 'several',
26
+ 'probably',
27
+ 'possibly',
28
+ 'somehow',
23
29
  ];
24
30
 
25
31
  export const GENERIC_OBJECTIVE_PATTERNS = [
@@ -29,8 +35,13 @@ export const GENERIC_OBJECTIVE_PATTERNS = [
29
35
  ];
30
36
 
31
37
  export const RATIONALE_INDICATORS = [
32
- 'because', 'since', 'due to', 'in order to',
33
- 'so that', 'given that', 'as a result',
38
+ 'because',
39
+ 'since',
40
+ 'due to',
41
+ 'in order to',
42
+ 'so that',
43
+ 'given that',
44
+ 'as a result',
34
45
  ];
35
46
 
36
47
  export const SHALLOW_INDICATORS = ['better', 'good', 'best', 'nice', 'great', 'improved'];
@@ -64,10 +75,12 @@ export function analyzeClarity(plan: Plan): PlanGap[] {
64
75
  if (found.length > 0) {
65
76
  gaps.push(
66
77
  gap(
67
- 'minor', 'clarity',
78
+ 'minor',
79
+ 'clarity',
68
80
  `Ambiguous language detected: ${found.slice(0, 5).join(', ')}${found.length > 5 ? ` (+${found.length - 5} more)` : ''}.`,
69
81
  'Replace vague terms with concrete, specific language.',
70
- undefined, `ambiguous_words:${found.join(',')}`,
82
+ undefined,
83
+ `ambiguous_words:${found.join(',')}`,
71
84
  ),
72
85
  );
73
86
  }
@@ -76,10 +89,12 @@ export function analyzeClarity(plan: Plan): PlanGap[] {
76
89
  if (shortTasks.length > 0) {
77
90
  gaps.push(
78
91
  gap(
79
- 'minor', 'clarity',
92
+ 'minor',
93
+ 'clarity',
80
94
  `${shortTasks.length} task(s) with very short descriptions: ${shortTasks.map((t) => t.id).join(', ')}.`,
81
95
  'Add detailed descriptions to all tasks explaining what needs to be done.',
82
- 'tasks', 'short_task_descriptions',
96
+ 'tasks',
97
+ 'short_task_descriptions',
83
98
  ),
84
99
  );
85
100
  }
@@ -99,10 +114,12 @@ export function analyzeSemanticQuality(plan: Plan): PlanGap[] {
99
114
  if (isGeneric || words.length < 5) {
100
115
  gaps.push(
101
116
  gap(
102
- 'major', 'semantic-quality',
117
+ 'major',
118
+ 'semantic-quality',
103
119
  `Objective is too generic${words.length < 5 ? ` (${words.length} words)` : ''}: "${plan.objective.trim()}".`,
104
120
  'Expand the objective to describe the specific outcome, context, and constraints.',
105
- 'objective', 'generic_objective',
121
+ 'objective',
122
+ 'generic_objective',
106
123
  ),
107
124
  );
108
125
  }
@@ -111,19 +128,23 @@ export function analyzeSemanticQuality(plan: Plan): PlanGap[] {
111
128
  if (plan.tasks.length > 0 && plan.tasks.length < 3) {
112
129
  gaps.push(
113
130
  gap(
114
- 'minor', 'semantic-quality',
131
+ 'minor',
132
+ 'semantic-quality',
115
133
  `Only ${plan.tasks.length} task(s) — plan may lack sufficient breakdown.`,
116
134
  'Break down the work into 3-15 well-defined tasks for better tracking.',
117
- 'tasks', 'too_few_tasks',
135
+ 'tasks',
136
+ 'too_few_tasks',
118
137
  ),
119
138
  );
120
139
  } else if (plan.tasks.length > 20) {
121
140
  gaps.push(
122
141
  gap(
123
- 'major', 'semantic-quality',
142
+ 'major',
143
+ 'semantic-quality',
124
144
  `${plan.tasks.length} tasks — plan scope may be too large.`,
125
145
  'Split into multiple plans or consolidate related tasks to stay under 20.',
126
- 'tasks', 'too_many_tasks',
146
+ 'tasks',
147
+ 'too_many_tasks',
127
148
  ),
128
149
  );
129
150
  }
@@ -135,10 +156,12 @@ export function analyzeSemanticQuality(plan: Plan): PlanGap[] {
135
156
  if (hasShallow && !hasRationale) {
136
157
  gaps.push(
137
158
  gap(
138
- 'minor', 'semantic-quality',
159
+ 'minor',
160
+ 'semantic-quality',
139
161
  `Decision ${i + 1} uses subjective language without justification.`,
140
162
  'Replace "better/good/best" with concrete reasoning using "because/since/due to".',
141
- `decisions[${i}]`, 'shallow_rationale',
163
+ `decisions[${i}]`,
164
+ 'shallow_rationale',
142
165
  ),
143
166
  );
144
167
  }
@@ -153,10 +176,12 @@ export function analyzeSemanticQuality(plan: Plan): PlanGap[] {
153
176
  if (duplicates.length > 0) {
154
177
  gaps.push(
155
178
  gap(
156
- 'minor', 'semantic-quality',
179
+ 'minor',
180
+ 'semantic-quality',
157
181
  `Duplicate task titles: ${[...new Set(duplicates)].join(', ')}.`,
158
182
  'Give each task a unique, descriptive title.',
159
- 'tasks', 'duplicate_task_titles',
183
+ 'tasks',
184
+ 'duplicate_task_titles',
160
185
  ),
161
186
  );
162
187
  }
@@ -164,10 +189,12 @@ export function analyzeSemanticQuality(plan: Plan): PlanGap[] {
164
189
  if (plan.tasks.length >= 3 && plan.decisions.length === 0) {
165
190
  gaps.push(
166
191
  gap(
167
- 'major', 'semantic-quality',
192
+ 'major',
193
+ 'semantic-quality',
168
194
  `${plan.tasks.length} tasks but no decisions documented.`,
169
195
  'Document key decisions and their rationale — at least 1 per 3 tasks.',
170
- 'decisions', 'no_decisions',
196
+ 'decisions',
197
+ 'no_decisions',
171
198
  ),
172
199
  );
173
200
  }
@@ -196,20 +223,35 @@ export function analyzeKnowledgeDepth(plan: Plan): PlanGap[] {
196
223
 
197
224
  if (namedPatternCount >= 5) {
198
225
  gaps.push(
199
- gap('bonus', 'knowledge-depth',
226
+ gap(
227
+ 'bonus',
228
+ 'knowledge-depth',
200
229
  `${namedPatternCount} vault pattern references across tasks — strong knowledge-informed plan.`,
201
- '', 'tasks', 'vault_pattern_refs_high'),
230
+ '',
231
+ 'tasks',
232
+ 'vault_pattern_refs_high',
233
+ ),
202
234
  );
203
235
  gaps.push(
204
- gap('bonus', 'knowledge-depth',
236
+ gap(
237
+ 'bonus',
238
+ 'knowledge-depth',
205
239
  'Vault pattern density indicates expert-level domain knowledge.',
206
- '', 'tasks', 'vault_pattern_density'),
240
+ '',
241
+ 'tasks',
242
+ 'vault_pattern_density',
243
+ ),
207
244
  );
208
245
  } else if (namedPatternCount >= 2) {
209
246
  gaps.push(
210
- gap('bonus', 'knowledge-depth',
247
+ gap(
248
+ 'bonus',
249
+ 'knowledge-depth',
211
250
  `${namedPatternCount} vault pattern references across tasks.`,
212
- '', 'tasks', 'vault_pattern_refs_medium'),
251
+ '',
252
+ 'tasks',
253
+ 'vault_pattern_refs_medium',
254
+ ),
213
255
  );
214
256
  }
215
257
 
@@ -224,9 +266,14 @@ export function analyzeKnowledgeDepth(plan: Plan): PlanGap[] {
224
266
 
225
267
  if (plan.tasks.length > 0 && tasksWithCriteria / plan.tasks.length >= 0.8) {
226
268
  gaps.push(
227
- gap('bonus', 'knowledge-depth',
269
+ gap(
270
+ 'bonus',
271
+ 'knowledge-depth',
228
272
  `${tasksWithCriteria}/${plan.tasks.length} tasks have acceptance criteria (${totalCriteria} total).`,
229
- '', 'tasks', 'high_acceptance_criteria'),
273
+ '',
274
+ 'tasks',
275
+ 'high_acceptance_criteria',
276
+ ),
230
277
  );
231
278
  }
232
279
 
@@ -237,9 +284,14 @@ export function analyzeKnowledgeDepth(plan: Plan): PlanGap[] {
237
284
 
238
285
  if (indicatorHits >= 4) {
239
286
  gaps.push(
240
- gap('bonus', 'knowledge-depth',
287
+ gap(
288
+ 'bonus',
289
+ 'knowledge-depth',
241
290
  `${indicatorHits} domain-specific knowledge indicators found (WCAG, ARIA, contrast ratios, touch targets, etc.).`,
242
- '', 'tasks', 'domain_knowledge_indicators'),
291
+ '',
292
+ 'tasks',
293
+ 'domain_knowledge_indicators',
294
+ ),
243
295
  );
244
296
  }
245
297
 
@@ -248,9 +300,14 @@ export function analyzeKnowledgeDepth(plan: Plan): PlanGap[] {
248
300
  plan.tasks.reduce((sum, t) => sum + (t.description?.length ?? 0), 0) / plan.tasks.length;
249
301
  if (avgDescLength >= 80) {
250
302
  gaps.push(
251
- gap('bonus', 'knowledge-depth',
303
+ gap(
304
+ 'bonus',
305
+ 'knowledge-depth',
252
306
  `Task descriptions average ${Math.round(avgDescLength)} chars — detailed and specific.`,
253
- '', 'tasks', 'rich_task_descriptions'),
307
+ '',
308
+ 'tasks',
309
+ 'rich_task_descriptions',
310
+ ),
254
311
  );
255
312
  }
256
313
  }
@@ -266,30 +323,42 @@ export function analyzeAlternatives(plan: Plan): PlanGap[] {
266
323
 
267
324
  if (!alts || alts.length === 0) {
268
325
  gaps.push(
269
- gap('major', 'alternative-analysis',
326
+ gap(
327
+ 'major',
328
+ 'alternative-analysis',
270
329
  'No alternatives considered — risk of tunnel vision.',
271
330
  'Add at least 2 rejected alternatives with pros, cons, and rejection rationale.',
272
- 'alternatives', 'no_alternatives'),
331
+ 'alternatives',
332
+ 'no_alternatives',
333
+ ),
273
334
  );
274
335
  return gaps;
275
336
  }
276
337
 
277
338
  if (alts.length < 2) {
278
339
  gaps.push(
279
- gap('minor', 'alternative-analysis',
340
+ gap(
341
+ 'minor',
342
+ 'alternative-analysis',
280
343
  `Only ${alts.length} alternative explored — consider at least 2.`,
281
344
  'Add another rejected alternative to strengthen decision rationale.',
282
- 'alternatives', 'few_alternatives'),
345
+ 'alternatives',
346
+ 'few_alternatives',
347
+ ),
283
348
  );
284
349
  }
285
350
 
286
351
  for (let i = 0; i < alts.length; i++) {
287
352
  if (!alts[i].rejected_reason || alts[i].rejected_reason.trim().length === 0) {
288
353
  gaps.push(
289
- gap('minor', 'alternative-analysis',
354
+ gap(
355
+ 'minor',
356
+ 'alternative-analysis',
290
357
  `Alternative ${i + 1} ("${alts[i].approach}") missing rejection rationale.`,
291
358
  'Explain why this alternative was rejected.',
292
- `alternatives[${i}]`, 'missing_rejection_rationale'),
359
+ `alternatives[${i}]`,
360
+ 'missing_rejection_rationale',
361
+ ),
293
362
  );
294
363
  }
295
364
  }