@devflow-tools/delivery-line 0.18.8

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 (259) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +50 -0
  3. package/dist/__tests__/artifact-generation.test.d.ts +2 -0
  4. package/dist/__tests__/artifact-generation.test.d.ts.map +1 -0
  5. package/dist/__tests__/artifact-generation.test.js +16 -0
  6. package/dist/__tests__/artifact-generation.test.js.map +1 -0
  7. package/dist/__tests__/backend-scenario.e2e.test.d.ts +2 -0
  8. package/dist/__tests__/backend-scenario.e2e.test.d.ts.map +1 -0
  9. package/dist/__tests__/backend-scenario.e2e.test.js +25 -0
  10. package/dist/__tests__/backend-scenario.e2e.test.js.map +1 -0
  11. package/dist/__tests__/claude-events.test.d.ts +2 -0
  12. package/dist/__tests__/claude-events.test.d.ts.map +1 -0
  13. package/dist/__tests__/claude-events.test.js +28 -0
  14. package/dist/__tests__/claude-events.test.js.map +1 -0
  15. package/dist/__tests__/facade-e2e.test.d.ts +2 -0
  16. package/dist/__tests__/facade-e2e.test.d.ts.map +1 -0
  17. package/dist/__tests__/facade-e2e.test.js +167 -0
  18. package/dist/__tests__/facade-e2e.test.js.map +1 -0
  19. package/dist/__tests__/frontend-scenario.e2e.test.d.ts +2 -0
  20. package/dist/__tests__/frontend-scenario.e2e.test.d.ts.map +1 -0
  21. package/dist/__tests__/frontend-scenario.e2e.test.js +35 -0
  22. package/dist/__tests__/frontend-scenario.e2e.test.js.map +1 -0
  23. package/dist/__tests__/git-artifact-store.test.d.ts +2 -0
  24. package/dist/__tests__/git-artifact-store.test.d.ts.map +1 -0
  25. package/dist/__tests__/git-artifact-store.test.js +44 -0
  26. package/dist/__tests__/git-artifact-store.test.js.map +1 -0
  27. package/dist/__tests__/governance-adapters.test.d.ts +2 -0
  28. package/dist/__tests__/governance-adapters.test.d.ts.map +1 -0
  29. package/dist/__tests__/governance-adapters.test.js +51 -0
  30. package/dist/__tests__/governance-adapters.test.js.map +1 -0
  31. package/dist/__tests__/handoff-validator.test.d.ts +2 -0
  32. package/dist/__tests__/handoff-validator.test.d.ts.map +1 -0
  33. package/dist/__tests__/handoff-validator.test.js +13 -0
  34. package/dist/__tests__/handoff-validator.test.js.map +1 -0
  35. package/dist/__tests__/interaction-session.test.d.ts +2 -0
  36. package/dist/__tests__/interaction-session.test.d.ts.map +1 -0
  37. package/dist/__tests__/interaction-session.test.js +13 -0
  38. package/dist/__tests__/interaction-session.test.js.map +1 -0
  39. package/dist/__tests__/interaction.test.d.ts +2 -0
  40. package/dist/__tests__/interaction.test.d.ts.map +1 -0
  41. package/dist/__tests__/interaction.test.js +27 -0
  42. package/dist/__tests__/interaction.test.js.map +1 -0
  43. package/dist/__tests__/persistence.test.d.ts +2 -0
  44. package/dist/__tests__/persistence.test.d.ts.map +1 -0
  45. package/dist/__tests__/persistence.test.js +41 -0
  46. package/dist/__tests__/persistence.test.js.map +1 -0
  47. package/dist/__tests__/recovery-spike.test.d.ts +2 -0
  48. package/dist/__tests__/recovery-spike.test.d.ts.map +1 -0
  49. package/dist/__tests__/recovery-spike.test.js +20 -0
  50. package/dist/__tests__/recovery-spike.test.js.map +1 -0
  51. package/dist/__tests__/sqlite-recovery.test.d.ts +2 -0
  52. package/dist/__tests__/sqlite-recovery.test.d.ts.map +1 -0
  53. package/dist/__tests__/sqlite-recovery.test.js +90 -0
  54. package/dist/__tests__/sqlite-recovery.test.js.map +1 -0
  55. package/dist/__tests__/state-machine.test.d.ts +2 -0
  56. package/dist/__tests__/state-machine.test.d.ts.map +1 -0
  57. package/dist/__tests__/state-machine.test.js +49 -0
  58. package/dist/__tests__/state-machine.test.js.map +1 -0
  59. package/dist/__tests__/subagent-orchestrator.test.d.ts +2 -0
  60. package/dist/__tests__/subagent-orchestrator.test.d.ts.map +1 -0
  61. package/dist/__tests__/subagent-orchestrator.test.js +33 -0
  62. package/dist/__tests__/subagent-orchestrator.test.js.map +1 -0
  63. package/dist/__tests__/subagent-recovery.e2e.test.d.ts +2 -0
  64. package/dist/__tests__/subagent-recovery.e2e.test.d.ts.map +1 -0
  65. package/dist/__tests__/subagent-recovery.e2e.test.js +35 -0
  66. package/dist/__tests__/subagent-recovery.e2e.test.js.map +1 -0
  67. package/dist/__tests__/task-dag.test.d.ts +2 -0
  68. package/dist/__tests__/task-dag.test.d.ts.map +1 -0
  69. package/dist/__tests__/task-dag.test.js +31 -0
  70. package/dist/__tests__/task-dag.test.js.map +1 -0
  71. package/dist/__tests__/worker-integration.test.d.ts +2 -0
  72. package/dist/__tests__/worker-integration.test.d.ts.map +1 -0
  73. package/dist/__tests__/worker-integration.test.js +217 -0
  74. package/dist/__tests__/worker-integration.test.js.map +1 -0
  75. package/dist/adapters/approval.d.ts +40 -0
  76. package/dist/adapters/approval.d.ts.map +1 -0
  77. package/dist/adapters/approval.js +100 -0
  78. package/dist/adapters/approval.js.map +1 -0
  79. package/dist/adapters/delivery-package.d.ts +23 -0
  80. package/dist/adapters/delivery-package.d.ts.map +1 -0
  81. package/dist/adapters/delivery-package.js +37 -0
  82. package/dist/adapters/delivery-package.js.map +1 -0
  83. package/dist/adapters/integration.d.ts +37 -0
  84. package/dist/adapters/integration.d.ts.map +1 -0
  85. package/dist/adapters/integration.js +84 -0
  86. package/dist/adapters/integration.js.map +1 -0
  87. package/dist/adapters/review.d.ts +12 -0
  88. package/dist/adapters/review.d.ts.map +1 -0
  89. package/dist/adapters/review.js +33 -0
  90. package/dist/adapters/review.js.map +1 -0
  91. package/dist/adapters/verification.d.ts +25 -0
  92. package/dist/adapters/verification.d.ts.map +1 -0
  93. package/dist/adapters/verification.js +57 -0
  94. package/dist/adapters/verification.js.map +1 -0
  95. package/dist/adapters/workflow-execution.d.ts +130 -0
  96. package/dist/adapters/workflow-execution.d.ts.map +1 -0
  97. package/dist/adapters/workflow-execution.js +135 -0
  98. package/dist/adapters/workflow-execution.js.map +1 -0
  99. package/dist/artifact-generation.test.d.ts +2 -0
  100. package/dist/artifact-generation.test.d.ts.map +1 -0
  101. package/dist/artifact-generation.test.js +11 -0
  102. package/dist/artifact-generation.test.js.map +1 -0
  103. package/dist/artifacts/context-manifest.d.ts +20 -0
  104. package/dist/artifacts/context-manifest.d.ts.map +1 -0
  105. package/dist/artifacts/context-manifest.js +24 -0
  106. package/dist/artifacts/context-manifest.js.map +1 -0
  107. package/dist/artifacts/generation-guard.d.ts +13 -0
  108. package/dist/artifacts/generation-guard.d.ts.map +1 -0
  109. package/dist/artifacts/generation-guard.js +16 -0
  110. package/dist/artifacts/generation-guard.js.map +1 -0
  111. package/dist/artifacts/git-artifact-store.d.ts +16 -0
  112. package/dist/artifacts/git-artifact-store.d.ts.map +1 -0
  113. package/dist/artifacts/git-artifact-store.js +47 -0
  114. package/dist/artifacts/git-artifact-store.js.map +1 -0
  115. package/dist/claude-events.test.d.ts +2 -0
  116. package/dist/claude-events.test.d.ts.map +1 -0
  117. package/dist/claude-events.test.js +14 -0
  118. package/dist/claude-events.test.js.map +1 -0
  119. package/dist/composition.d.ts +13 -0
  120. package/dist/composition.d.ts.map +1 -0
  121. package/dist/composition.js +47 -0
  122. package/dist/composition.js.map +1 -0
  123. package/dist/domain/interaction.d.ts +23 -0
  124. package/dist/domain/interaction.d.ts.map +1 -0
  125. package/dist/domain/interaction.js +60 -0
  126. package/dist/domain/interaction.js.map +1 -0
  127. package/dist/domain/state-machine.d.ts +26 -0
  128. package/dist/domain/state-machine.d.ts.map +1 -0
  129. package/dist/domain/state-machine.js +67 -0
  130. package/dist/domain/state-machine.js.map +1 -0
  131. package/dist/domain/task-dag.d.ts +5 -0
  132. package/dist/domain/task-dag.d.ts.map +1 -0
  133. package/dist/domain/task-dag.js +78 -0
  134. package/dist/domain/task-dag.js.map +1 -0
  135. package/dist/domain/types.d.ts +320 -0
  136. package/dist/domain/types.d.ts.map +1 -0
  137. package/dist/domain/types.js +9 -0
  138. package/dist/domain/types.js.map +1 -0
  139. package/dist/facade/delivery-line-facade.d.ts +77 -0
  140. package/dist/facade/delivery-line-facade.d.ts.map +1 -0
  141. package/dist/facade/delivery-line-facade.js +417 -0
  142. package/dist/facade/delivery-line-facade.js.map +1 -0
  143. package/dist/git-artifact-store.test.d.ts +2 -0
  144. package/dist/git-artifact-store.test.d.ts.map +1 -0
  145. package/dist/git-artifact-store.test.js +27 -0
  146. package/dist/git-artifact-store.test.js.map +1 -0
  147. package/dist/governance-adapters.test.d.ts +2 -0
  148. package/dist/governance-adapters.test.d.ts.map +1 -0
  149. package/dist/governance-adapters.test.js +14 -0
  150. package/dist/governance-adapters.test.js.map +1 -0
  151. package/dist/handoff-validator.test.d.ts +2 -0
  152. package/dist/handoff-validator.test.d.ts.map +1 -0
  153. package/dist/handoff-validator.test.js +10 -0
  154. package/dist/handoff-validator.test.js.map +1 -0
  155. package/dist/index.d.ts +37 -0
  156. package/dist/index.d.ts.map +1 -0
  157. package/dist/index.js +37 -0
  158. package/dist/index.js.map +1 -0
  159. package/dist/interaction-session.d.ts +12 -0
  160. package/dist/interaction-session.d.ts.map +1 -0
  161. package/dist/interaction-session.js +21 -0
  162. package/dist/interaction-session.js.map +1 -0
  163. package/dist/interaction-session.test.d.ts +2 -0
  164. package/dist/interaction-session.test.d.ts.map +1 -0
  165. package/dist/interaction-session.test.js +13 -0
  166. package/dist/interaction-session.test.js.map +1 -0
  167. package/dist/persistence/cas.d.ts +4 -0
  168. package/dist/persistence/cas.d.ts.map +1 -0
  169. package/dist/persistence/cas.js +7 -0
  170. package/dist/persistence/cas.js.map +1 -0
  171. package/dist/persistence/event-ledger.d.ts +10 -0
  172. package/dist/persistence/event-ledger.d.ts.map +1 -0
  173. package/dist/persistence/event-ledger.js +6 -0
  174. package/dist/persistence/event-ledger.js.map +1 -0
  175. package/dist/persistence/memory-store.d.ts +51 -0
  176. package/dist/persistence/memory-store.d.ts.map +1 -0
  177. package/dist/persistence/memory-store.js +99 -0
  178. package/dist/persistence/memory-store.js.map +1 -0
  179. package/dist/persistence/schema.d.ts +2 -0
  180. package/dist/persistence/schema.d.ts.map +1 -0
  181. package/dist/persistence/schema.js +107 -0
  182. package/dist/persistence/schema.js.map +1 -0
  183. package/dist/persistence/sqlite-store.d.ts +59 -0
  184. package/dist/persistence/sqlite-store.d.ts.map +1 -0
  185. package/dist/persistence/sqlite-store.js +133 -0
  186. package/dist/persistence/sqlite-store.js.map +1 -0
  187. package/dist/persistence.test.d.ts +2 -0
  188. package/dist/persistence.test.d.ts.map +1 -0
  189. package/dist/persistence.test.js +28 -0
  190. package/dist/persistence.test.js.map +1 -0
  191. package/dist/ports/index.d.ts +248 -0
  192. package/dist/ports/index.d.ts.map +1 -0
  193. package/dist/ports/index.js +2 -0
  194. package/dist/ports/index.js.map +1 -0
  195. package/dist/recovery-spike.d.ts +22 -0
  196. package/dist/recovery-spike.d.ts.map +1 -0
  197. package/dist/recovery-spike.js +28 -0
  198. package/dist/recovery-spike.js.map +1 -0
  199. package/dist/recovery-spike.test.d.ts +2 -0
  200. package/dist/recovery-spike.test.d.ts.map +1 -0
  201. package/dist/recovery-spike.test.js +20 -0
  202. package/dist/recovery-spike.test.js.map +1 -0
  203. package/dist/runtime/claude-host-adapter.d.ts +28 -0
  204. package/dist/runtime/claude-host-adapter.d.ts.map +1 -0
  205. package/dist/runtime/claude-host-adapter.js +43 -0
  206. package/dist/runtime/claude-host-adapter.js.map +1 -0
  207. package/dist/runtime/context-binder.d.ts +61 -0
  208. package/dist/runtime/context-binder.d.ts.map +1 -0
  209. package/dist/runtime/context-binder.js +26 -0
  210. package/dist/runtime/context-binder.js.map +1 -0
  211. package/dist/runtime/execution-coordinator.d.ts +32 -0
  212. package/dist/runtime/execution-coordinator.d.ts.map +1 -0
  213. package/dist/runtime/execution-coordinator.js +243 -0
  214. package/dist/runtime/execution-coordinator.js.map +1 -0
  215. package/dist/runtime/gate-queue.d.ts +17 -0
  216. package/dist/runtime/gate-queue.d.ts.map +1 -0
  217. package/dist/runtime/gate-queue.js +33 -0
  218. package/dist/runtime/gate-queue.js.map +1 -0
  219. package/dist/runtime/host-conformance.d.ts +47 -0
  220. package/dist/runtime/host-conformance.d.ts.map +1 -0
  221. package/dist/runtime/host-conformance.js +61 -0
  222. package/dist/runtime/host-conformance.js.map +1 -0
  223. package/dist/runtime/host-routing.d.ts +5 -0
  224. package/dist/runtime/host-routing.d.ts.map +1 -0
  225. package/dist/runtime/host-routing.js +42 -0
  226. package/dist/runtime/host-routing.js.map +1 -0
  227. package/dist/runtime/memory-writeback.d.ts +18 -0
  228. package/dist/runtime/memory-writeback.d.ts.map +1 -0
  229. package/dist/runtime/memory-writeback.js +23 -0
  230. package/dist/runtime/memory-writeback.js.map +1 -0
  231. package/dist/runtime/operation-journal.d.ts +13 -0
  232. package/dist/runtime/operation-journal.d.ts.map +1 -0
  233. package/dist/runtime/operation-journal.js +38 -0
  234. package/dist/runtime/operation-journal.js.map +1 -0
  235. package/dist/runtime/plan-compiler.d.ts +37 -0
  236. package/dist/runtime/plan-compiler.d.ts.map +1 -0
  237. package/dist/runtime/plan-compiler.js +62 -0
  238. package/dist/runtime/plan-compiler.js.map +1 -0
  239. package/dist/runtime/project-runtime-factory.d.ts +15 -0
  240. package/dist/runtime/project-runtime-factory.d.ts.map +1 -0
  241. package/dist/runtime/project-runtime-factory.js +11 -0
  242. package/dist/runtime/project-runtime-factory.js.map +1 -0
  243. package/dist/runtime/review-coordinator.d.ts +25 -0
  244. package/dist/runtime/review-coordinator.d.ts.map +1 -0
  245. package/dist/runtime/review-coordinator.js +22 -0
  246. package/dist/runtime/review-coordinator.js.map +1 -0
  247. package/dist/subagents/claude-events.d.ts +59 -0
  248. package/dist/subagents/claude-events.d.ts.map +1 -0
  249. package/dist/subagents/claude-events.js +60 -0
  250. package/dist/subagents/claude-events.js.map +1 -0
  251. package/dist/subagents/handoff-validator.d.ts +8 -0
  252. package/dist/subagents/handoff-validator.d.ts.map +1 -0
  253. package/dist/subagents/handoff-validator.js +20 -0
  254. package/dist/subagents/handoff-validator.js.map +1 -0
  255. package/dist/subagents/orchestrator.d.ts +47 -0
  256. package/dist/subagents/orchestrator.d.ts.map +1 -0
  257. package/dist/subagents/orchestrator.js +85 -0
  258. package/dist/subagents/orchestrator.js.map +1 -0
  259. package/package.json +45 -0
@@ -0,0 +1,51 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { assertApproval } from '../adapters/approval.js';
3
+ import { assertIntegrationTarget, freezeIntegration } from '../adapters/integration.js';
4
+ import { assertReviewPasses } from '../adapters/review.js';
5
+ import { LocalDeliveryPackageAdapter, verifyDeliveryPackage } from '../adapters/delivery-package.js';
6
+ import { PolicyApprovalAdapter, assertNonBypassableGates, assertWaiver, assertRequiredApprovals, loadApprovalPolicy, loadApprovalOwners, requiredApprovalRoles } from '../adapters/approval.js';
7
+ import { resolve } from 'node:path';
8
+ describe('delivery governance adapters', () => {
9
+ it('requires independent approval, complete reviews, and candidate integration ref', () => {
10
+ const actor = { id: 'owner', kind: 'human', role: 'code-owner' };
11
+ expect(() => assertApproval({ id: 'a', stage: 'review', actor, policyVersion: '1', artifactHash: 'h', decision: 'approved', reason: 'ok', createdAt: 1 }, { id: 'agent', kind: 'agent', role: 'implementer' }, 'h')).not.toThrow();
12
+ expect(() => assertIntegrationTarget({ integrationRef: 'refs/devflow/c', targetRef: 'main', candidateRevision: 'abc' })).not.toThrow();
13
+ const receipts = ['bugs', 'security', 'compliance'].map(kind => ({ id: kind, kind, artifactHash: 'h', findings: [], passed: true, createdAt: 1 }));
14
+ expect(() => assertReviewPasses(receipts, 'h')).not.toThrow();
15
+ expect(freezeIntegration({ baseRef: 'base', integrationRef: 'refs/devflow/c', targetRef: 'main', candidateRevision: 'abc' })).toMatchObject({ candidateRevision: 'abc' });
16
+ expect(() => freezeIntegration({ baseRef: 'main', integrationRef: 'refs/devflow/c', targetRef: 'main', candidateRevision: 'abc' })).toThrow('distinct');
17
+ });
18
+ it('composes policy approval and local package adapters', async () => {
19
+ const actor = { id: 'owner', kind: 'human', role: 'code-owner' };
20
+ const approval = new PolicyApprovalAdapter({ id: 'agent', kind: 'agent', role: 'implementer' }, 'delivery-line.v1', ['code-owner']);
21
+ await expect(approval.approve({ id: 'a', stage: 'review', actor, policyVersion: 'delivery-line.v1', artifactHash: 'h', decision: 'approved', reason: 'ok', createdAt: 1 })).resolves.toMatchObject({ decision: 'approved' });
22
+ await expect(approval.approve({ id: 'expired', stage: 'review', actor, policyVersion: 'delivery-line.v1', artifactHash: 'h', decision: 'approved', reason: 'ok', createdAt: 1, expiresAt: 1 })).rejects.toThrow('expired');
23
+ expect(() => assertNonBypassableGates({ auth: true, dataIntegrity: true, redTest: true })).not.toThrow();
24
+ expect(() => assertNonBypassableGates({ auth: false, dataIntegrity: true, redTest: true })).toThrow('safety');
25
+ expect(() => assertWaiver({ id: 'w', kind: 'waiver', question: 'waive', options: [], decision: 'allow', decidedBy: actor, artifactHash: 'h', expiresAt: Date.now() + 1000 }, 'h')).not.toThrow();
26
+ const pkg = new LocalDeliveryPackageAdapter();
27
+ const exported = await pkg.prepare({ record: { id: 'case', stage: 'release_pending', baseRef: 'main', integrationRef: 'refs/devflow/delivery/case', targetRef: 'main', artifacts: [], tasks: [], approvals: [], decisions: [], evidence: [] }, path: '/tmp/delivery-line-package.json' });
28
+ expect(exported).toMatchObject({ path: '/tmp/delivery-line-package.json' });
29
+ expect(() => pkg.verify?.({ path: exported.path, packageHash: exported.packageHash })).not.toThrow();
30
+ expect(() => verifyDeliveryPackage(exported.path, exported.packageHash)).not.toThrow();
31
+ expect(() => verifyDeliveryPackage(exported.path, 'stale')).toThrow('HASH_MISMATCH');
32
+ });
33
+ it('loads versioned project policy and requires every risk-specific owner', () => {
34
+ const policy = loadApprovalPolicy(resolve(process.cwd(), '../../.devflow/delivery-line/approval-policy.yaml'));
35
+ expect(policy.version).toBe('delivery-line.v1');
36
+ expect(requiredApprovalRoles(policy, 'high', 'review')).toEqual(['code_owner', 'security_owner']);
37
+ const approvals = [
38
+ { id: 'c', stage: 'review', actor: { id: 'c', kind: 'human', role: 'code_owner' }, policyVersion: policy.version, artifactHash: 'h', decision: 'approved', reason: 'ok', createdAt: Date.now() },
39
+ { id: 's', stage: 'review', actor: { id: 's', kind: 'human', role: 'security_owner' }, policyVersion: policy.version, artifactHash: 'h', decision: 'approved', reason: 'ok', createdAt: Date.now() },
40
+ ];
41
+ expect(() => assertRequiredApprovals({ policy, riskTier: 'high', stage: 'review', approvals, artifactHash: 'h' })).not.toThrow();
42
+ expect(() => assertRequiredApprovals({ policy, riskTier: 'high', stage: 'review', approvals: approvals.slice(0, 1), artifactHash: 'h' })).toThrow('security_owner');
43
+ });
44
+ it('rejects an actor that is not assigned to the approval role', async () => {
45
+ const owners = loadApprovalOwners(resolve(process.cwd(), '../../.devflow/delivery-line/owners.yaml'));
46
+ const adapter = new PolicyApprovalAdapter({ id: 'agent', kind: 'agent' }, 'delivery-line.v1', [], undefined, { code_owner: ['alice'] });
47
+ await expect(adapter.approve({ id: 'a', stage: 'review', actor: { id: 'bob', kind: 'human', role: 'code_owner' }, policyVersion: 'delivery-line.v1', artifactHash: 'h', decision: 'approved', reason: 'ok', createdAt: Date.now() })).rejects.toThrow('assigned owner');
48
+ expect(owners).toHaveProperty('code_owner');
49
+ });
50
+ });
51
+ //# sourceMappingURL=governance-adapters.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"governance-adapters.test.js","sourceRoot":"","sources":["../../src/__tests__/governance-adapters.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=handoff-validator.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handoff-validator.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/handoff-validator.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,13 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { validateAgentHandoff } from '../subagents/handoff-validator.js';
3
+ const handoff = { caseId: 'case', from: { role: 'designer', agentId: 'a1', version: 'v1', sessionId: 's1' }, to: { role: 'planner', agentId: 'a2', version: 'v1' }, inputRefs: ['spec-hash'], contextReceipt: 'ctx', claims: [{ statement: 'scope verified', evidenceRefs: ['e1'] }], outputRefs: ['plan-hash'], status: 'complete', idempotencyKey: 'h1' };
4
+ describe('handoff validation', () => {
5
+ it('accepts evidence-bound handoffs and rejects untrusted control changes', () => {
6
+ expect(() => validateAgentHandoff(handoff, { caseId: 'case', contextReceipt: 'ctx', inputRefs: ['spec-hash'] })).not.toThrow();
7
+ expect(() => validateAgentHandoff({ ...handoff, outputRefs: ['targetRef'] }, { caseId: 'case', contextReceipt: 'ctx', inputRefs: ['spec-hash'] })).toThrow('protected');
8
+ expect(() => validateAgentHandoff({ ...handoff, from: { ...handoff.from, role: 'unknown' } }, { caseId: 'case', contextReceipt: 'ctx', inputRefs: ['spec-hash'] })).toThrow('not allowed');
9
+ expect(() => validateAgentHandoff({ ...handoff, status: 'invalid' }, { caseId: 'case', contextReceipt: 'ctx', inputRefs: ['spec-hash'] })).toThrow('evidence');
10
+ expect(() => validateAgentHandoff({ ...handoff, transcript: 'hidden' }, { caseId: 'case', contextReceipt: 'ctx', inputRefs: ['spec-hash'] })).toThrow('forbidden');
11
+ });
12
+ });
13
+ //# sourceMappingURL=handoff-validator.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handoff-validator.test.js","sourceRoot":"","sources":["../../src/__tests__/handoff-validator.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAEzE,MAAM,OAAO,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,CAAC,WAAW,CAAC,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,gBAAgB,EAAE,YAAY,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,WAAW,CAAC,EAAE,MAAM,EAAE,UAAmB,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC;AACrW,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;IAClC,EAAE,CAAC,uEAAuE,EAAE,GAAG,EAAE;QAC/E,MAAM,CAAC,GAAG,EAAE,CAAC,oBAAoB,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;QAC/H,MAAM,CAAC,GAAG,EAAE,CAAC,oBAAoB,CAAC,EAAE,GAAG,OAAO,EAAE,UAAU,EAAE,CAAC,WAAW,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QACxK,MAAM,CAAC,GAAG,EAAE,CAAC,oBAAoB,CAAC,EAAE,GAAG,OAAO,EAAE,IAAI,EAAE,EAAE,GAAG,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QAC3L,MAAM,CAAC,GAAG,EAAE,CAAC,oBAAoB,CAAC,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,SAAkB,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QACxK,MAAM,CAAC,GAAG,EAAE,CAAC,oBAAoB,CAAC,EAAE,GAAG,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAW,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IAC9K,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=interaction-session.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interaction-session.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/interaction-session.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,13 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { InteractionSession } from '../interaction-session.js';
3
+ import { DeliveryLineError } from '../domain/types.js';
4
+ describe('interaction session', () => {
5
+ it('requires one answer before the next blocking question and enforces budget', () => {
6
+ const session = new InteractionSession({ phase: 'clarification', status: 'active', turn: 0, openQuestions: [], blockingUnknowns: [], summaryRefs: [] }, 1);
7
+ session.ask('Who owns the decision?');
8
+ expect(() => session.ask('What is the error contract?')).toThrow(DeliveryLineError);
9
+ session.answer(1);
10
+ expect(() => session.ask('What is the error contract?')).toThrow('interaction budget exceeded');
11
+ });
12
+ });
13
+ //# sourceMappingURL=interaction-session.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interaction-session.test.js","sourceRoot":"","sources":["../../src/__tests__/interaction-session.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAEvD,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;IACnC,EAAE,CAAC,2EAA2E,EAAE,GAAG,EAAE;QACnF,MAAM,OAAO,GAAG,IAAI,kBAAkB,CAAC,EAAE,KAAK,EAAE,eAAe,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,EAAE,aAAa,EAAE,EAAE,EAAE,gBAAgB,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;QAC3J,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;QACtC,MAAM,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,6BAA6B,CAAC,CAAC,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC;QACpF,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAClB,MAAM,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,6BAA6B,CAAC,CAAC,CAAC,OAAO,CAAC,6BAA6B,CAAC,CAAC;IAClG,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=interaction.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interaction.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/interaction.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,27 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { addNonBlockingUnknown, answerInteraction, appendInteractionSummary, cancelInteraction, createExecutionInteractionRequest, pauseInteraction, reviseInteraction, validateDecision, validateWaiver } from '../domain/interaction.js';
3
+ const state = { phase: 'clarification', status: 'active', turn: 1, openQuestions: [], blockingUnknowns: [], summaryRefs: [] };
4
+ describe('delivery interaction and decision contracts', () => {
5
+ it('supports pause, revise, answer and cancel without approving implicitly', () => {
6
+ const revised = reviseInteraction(state, 'Which scope?', 2);
7
+ expect(revised.status).toBe('awaiting_user');
8
+ expect(answerInteraction(revised, 2).status).toBe('active');
9
+ expect(pauseInteraction(state).status).toBe('paused');
10
+ expect(cancelInteraction(revised).openQuestions).toEqual([]);
11
+ });
12
+ it('validates decision categories and expiry independently of approval', () => {
13
+ expect(() => validateDecision({ id: 'd', kind: 'technical', question: 'scope?', options: ['a'], decision: 'a', decidedBy: { id: 'u', kind: 'human' }, artifactHash: 'h' })).not.toThrow();
14
+ expect(() => validateDecision({ id: 'w', kind: 'waiver', question: 'skip?', options: ['yes'], decision: 'yes', decidedBy: { id: 'u', kind: 'human' }, artifactHash: 'h', expiresAt: 1 }, 2)).toThrow('expired');
15
+ expect(() => validateWaiver({ id: 'w', kind: 'waiver', question: 'skip?', options: ['yes'], decision: 'yes', decidedBy: { id: 'u', kind: 'human' }, artifactHash: 'old', expiresAt: 10 }, 'current', 2)).toThrow('current artifact');
16
+ expect(() => validateWaiver({ id: 'w', kind: 'waiver', question: 'skip?', options: ['yes'], decision: 'yes', decidedBy: { id: 'u', kind: 'human' }, artifactHash: 'current', expiresAt: 10 }, 'current', 2)).not.toThrow();
17
+ });
18
+ it('records structured summaries and owner-bound non-blocking unknowns', () => {
19
+ const summary = { id: 'summary-1', phase: 'clarification', turn: 1, userStatement: 'Need a bounded change', contextReceipt: 'ctx', actor: { id: 'u', kind: 'human' } };
20
+ const withSummary = appendInteractionSummary(state, summary);
21
+ expect(withSummary.summaryRefs).toEqual(['summary-1']);
22
+ const withUnknown = addNonBlockingUnknown(withSummary, { question: 'Which browser?', owner: { id: 'u', kind: 'human' }, dueAt: Date.now() + 1000 });
23
+ expect(withUnknown.nonBlockingUnknowns?.[0].owner.id).toBe('u');
24
+ expect(createExecutionInteractionRequest({ ...state, phase: 'execution' }, 'task-1', 'Which API contract is authoritative?')).toMatchObject({ taskId: 'task-1', blocking: true, turn: 2 });
25
+ });
26
+ });
27
+ //# sourceMappingURL=interaction.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interaction.test.js","sourceRoot":"","sources":["../../src/__tests__/interaction.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=persistence.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"persistence.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/persistence.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,41 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { mkdtempSync } from 'node:fs';
3
+ import { tmpdir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ import { SQLiteCaseStore } from '../persistence/sqlite-store.js';
6
+ import { createDeliveryLineFacade } from '../composition.js';
7
+ function caseRecord() {
8
+ return { id: 'case-1', projectRoot: '/repo', baseRef: 'main', integrationRef: 'refs/devflow/case-1', targetRef: 'main', title: 'demo', source: 'human', riskTier: 'low', autonomy: 'manual', stage: 'draft', budgets: { maxInteractionTurns: 4, maxTaskAttempts: 2, maxWallTimeMs: 1000, maxToolCalls: 10 }, artifacts: [], tasks: [], approvals: [], decisions: [], evidence: [], interaction: { phase: 'clarification', status: 'active', turn: 0, openQuestions: [], blockingUnknowns: [], summaryRefs: [] }, createdBy: { id: 'u', kind: 'human' }, updatedAt: 1, version: 0 };
9
+ }
10
+ describe('delivery line sqlite persistence', () => {
11
+ it('persists cases, rejects stale CAS, deduplicates events, and fences leases', async () => {
12
+ const store = new SQLiteCaseStore(join(mkdtempSync(join(tmpdir(), 'delivery-line-')), 'state.db'));
13
+ const record = caseRecord();
14
+ await store.create(record);
15
+ const next = { ...record, version: 1, stage: 'clarifying', updatedAt: 2 };
16
+ await store.save(next, 0);
17
+ await expect(store.save({ ...next, version: 2 }, 0)).rejects.toMatchObject({ code: 'STATE_VERSION_CONFLICT' });
18
+ const event = { id: 'event-1', caseId: record.id, type: 'case.created', idempotencyKey: 'event-key', actor: record.createdBy, payload: { stage: 'clarifying' }, createdAt: 2 };
19
+ await store.appendEvent(event);
20
+ await store.appendEvent(event);
21
+ await expect(store.appendEvent({ ...event, payload: { stage: 'draft' } })).rejects.toMatchObject({ code: 'IDEMPOTENCY_CONFLICT' });
22
+ expect(store.acquireTaskLease({ taskId: 'task-1', caseId: record.id, owner: 'worker-a', now: 10, leaseMs: 100 })).toBe(true);
23
+ expect(store.acquireTaskLease({ taskId: 'task-1', caseId: record.id, owner: 'worker-b', now: 50, leaseMs: 100 })).toBe(false);
24
+ expect(store.acquireTaskLease({ taskId: 'task-1', caseId: record.id, owner: 'worker-b', now: 111, leaseMs: 100 })).toBe(true);
25
+ expect(store.releaseTaskLease('task-1', 'worker-b')).toBe(true);
26
+ store.close();
27
+ });
28
+ it('uses one project-local composition and leaves execution explicitly unavailable', async () => {
29
+ const root = mkdtempSync(join(tmpdir(), 'delivery-composition-'));
30
+ const facade = createDeliveryLineFacade(root);
31
+ const identity = { actor: { id: 'user', kind: 'human' }, sessionId: 's', executionId: 'e', contextReceipt: 'ctx' };
32
+ const created = await facade.createCase({ ...identity, idempotencyKey: 'create', projectRoot: root, baseRef: 'HEAD', targetRef: 'main', title: 'composition' });
33
+ const answered = await facade.answer(created.caseId, { ...identity, idempotencyKey: 'answer', turn: 1, value: 'criteria' });
34
+ expect(answered.stage).toBe('clarifying');
35
+ const stored = await facade.status(created.caseId);
36
+ expect(stored.caseId).toBe(created.caseId);
37
+ const approved = await facade.approve(created.caseId, { ...identity, idempotencyKey: 'intent-compose', turn: 1, stage: 'intent' });
38
+ expect(approved.stage).toBe('designing');
39
+ });
40
+ });
41
+ //# sourceMappingURL=persistence.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"persistence.test.js","sourceRoot":"","sources":["../../src/__tests__/persistence.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAEjE,OAAO,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAE7D,SAAS,UAAU;IACjB,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,qBAAqB,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,mBAAmB,EAAE,CAAC,EAAE,eAAe,EAAE,CAAC,EAAE,aAAa,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,KAAK,EAAE,eAAe,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,EAAE,aAAa,EAAE,EAAE,EAAE,gBAAgB,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,SAAS,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;AACrjB,CAAC;AAED,QAAQ,CAAC,kCAAkC,EAAE,GAAG,EAAE;IAChD,EAAE,CAAC,2EAA2E,EAAE,KAAK,IAAI,EAAE;QACzF,MAAM,KAAK,GAAG,IAAI,eAAe,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,gBAAgB,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC;QACnG,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;QAC5B,MAAM,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC3B,MAAM,IAAI,GAAG,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,YAAqB,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;QACnF,MAAM,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QAC1B,MAAM,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,wBAAwB,EAAE,CAAC,CAAC;QAC/G,MAAM,KAAK,GAAG,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,cAAc,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;QAC/K,MAAM,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;QAC/B,MAAM,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;QAC/B,MAAM,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,EAAE,GAAG,KAAK,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,sBAAsB,EAAE,CAAC,CAAC;QACnI,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7H,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC9H,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9H,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAChE,KAAK,CAAC,KAAK,EAAE,CAAC;IAChB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gFAAgF,EAAE,KAAK,IAAI,EAAE;QAC9F,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,uBAAuB,CAAC,CAAC,CAAC;QAClE,MAAM,MAAM,GAAG,wBAAwB,CAAC,IAAI,CAAC,CAAC;QAC9C,MAAM,QAAQ,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,OAAgB,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,WAAW,EAAE,GAAG,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC;QAC5H,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,EAAE,GAAG,QAAQ,EAAE,cAAc,EAAE,QAAQ,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC,CAAC;QAChK,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,GAAG,QAAQ,EAAE,cAAc,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC;QAC5H,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAC1C,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACnD,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAC3C,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,GAAG,QAAQ,EAAE,cAAc,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;QACnI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=recovery-spike.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recovery-spike.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/recovery-spike.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,20 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { cloneRecoverySnapshot, RecoveryCoordinator } from '../recovery-spike.js';
3
+ describe('delivery line recovery spike', () => {
4
+ it('resumes an unfinished task once after process reconstruction', async () => {
5
+ const calls = [];
6
+ const initial = {
7
+ caseId: 'case-1', stage: 'executing',
8
+ tasks: [{ taskId: 'task-1', status: 'pending', attempt: 0, idempotencyKey: 'case-1:task-1:1' }],
9
+ };
10
+ const first = new RecoveryCoordinator(cloneRecoverySnapshot(initial), { startTask: async (id) => { calls.push(id); } });
11
+ const recovered = await first.resume();
12
+ expect(recovered.tasks[0]).toMatchObject({ status: 'running', attempt: 1 });
13
+ expect(calls).toEqual(['task-1']);
14
+ const second = new RecoveryCoordinator(cloneRecoverySnapshot(recovered), { startTask: async (id) => { calls.push(id); } });
15
+ const resumed = await second.resume();
16
+ expect(resumed.stage).toBe('held');
17
+ expect(calls).toEqual(['task-1']);
18
+ });
19
+ });
20
+ //# sourceMappingURL=recovery-spike.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recovery-spike.test.js","sourceRoot":"","sources":["../../src/__tests__/recovery-spike.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAyB,MAAM,sBAAsB,CAAC;AAEzG,QAAQ,CAAC,8BAA8B,EAAE,GAAG,EAAE;IAC5C,EAAE,CAAC,8DAA8D,EAAE,KAAK,IAAI,EAAE;QAC5E,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,MAAM,OAAO,GAAqB;YAChC,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW;YACpC,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE,cAAc,EAAE,iBAAiB,EAAE,CAAC;SAChG,CAAC;QACF,MAAM,KAAK,GAAG,IAAI,mBAAmB,CAAC,qBAAqB,CAAC,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACxH,MAAM,SAAS,GAAG,MAAM,KAAK,CAAC,MAAM,EAAE,CAAC;QACvC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;QAC5E,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;QAElC,MAAM,MAAM,GAAG,IAAI,mBAAmB,CAAC,qBAAqB,CAAC,SAAS,CAAC,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC3H,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,MAAM,EAAE,CAAC;QACtC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACnC,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=sqlite-recovery.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite-recovery.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/sqlite-recovery.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,90 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { mkdtempSync } from 'node:fs';
3
+ import { tmpdir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ import { SQLiteCaseStore } from '../persistence/sqlite-store.js';
6
+ import { DeliveryLineFacade } from '../facade/delivery-line-facade.js';
7
+ const makeCase = (overrides = {}) => ({
8
+ id: 'recovery-case', projectRoot: '/repo', baseRef: 'main', integrationRef: 'refs/devflow/delivery/recovery-case', targetRef: 'main', title: 'recovery', source: 'human', riskTier: 'low', autonomy: 'manual', stage: 'executing',
9
+ budgets: { maxInteractionTurns: 4, maxTaskAttempts: 2, maxWallTimeMs: 1000, maxToolCalls: 10 }, artifacts: [], tasks: [], approvals: [], decisions: [], evidence: [],
10
+ interaction: { phase: 'execution', status: 'awaiting_user', turn: 1, openQuestions: ['choose'], blockingUnknowns: ['choose'], summaryRefs: [], summaries: [{ id: 'summary-1', phase: 'execution', turn: 1, question: 'choose', actor: { id: 'user', kind: 'human' } }] },
11
+ createdBy: { id: 'user', kind: 'human' }, updatedAt: 10, version: 0, ...overrides,
12
+ });
13
+ describe('SQLite recovery contract', () => {
14
+ it('creates every required table and column', () => {
15
+ const store = new SQLiteCaseStore(join(mkdtempSync(join(tmpdir(), 'delivery-line-schema-')), 'state.db'));
16
+ const required = {
17
+ delivery_cases: ['id', 'payload', 'version', 'updated_at'],
18
+ delivery_tasks: ['id', 'case_id', 'payload', 'version', 'lease_owner', 'lease_expires_at', 'attempt'],
19
+ delivery_artifacts: ['id', 'case_id', 'payload', 'content_hash'],
20
+ delivery_approvals: ['id', 'case_id', 'payload'],
21
+ delivery_evidence: ['id', 'case_id', 'payload', 'evidence_hash'],
22
+ delivery_events: ['id', 'case_id', 'idempotency_key', 'actor', 'type', 'payload', 'created_at', 'processed_at'],
23
+ delivery_interactions: ['case_id', 'phase', 'turn', 'prompt_or_summary', 'answer', 'decision', 'blocking', 'artifact_hash'],
24
+ delivery_subagent_sessions: ['session_id', 'case_id', 'role', 'agent_id', 'agent_version', 'permission_snapshot', 'status', 'last_event_id'],
25
+ delivery_handoffs: ['case_id', 'idempotency_key', 'source_role', 'target_role', 'input_refs', 'output_refs', 'context_receipt', 'status', 'payload', 'processed_at'],
26
+ };
27
+ for (const [table, columns] of Object.entries(required)) {
28
+ const actual = new Set(store.db.prepare(`PRAGMA table_info(${table})`).all().map(column => column.name));
29
+ for (const column of columns)
30
+ expect(actual.has(column), `${table}.${column}`).toBe(true);
31
+ }
32
+ store.close();
33
+ });
34
+ it('recovers pending work, event cursor and hand-off state after reopening', async () => {
35
+ const path = join(mkdtempSync(join(tmpdir(), 'delivery-line-recovery-')), 'state.db');
36
+ const first = new SQLiteCaseStore(path);
37
+ const record = makeCase({ tasks: [{ id: 'task-1', caseId: 'recovery-case', title: 'task', attempt: 1, version: 0, baseRevision: 'main', dependsOn: [], allowedFiles: [], expectedChanges: [], verificationCommands: [], status: 'verified', evidence: [] }] });
38
+ await first.create(record);
39
+ const event = { id: 'event-1', caseId: record.id, type: 'user.answer', idempotencyKey: 'answer-1', actor: record.createdBy, payload: { answer: 'yes' }, createdAt: 20 };
40
+ await first.appendEvent(event);
41
+ const handoff = { caseId: record.id, from: { role: 'designer', agentId: 'designer', version: 'v1', sessionId: 's1' }, to: { role: 'planner', agentId: 'planner', version: 'v1' }, inputRefs: ['intent'], contextReceipt: 'ctx-1', claims: [{ statement: 'ok', evidenceRefs: ['e1'] }], outputRefs: ['plan'], status: 'complete', idempotencyKey: 'handoff-1' };
42
+ first.recordHandoff(handoff, 21);
43
+ expect(first.acquireTaskLease({ taskId: 'task-1', caseId: record.id, owner: 'worker-a', now: 30, leaseMs: 10, payload: JSON.stringify(record.tasks[0]) })).toBe(true);
44
+ first.close();
45
+ const recovered = new SQLiteCaseStore(path);
46
+ expect((await recovered.get(record.id)).tasks[0].status).toBe('verified');
47
+ expect((await recovered.listUnprocessedEvents(record.id)).map(item => item.idempotencyKey)).toEqual(['answer-1']);
48
+ expect(recovered.latestEventCursor(record.id)?.id).toBe('event-1');
49
+ expect(recovered.listPendingInteractions(record.id)).toHaveLength(1);
50
+ expect(recovered.listUnprocessedHandoffs(record.id)).toHaveLength(1);
51
+ expect(recovered.listVerifiedNotIntegratedTasks(record.id).map(task => task.id)).toEqual(['task-1']);
52
+ expect(recovered.listActiveLeases(35, record.id)).toHaveLength(1);
53
+ expect(await recovered.markEventProcessed('answer-1', 40)).toBe(true);
54
+ expect(await recovered.markEventProcessed('answer-1', 41)).toBe(false);
55
+ expect(recovered.markHandoffProcessed('handoff-1', 42)).toBe(true);
56
+ expect(recovered.listUnprocessedHandoffs(record.id)).toHaveLength(0);
57
+ recovered.close();
58
+ });
59
+ it('fences concurrent lease takeover and records it in the ledger', () => {
60
+ const store = new SQLiteCaseStore(join(mkdtempSync(join(tmpdir(), 'delivery-line-lease-')), 'state.db'));
61
+ expect(store.acquireTaskLease({ taskId: 'task-1', caseId: 'case-1', owner: 'a', now: 1, leaseMs: 100 })).toBe(true);
62
+ expect(store.acquireTaskLease({ taskId: 'task-1', caseId: 'case-1', owner: 'b', now: 2, leaseMs: 100 })).toBe(false);
63
+ expect(store.acquireTaskLease({ taskId: 'task-1', caseId: 'case-1', owner: 'b', now: 101, leaseMs: 100 })).toBe(true);
64
+ expect(store.latestEventCursor('case-1')?.id).toContain('lease-takeover:task-1:b:101');
65
+ store.close();
66
+ });
67
+ it('persists explicit subagent cursor and processed user events', async () => {
68
+ const store = new SQLiteCaseStore(join(mkdtempSync(join(tmpdir(), 'delivery-line-session-')), 'state.db'));
69
+ const session = { sessionId: 's1', caseId: 'case-1', role: 'designer', agentId: 'delivery-designer', agentVersion: 'v1', permissionSnapshot: { readArtifacts: true }, status: 'running', lastEventId: 'e1', processedUserEventIds: ['u1'], lastUserTurn: 2, budget: { toolCalls: 1 }, createdAt: 1 };
70
+ await store.saveSubAgentSession(session);
71
+ expect(await store.getSubAgentSession('s1')).toMatchObject({ lastEventId: 'e1', processedUserEventIds: ['u1'], lastUserTurn: 2 });
72
+ store.close();
73
+ });
74
+ it('reconstructs the facade after restart without replaying a running task', async () => {
75
+ const path = join(mkdtempSync(join(tmpdir(), 'delivery-line-facade-restart-')), 'state.db');
76
+ const firstStore = new SQLiteCaseStore(path);
77
+ const record = makeCase({ contextReceipt: 'ctx-restart', tasks: [{ id: 'task-1', caseId: 'recovery-case', title: 'task', attempt: 1, version: 0, baseRevision: 'main', dependsOn: [], allowedFiles: [], expectedChanges: [], verificationCommands: [], status: 'running', evidence: [] }] });
78
+ await firstStore.create(record);
79
+ firstStore.close();
80
+ let starts = 0;
81
+ const secondStore = new SQLiteCaseStore(path);
82
+ const facade = new DeliveryLineFacade({ store: secondStore, execution: { startTask: async () => { starts += 1; return { taskId: 'task-1', status: 'started' }; }, cancelTask: async () => { } } });
83
+ const recovered = await facade.resume(record.id, { actor: record.createdBy, sessionId: 's', executionId: 'e', idempotencyKey: 'resume-restart', contextReceipt: 'ctx-restart' });
84
+ expect(recovered.stage).toBe('held');
85
+ expect(recovered.taskSummaries[0]).toMatchObject({ status: 'running', attempt: 1 });
86
+ expect(starts).toBe(0);
87
+ secondStore.close();
88
+ });
89
+ });
90
+ //# sourceMappingURL=sqlite-recovery.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite-recovery.test.js","sourceRoot":"","sources":["../../src/__tests__/sqlite-recovery.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=state-machine.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state-machine.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/state-machine.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,49 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { cancelCase, canAdvance, invalidateDownstream, transitionCase } from '../domain/state-machine.js';
3
+ import { DeliveryLineError } from '../domain/types.js';
4
+ function record(stage) {
5
+ return { id: 'case', projectRoot: '/repo', baseRef: 'main', integrationRef: 'refs/devflow/case', targetRef: 'main', title: 'change', source: 'human', riskTier: 'low', autonomy: 'manual', stage, budgets: { maxInteractionTurns: 4, maxTaskAttempts: 2, maxWallTimeMs: 1000, maxToolCalls: 10 }, artifacts: [], tasks: [], approvals: [], decisions: [], evidence: [], interaction: { phase: 'clarification', status: 'active', turn: 0, openQuestions: [], blockingUnknowns: [], summaryRefs: [] }, createdBy: { id: 'u', kind: 'human' }, updatedAt: 1, version: 0 };
6
+ }
7
+ describe('delivery state machine', () => {
8
+ it('supports the complete linear happy-path transition sequence', () => {
9
+ const facts = [
10
+ {}, { intentApproved: true }, { designStarted: true }, { specHash: 'spec' }, { specApproved: true },
11
+ { planHash: 'plan' }, { planApproved: true }, { executableTask: true }, { allTasksIntegrated: true, planVerificationFresh: true },
12
+ { reviewsPassed: true }, { releaseApproved: true }, { packageExported: true },
13
+ ];
14
+ const stages = ['draft', 'clarifying', 'intent_approved', 'designing', 'spec_ready', 'spec_approved', 'plan_ready', 'plan_approved', 'executing', 'verified', 'reviewed', 'release_pending', 'delivered'];
15
+ let current = record(stages[0]);
16
+ for (let index = 0; index < facts.length; index += 1) {
17
+ current = transitionCase(current, facts[index]);
18
+ expect(current.stage).toBe(stages[index + 1]);
19
+ }
20
+ });
21
+ it('advances only with current artifacts and evidence', () => {
22
+ expect(canAdvance('clarifying', { intentApproved: true, blockingUnknowns: 1 })).toBe(false);
23
+ const current = record('executing');
24
+ expect(() => transitionCase(current, { allTasksIntegrated: true, planVerificationFresh: true, currentArtifactHash: 'old', requiredArtifactHash: 'new' })).toThrow(DeliveryLineError);
25
+ const next = transitionCase(current, { allTasksIntegrated: true, planVerificationFresh: true, currentArtifactHash: 'h', requiredArtifactHash: 'h', requiredEvidenceRefs: ['verification'], evidenceRefs: ['verification'] });
26
+ expect(next.stage).toBe('verified');
27
+ expect(next.version).toBe(1);
28
+ });
29
+ it('treats cancellation as terminal and rejects invalid terminal transitions', () => {
30
+ const cancelled = cancelCase(record('executing'));
31
+ expect(cancelled.stage).toBe('cancelled');
32
+ expect(() => cancelCase(cancelled)).toThrow('cannot be cancelled');
33
+ expect(() => transitionCase(cancelled, {})).toThrow(DeliveryLineError);
34
+ });
35
+ it('invalidates downstream approvals, task integration and evidence after scope revision', () => {
36
+ const current = record('reviewed');
37
+ current.approvals = [
38
+ { id: 'intent', stage: 'intent', actor: { id: 'u', kind: 'human' }, policyVersion: '1', artifactHash: 'i', decision: 'approved', reason: 'ok', createdAt: 1 },
39
+ { id: 'spec', stage: 'spec', actor: { id: 'u', kind: 'human' }, policyVersion: '1', artifactHash: 's', decision: 'approved', reason: 'ok', createdAt: 1 },
40
+ ];
41
+ current.tasks = [{ id: 't', caseId: 'case', title: 'task', attempt: 1, version: 1, baseRevision: 'main', dependsOn: [], allowedFiles: [], expectedChanges: [], verificationCommands: [], status: 'integrated', integratedRevision: 'candidate', evidence: [] }];
42
+ current.evidence = [{ id: 'e', kind: 'verification', hash: 'h', createdAt: 1, metadata: {} }];
43
+ const revised = invalidateDownstream('clarifying', current);
44
+ expect(revised.approvals).toEqual([]);
45
+ expect(revised.tasks[0]).toMatchObject({ status: 'pending', integratedRevision: undefined });
46
+ expect(revised.evidence).toEqual([]);
47
+ });
48
+ });
49
+ //# sourceMappingURL=state-machine.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state-machine.test.js","sourceRoot":"","sources":["../../src/__tests__/state-machine.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=subagent-orchestrator.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subagent-orchestrator.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/subagent-orchestrator.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,33 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { MemoryCaseStore } from '../persistence/memory-store.js';
3
+ import { SubAgentOrchestrator } from '../subagents/orchestrator.js';
4
+ describe('SubAgent orchestrator contract', () => {
5
+ it('uses fixed role identity, persists session cursor and recovers without creating an agent', async () => {
6
+ const calls = [];
7
+ const port = {
8
+ start: async () => { calls.push('start'); },
9
+ send: async (value) => value,
10
+ interrupt: async () => { calls.push('interrupt'); },
11
+ recover: async () => { calls.push('recover'); return { sessionId: 's1', caseId: 'case', role: 'designer', agentId: 'delivery-designer', agentVersion: 'v1', permissionSnapshot: { readArtifacts: true }, status: 'running', processedUserEventIds: ['u1'], lastUserTurn: 1, budget: { toolCalls: 1 }, createdAt: 1 }; },
12
+ };
13
+ const store = new MemoryCaseStore();
14
+ const orchestrator = new SubAgentOrchestrator(port, store);
15
+ const session = await orchestrator.startSession({ caseId: 'case', role: 'designer', sessionId: 's1' });
16
+ expect(session.agentId).toBe('delivery-designer');
17
+ orchestrator.recordUserEvent('s1', { eventId: 'u1', turn: 1 });
18
+ expect(() => orchestrator.recordUserEvent('s1', { eventId: 'u1', turn: 1 })).toThrow('already');
19
+ orchestrator.recordEvent('s1', { id: 'e1', toolCall: true });
20
+ expect((await store.getSubAgentSession('s1')).lastEventId).toBe('e1');
21
+ await orchestrator.recoverSession('s1');
22
+ expect(calls).toEqual(['start', 'recover']);
23
+ });
24
+ it('deduplicates the same hand-off key and rejects a changed payload', async () => {
25
+ const port = { start: async () => undefined, send: async (value) => value, interrupt: async () => undefined, recover: async () => undefined };
26
+ const orchestrator = new SubAgentOrchestrator(port);
27
+ const handoff = { caseId: 'case', from: { role: 'designer', agentId: 'delivery-designer', version: 'v1', sessionId: 's1' }, to: { role: 'planner', agentId: 'delivery-planner', version: 'v1' }, inputRefs: ['spec'], contextReceipt: 'ctx', claims: [{ statement: 'ok', evidenceRefs: ['e1'] }], outputRefs: ['plan'], status: 'complete', idempotencyKey: 'h1' };
28
+ await orchestrator.handoff(handoff, { caseId: 'case', contextReceipt: 'ctx', inputRefs: ['spec'] });
29
+ await expect(orchestrator.handoff(handoff, { caseId: 'case', contextReceipt: 'ctx', inputRefs: ['spec'] })).resolves.toMatchObject({ status: 'duplicate' });
30
+ await expect(orchestrator.handoff({ ...handoff, outputRefs: ['other'] }, { caseId: 'case', contextReceipt: 'ctx', inputRefs: ['spec'] })).rejects.toMatchObject({ code: 'IDEMPOTENCY_CONFLICT' });
31
+ });
32
+ });
33
+ //# sourceMappingURL=subagent-orchestrator.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subagent-orchestrator.test.js","sourceRoot":"","sources":["../../src/__tests__/subagent-orchestrator.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=subagent-recovery.e2e.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subagent-recovery.e2e.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/subagent-recovery.e2e.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,35 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { consumeClaudeSession, createClaudeSessionState, mapClaudeEvent, shouldBreak } from '../subagents/claude-events.js';
3
+ import { validateAgentHandoff } from '../subagents/handoff-validator.js';
4
+ import { MemoryCaseStore } from '../persistence/memory-store.js';
5
+ import { SubAgentOrchestrator } from '../subagents/orchestrator.js';
6
+ describe('subagent recovery journey', () => {
7
+ it('replays history once and keeps tool confirmation non-terminal', () => {
8
+ const state = createClaudeSessionState();
9
+ const event = { id: 'tool-1', type: 'agent.tool_use', name: 'confirm', input: { value: 'yes' } };
10
+ expect(mapClaudeEvent(event, state)).toMatchObject({ type: 'tool_confirmation_required' });
11
+ expect(mapClaudeEvent(event, state)).toBeUndefined();
12
+ const waiting = mapClaudeEvent({ id: 'idle-1', type: 'session.status_idle', stop_reason: { type: 'requires_action' } }, state);
13
+ expect(shouldBreak(waiting)).toBe(false);
14
+ const handoff = { caseId: 'case', from: { role: 'designer', agentId: 'designer', version: 'v1', sessionId: 'session' }, to: { role: 'planner', agentId: 'planner', version: 'v1' }, inputRefs: ['spec'], contextReceipt: 'ctx', claims: [{ statement: 'scope preserved', evidenceRefs: ['evidence'] }], outputRefs: ['plan'], status: 'complete', idempotencyKey: 'handoff-1' };
15
+ expect(() => validateAgentHandoff(handoff, { caseId: 'case', contextReceipt: 'ctx', inputRefs: ['spec'] })).not.toThrow();
16
+ });
17
+ it('recovers a dropped stream without duplicating user turn or hand-off', async () => {
18
+ const state = createClaudeSessionState();
19
+ const stream = async function* () { yield { id: 'tool-2', type: 'agent.tool_use', name: 'confirm', input: {} }; };
20
+ const events = await consumeClaudeSession({ openStream: async () => stream(), kickoff: async () => { }, history: async () => [{ id: 'tool-2', type: 'agent.tool_use', name: 'confirm', input: {} }, { id: 'compact-1', type: 'compaction', marker: 'compact-1' }] }, state);
21
+ expect(events.map(event => event.type)).toEqual(['tool_confirmation_required', 'compaction']);
22
+ const store = new MemoryCaseStore();
23
+ const calls = [];
24
+ const port = { start: async () => { calls.push('start'); }, send: async (value) => value, interrupt: async () => { }, recover: async () => { calls.push('recover'); return store.getSubAgentSession('session-1'); } };
25
+ const first = new SubAgentOrchestrator(port, store);
26
+ await first.startSession({ caseId: 'case', role: 'designer', sessionId: 'session-1' });
27
+ first.recordUserEvent('session-1', { eventId: 'user-1', turn: 1 });
28
+ const restarted = new SubAgentOrchestrator(port, store);
29
+ const recovered = await restarted.recoverSession('session-1');
30
+ expect(recovered.processedUserEventIds).toEqual(['user-1']);
31
+ expect(() => restarted.recordUserEvent('session-1', { eventId: 'user-1', turn: 1 })).toThrow('already');
32
+ expect(calls).toEqual(['start', 'recover']);
33
+ });
34
+ });
35
+ //# sourceMappingURL=subagent-recovery.e2e.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subagent-recovery.e2e.test.js","sourceRoot":"","sources":["../../src/__tests__/subagent-recovery.e2e.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=task-dag.test.d.ts.map