@soleri/core 9.2.0 → 9.3.1

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 (316) 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 +2 -0
  14. package/dist/engine/module-manifest.d.ts.map +1 -1
  15. package/dist/engine/module-manifest.js +136 -1
  16. package/dist/engine/module-manifest.js.map +1 -1
  17. package/dist/engine/register-engine.d.ts.map +1 -1
  18. package/dist/engine/register-engine.js +25 -1
  19. package/dist/engine/register-engine.js.map +1 -1
  20. package/dist/flows/gate-evaluator.js.map +1 -1
  21. package/dist/index.d.ts +2 -0
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +2 -0
  24. package/dist/index.js.map +1 -1
  25. package/dist/operator/operator-profile.d.ts.map +1 -1
  26. package/dist/operator/operator-profile.js +11 -5
  27. package/dist/operator/operator-profile.js.map +1 -1
  28. package/dist/operator/operator-signals.d.ts.map +1 -1
  29. package/dist/operator/operator-signals.js.map +1 -1
  30. package/dist/planning/evidence-collector.js.map +1 -1
  31. package/dist/planning/gap-passes.d.ts.map +1 -1
  32. package/dist/planning/gap-passes.js +23 -6
  33. package/dist/planning/gap-passes.js.map +1 -1
  34. package/dist/planning/gap-patterns.d.ts.map +1 -1
  35. package/dist/planning/gap-patterns.js +57 -11
  36. package/dist/planning/gap-patterns.js.map +1 -1
  37. package/dist/planning/github-projection.d.ts.map +1 -1
  38. package/dist/planning/github-projection.js +39 -20
  39. package/dist/planning/github-projection.js.map +1 -1
  40. package/dist/planning/impact-analyzer.d.ts.map +1 -1
  41. package/dist/planning/impact-analyzer.js +20 -18
  42. package/dist/planning/impact-analyzer.js.map +1 -1
  43. package/dist/planning/plan-lifecycle.d.ts.map +1 -1
  44. package/dist/planning/plan-lifecycle.js +22 -9
  45. package/dist/planning/plan-lifecycle.js.map +1 -1
  46. package/dist/planning/planner.d.ts.map +1 -1
  47. package/dist/planning/planner.js +60 -17
  48. package/dist/planning/planner.js.map +1 -1
  49. package/dist/planning/rationalization-detector.d.ts.map +1 -1
  50. package/dist/planning/rationalization-detector.js.map +1 -1
  51. package/dist/planning/reconciliation-engine.d.ts.map +1 -1
  52. package/dist/planning/reconciliation-engine.js.map +1 -1
  53. package/dist/planning/task-complexity-assessor.d.ts +42 -0
  54. package/dist/planning/task-complexity-assessor.d.ts.map +1 -0
  55. package/dist/planning/task-complexity-assessor.js +132 -0
  56. package/dist/planning/task-complexity-assessor.js.map +1 -0
  57. package/dist/planning/task-verifier.d.ts.map +1 -1
  58. package/dist/planning/task-verifier.js +14 -6
  59. package/dist/planning/task-verifier.js.map +1 -1
  60. package/dist/runtime/admin-ops.d.ts.map +1 -1
  61. package/dist/runtime/admin-ops.js +18 -0
  62. package/dist/runtime/admin-ops.js.map +1 -1
  63. package/dist/runtime/admin-setup-ops.d.ts.map +1 -1
  64. package/dist/runtime/admin-setup-ops.js +2 -1
  65. package/dist/runtime/admin-setup-ops.js.map +1 -1
  66. package/dist/runtime/branching-ops.d.ts +12 -0
  67. package/dist/runtime/branching-ops.d.ts.map +1 -0
  68. package/dist/runtime/branching-ops.js +100 -0
  69. package/dist/runtime/branching-ops.js.map +1 -0
  70. package/dist/runtime/context-health.d.ts.map +1 -1
  71. package/dist/runtime/context-health.js.map +1 -1
  72. package/dist/runtime/facades/branching-facade.d.ts +7 -0
  73. package/dist/runtime/facades/branching-facade.d.ts.map +1 -0
  74. package/dist/runtime/facades/branching-facade.js +8 -0
  75. package/dist/runtime/facades/branching-facade.js.map +1 -0
  76. package/dist/runtime/facades/chat-service-ops.d.ts.map +1 -1
  77. package/dist/runtime/facades/chat-service-ops.js +3 -1
  78. package/dist/runtime/facades/chat-service-ops.js.map +1 -1
  79. package/dist/runtime/facades/chat-transport-ops.d.ts.map +1 -1
  80. package/dist/runtime/facades/chat-transport-ops.js.map +1 -1
  81. package/dist/runtime/facades/index.d.ts.map +1 -1
  82. package/dist/runtime/facades/index.js +42 -0
  83. package/dist/runtime/facades/index.js.map +1 -1
  84. package/dist/runtime/facades/intake-facade.d.ts +9 -0
  85. package/dist/runtime/facades/intake-facade.d.ts.map +1 -0
  86. package/dist/runtime/facades/intake-facade.js +11 -0
  87. package/dist/runtime/facades/intake-facade.js.map +1 -0
  88. package/dist/runtime/facades/links-facade.d.ts +9 -0
  89. package/dist/runtime/facades/links-facade.d.ts.map +1 -0
  90. package/dist/runtime/facades/links-facade.js +10 -0
  91. package/dist/runtime/facades/links-facade.js.map +1 -0
  92. package/dist/runtime/facades/operator-facade.d.ts.map +1 -1
  93. package/dist/runtime/facades/operator-facade.js.map +1 -1
  94. package/dist/runtime/facades/plan-facade.d.ts.map +1 -1
  95. package/dist/runtime/facades/plan-facade.js +4 -1
  96. package/dist/runtime/facades/plan-facade.js.map +1 -1
  97. package/dist/runtime/facades/tier-facade.d.ts +7 -0
  98. package/dist/runtime/facades/tier-facade.d.ts.map +1 -0
  99. package/dist/runtime/facades/tier-facade.js +8 -0
  100. package/dist/runtime/facades/tier-facade.js.map +1 -0
  101. package/dist/runtime/facades/vault-facade.d.ts +9 -1
  102. package/dist/runtime/facades/vault-facade.d.ts.map +1 -1
  103. package/dist/runtime/facades/vault-facade.js +44 -187
  104. package/dist/runtime/facades/vault-facade.js.map +1 -1
  105. package/dist/runtime/github-integration.d.ts.map +1 -1
  106. package/dist/runtime/github-integration.js +11 -4
  107. package/dist/runtime/github-integration.js.map +1 -1
  108. package/dist/runtime/orchestrate-ops.d.ts.map +1 -1
  109. package/dist/runtime/orchestrate-ops.js +75 -42
  110. package/dist/runtime/orchestrate-ops.js.map +1 -1
  111. package/dist/runtime/planning-extra-ops.d.ts.map +1 -1
  112. package/dist/runtime/planning-extra-ops.js.map +1 -1
  113. package/dist/runtime/runtime.d.ts.map +1 -1
  114. package/dist/runtime/runtime.js +3 -1
  115. package/dist/runtime/runtime.js.map +1 -1
  116. package/dist/runtime/session-briefing.d.ts.map +1 -1
  117. package/dist/runtime/session-briefing.js +5 -1
  118. package/dist/runtime/session-briefing.js.map +1 -1
  119. package/dist/runtime/tier-ops.d.ts +13 -0
  120. package/dist/runtime/tier-ops.d.ts.map +1 -0
  121. package/dist/runtime/tier-ops.js +110 -0
  122. package/dist/runtime/tier-ops.js.map +1 -0
  123. package/dist/skills/sync-skills.d.ts.map +1 -1
  124. package/dist/skills/sync-skills.js +1 -1
  125. package/dist/skills/sync-skills.js.map +1 -1
  126. package/dist/vault/linking.d.ts.map +1 -1
  127. package/dist/vault/linking.js +41 -5
  128. package/dist/vault/linking.js.map +1 -1
  129. package/dist/vault/vault-entries.d.ts.map +1 -1
  130. package/dist/vault/vault-entries.js +68 -26
  131. package/dist/vault/vault-entries.js.map +1 -1
  132. package/dist/vault/vault-maintenance.d.ts.map +1 -1
  133. package/dist/vault/vault-maintenance.js +6 -2
  134. package/dist/vault/vault-maintenance.js.map +1 -1
  135. package/dist/vault/vault-markdown-sync.d.ts.map +1 -1
  136. package/dist/vault/vault-markdown-sync.js.map +1 -1
  137. package/dist/vault/vault-memories.d.ts.map +1 -1
  138. package/dist/vault/vault-memories.js +3 -1
  139. package/dist/vault/vault-memories.js.map +1 -1
  140. package/dist/vault/vault-schema.js +36 -10
  141. package/dist/vault/vault-schema.js.map +1 -1
  142. package/dist/vault/vault.d.ts.map +1 -1
  143. package/dist/vault/vault.js +5 -1
  144. package/dist/vault/vault.js.map +1 -1
  145. package/package.json +7 -7
  146. package/src/agency/agency-manager.test.ts +60 -40
  147. package/src/agency/default-rules.test.ts +17 -9
  148. package/src/capabilities/registry.test.ts +2 -12
  149. package/src/chat/agent-loop.test.ts +33 -43
  150. package/src/chat/mcp-bridge.test.ts +7 -2
  151. package/src/claudemd/inject.test.ts +2 -12
  152. package/src/context/context-engine.test.ts +96 -51
  153. package/src/control/intent-router.test.ts +3 -3
  154. package/src/curator/classifier.test.ts +14 -8
  155. package/src/curator/contradiction-detector.test.ts +30 -5
  156. package/src/curator/curator.ts +278 -56
  157. package/src/curator/duplicate-detector.test.ts +77 -15
  158. package/src/curator/quality-gate.test.ts +71 -31
  159. package/src/curator/tag-manager.test.ts +12 -4
  160. package/src/domain-packs/knowledge-installer.test.ts +2 -10
  161. package/src/domain-packs/token-resolver.test.ts +1 -3
  162. package/src/domain-packs/types.test.ts +16 -2
  163. package/src/enforcement/registry.test.ts +2 -8
  164. package/src/engine/bin/soleri-engine.ts +3 -1
  165. package/src/engine/module-manifest.test.ts +48 -4
  166. package/src/engine/module-manifest.ts +138 -1
  167. package/src/engine/register-engine.test.ts +6 -1
  168. package/src/engine/register-engine.ts +26 -3
  169. package/src/errors/classify.test.ts +6 -2
  170. package/src/errors/retry.test.ts +1 -4
  171. package/src/facades/facade-factory.test.ts +110 -64
  172. package/src/flows/epilogue.test.ts +16 -10
  173. package/src/flows/gate-evaluator.test.ts +12 -6
  174. package/src/flows/gate-evaluator.ts +1 -3
  175. package/src/governance/governance.test.ts +137 -21
  176. package/src/health/health-registry.test.ts +8 -1
  177. package/src/index.ts +8 -0
  178. package/src/intake/content-classifier.test.ts +121 -51
  179. package/src/intake/dedup-gate.test.ts +38 -22
  180. package/src/intake/intake-pipeline.test.ts +5 -3
  181. package/src/intake/text-ingester.test.ts +26 -20
  182. package/src/llm/key-pool.test.ts +1 -3
  183. package/src/llm/llm-client.test.ts +1 -4
  184. package/src/llm/oauth-discovery.test.ts +16 -16
  185. package/src/llm/utils.test.ts +62 -18
  186. package/src/logging/logger.test.ts +4 -1
  187. package/src/loop/loop-manager.test.ts +2 -6
  188. package/src/migrations/migration-runner.edge-cases.test.ts +2 -7
  189. package/src/operator/operator-profile-extended.test.ts +15 -5
  190. package/src/operator/operator-profile.test.ts +26 -8
  191. package/src/operator/operator-profile.ts +38 -22
  192. package/src/operator/operator-signals-extended.test.ts +35 -23
  193. package/src/operator/operator-signals.test.ts +6 -10
  194. package/src/operator/operator-signals.ts +2 -1
  195. package/src/operator/prompts/hook-precompact-operator-dispatch.md +10 -6
  196. package/src/operator/prompts/subagent-soft-signal-extractor.md +5 -0
  197. package/src/operator/prompts/subagent-synthesis-cognition.md +19 -10
  198. package/src/operator/prompts/subagent-synthesis-communication.md +13 -7
  199. package/src/operator/prompts/subagent-synthesis-technical.md +19 -9
  200. package/src/operator/prompts/subagent-synthesis-trust.md +27 -21
  201. package/src/persona/defaults.test.ts +1 -5
  202. package/src/planning/evidence-collector.test.ts +147 -38
  203. package/src/planning/evidence-collector.ts +1 -4
  204. package/src/planning/gap-analysis-alternatives.test.ts +41 -11
  205. package/src/planning/gap-passes.test.ts +215 -33
  206. package/src/planning/gap-passes.ts +115 -46
  207. package/src/planning/gap-patterns.test.ts +87 -13
  208. package/src/planning/gap-patterns.ts +114 -31
  209. package/src/planning/github-projection.test.ts +6 -1
  210. package/src/planning/github-projection.ts +41 -20
  211. package/src/planning/impact-analyzer.test.ts +10 -23
  212. package/src/planning/impact-analyzer.ts +33 -46
  213. package/src/planning/plan-lifecycle.test.ts +103 -36
  214. package/src/planning/plan-lifecycle.ts +49 -18
  215. package/src/planning/planner.test.ts +12 -2
  216. package/src/planning/planner.ts +198 -58
  217. package/src/planning/rationalization-detector.test.ts +5 -20
  218. package/src/planning/rationalization-detector.ts +14 -16
  219. package/src/planning/reconciliation-engine.test.ts +20 -3
  220. package/src/planning/reconciliation-engine.ts +1 -2
  221. package/src/planning/task-complexity-assessor.test.ts +298 -0
  222. package/src/planning/task-complexity-assessor.ts +183 -0
  223. package/src/planning/task-verifier.test.ts +59 -27
  224. package/src/planning/task-verifier.ts +15 -9
  225. package/src/playbooks/playbook-executor.test.ts +1 -3
  226. package/src/plugins/plugin-loader.test.ts +19 -14
  227. package/src/plugins/plugin-registry.test.ts +45 -33
  228. package/src/project/project-registry.test.ts +23 -12
  229. package/src/prompts/template-manager.test.ts +4 -1
  230. package/src/queue/job-queue.test.ts +10 -14
  231. package/src/runtime/admin-extra-ops.test.ts +5 -19
  232. package/src/runtime/admin-ops.test.ts +22 -1
  233. package/src/runtime/admin-ops.ts +19 -0
  234. package/src/runtime/admin-setup-ops.test.ts +3 -4
  235. package/src/runtime/admin-setup-ops.ts +9 -2
  236. package/src/runtime/archive-ops.test.ts +4 -1
  237. package/src/runtime/branching-ops.test.ts +144 -0
  238. package/src/runtime/branching-ops.ts +107 -0
  239. package/src/runtime/capture-ops.test.ts +7 -21
  240. package/src/runtime/chain-ops.test.ts +16 -6
  241. package/src/runtime/claude-md-helpers.test.ts +1 -3
  242. package/src/runtime/context-health.test.ts +1 -3
  243. package/src/runtime/context-health.ts +1 -3
  244. package/src/runtime/curator-extra-ops.test.ts +3 -1
  245. package/src/runtime/domain-ops.test.ts +46 -36
  246. package/src/runtime/facades/admin-facade.test.ts +1 -4
  247. package/src/runtime/facades/archive-facade.test.ts +21 -7
  248. package/src/runtime/facades/brain-facade.test.ts +176 -72
  249. package/src/runtime/facades/branching-facade.test.ts +43 -0
  250. package/src/runtime/facades/branching-facade.ts +11 -0
  251. package/src/runtime/facades/chat-facade.test.ts +81 -28
  252. package/src/runtime/facades/chat-service-ops.test.ts +178 -73
  253. package/src/runtime/facades/chat-service-ops.ts +3 -1
  254. package/src/runtime/facades/chat-session-ops.test.ts +25 -10
  255. package/src/runtime/facades/chat-transport-ops.test.ts +101 -34
  256. package/src/runtime/facades/chat-transport-ops.ts +0 -1
  257. package/src/runtime/facades/context-facade.test.ts +19 -4
  258. package/src/runtime/facades/control-facade.test.ts +3 -3
  259. package/src/runtime/facades/index.ts +42 -0
  260. package/src/runtime/facades/intake-facade.test.ts +215 -0
  261. package/src/runtime/facades/intake-facade.ts +14 -0
  262. package/src/runtime/facades/links-facade.test.ts +203 -0
  263. package/src/runtime/facades/links-facade.ts +13 -0
  264. package/src/runtime/facades/loop-facade.test.ts +22 -5
  265. package/src/runtime/facades/memory-facade.test.ts +19 -5
  266. package/src/runtime/facades/operator-facade.test.ts +17 -4
  267. package/src/runtime/facades/operator-facade.ts +11 -3
  268. package/src/runtime/facades/orchestrate-facade.test.ts +7 -1
  269. package/src/runtime/facades/plan-facade.test.ts +29 -12
  270. package/src/runtime/facades/plan-facade.ts +7 -2
  271. package/src/runtime/facades/tier-facade.test.ts +47 -0
  272. package/src/runtime/facades/tier-facade.ts +11 -0
  273. package/src/runtime/facades/vault-facade.test.ts +174 -242
  274. package/src/runtime/facades/vault-facade.ts +55 -199
  275. package/src/runtime/github-integration.ts +11 -8
  276. package/src/runtime/grading-ops.test.ts +39 -8
  277. package/src/runtime/intake-ops.test.ts +69 -16
  278. package/src/runtime/loop-ops.test.ts +16 -6
  279. package/src/runtime/memory-cross-project-ops.test.ts +25 -14
  280. package/src/runtime/orchestrate-ops.test.ts +204 -0
  281. package/src/runtime/orchestrate-ops.ts +103 -65
  282. package/src/runtime/pack-ops.test.ts +23 -6
  283. package/src/runtime/planning-extra-ops.test.ts +17 -7
  284. package/src/runtime/planning-extra-ops.ts +3 -1
  285. package/src/runtime/playbook-ops.test.ts +26 -3
  286. package/src/runtime/plugin-ops.test.ts +83 -25
  287. package/src/runtime/project-ops.test.ts +26 -6
  288. package/src/runtime/runtime.ts +3 -1
  289. package/src/runtime/session-briefing.test.ts +183 -54
  290. package/src/runtime/session-briefing.ts +8 -2
  291. package/src/runtime/sync-ops.test.ts +3 -12
  292. package/src/runtime/telemetry-ops.test.ts +31 -6
  293. package/src/runtime/tier-ops.test.ts +159 -0
  294. package/src/runtime/tier-ops.ts +119 -0
  295. package/src/runtime/vault-extra-ops.test.ts +32 -8
  296. package/src/runtime/vault-sharing-ops.test.ts +1 -4
  297. package/src/skills/sync-skills.ts +2 -12
  298. package/src/transport/ws-server.test.ts +7 -4
  299. package/src/vault/__tests__/vault-characterization.test.ts +492 -81
  300. package/src/vault/linking.test.ts +50 -17
  301. package/src/vault/linking.ts +48 -7
  302. package/src/vault/obsidian-sync.test.ts +6 -3
  303. package/src/vault/scope-detector.test.ts +1 -3
  304. package/src/vault/vault-branching.test.ts +9 -7
  305. package/src/vault/vault-entries.ts +209 -65
  306. package/src/vault/vault-maintenance.ts +7 -12
  307. package/src/vault/vault-manager.test.ts +10 -10
  308. package/src/vault/vault-markdown-sync.ts +4 -1
  309. package/src/vault/vault-memories.ts +7 -7
  310. package/src/vault/vault-scaling.test.ts +5 -5
  311. package/src/vault/vault-schema.ts +72 -15
  312. package/src/vault/vault.ts +55 -9
  313. package/src/brain/strength-scorer.ts +0 -404
  314. package/src/engine/index.ts +0 -21
  315. package/src/persona/index.ts +0 -9
  316. package/src/vault/vault-interfaces.ts +0 -56
@@ -1,5 +1,11 @@
1
1
  import { describe, it, expect, vi, beforeEach } from 'vitest';
2
- import { CircuitBreaker, CircuitOpenError, computeDelay, retry, parseRateLimitHeaders } from './utils.js';
2
+ import {
3
+ CircuitBreaker,
4
+ CircuitOpenError,
5
+ computeDelay,
6
+ retry,
7
+ parseRateLimitHeaders,
8
+ } from './utils.js';
3
9
  import { LLMError } from './types.js';
4
10
 
5
11
  // ─── CircuitOpenError ───────────────────────────────────────────────
@@ -46,7 +52,11 @@ describe('CircuitBreaker', () => {
46
52
 
47
53
  it('should rethrow errors from the wrapped function', async () => {
48
54
  const cb = new CircuitBreaker({ name: 'test' });
49
- await expect(cb.call(async () => { throw new Error('boom'); })).rejects.toThrow('boom');
55
+ await expect(
56
+ cb.call(async () => {
57
+ throw new Error('boom');
58
+ }),
59
+ ).rejects.toThrow('boom');
50
60
  });
51
61
 
52
62
  it('should not count non-retryable errors as failures', async () => {
@@ -54,7 +64,11 @@ describe('CircuitBreaker', () => {
54
64
  const nonRetryable = new Error('not retryable');
55
65
 
56
66
  for (let i = 0; i < 5; i++) {
57
- await expect(cb.call(async () => { throw nonRetryable; })).rejects.toThrow();
67
+ await expect(
68
+ cb.call(async () => {
69
+ throw nonRetryable;
70
+ }),
71
+ ).rejects.toThrow();
58
72
  }
59
73
  expect(cb.getState().state).toBe('closed');
60
74
  });
@@ -63,8 +77,16 @@ describe('CircuitBreaker', () => {
63
77
  const cb = new CircuitBreaker({ name: 'test', failureThreshold: 2 });
64
78
  const retryableErr = new LLMError('rate limited', { retryable: true });
65
79
 
66
- await expect(cb.call(async () => { throw retryableErr; })).rejects.toThrow();
67
- await expect(cb.call(async () => { throw retryableErr; })).rejects.toThrow();
80
+ await expect(
81
+ cb.call(async () => {
82
+ throw retryableErr;
83
+ }),
84
+ ).rejects.toThrow();
85
+ await expect(
86
+ cb.call(async () => {
87
+ throw retryableErr;
88
+ }),
89
+ ).rejects.toThrow();
68
90
  expect(cb.isOpen()).toBe(true);
69
91
  });
70
92
 
@@ -72,7 +94,11 @@ describe('CircuitBreaker', () => {
72
94
  const cb = new CircuitBreaker({ name: 'test', failureThreshold: 1, resetTimeoutMs: 999_999 });
73
95
  const retryableErr = new LLMError('fail', { retryable: true });
74
96
 
75
- await expect(cb.call(async () => { throw retryableErr; })).rejects.toThrow();
97
+ await expect(
98
+ cb.call(async () => {
99
+ throw retryableErr;
100
+ }),
101
+ ).rejects.toThrow();
76
102
  await expect(cb.call(async () => 'ok')).rejects.toThrow(CircuitOpenError);
77
103
  });
78
104
 
@@ -80,7 +106,11 @@ describe('CircuitBreaker', () => {
80
106
  const cb = new CircuitBreaker({ name: 'test', failureThreshold: 1, resetTimeoutMs: 10 });
81
107
  const retryableErr = new LLMError('fail', { retryable: true });
82
108
 
83
- await expect(cb.call(async () => { throw retryableErr; })).rejects.toThrow();
109
+ await expect(
110
+ cb.call(async () => {
111
+ throw retryableErr;
112
+ }),
113
+ ).rejects.toThrow();
84
114
  expect(cb.isOpen()).toBe(true);
85
115
 
86
116
  // Wait for reset timeout
@@ -93,7 +123,11 @@ describe('CircuitBreaker', () => {
93
123
  const cb = new CircuitBreaker({ name: 'test', failureThreshold: 1, resetTimeoutMs: 10 });
94
124
  const retryableErr = new LLMError('fail', { retryable: true });
95
125
 
96
- await expect(cb.call(async () => { throw retryableErr; })).rejects.toThrow();
126
+ await expect(
127
+ cb.call(async () => {
128
+ throw retryableErr;
129
+ }),
130
+ ).rejects.toThrow();
97
131
  await new Promise((r) => setTimeout(r, 20));
98
132
 
99
133
  const result = await cb.call(async () => 'recovered');
@@ -105,10 +139,18 @@ describe('CircuitBreaker', () => {
105
139
  const cb = new CircuitBreaker({ name: 'test', failureThreshold: 1, resetTimeoutMs: 10 });
106
140
  const retryableErr = new LLMError('fail', { retryable: true });
107
141
 
108
- await expect(cb.call(async () => { throw retryableErr; })).rejects.toThrow();
142
+ await expect(
143
+ cb.call(async () => {
144
+ throw retryableErr;
145
+ }),
146
+ ).rejects.toThrow();
109
147
  await new Promise((r) => setTimeout(r, 20));
110
148
 
111
- await expect(cb.call(async () => { throw retryableErr; })).rejects.toThrow();
149
+ await expect(
150
+ cb.call(async () => {
151
+ throw retryableErr;
152
+ }),
153
+ ).rejects.toThrow();
112
154
  expect(cb.getState().state).toBe('open');
113
155
  });
114
156
 
@@ -116,7 +158,11 @@ describe('CircuitBreaker', () => {
116
158
  const cb = new CircuitBreaker({ name: 'test', failureThreshold: 1, resetTimeoutMs: 999_999 });
117
159
  const retryableErr = new LLMError('fail', { retryable: true });
118
160
 
119
- await expect(cb.call(async () => { throw retryableErr; })).rejects.toThrow();
161
+ await expect(
162
+ cb.call(async () => {
163
+ throw retryableErr;
164
+ }),
165
+ ).rejects.toThrow();
120
166
  expect(cb.isOpen()).toBe(true);
121
167
 
122
168
  cb.reset();
@@ -204,7 +250,9 @@ describe('retry', () => {
204
250
 
205
251
  it('should throw if maxAttempts < 1', async () => {
206
252
  vi.useRealTimers();
207
- await expect(retry(async () => 'ok', { maxAttempts: 0 })).rejects.toThrow('maxAttempts must be >= 1');
253
+ await expect(retry(async () => 'ok', { maxAttempts: 0 })).rejects.toThrow(
254
+ 'maxAttempts must be >= 1',
255
+ );
208
256
  });
209
257
 
210
258
  it('should throw non-retryable errors immediately', async () => {
@@ -227,9 +275,7 @@ describe('retry', () => {
227
275
  it('should succeed on retry after initial failures', async () => {
228
276
  vi.useRealTimers();
229
277
  const retryableErr = new LLMError('transient', { retryable: true });
230
- const fn = vi.fn()
231
- .mockRejectedValueOnce(retryableErr)
232
- .mockResolvedValueOnce('recovered');
278
+ const fn = vi.fn().mockRejectedValueOnce(retryableErr).mockResolvedValueOnce('recovered');
233
279
 
234
280
  const result = await retry(fn, { maxAttempts: 3, baseDelayMs: 1, maxDelayMs: 1, jitter: 0 });
235
281
  expect(result).toBe('recovered');
@@ -240,9 +286,7 @@ describe('retry', () => {
240
286
  vi.useRealTimers();
241
287
  const retryableErr = new LLMError('transient', { retryable: true });
242
288
  const onRetry = vi.fn();
243
- const fn = vi.fn()
244
- .mockRejectedValueOnce(retryableErr)
245
- .mockResolvedValueOnce('ok');
289
+ const fn = vi.fn().mockRejectedValueOnce(retryableErr).mockResolvedValueOnce('ok');
246
290
 
247
291
  await retry(fn, { maxAttempts: 3, baseDelayMs: 1, maxDelayMs: 1, jitter: 0, onRetry });
248
292
  expect(onRetry).toHaveBeenCalledTimes(1);
@@ -103,7 +103,10 @@ describe('Logger file logging', () => {
103
103
  beforeEach(() => {
104
104
  vi.spyOn(console, 'error').mockImplementation(() => {});
105
105
  vi.spyOn(console, 'warn').mockImplementation(() => {});
106
- tempDir = join(tmpdir(), `logger-colocated-${Date.now()}-${Math.random().toString(36).slice(2)}`);
106
+ tempDir = join(
107
+ tmpdir(),
108
+ `logger-colocated-${Date.now()}-${Math.random().toString(36).slice(2)}`,
109
+ );
107
110
  mkdirSync(tempDir, { recursive: true });
108
111
  });
109
112
 
@@ -34,9 +34,7 @@ describe('extractPromise', () => {
34
34
  });
35
35
 
36
36
  it('handles multiline content inside tags', () => {
37
- expect(extractPromise('<promise>\nSALVADOR_VALIDATED\n</promise>')).toBe(
38
- 'SALVADOR_VALIDATED',
39
- );
37
+ expect(extractPromise('<promise>\nSALVADOR_VALIDATED\n</promise>')).toBe('SALVADOR_VALIDATED');
40
38
  });
41
39
 
42
40
  it('extracts only the first match', () => {
@@ -349,9 +347,7 @@ describe('LoopManager', () => {
349
347
 
350
348
  it('detects completion promise and ends loop', () => {
351
349
  mgr.startLoop(makeConfig({ completionPromise: 'SALVADOR_VALIDATED' }));
352
- const result = mgr.iterateWithGate(
353
- 'Done! <promise>SALVADOR_VALIDATED</promise>',
354
- );
350
+ const result = mgr.iterateWithGate('Done! <promise>SALVADOR_VALIDATED</promise>');
355
351
 
356
352
  expect(result.decision).toBe('allow');
357
353
  expect(result.outcome).toBe('completed');
@@ -8,9 +8,7 @@
8
8
  import { describe, test, expect, beforeEach, afterEach, vi } from 'vitest';
9
9
  import Database from 'better-sqlite3';
10
10
  import { MigrationRunner } from './migration-runner.js';
11
- import {
12
- createMigration,
13
- } from './migration-runner.test-helpers.js';
11
+ import { createMigration } from './migration-runner.test-helpers.js';
14
12
 
15
13
  describe('MigrationRunner — edge cases', () => {
16
14
  let db: Database.Database;
@@ -302,10 +300,7 @@ describe('MigrationRunner — edge cases', () => {
302
300
  test('getPending consistent across runners sharing same db', () => {
303
301
  // Arrange
304
302
  const runner2 = new MigrationRunner(db);
305
- const all = [
306
- createMigration({ version: '1.0.0' }),
307
- createMigration({ version: '2.0.0' }),
308
- ];
303
+ const all = [createMigration({ version: '1.0.0' }), createMigration({ version: '2.0.0' })];
309
304
  runner.run([all[0]]);
310
305
 
311
306
  // Act
@@ -61,7 +61,9 @@ describe('OperatorProfileStore (extended)', () => {
61
61
 
62
62
  describe('parallel section updates', () => {
63
63
  it('two concurrent updateSection for different sections both succeed', () => {
64
- store.accumulateSignals([makeSignal(SignalType.CommandStyle, { style: 'terse', snippet: 'x' })]);
64
+ store.accumulateSignals([
65
+ makeSignal(SignalType.CommandStyle, { style: 'terse', snippet: 'x' }),
66
+ ]);
65
67
 
66
68
  const commData: CommunicationSection = {
67
69
  style: 'formal',
@@ -88,7 +90,9 @@ describe('OperatorProfileStore (extended)', () => {
88
90
  });
89
91
 
90
92
  it('updating one section does not overwrite another', () => {
91
- store.accumulateSignals([makeSignal(SignalType.CommandStyle, { style: 'terse', snippet: 'x' })]);
93
+ store.accumulateSignals([
94
+ makeSignal(SignalType.CommandStyle, { style: 'terse', snippet: 'x' }),
95
+ ]);
92
96
 
93
97
  const identity: IdentitySection = {
94
98
  background: 'Senior engineer',
@@ -116,7 +120,9 @@ describe('OperatorProfileStore (extended)', () => {
116
120
 
117
121
  describe('profile snapshot', () => {
118
122
  it('creates history entry with correct version increment', () => {
119
- store.accumulateSignals([makeSignal(SignalType.CommandStyle, { style: 'terse', snippet: 'x' })]);
123
+ store.accumulateSignals([
124
+ makeSignal(SignalType.CommandStyle, { style: 'terse', snippet: 'x' }),
125
+ ]);
120
126
  const profileBefore = store.getProfile();
121
127
  expect(profileBefore!.version).toBe(0);
122
128
 
@@ -197,7 +203,9 @@ describe('OperatorProfileStore (extended)', () => {
197
203
  });
198
204
 
199
205
  it('lastSynthesis updates after snapshot', () => {
200
- store.accumulateSignals([makeSignal(SignalType.CommandStyle, { style: 'terse', snippet: 'x' })]);
206
+ store.accumulateSignals([
207
+ makeSignal(SignalType.CommandStyle, { style: 'terse', snippet: 'x' }),
208
+ ]);
201
209
  store.snapshot('synthesis');
202
210
  const stats = store.signalStats();
203
211
  expect(stats.lastSynthesis).not.toBeNull();
@@ -273,7 +281,9 @@ describe('OperatorProfileStore (extended)', () => {
273
281
 
274
282
  describe('delete profile archives to history', () => {
275
283
  it('archives profile snapshot before deletion', () => {
276
- store.accumulateSignals([makeSignal(SignalType.CommandStyle, { style: 'terse', snippet: 'x' })]);
284
+ store.accumulateSignals([
285
+ makeSignal(SignalType.CommandStyle, { style: 'terse', snippet: 'x' }),
286
+ ]);
277
287
  const identity: IdentitySection = {
278
288
  background: 'Architect',
279
289
  role: 'Principal',
@@ -2,7 +2,11 @@ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
2
  import { Vault } from '../vault/vault.js';
3
3
  import { OperatorProfileStore } from './operator-profile.js';
4
4
  import { SignalType } from './operator-types.js';
5
- import type { OperatorSignal, CommunicationSection, TechnicalContextSection } from './operator-types.js';
5
+ import type {
6
+ OperatorSignal,
7
+ CommunicationSection,
8
+ TechnicalContextSection,
9
+ } from './operator-types.js';
6
10
 
7
11
  // ─── Helpers ──────────────────────────────────────────────────────────
8
12
 
@@ -83,7 +87,9 @@ describe('OperatorProfileStore', () => {
83
87
  });
84
88
 
85
89
  it('auto-creates profile when accumulating signals', () => {
86
- store.accumulateSignals([makeSignal(SignalType.WorkRhythm, { pattern: 'burst', durationMinutes: 30, taskCount: 5 })]);
90
+ store.accumulateSignals([
91
+ makeSignal(SignalType.WorkRhythm, { pattern: 'burst', durationMinutes: 30, taskCount: 5 }),
92
+ ]);
87
93
  const profile = store.getProfile();
88
94
  expect(profile).not.toBeNull();
89
95
  expect(profile!.sessionCount).toBe(0);
@@ -127,7 +133,9 @@ describe('OperatorProfileStore', () => {
127
133
 
128
134
  it('parallel updateSection on different sections both succeed', () => {
129
135
  // Ensure profile exists
130
- store.accumulateSignals([makeSignal(SignalType.CommandStyle, { style: 'terse', snippet: 'x' })]);
136
+ store.accumulateSignals([
137
+ makeSignal(SignalType.CommandStyle, { style: 'terse', snippet: 'x' }),
138
+ ]);
131
139
 
132
140
  const commData: CommunicationSection = {
133
141
  style: 'concise',
@@ -157,7 +165,9 @@ describe('OperatorProfileStore', () => {
157
165
  // ─── correctSection ─────────────────────────────────────────────
158
166
 
159
167
  it('correctSection records history with correction trigger', () => {
160
- store.accumulateSignals([makeSignal(SignalType.CommandStyle, { style: 'terse', snippet: 'x' })]);
168
+ store.accumulateSignals([
169
+ makeSignal(SignalType.CommandStyle, { style: 'terse', snippet: 'x' }),
170
+ ]);
161
171
  const profileBefore = store.getProfile();
162
172
  expect(profileBefore).not.toBeNull();
163
173
 
@@ -183,7 +193,9 @@ describe('OperatorProfileStore', () => {
183
193
  // ─── snapshot ───────────────────────────────────────────────────
184
194
 
185
195
  it('creates history row with full profile JSON', () => {
186
- store.accumulateSignals([makeSignal(SignalType.CommandStyle, { style: 'terse', snippet: 'x' })]);
196
+ store.accumulateSignals([
197
+ makeSignal(SignalType.CommandStyle, { style: 'terse', snippet: 'x' }),
198
+ ]);
187
199
  const profile = store.getProfile();
188
200
  expect(profile).not.toBeNull();
189
201
 
@@ -205,7 +217,9 @@ describe('OperatorProfileStore', () => {
205
217
  });
206
218
 
207
219
  it('increments synthesis_version on snapshot', () => {
208
- store.accumulateSignals([makeSignal(SignalType.CommandStyle, { style: 'terse', snippet: 'x' })]);
220
+ store.accumulateSignals([
221
+ makeSignal(SignalType.CommandStyle, { style: 'terse', snippet: 'x' }),
222
+ ]);
209
223
 
210
224
  store.snapshot('synthesis');
211
225
  store.snapshot('synthesis');
@@ -219,7 +233,9 @@ describe('OperatorProfileStore', () => {
219
233
  // ─── deleteProfile ──────────────────────────────────────────────
220
234
 
221
235
  it('archives to history before deletion', () => {
222
- store.accumulateSignals([makeSignal(SignalType.CommandStyle, { style: 'terse', snippet: 'x' })]);
236
+ store.accumulateSignals([
237
+ makeSignal(SignalType.CommandStyle, { style: 'terse', snippet: 'x' }),
238
+ ]);
223
239
  const profile = store.getProfile();
224
240
  expect(profile).not.toBeNull();
225
241
  const profileId = profile!.id;
@@ -287,7 +303,9 @@ describe('OperatorProfileStore', () => {
287
303
  });
288
304
 
289
305
  it('returns section data after update', () => {
290
- store.accumulateSignals([makeSignal(SignalType.CommandStyle, { style: 'terse', snippet: 'x' })]);
306
+ store.accumulateSignals([
307
+ makeSignal(SignalType.CommandStyle, { style: 'terse', snippet: 'x' }),
308
+ ]);
291
309
  const commData: CommunicationSection = {
292
310
  style: 'detailed',
293
311
  signalWords: ['please'],
@@ -165,11 +165,7 @@ export class OperatorProfileStore {
165
165
  return JSON.parse((row[col] as string) || '{}') as ProfileSection;
166
166
  }
167
167
 
168
- updateSection(
169
- section: ProfileSectionKey,
170
- data: ProfileSection,
171
- profileId?: string,
172
- ): boolean {
168
+ updateSection(section: ProfileSectionKey, data: ProfileSection, profileId?: string): boolean {
173
169
  const id = profileId ?? this.ensureProfile();
174
170
  const col = SECTION_COLUMNS[section];
175
171
  const result = this.provider.run(
@@ -179,11 +175,7 @@ export class OperatorProfileStore {
179
175
  return result.changes > 0;
180
176
  }
181
177
 
182
- correctSection(
183
- section: ProfileSectionKey,
184
- data: ProfileSection,
185
- profileId?: string,
186
- ): boolean {
178
+ correctSection(section: ProfileSectionKey, data: ProfileSection, profileId?: string): boolean {
187
179
  const id = profileId ?? this.ensureProfile();
188
180
  this.snapshot('correction', id);
189
181
  return this.updateSection(section, data, id);
@@ -210,7 +202,14 @@ export class OperatorProfileStore {
210
202
  this.provider.run(
211
203
  `INSERT INTO operator_signals (profile_id, signal_type, signal_data, source, confidence, created_at)
212
204
  VALUES (?, ?, ?, ?, ?, ?)`,
213
- [id, signal.signalType, JSON.stringify(signal.data), signal.source ?? null, signal.confidence, signal.timestamp],
205
+ [
206
+ id,
207
+ signal.signalType,
208
+ JSON.stringify(signal.data),
209
+ signal.source ?? null,
210
+ signal.confidence,
211
+ signal.timestamp,
212
+ ],
214
213
  );
215
214
  inserted++;
216
215
  }
@@ -225,7 +224,15 @@ export class OperatorProfileStore {
225
224
  listSignals(
226
225
  filter: { types?: string[]; processed?: boolean; limit?: number } = {},
227
226
  profileId?: string,
228
- ): Array<{ id: number; signalType: string; signalData: unknown; source: string | null; confidence: number; processed: boolean; createdAt: string }> {
227
+ ): Array<{
228
+ id: number;
229
+ signalType: string;
230
+ signalData: unknown;
231
+ source: string | null;
232
+ confidence: number;
233
+ processed: boolean;
234
+ createdAt: string;
235
+ }> {
229
236
  const id = profileId ?? this.getDefaultProfileId();
230
237
  if (!id) return [];
231
238
  const conditions = ['profile_id = ?'];
@@ -296,7 +303,11 @@ export class OperatorProfileStore {
296
303
  };
297
304
  }
298
305
  const stats = this.signalStats(id);
299
- const profile = this.provider.get<{ session_count: number; last_synthesis: string | null; synthesis_version: number }>(
306
+ const profile = this.provider.get<{
307
+ session_count: number;
308
+ last_synthesis: string | null;
309
+ synthesis_version: number;
310
+ }>(
300
311
  'SELECT session_count, last_synthesis, synthesis_version FROM operator_profiles WHERE id = ?',
301
312
  [id],
302
313
  );
@@ -311,14 +322,14 @@ export class OperatorProfileStore {
311
322
  }
312
323
 
313
324
  const pending = stats.totalUnprocessed;
314
- const sessionsSinceSynthesis = profile.synthesis_version === 0
315
- ? profile.session_count
316
- : profile.session_count; // tracked by incrementing session_count
325
+ const sessionsSinceSynthesis =
326
+ profile.synthesis_version === 0 ? profile.session_count : profile.session_count; // tracked by incrementing session_count
317
327
 
318
328
  const signalThresholdMet = pending >= SYNTHESIS_SIGNAL_THRESHOLD;
319
- const sessionThresholdMet = sessionsSinceSynthesis >= SYNTHESIS_SESSION_THRESHOLD && profile.synthesis_version > 0
320
- ? false // sessions threshold only meaningful after first synthesis
321
- : sessionsSinceSynthesis >= SYNTHESIS_SESSION_THRESHOLD;
329
+ const sessionThresholdMet =
330
+ sessionsSinceSynthesis >= SYNTHESIS_SESSION_THRESHOLD && profile.synthesis_version > 0
331
+ ? false // sessions threshold only meaningful after first synthesis
332
+ : sessionsSinceSynthesis >= SYNTHESIS_SESSION_THRESHOLD;
322
333
 
323
334
  const due = signalThresholdMet || sessionThresholdMet;
324
335
 
@@ -332,8 +343,12 @@ export class OperatorProfileStore {
332
343
  }
333
344
 
334
345
  const reasons: string[] = [];
335
- if (signalThresholdMet) reasons.push(`${pending} unprocessed signals (threshold: ${SYNTHESIS_SIGNAL_THRESHOLD})`);
336
- if (sessionThresholdMet) reasons.push(`${sessionsSinceSynthesis} sessions since last synthesis (threshold: ${SYNTHESIS_SESSION_THRESHOLD})`);
346
+ if (signalThresholdMet)
347
+ reasons.push(`${pending} unprocessed signals (threshold: ${SYNTHESIS_SIGNAL_THRESHOLD})`);
348
+ if (sessionThresholdMet)
349
+ reasons.push(
350
+ `${sessionsSinceSynthesis} sessions since last synthesis (threshold: ${SYNTHESIS_SESSION_THRESHOLD})`,
351
+ );
337
352
  if (!due) reasons.push('Below all thresholds');
338
353
 
339
354
  return {
@@ -385,7 +400,8 @@ export class OperatorProfileStore {
385
400
  `INSERT INTO operator_profiles (id, agent_id, identity, cognition, communication, working_rules, trust_model, taste_profile, growth_edges, technical_context)
386
401
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
387
402
  [
388
- id, agentId,
403
+ id,
404
+ agentId,
389
405
  JSON.stringify(profile.identity),
390
406
  JSON.stringify(profile.cognition),
391
407
  JSON.stringify(profile.communication),
@@ -106,12 +106,14 @@ describe('operator-signals (extended)', () => {
106
106
 
107
107
  describe('edge cases', () => {
108
108
  it('empty session data — null fields produce minimal signals', () => {
109
- const signals = extractFromSession(makeSession({
110
- intent: null as unknown as string,
111
- toolsUsed: null as unknown as string[],
112
- filesModified: null as unknown as string[],
113
- decisions: null as unknown as string[],
114
- }));
109
+ const signals = extractFromSession(
110
+ makeSession({
111
+ intent: null as unknown as string,
112
+ toolsUsed: null as unknown as string[],
113
+ filesModified: null as unknown as string[],
114
+ decisions: null as unknown as string[],
115
+ }),
116
+ );
115
117
  // Should still get work_rhythm and session_depth
116
118
  expect(signals.some((s) => s.signalType === 'work_rhythm')).toBe(true);
117
119
  expect(signals.some((s) => s.signalType === 'session_depth')).toBe(true);
@@ -139,10 +141,12 @@ describe('operator-signals (extended)', () => {
139
141
  });
140
142
 
141
143
  it('empty filesModified and decisions produces shallow depth', () => {
142
- const signals = extractFromSession(makeSession({
143
- filesModified: [],
144
- decisions: [],
145
- }));
144
+ const signals = extractFromSession(
145
+ makeSession({
146
+ filesModified: [],
147
+ decisions: [],
148
+ }),
149
+ );
146
150
  const sd = signals.find((s) => s.signalType === 'session_depth');
147
151
  expect(sd).toBeDefined();
148
152
  expect((sd!.data as { depth: string }).depth).toBe('shallow');
@@ -211,7 +215,9 @@ describe('operator-signals (extended)', () => {
211
215
  });
212
216
 
213
217
  it('returns empty array for completely unknown type', () => {
214
- expect(extractFromRadar(makeRadarCandidate({ signalType: 'alien_signal' as never }))).toEqual([]);
218
+ expect(extractFromRadar(makeRadarCandidate({ signalType: 'alien_signal' as never }))).toEqual(
219
+ [],
220
+ );
215
221
  });
216
222
  });
217
223
 
@@ -219,26 +225,32 @@ describe('operator-signals (extended)', () => {
219
225
 
220
226
  describe('extractFromRadar frustration level mapping', () => {
221
227
  it('high confidence (>=0.7) → high frustration', () => {
222
- const signals = extractFromRadar(makeRadarCandidate({
223
- signalType: 'repeated_question',
224
- confidence: 0.75,
225
- }));
228
+ const signals = extractFromRadar(
229
+ makeRadarCandidate({
230
+ signalType: 'repeated_question',
231
+ confidence: 0.75,
232
+ }),
233
+ );
226
234
  expect((signals[0].data as { level: string }).level).toBe('high');
227
235
  });
228
236
 
229
237
  it('medium confidence (0.5-0.7) → moderate frustration', () => {
230
- const signals = extractFromRadar(makeRadarCandidate({
231
- signalType: 'repeated_question',
232
- confidence: 0.55,
233
- }));
238
+ const signals = extractFromRadar(
239
+ makeRadarCandidate({
240
+ signalType: 'repeated_question',
241
+ confidence: 0.55,
242
+ }),
243
+ );
234
244
  expect((signals[0].data as { level: string }).level).toBe('moderate');
235
245
  });
236
246
 
237
247
  it('low confidence (<0.5) → mild frustration', () => {
238
- const signals = extractFromRadar(makeRadarCandidate({
239
- signalType: 'repeated_question',
240
- confidence: 0.3,
241
- }));
248
+ const signals = extractFromRadar(
249
+ makeRadarCandidate({
250
+ signalType: 'repeated_question',
251
+ confidence: 0.3,
252
+ }),
253
+ );
242
254
  expect((signals[0].data as { level: string }).level).toBe('mild');
243
255
  });
244
256
  });
@@ -123,18 +123,14 @@ describe('operator-signals', () => {
123
123
  });
124
124
 
125
125
  it('returns shallow depth for minimal session', () => {
126
- const signals = extractFromSession(
127
- makeSession({ filesModified: [], decisions: [] }),
128
- );
126
+ const signals = extractFromSession(makeSession({ filesModified: [], decisions: [] }));
129
127
  const sd = signals.find((s) => s.signalType === 'session_depth');
130
128
  expect(sd).toBeDefined();
131
129
  expect((sd!.data as { depth: string }).depth).toBe('shallow');
132
130
  });
133
131
 
134
132
  it('handles null toolsUsed gracefully — returns signals for other fields', () => {
135
- const signals = extractFromSession(
136
- makeSession({ toolsUsed: null as unknown as string[] }),
137
- );
133
+ const signals = extractFromSession(makeSession({ toolsUsed: null as unknown as string[] }));
138
134
  // Should still have command_style, work_rhythm, session_depth
139
135
  expect(signals.some((s) => s.signalType === 'command_style')).toBe(true);
140
136
  expect(signals.some((s) => s.signalType === 'work_rhythm')).toBe(true);
@@ -144,9 +140,7 @@ describe('operator-signals', () => {
144
140
  });
145
141
 
146
142
  it('handles null intent gracefully — skips command_style', () => {
147
- const signals = extractFromSession(
148
- makeSession({ intent: null as unknown as string }),
149
- );
143
+ const signals = extractFromSession(makeSession({ intent: null as unknown as string }));
150
144
  expect(signals.some((s) => s.signalType === 'command_style')).toBe(false);
151
145
  expect(signals.some((s) => s.signalType === 'work_rhythm')).toBe(true);
152
146
  });
@@ -208,7 +202,9 @@ describe('operator-signals', () => {
208
202
 
209
203
  describe('extractFromBrainStrengths', () => {
210
204
  it('creates domain_expertise for strengths with score > 0.6', () => {
211
- const strengths = [makeStrength({ pattern: 'typescript-strict', domain: 'typescript', strength: 0.8 })];
205
+ const strengths = [
206
+ makeStrength({ pattern: 'typescript-strict', domain: 'typescript', strength: 0.8 }),
207
+ ];
212
208
  const signals = extractFromBrainStrengths(strengths);
213
209
  expect(signals.length).toBe(1);
214
210
  expect(signals[0].signalType).toBe('domain_expertise');
@@ -221,7 +221,8 @@ export function extractFromRadar(candidate: RadarCandidate): OperatorSignal[] {
221
221
  signalType: SignalType.Frustration,
222
222
  source: 'learning_radar',
223
223
  data: {
224
- level: candidate.confidence >= 0.7 ? 'high' : candidate.confidence >= 0.5 ? 'moderate' : 'mild',
224
+ level:
225
+ candidate.confidence >= 0.7 ? 'high' : candidate.confidence >= 0.5 ? 'moderate' : 'mild',
225
226
  trigger: candidate.sourceQuery ?? candidate.title,
226
227
  context: candidate.context ?? candidate.description,
227
228
  },