@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
@@ -393,10 +393,17 @@ export function createAdminSetupOps(runtime: AgentRuntime): OpDefinition[] {
393
393
  }
394
394
 
395
395
  // 2. Skills — use shared sync (same logic as engine startup)
396
- let skillsResults: { installed: string[]; updated: string[]; skipped: string[]; failed: string[] };
396
+ let skillsResults: {
397
+ installed: string[];
398
+ updated: string[];
399
+ skipped: string[];
400
+ failed: string[];
401
+ };
397
402
  if (!hooksOnly && !settingsJsonOnly) {
398
403
  if (install) {
399
- const agentName = runtime.persona?.name ?? config.agentId.charAt(0).toUpperCase() + config.agentId.slice(1);
404
+ const agentName =
405
+ runtime.persona?.name ??
406
+ config.agentId.charAt(0).toUpperCase() + config.agentId.slice(1);
400
407
  skillsResults = syncSkillsToClaudeCode(skillsSourceDirs, agentName);
401
408
  } else {
402
409
  // Dry run — just discover what would be synced
@@ -182,7 +182,10 @@ describe('createArchiveOps', () => {
182
182
 
183
183
  describe('vault_find_expired', () => {
184
184
  it('finds expired entries', async () => {
185
- const result = (await findOp(ops, 'vault_find_expired').handler({})) as Record<string, unknown>;
185
+ const result = (await findOp(ops, 'vault_find_expired').handler({})) as Record<
186
+ string,
187
+ unknown
188
+ >;
186
189
  expect(result.count).toBe(1);
187
190
  });
188
191
  });
@@ -0,0 +1,144 @@
1
+ /**
2
+ * Colocated contract tests for branching-ops.ts.
3
+ * Tests the 5 vault branching ops extracted from vault-facade.
4
+ */
5
+
6
+ import { describe, it, expect, beforeEach, vi } from 'vitest';
7
+ import { createBranchingOps } from './branching-ops.js';
8
+ import { captureOps, executeOp } from '../engine/test-helpers.js';
9
+ import type { CapturedOp } from '../engine/test-helpers.js';
10
+ import type { AgentRuntime } from './types.js';
11
+
12
+ function makeMockVaultBranching() {
13
+ return {
14
+ branch: vi.fn(),
15
+ addOperation: vi.fn(),
16
+ listBranches: vi.fn().mockReturnValue([{ name: 'experiment', ops: 3 }]),
17
+ merge: vi.fn().mockReturnValue({ merged: true, applied: 3 }),
18
+ deleteBranch: vi.fn().mockReturnValue(true),
19
+ };
20
+ }
21
+
22
+ function makeRuntime(): AgentRuntime {
23
+ return {
24
+ vaultBranching: makeMockVaultBranching(),
25
+ config: { agentId: 'test-agent' },
26
+ } as unknown as AgentRuntime;
27
+ }
28
+
29
+ describe('branching-ops', () => {
30
+ let runtime: AgentRuntime;
31
+ let ops: Map<string, CapturedOp>;
32
+
33
+ beforeEach(() => {
34
+ runtime = makeRuntime();
35
+ ops = captureOps(createBranchingOps(runtime));
36
+ });
37
+
38
+ it('registers all 5 branching ops', () => {
39
+ expect(ops.size).toBe(5);
40
+ expect(ops.has('vault_branch')).toBe(true);
41
+ expect(ops.has('vault_branch_add')).toBe(true);
42
+ expect(ops.has('vault_branch_list')).toBe(true);
43
+ expect(ops.has('vault_merge_branch')).toBe(true);
44
+ expect(ops.has('vault_delete_branch')).toBe(true);
45
+ });
46
+
47
+ it('has correct auth levels', () => {
48
+ expect(ops.get('vault_branch')!.auth).toBe('write');
49
+ expect(ops.get('vault_branch_add')!.auth).toBe('write');
50
+ expect(ops.get('vault_branch_list')!.auth).toBe('read');
51
+ expect(ops.get('vault_merge_branch')!.auth).toBe('admin');
52
+ expect(ops.get('vault_delete_branch')!.auth).toBe('admin');
53
+ });
54
+
55
+ describe('vault_branch', () => {
56
+ it('creates a branch', async () => {
57
+ const result = await executeOp(ops, 'vault_branch', { name: 'experiment' });
58
+ expect(result.success).toBe(true);
59
+ const data = result.data as { created: boolean; name: string };
60
+ expect(data.created).toBe(true);
61
+ expect(data.name).toBe('experiment');
62
+ });
63
+
64
+ it('returns error on duplicate branch', async () => {
65
+ const vb = runtime.vaultBranching as ReturnType<typeof makeMockVaultBranching>;
66
+ vb.branch.mockImplementation(() => {
67
+ throw new Error('Branch already exists');
68
+ });
69
+ const result = await executeOp(ops, 'vault_branch', { name: 'dup' });
70
+ expect(result.success).toBe(true);
71
+ const data = result.data as { error: string };
72
+ expect(data.error).toBe('Branch already exists');
73
+ });
74
+ });
75
+
76
+ describe('vault_branch_add', () => {
77
+ it('adds an operation to a branch', async () => {
78
+ const result = await executeOp(ops, 'vault_branch_add', {
79
+ branchName: 'exp',
80
+ entryId: 'e1',
81
+ action: 'add',
82
+ entryData: { id: 'e1', title: 'New', type: 'pattern', domain: 'test' },
83
+ });
84
+ expect(result.success).toBe(true);
85
+ const data = result.data as { added: boolean; action: string };
86
+ expect(data.added).toBe(true);
87
+ expect(data.action).toBe('add');
88
+ });
89
+
90
+ it('returns error on invalid branch', async () => {
91
+ const vb = runtime.vaultBranching as ReturnType<typeof makeMockVaultBranching>;
92
+ vb.addOperation.mockImplementation(() => {
93
+ throw new Error('Branch not found');
94
+ });
95
+ const result = await executeOp(ops, 'vault_branch_add', {
96
+ branchName: 'missing',
97
+ entryId: 'e1',
98
+ action: 'remove',
99
+ });
100
+ expect(result.success).toBe(true);
101
+ expect((result.data as { error: string }).error).toBe('Branch not found');
102
+ });
103
+ });
104
+
105
+ describe('vault_branch_list', () => {
106
+ it('lists all branches', async () => {
107
+ const result = await executeOp(ops, 'vault_branch_list', {});
108
+ expect(result.success).toBe(true);
109
+ const data = result.data as { branches: Array<{ name: string }> };
110
+ expect(data.branches).toHaveLength(1);
111
+ expect(data.branches[0].name).toBe('experiment');
112
+ });
113
+ });
114
+
115
+ describe('vault_merge_branch', () => {
116
+ it('merges a branch', async () => {
117
+ const result = await executeOp(ops, 'vault_merge_branch', { branchName: 'experiment' });
118
+ expect(result.success).toBe(true);
119
+ const data = result.data as { merged: boolean; applied: number };
120
+ expect(data.merged).toBe(true);
121
+ expect(data.applied).toBe(3);
122
+ });
123
+
124
+ it('returns error on merge failure', async () => {
125
+ const vb = runtime.vaultBranching as ReturnType<typeof makeMockVaultBranching>;
126
+ vb.merge.mockImplementation(() => {
127
+ throw new Error('Conflict detected');
128
+ });
129
+ const result = await executeOp(ops, 'vault_merge_branch', { branchName: 'conflict' });
130
+ expect(result.success).toBe(true);
131
+ expect((result.data as { error: string }).error).toBe('Conflict detected');
132
+ });
133
+ });
134
+
135
+ describe('vault_delete_branch', () => {
136
+ it('deletes a branch', async () => {
137
+ const result = await executeOp(ops, 'vault_delete_branch', { branchName: 'experiment' });
138
+ expect(result.success).toBe(true);
139
+ const data = result.data as { deleted: boolean; branchName: string };
140
+ expect(data.deleted).toBe(true);
141
+ expect(data.branchName).toBe('experiment');
142
+ });
143
+ });
144
+ });
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Branching Ops — vault branch lifecycle operations.
3
+ *
4
+ * Covers:
5
+ * - Create named branches for experimentation
6
+ * - Add operations (add/modify/remove) to branches
7
+ * - List, merge, and delete branches
8
+ */
9
+
10
+ import { z } from 'zod';
11
+ import type { OpDefinition } from '../facades/types.js';
12
+ import type { IntelligenceEntry } from '../intelligence/types.js';
13
+ import type { AgentRuntime } from './types.js';
14
+
15
+ export function createBranchingOps(runtime: AgentRuntime): OpDefinition[] {
16
+ return [
17
+ {
18
+ name: 'vault_branch',
19
+ description:
20
+ 'Create a named vault branch for experimentation. Changes can be reviewed and merged later.',
21
+ auth: 'write',
22
+ schema: z.object({
23
+ name: z.string().describe('Unique branch name'),
24
+ }),
25
+ handler: async (params) => {
26
+ const { vaultBranching } = runtime;
27
+ try {
28
+ vaultBranching.branch(params.name as string);
29
+ return { created: true, name: params.name };
30
+ } catch (err) {
31
+ return { error: (err as Error).message };
32
+ }
33
+ },
34
+ },
35
+ {
36
+ name: 'vault_branch_add',
37
+ description: 'Add an operation (add/modify/remove) to a vault branch.',
38
+ auth: 'write',
39
+ schema: z.object({
40
+ branchName: z.string().describe('Branch to add the operation to'),
41
+ entryId: z.string().describe('Entry ID'),
42
+ action: z.enum(['add', 'modify', 'remove']).describe('Operation type'),
43
+ entryData: z
44
+ .record(z.unknown())
45
+ .optional()
46
+ .describe('Full entry data (required for add/modify)'),
47
+ }),
48
+ handler: async (params) => {
49
+ const { vaultBranching } = runtime;
50
+ try {
51
+ vaultBranching.addOperation(
52
+ params.branchName as string,
53
+ params.entryId as string,
54
+ params.action as 'add' | 'modify' | 'remove',
55
+ params.entryData as IntelligenceEntry | undefined,
56
+ );
57
+ return {
58
+ added: true,
59
+ branchName: params.branchName,
60
+ entryId: params.entryId,
61
+ action: params.action,
62
+ };
63
+ } catch (err) {
64
+ return { error: (err as Error).message };
65
+ }
66
+ },
67
+ },
68
+ {
69
+ name: 'vault_branch_list',
70
+ description: 'List all vault branches with entry counts and merge status.',
71
+ auth: 'read',
72
+ handler: async () => {
73
+ const { vaultBranching } = runtime;
74
+ return { branches: vaultBranching.listBranches() };
75
+ },
76
+ },
77
+ {
78
+ name: 'vault_merge_branch',
79
+ description: 'Merge a branch into the main vault. Branch entries win on conflict.',
80
+ auth: 'admin',
81
+ schema: z.object({
82
+ branchName: z.string().describe('Branch to merge'),
83
+ }),
84
+ handler: async (params) => {
85
+ const { vaultBranching } = runtime;
86
+ try {
87
+ return vaultBranching.merge(params.branchName as string);
88
+ } catch (err) {
89
+ return { error: (err as Error).message };
90
+ }
91
+ },
92
+ },
93
+ {
94
+ name: 'vault_delete_branch',
95
+ description: 'Delete a vault branch and all its operations.',
96
+ auth: 'admin',
97
+ schema: z.object({
98
+ branchName: z.string().describe('Branch to delete'),
99
+ }),
100
+ handler: async (params) => {
101
+ const { vaultBranching } = runtime;
102
+ const deleted = vaultBranching.deleteBranch(params.branchName as string);
103
+ return { deleted, branchName: params.branchName };
104
+ },
105
+ },
106
+ ];
107
+ }
@@ -36,9 +36,7 @@ function createMockRuntime(): AgentRuntime {
36
36
  blocked: false,
37
37
  entry: { id: 'captured-1' },
38
38
  })),
39
- intelligentSearch: vi.fn(async () => [
40
- { id: 'r1', title: 'Result 1', score: 0.8 },
41
- ]),
39
+ intelligentSearch: vi.fn(async () => [{ id: 'r1', title: 'Result 1', score: 0.8 }]),
42
40
  recordFeedback: vi.fn(),
43
41
  },
44
42
  governance: {
@@ -113,9 +111,7 @@ describe('createCaptureOps', () => {
113
111
  it('uses manual tier override when provided', async () => {
114
112
  const result = (await findOp(ops, 'capture_knowledge').handler({
115
113
  tier: 'team',
116
- entries: [
117
- { type: 'pattern', domain: 'a', title: 'A', description: 'a', tags: [] },
118
- ],
114
+ entries: [{ type: 'pattern', domain: 'a', title: 'A', description: 'a', tags: [] }],
119
115
  })) as Record<string, unknown>;
120
116
  expect(result.captured).toBe(1);
121
117
  const results = result.results as Array<Record<string, unknown>>;
@@ -129,9 +125,7 @@ describe('createCaptureOps', () => {
129
125
  reason: 'Requires review',
130
126
  } as unknown);
131
127
  const result = (await findOp(ops, 'capture_knowledge').handler({
132
- entries: [
133
- { type: 'pattern', domain: 'a', title: 'A', description: 'a', tags: [] },
134
- ],
128
+ entries: [{ type: 'pattern', domain: 'a', title: 'A', description: 'a', tags: [] }],
135
129
  })) as Record<string, unknown>;
136
130
  expect(result.proposed).toBe(1);
137
131
  expect(result.captured).toBe(0);
@@ -144,9 +138,7 @@ describe('createCaptureOps', () => {
144
138
  reason: 'Not allowed',
145
139
  } as unknown);
146
140
  const result = (await findOp(ops, 'capture_knowledge').handler({
147
- entries: [
148
- { type: 'pattern', domain: 'a', title: 'A', description: 'a', tags: [] },
149
- ],
141
+ entries: [{ type: 'pattern', domain: 'a', title: 'A', description: 'a', tags: [] }],
150
142
  })) as Record<string, unknown>;
151
143
  expect(result.rejected).toBe(1);
152
144
  });
@@ -157,9 +149,7 @@ describe('createCaptureOps', () => {
157
149
  duplicate: { id: 'existing-1' },
158
150
  } as unknown);
159
151
  const result = (await findOp(ops, 'capture_knowledge').handler({
160
- entries: [
161
- { type: 'pattern', domain: 'a', title: 'A', description: 'a', tags: [] },
162
- ],
152
+ entries: [{ type: 'pattern', domain: 'a', title: 'A', description: 'a', tags: [] }],
163
153
  })) as Record<string, unknown>;
164
154
  expect(result.duplicated).toBe(1);
165
155
  expect(result.captured).toBe(0);
@@ -170,9 +160,7 @@ describe('createCaptureOps', () => {
170
160
  throw new Error('Capture failed');
171
161
  });
172
162
  const result = (await findOp(ops, 'capture_knowledge').handler({
173
- entries: [
174
- { type: 'pattern', domain: 'a', title: 'A', description: 'a', tags: [] },
175
- ],
163
+ entries: [{ type: 'pattern', domain: 'a', title: 'A', description: 'a', tags: [] }],
176
164
  })) as Record<string, unknown>;
177
165
  expect(result.rejected).toBe(1);
178
166
  const results = result.results as Array<Record<string, unknown>>;
@@ -185,9 +173,7 @@ describe('createCaptureOps', () => {
185
173
  { entryId: 'related-1', title: 'Related', suggestedType: 'related', score: 0.9 },
186
174
  ]);
187
175
  const result = (await findOp(ops, 'capture_knowledge').handler({
188
- entries: [
189
- { type: 'pattern', domain: 'a', title: 'A', description: 'a', tags: [] },
190
- ],
176
+ entries: [{ type: 'pattern', domain: 'a', title: 'A', description: 'a', tags: [] }],
191
177
  })) as Record<string, unknown>;
192
178
  expect(result.autoLinkedCount).toBe(1);
193
179
  expect(result.suggestedLinks).toBeDefined();
@@ -95,7 +95,11 @@ describe('createChainOps', () => {
95
95
  const rt = mockRuntime();
96
96
  const ops = captureOps(createChainOps(rt));
97
97
  await ops.get('chain_resume')!.handler({ instanceId: 'inst-1', chain: VALID_CHAIN });
98
- expect(rt.chainRunner.resume).toHaveBeenCalledWith('inst-1', VALID_CHAIN, expect.any(Function));
98
+ expect(rt.chainRunner.resume).toHaveBeenCalledWith(
99
+ 'inst-1',
100
+ VALID_CHAIN,
101
+ expect.any(Function),
102
+ );
99
103
  });
100
104
  });
101
105
 
@@ -113,7 +117,11 @@ describe('createChainOps', () => {
113
117
  const rt = mockRuntime();
114
118
  const ops = captureOps(createChainOps(rt));
115
119
  await ops.get('chain_step_approve')!.handler({ instanceId: 'inst-1', chain: VALID_CHAIN });
116
- expect(rt.chainRunner.approve).toHaveBeenCalledWith('inst-1', VALID_CHAIN, expect.any(Function));
120
+ expect(rt.chainRunner.approve).toHaveBeenCalledWith(
121
+ 'inst-1',
122
+ VALID_CHAIN,
123
+ expect.any(Function),
124
+ );
117
125
  });
118
126
  });
119
127
 
@@ -138,10 +146,12 @@ describe('createChainOps', () => {
138
146
  (ops as typeof ops & { _setAllOps: (o: typeof fakeOps) => void })._setAllOps(fakeOps);
139
147
 
140
148
  // Execute chain — the dispatch should be able to find 'some_op'
141
- await ops.find((o) => o.name === 'chain_execute')!.handler({
142
- chain: VALID_CHAIN,
143
- input: {},
144
- });
149
+ await ops
150
+ .find((o) => o.name === 'chain_execute')!
151
+ .handler({
152
+ chain: VALID_CHAIN,
153
+ input: {},
154
+ });
145
155
  // The dispatch function was passed to chainRunner.execute; verify it was called
146
156
  expect(rt.chainRunner.execute).toHaveBeenCalledOnce();
147
157
  });
@@ -124,9 +124,7 @@ describe('composeIntegrationSection', () => {
124
124
  });
125
125
 
126
126
  it('renders provided facades with up to 5 ops', () => {
127
- const facades = [
128
- { name: 'mybot_custom', ops: ['a', 'b', 'c', 'd', 'e', 'f'] },
129
- ];
127
+ const facades = [{ name: 'mybot_custom', ops: ['a', 'b', 'c', 'd', 'e', 'f'] }];
130
128
  const result = composeIntegrationSection(CONFIG, facades);
131
129
  expect(result).toContain('`mybot_custom`');
132
130
  expect(result).toContain('`a`');
@@ -43,9 +43,7 @@ describe('ContextHealthMonitor', () => {
43
43
  expect(status.level).toBe('red');
44
44
  expect(status.estimatedFill).toBeGreaterThanOrEqual(0.6);
45
45
  expect(status.toolCallCount).toBe(200);
46
- expect(status.recommendation).toBe(
47
- 'Session capture recommended before context degradation.',
48
- );
46
+ expect(status.recommendation).toBe('Session capture recommended before context degradation.');
49
47
  });
50
48
 
51
49
  it('should reset all tracking', () => {
@@ -56,9 +56,7 @@ export class ContextHealthMonitor {
56
56
 
57
57
  /** Check current context health status. */
58
58
  check(): ContextHealthStatus {
59
- const estimatedTokens = Math.round(
60
- this.totalPayloadSize * OVERHEAD_FACTOR,
61
- );
59
+ const estimatedTokens = Math.round(this.totalPayloadSize * OVERHEAD_FACTOR);
62
60
  const estimatedFill = Math.min(estimatedTokens / CONTEXT_WINDOW, 1.0);
63
61
  const level = this.classifyLevel(estimatedFill);
64
62
 
@@ -14,7 +14,9 @@ function mockRuntime() {
14
14
  recordSnapshot: vi.fn().mockReturnValue({ recorded: true, historyId: 42 }),
15
15
  getQueueStats: vi.fn().mockReturnValue({ totalEntries: 5, groomedEntries: 3 }),
16
16
  enrichMetadata: vi.fn().mockReturnValue({ enriched: false, changes: [] }),
17
- detectContradictionsHybrid: vi.fn().mockResolvedValue({ contradictions: [], method: 'tfidf-only' }),
17
+ detectContradictionsHybrid: vi
18
+ .fn()
19
+ .mockResolvedValue({ contradictions: [], method: 'tfidf-only' }),
18
20
  consolidate: vi.fn(),
19
21
  },
20
22
  jobQueue: {
@@ -94,13 +94,13 @@ describe('createDomainFacade', () => {
94
94
  it('falls back to vault FTS when brain returns empty', async () => {
95
95
  const rt = mockRuntime();
96
96
  (rt.brain.intelligentSearch as ReturnType<typeof vi.fn>).mockResolvedValue([]);
97
- (rt.vault.search as ReturnType<typeof vi.fn>).mockReturnValue([
98
- { entry: { id: 'e1' } },
99
- ]);
97
+ (rt.vault.search as ReturnType<typeof vi.fn>).mockReturnValue([{ entry: { id: 'e1' } }]);
100
98
  const facade = createDomainFacade(rt, 'a', 'security');
101
- const results = (await facade.ops.find((o) => o.name === 'search')!.handler({
102
- query: 'test',
103
- })) as unknown[];
99
+ const results = (await facade.ops
100
+ .find((o) => o.name === 'search')!
101
+ .handler({
102
+ query: 'test',
103
+ })) as unknown[];
104
104
  expect(rt.vault.search).toHaveBeenCalledWith('test', { domain: 'security', limit: 10 });
105
105
  expect(results).toHaveLength(1);
106
106
  });
@@ -118,9 +118,11 @@ describe('createDomainFacade', () => {
118
118
  it('returns error object for missing entry', async () => {
119
119
  const rt = mockRuntime();
120
120
  const facade = createDomainFacade(rt, 'a', 'testing');
121
- const result = (await facade.ops.find((o) => o.name === 'get_entry')!.handler({
122
- id: 'missing',
123
- })) as { error: string };
121
+ const result = (await facade.ops
122
+ .find((o) => o.name === 'get_entry')!
123
+ .handler({
124
+ id: 'missing',
125
+ })) as { error: string };
124
126
  expect(result.error).toContain('missing');
125
127
  });
126
128
  });
@@ -129,14 +131,16 @@ describe('createDomainFacade', () => {
129
131
  it('calls brain.enrichAndCapture when governance allows', async () => {
130
132
  const rt = mockRuntime();
131
133
  const facade = createDomainFacade(rt, 'a', 'security');
132
- const result = (await facade.ops.find((o) => o.name === 'capture')!.handler({
133
- id: 'new-1',
134
- type: 'pattern',
135
- title: 'Test',
136
- severity: 'warning',
137
- description: 'desc',
138
- tags: ['x'],
139
- })) as { captured: boolean; governance: { action: string } };
134
+ const result = (await facade.ops
135
+ .find((o) => o.name === 'capture')!
136
+ .handler({
137
+ id: 'new-1',
138
+ type: 'pattern',
139
+ title: 'Test',
140
+ severity: 'warning',
141
+ description: 'desc',
142
+ tags: ['x'],
143
+ })) as { captured: boolean; governance: { action: string } };
140
144
  expect(rt.brain.enrichAndCapture).toHaveBeenCalledOnce();
141
145
  expect(result.governance.action).toBe('capture');
142
146
  });
@@ -148,14 +152,16 @@ describe('createDomainFacade', () => {
148
152
  reason: 'needs review',
149
153
  });
150
154
  const facade = createDomainFacade(rt, 'a', 'security');
151
- const result = (await facade.ops.find((o) => o.name === 'capture')!.handler({
152
- id: 'p1',
153
- type: 'pattern',
154
- title: 'Proposed',
155
- severity: 'warning',
156
- description: 'desc',
157
- tags: [],
158
- })) as { captured: boolean; governance: { action: string; proposalId: number } };
155
+ const result = (await facade.ops
156
+ .find((o) => o.name === 'capture')!
157
+ .handler({
158
+ id: 'p1',
159
+ type: 'pattern',
160
+ title: 'Proposed',
161
+ severity: 'warning',
162
+ description: 'desc',
163
+ tags: [],
164
+ })) as { captured: boolean; governance: { action: string; proposalId: number } };
159
165
  expect(result.captured).toBe(false);
160
166
  expect(result.governance.action).toBe('propose');
161
167
  expect(result.governance.proposalId).toBe(1);
@@ -168,14 +174,16 @@ describe('createDomainFacade', () => {
168
174
  reason: 'quota exceeded',
169
175
  });
170
176
  const facade = createDomainFacade(rt, 'a', 'security');
171
- const result = (await facade.ops.find((o) => o.name === 'capture')!.handler({
172
- id: 'r1',
173
- type: 'rule',
174
- title: 'Rejected',
175
- severity: 'warning',
176
- description: 'desc',
177
- tags: [],
178
- })) as { captured: boolean; governance: { action: string; reason: string } };
177
+ const result = (await facade.ops
178
+ .find((o) => o.name === 'capture')!
179
+ .handler({
180
+ id: 'r1',
181
+ type: 'rule',
182
+ title: 'Rejected',
183
+ severity: 'warning',
184
+ description: 'desc',
185
+ tags: [],
186
+ })) as { captured: boolean; governance: { action: string; reason: string } };
179
187
  expect(result.captured).toBe(false);
180
188
  expect(result.governance.action).toBe('reject');
181
189
  expect(result.governance.reason).toBe('quota exceeded');
@@ -186,9 +194,11 @@ describe('createDomainFacade', () => {
186
194
  it('returns removed status', async () => {
187
195
  const rt = mockRuntime();
188
196
  const facade = createDomainFacade(rt, 'a', 'testing');
189
- const result = (await facade.ops.find((o) => o.name === 'remove')!.handler({
190
- id: 'del-1',
191
- })) as { removed: boolean; id: string };
197
+ const result = (await facade.ops
198
+ .find((o) => o.name === 'remove')!
199
+ .handler({
200
+ id: 'del-1',
201
+ })) as { removed: boolean; id: string };
192
202
  expect(result.removed).toBe(true);
193
203
  expect(result.id).toBe('del-1');
194
204
  });
@@ -244,10 +244,7 @@ describe('createAdminFacadeOps', () => {
244
244
 
245
245
  describe('admin_vault_size (from satellite)', () => {
246
246
  it('returns in-memory for :memory: vault', async () => {
247
- const result = (await findOp(ops, 'admin_vault_size').handler({})) as Record<
248
- string,
249
- unknown
250
- >;
247
+ const result = (await findOp(ops, 'admin_vault_size').handler({})) as Record<string, unknown>;
251
248
  expect(result.path).toBe(':memory:');
252
249
  expect(result.sizeHuman).toBe('in-memory');
253
250
  });
@@ -90,9 +90,18 @@ describe('archive-facade', () => {
90
90
 
91
91
  it('includes all expected op names', () => {
92
92
  const expected = [
93
- 'vault_archive', 'vault_restore', 'vault_optimize', 'vault_backup',
94
- 'vault_age_report', 'vault_set_temporal', 'vault_find_expiring', 'vault_find_expired',
95
- 'knowledge_audit', 'knowledge_health', 'knowledge_merge', 'knowledge_reorganize',
93
+ 'vault_archive',
94
+ 'vault_restore',
95
+ 'vault_optimize',
96
+ 'vault_backup',
97
+ 'vault_age_report',
98
+ 'vault_set_temporal',
99
+ 'vault_find_expiring',
100
+ 'vault_find_expired',
101
+ 'knowledge_audit',
102
+ 'knowledge_health',
103
+ 'knowledge_merge',
104
+ 'knowledge_reorganize',
96
105
  ];
97
106
  for (const name of expected) {
98
107
  expect(ops.has(name), `missing op: ${name}`).toBe(true);
@@ -121,7 +130,8 @@ describe('archive-facade', () => {
121
130
  describe('vault_archive', () => {
122
131
  it('archives old entries', async () => {
123
132
  const result = await executeOp(ops, 'vault_archive', {
124
- olderThanDays: 90, reason: 'cleanup',
133
+ olderThanDays: 90,
134
+ reason: 'cleanup',
125
135
  });
126
136
  expect(result.success).toBe(true);
127
137
  const vault = runtime.vault as ReturnType<typeof makeMockVault>;
@@ -178,7 +188,9 @@ describe('archive-facade', () => {
178
188
  describe('vault_set_temporal', () => {
179
189
  it('sets temporal fields', async () => {
180
190
  const result = await executeOp(ops, 'vault_set_temporal', {
181
- id: 'entry-1', validFrom: 1000, validUntil: 2000,
191
+ id: 'entry-1',
192
+ validFrom: 1000,
193
+ validUntil: 2000,
182
194
  });
183
195
  expect(result.success).toBe(true);
184
196
  const data = result.data as { updated: boolean };
@@ -251,7 +263,8 @@ describe('archive-facade', () => {
251
263
  .mockReturnValueOnce(makeEntry({ id: 'remove', tags: ['b'] }));
252
264
 
253
265
  const result = await executeOp(ops, 'knowledge_merge', {
254
- keepId: 'keep', removeId: 'remove',
266
+ keepId: 'keep',
267
+ removeId: 'remove',
255
268
  });
256
269
  expect(result.success).toBe(true);
257
270
  const data = result.data as { merged: boolean; keptId: string };
@@ -263,7 +276,8 @@ describe('archive-facade', () => {
263
276
  const vault = runtime.vault as ReturnType<typeof makeMockVault>;
264
277
  vault.get.mockReturnValue(null);
265
278
  const result = await executeOp(ops, 'knowledge_merge', {
266
- keepId: 'x', removeId: 'y',
279
+ keepId: 'x',
280
+ removeId: 'y',
267
281
  });
268
282
  expect(result.success).toBe(true);
269
283
  const data = result.data as { error: string };