agent-dealer 0.2.0 → 1.0.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 (276) hide show
  1. package/bundle/server/dist/adapters/agent-deck-bind.js +443 -0
  2. package/bundle/server/dist/adapters/agent-deck-bind.test.js +454 -0
  3. package/bundle/server/dist/adapters/agent-deck.js +38 -50
  4. package/bundle/server/dist/adapters/agent-deck.test.js +63 -0
  5. package/bundle/server/dist/adapters/agent-health.js +230 -33
  6. package/bundle/server/dist/adapters/agent-health.test.js +237 -0
  7. package/bundle/server/dist/adapters/authority-lifecycle.js +289 -0
  8. package/bundle/server/dist/adapters/codex-scoped-config.js +103 -0
  9. package/bundle/server/dist/adapters/execution-authority.js +103 -0
  10. package/bundle/server/dist/adapters/execution-authority.test.js +166 -0
  11. package/bundle/server/dist/adapters/external.js +0 -1
  12. package/bundle/server/dist/adapters/git-worktree.js +514 -0
  13. package/bundle/server/dist/adapters/git-worktree.test.js +502 -0
  14. package/bundle/server/dist/adapters/github.js +170 -0
  15. package/bundle/server/dist/adapters/github.test.js +205 -0
  16. package/bundle/server/dist/adapters/linear-inbox.js +199 -30
  17. package/bundle/server/dist/adapters/linear-inbox.test.js +35 -0
  18. package/bundle/server/dist/adapters/linear-sync.js +3 -101
  19. package/bundle/server/dist/adapters/managed-repo.js +168 -0
  20. package/bundle/server/dist/adapters/managed-repo.test.js +63 -0
  21. package/bundle/server/dist/adapters/outbound-delivery.js +83 -0
  22. package/bundle/server/dist/adapters/outbound-delivery.test.js +53 -0
  23. package/bundle/server/dist/adapters/reflect-authority.js +43 -0
  24. package/bundle/server/dist/cli-env.js +26 -0
  25. package/bundle/server/dist/cli-env.test.js +14 -1
  26. package/bundle/server/dist/coordinator/abort.integration.test.js +74 -0
  27. package/bundle/server/dist/coordinator/admission.js +330 -0
  28. package/bundle/server/dist/coordinator/admission.test.js +338 -0
  29. package/bundle/server/dist/coordinator/args.js +128 -0
  30. package/bundle/server/dist/coordinator/args.test.js +132 -0
  31. package/bundle/server/dist/coordinator/authority-recovery.integration.test.js +661 -0
  32. package/bundle/server/dist/coordinator/auto-merge.integration.test.js +363 -0
  33. package/bundle/server/dist/coordinator/auto-merge.js +286 -0
  34. package/bundle/server/dist/coordinator/auto-merge.timeout.test.js +14 -0
  35. package/bundle/server/dist/coordinator/branch-progress.js +81 -0
  36. package/bundle/server/dist/coordinator/clock-jump.js +98 -0
  37. package/bundle/server/dist/coordinator/commands.js +1088 -0
  38. package/bundle/server/dist/coordinator/commands.test.js +554 -0
  39. package/bundle/server/dist/coordinator/deck-outage-config.js +32 -0
  40. package/bundle/server/dist/coordinator/deck-outage.integration.test.js +200 -0
  41. package/bundle/server/dist/coordinator/dependencies.js +242 -0
  42. package/bundle/server/dist/coordinator/dependency-readiness.integration.test.js +535 -0
  43. package/bundle/server/dist/coordinator/developer-effect.js +959 -0
  44. package/bundle/server/dist/coordinator/developer-effect.test.js +1143 -0
  45. package/bundle/server/dist/coordinator/dispatcher.js +58 -0
  46. package/bundle/server/dist/coordinator/dispatcher.test.js +81 -0
  47. package/bundle/server/dist/coordinator/effect-registry.js +17 -0
  48. package/bundle/server/dist/coordinator/execution-env.integration.test.js +122 -0
  49. package/bundle/server/dist/coordinator/failure-reason.js +273 -0
  50. package/bundle/server/dist/coordinator/failure-reason.test.js +234 -0
  51. package/bundle/server/dist/coordinator/guidance.js +33 -0
  52. package/bundle/server/dist/coordinator/guidance.test.js +110 -0
  53. package/bundle/server/dist/coordinator/human-resolution.js +88 -0
  54. package/bundle/server/dist/coordinator/human-resolution.test.js +72 -0
  55. package/bundle/server/dist/coordinator/intent-forecast.js +13 -0
  56. package/bundle/server/dist/coordinator/intent-forecast.test.js +34 -0
  57. package/bundle/server/dist/coordinator/latest-failure.js +100 -0
  58. package/bundle/server/dist/coordinator/latest-failure.test.js +125 -0
  59. package/bundle/server/dist/coordinator/metrics.js +35 -0
  60. package/bundle/server/dist/coordinator/metrics.test.js +52 -0
  61. package/bundle/server/dist/coordinator/permissions.js +127 -0
  62. package/bundle/server/dist/coordinator/permissions.test.js +184 -0
  63. package/bundle/server/dist/coordinator/presumed-dead-budget.integration.test.js +146 -0
  64. package/bundle/server/dist/coordinator/process-liveness.js +191 -0
  65. package/bundle/server/dist/coordinator/profile-snapshot.js +20 -0
  66. package/bundle/server/dist/coordinator/profile-snapshot.test.js +118 -0
  67. package/bundle/server/dist/coordinator/projection.js +147 -0
  68. package/bundle/server/dist/coordinator/projection.test.js +121 -0
  69. package/bundle/server/dist/coordinator/prompts.js +164 -0
  70. package/bundle/server/dist/coordinator/prompts.test.js +177 -0
  71. package/bundle/server/dist/coordinator/queue-by-default.integration.test.js +271 -0
  72. package/bundle/server/dist/coordinator/reclaim-republish.integration.test.js +372 -0
  73. package/bundle/server/dist/coordinator/recovery.js +466 -0
  74. package/bundle/server/dist/coordinator/recovery.test.js +164 -0
  75. package/bundle/server/dist/coordinator/reflect-trigger.js +218 -0
  76. package/bundle/server/dist/coordinator/reflect-trigger.test.js +324 -0
  77. package/bundle/server/dist/coordinator/repair-cycle.integration.test.js +234 -0
  78. package/bundle/server/dist/coordinator/restart-recovery.integration.test.js +363 -0
  79. package/bundle/server/dist/coordinator/reviewer-effect.js +507 -0
  80. package/bundle/server/dist/coordinator/reviewer-effect.test.js +612 -0
  81. package/bundle/server/dist/coordinator/reviewer-result.js +38 -0
  82. package/bundle/server/dist/coordinator/reviewer-result.test.js +45 -0
  83. package/bundle/server/dist/coordinator/routing.js +160 -0
  84. package/bundle/server/dist/coordinator/routing.test.js +336 -0
  85. package/bundle/server/dist/coordinator/session-lifecycle.integration.test.js +127 -0
  86. package/bundle/server/dist/coordinator/session-lifecycle.js +280 -0
  87. package/bundle/server/dist/coordinator/session-lifecycle.test.js +121 -0
  88. package/bundle/server/dist/coordinator/session-progress.js +351 -0
  89. package/bundle/server/dist/coordinator/session-progress.test.js +190 -0
  90. package/bundle/server/dist/coordinator/session-timeouts.js +33 -0
  91. package/bundle/server/dist/coordinator/sleep-recovery.integration.test.js +233 -0
  92. package/bundle/server/dist/coordinator/spawn.js +49 -0
  93. package/bundle/server/dist/coordinator/sync-issue-base-branch.js +18 -0
  94. package/bundle/server/dist/coordinator/usage-cap-config.js +10 -0
  95. package/bundle/server/dist/coordinator/usage-cap-defer.js +189 -0
  96. package/bundle/server/dist/coordinator/usage-cap.integration.test.js +193 -0
  97. package/bundle/server/dist/coordinator/usage.js +40 -0
  98. package/bundle/server/dist/coordinator/usage.test.js +69 -0
  99. package/bundle/server/dist/coordinator/verification-receipt.js +333 -0
  100. package/bundle/server/dist/coordinator/verification-receipt.test.js +382 -0
  101. package/bundle/server/dist/coordinator/worker-loop.js +363 -0
  102. package/bundle/server/dist/coordinator/worker-loop.test.js +327 -0
  103. package/bundle/server/dist/coordinator/workflow-instances.test.js +45 -0
  104. package/bundle/server/dist/coordinator/worktree-owner-liveness.js +82 -0
  105. package/bundle/server/dist/coordinator/worktree-owner-liveness.test.js +199 -0
  106. package/bundle/server/dist/db/artifacts-issue-columns.test.js +18 -0
  107. package/bundle/server/dist/db/authority-attempts-stale-at-migration.test.js +65 -0
  108. package/bundle/server/dist/db/idempotency-index-migration.test.js +57 -0
  109. package/bundle/server/dist/db/index.js +359 -7
  110. package/bundle/server/dist/db/linear-inbox-unlock.test.js +67 -0
  111. package/bundle/server/dist/db/migrate-to-issues.js +824 -0
  112. package/bundle/server/dist/db/migrate-to-issues.test.js +842 -0
  113. package/bundle/server/dist/db/schema.sql +311 -1
  114. package/bundle/server/dist/db/schema.test.js +66 -0
  115. package/bundle/server/dist/db/worker-session-pid-migration.test.js +112 -0
  116. package/bundle/server/dist/dev-review-cli-happy-path.integration.test.js +338 -0
  117. package/bundle/server/dist/direct-start-liveness.integration.test.js +350 -0
  118. package/bundle/server/dist/index.js +58 -5
  119. package/bundle/server/dist/paths.js +40 -4
  120. package/bundle/server/dist/paths.test.js +42 -0
  121. package/bundle/server/dist/pid-marker-claim-child.js +18 -0
  122. package/bundle/server/dist/pid-marker-hold-arbiter-child.js +24 -0
  123. package/bundle/server/dist/pid-marker.js +141 -0
  124. package/bundle/server/dist/pid-marker.test.js +208 -0
  125. package/bundle/server/dist/queue/approve-deliver.js +128 -28
  126. package/bundle/server/dist/queue/reflect-schedule.js +25 -0
  127. package/bundle/server/dist/queue/result-qa.js +1 -1
  128. package/bundle/server/dist/queue/send-gate.test.js +172 -36
  129. package/bundle/server/dist/repository/agents.js +55 -18
  130. package/bundle/server/dist/repository/artifacts-for-issue.js +42 -0
  131. package/bundle/server/dist/repository/artifacts-for-issue.test.js +48 -0
  132. package/bundle/server/dist/repository/artifacts.js +47 -0
  133. package/bundle/server/dist/repository/artifacts.test.js +48 -0
  134. package/bundle/server/dist/repository/authority-attempts.js +189 -0
  135. package/bundle/server/dist/repository/findings.js +70 -0
  136. package/bundle/server/dist/repository/findings.test.js +68 -0
  137. package/bundle/server/dist/repository/human-actions.js +142 -0
  138. package/bundle/server/dist/repository/human-actions.test.js +123 -0
  139. package/bundle/server/dist/repository/intake-settings.js +5 -59
  140. package/bundle/server/dist/repository/issues.js +305 -0
  141. package/bundle/server/dist/repository/issues.test.js +69 -0
  142. package/bundle/server/dist/repository/outbound-drafts.js +28 -0
  143. package/bundle/server/dist/repository/queue-entries.js +162 -0
  144. package/bundle/server/dist/repository/review-publications.js +153 -0
  145. package/bundle/server/dist/repository/review-publications.test.js +78 -0
  146. package/bundle/server/dist/repository/runs-external-id.test.js +5 -3
  147. package/bundle/server/dist/repository/runs.js +4 -211
  148. package/bundle/server/dist/repository/runtime-availability.js +54 -0
  149. package/bundle/server/dist/repository/usage-events.js +60 -0
  150. package/bundle/server/dist/repository/usage-events.test.js +56 -0
  151. package/bundle/server/dist/repository/work-items.js +305 -0
  152. package/bundle/server/dist/repository/work-items.test.js +142 -0
  153. package/bundle/server/dist/repository/worker-sessions.js +219 -0
  154. package/bundle/server/dist/repository/worker-sessions.test.js +60 -0
  155. package/bundle/server/dist/repository/workflow-events.js +193 -0
  156. package/bundle/server/dist/repository/workflow-events.test.js +89 -0
  157. package/bundle/server/dist/resolve-tsx-bin.js +19 -0
  158. package/bundle/server/dist/routes/human-actions.js +68 -0
  159. package/bundle/server/dist/routes/human-actions.test.js +284 -0
  160. package/bundle/server/dist/routes/index.js +29 -496
  161. package/bundle/server/dist/routes/issues.js +240 -0
  162. package/bundle/server/dist/routes/issues.test.js +451 -0
  163. package/bundle/server/dist/routes/queue.js +41 -0
  164. package/bundle/server/dist/routes/queue.test.js +78 -0
  165. package/bundle/server/dist/runners/claude-args.js +18 -30
  166. package/bundle/server/dist/runners/claude.js +19 -163
  167. package/bundle/server/dist/runners/claude.test.js +15 -70
  168. package/bundle/server/dist/runners/codex-args.js +59 -0
  169. package/bundle/server/dist/runners/codex-args.test.js +72 -0
  170. package/bundle/server/dist/runners/codex-jsonl.js +150 -0
  171. package/bundle/server/dist/runners/codex-jsonl.test.js +36 -0
  172. package/bundle/server/dist/runners/codex-resume.test.js +19 -0
  173. package/bundle/server/dist/runners/codex.js +46 -0
  174. package/bundle/server/dist/runners/models-codex.test.js +14 -0
  175. package/bundle/server/dist/runners/models.js +53 -1
  176. package/bundle/server/dist/runners/persist.js +4 -1
  177. package/bundle/server/dist/runners/process-registry.js +11 -0
  178. package/bundle/server/dist/runners/prompts.js +13 -280
  179. package/bundle/server/dist/runners/prompts.test.js +22 -153
  180. package/bundle/server/dist/runners/qa.js +17 -1
  181. package/bundle/server/dist/runners/reflect.js +4 -16
  182. package/bundle/server/dist/runners/spawn-cli.js +197 -0
  183. package/bundle/server/dist/runners/spawn-cli.test.js +130 -0
  184. package/bundle/server/dist/runners/stream-json.js +0 -141
  185. package/bundle/server/dist/runners/usage-cap.js +242 -0
  186. package/bundle/server/dist/runners/usage-cap.test.js +97 -0
  187. package/bundle/server/dist/server-liveness.js +37 -0
  188. package/bundle/server/dist/server-liveness.test.js +82 -0
  189. package/bundle/server/dist/static-ui.test.js +44 -0
  190. package/bundle/server/package.json +4 -2
  191. package/bundle/server/static-ui/assets/index-BTtPGYpY.js +60 -0
  192. package/bundle/server/static-ui/assets/index-BxTx4b1d.css +1 -0
  193. package/bundle/server/static-ui/index.html +2 -2
  194. package/bundle/shared/dist/agents.d.ts +262 -133
  195. package/bundle/shared/dist/agents.js +85 -19
  196. package/bundle/shared/dist/cursor-auth-health.d.ts +12 -0
  197. package/bundle/shared/dist/cursor-auth-health.js +36 -0
  198. package/bundle/shared/dist/cursor-auth-health.test.d.ts +1 -0
  199. package/bundle/shared/dist/cursor-auth-health.test.js +40 -0
  200. package/bundle/shared/dist/findings.d.ts +46 -0
  201. package/bundle/shared/dist/findings.js +17 -0
  202. package/bundle/shared/dist/findings.test.d.ts +1 -0
  203. package/bundle/shared/dist/findings.test.js +20 -0
  204. package/bundle/shared/dist/github-repo.d.ts +28 -0
  205. package/bundle/shared/dist/github-repo.js +98 -0
  206. package/bundle/shared/dist/github-repo.test.d.ts +1 -0
  207. package/bundle/shared/dist/github-repo.test.js +32 -0
  208. package/bundle/shared/dist/human-actions.d.ts +66 -0
  209. package/bundle/shared/dist/human-actions.js +51 -0
  210. package/bundle/shared/dist/human-actions.test.d.ts +1 -0
  211. package/bundle/shared/dist/human-actions.test.js +57 -0
  212. package/bundle/shared/dist/index.d.ts +567 -757
  213. package/bundle/shared/dist/index.js +22 -36
  214. package/bundle/shared/dist/issues.d.ts +385 -0
  215. package/bundle/shared/dist/issues.js +169 -0
  216. package/bundle/shared/dist/issues.test.d.ts +1 -0
  217. package/bundle/shared/dist/issues.test.js +72 -0
  218. package/bundle/shared/dist/outbound-draft.d.ts +6 -0
  219. package/bundle/shared/dist/outbound-draft.js +4 -0
  220. package/bundle/shared/dist/plan-triage.d.ts +6 -6
  221. package/bundle/shared/dist/profile-snapshot.d.ts +154 -0
  222. package/bundle/shared/dist/profile-snapshot.js +145 -0
  223. package/bundle/shared/dist/profile-snapshot.test.d.ts +1 -0
  224. package/bundle/shared/dist/profile-snapshot.test.js +67 -0
  225. package/bundle/shared/dist/queue-entries.d.ts +89 -0
  226. package/bundle/shared/dist/queue-entries.js +19 -0
  227. package/bundle/shared/dist/result-qa.d.ts +2 -2
  228. package/bundle/shared/dist/runtime-auth-health.d.ts +66 -0
  229. package/bundle/shared/dist/runtime-auth-health.js +186 -0
  230. package/bundle/shared/dist/runtime-auth-health.test.d.ts +1 -0
  231. package/bundle/shared/dist/runtime-auth-health.test.js +200 -0
  232. package/bundle/shared/dist/runtime-availability.d.ts +51 -0
  233. package/bundle/shared/dist/runtime-availability.js +13 -0
  234. package/bundle/shared/dist/runtime.d.ts +1 -1
  235. package/bundle/shared/dist/runtime.js +1 -1
  236. package/bundle/shared/dist/usage-events.d.ts +36 -0
  237. package/bundle/shared/dist/usage-events.js +13 -0
  238. package/bundle/shared/dist/worker-sessions.d.ts +140 -0
  239. package/bundle/shared/dist/worker-sessions.js +70 -0
  240. package/bundle/shared/dist/worker-sessions.test.d.ts +1 -0
  241. package/bundle/shared/dist/worker-sessions.test.js +39 -0
  242. package/bundle/shared/dist/workflow.d.ts +78 -0
  243. package/bundle/shared/dist/workflow.js +59 -0
  244. package/bundle/shared/dist/workflow.test.d.ts +1 -0
  245. package/bundle/shared/dist/workflow.test.js +61 -0
  246. package/bundle/shared/package.json +1 -1
  247. package/dist/action.d.ts +1 -0
  248. package/dist/action.js +50 -0
  249. package/dist/action.test.d.ts +1 -0
  250. package/dist/action.test.js +70 -0
  251. package/dist/agent.d.ts +1 -0
  252. package/dist/agent.js +17 -0
  253. package/dist/agent.test.d.ts +1 -0
  254. package/dist/agent.test.js +19 -0
  255. package/dist/cli-check.d.ts +7 -0
  256. package/dist/cli-check.js +40 -0
  257. package/dist/doctor.js +59 -1
  258. package/dist/http.d.ts +5 -0
  259. package/dist/http.js +19 -0
  260. package/dist/index.js +28 -0
  261. package/dist/issue.d.ts +35 -0
  262. package/dist/issue.js +143 -0
  263. package/dist/issue.test.d.ts +1 -0
  264. package/dist/issue.test.js +240 -0
  265. package/dist/lifecycle.contract.test.d.ts +1 -0
  266. package/dist/lifecycle.contract.test.js +135 -0
  267. package/dist/queue.d.ts +11 -0
  268. package/dist/queue.js +60 -0
  269. package/dist/queue.test.d.ts +1 -0
  270. package/dist/queue.test.js +46 -0
  271. package/dist/test-fetch-stub.d.ts +6 -0
  272. package/dist/test-fetch-stub.js +31 -0
  273. package/package.json +2 -1
  274. package/templates/prod.env.example +1 -1
  275. package/bundle/server/static-ui/assets/index-B_7S7xJb.js +0 -79
  276. package/bundle/server/static-ui/assets/index-QYv3zmE6.css +0 -1
@@ -0,0 +1,382 @@
1
+ // packages/server/src/coordinator/verification-receipt.test.ts
2
+ import { test } from "node:test";
3
+ import assert from "node:assert/strict";
4
+ import fs from "node:fs";
5
+ import os from "node:os";
6
+ import path from "node:path";
7
+ import { extractVerificationReceiptFromLog, formatVerificationReceiptSection, isVerificationCommand, parseVerificationReceipt, receiptForCurrentHead, receiptSupersededByFailedChecks, shouldCarryVerificationReceipt, } from "./verification-receipt.js";
8
+ const SHA = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
9
+ const SHA2 = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
10
+ function writeLog(lines) {
11
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), "dealer-receipt-"));
12
+ const logPath = path.join(dir, "session.ndjson");
13
+ fs.writeFileSync(logPath, lines.map((l) => JSON.stringify(l)).join("\n") + "\n");
14
+ return logPath;
15
+ }
16
+ test("extracts a Claude Bash green suite and pins it to headShaHint", () => {
17
+ const logPath = writeLog([
18
+ {
19
+ type: "assistant",
20
+ message: {
21
+ content: [
22
+ {
23
+ type: "tool_use",
24
+ id: "t1",
25
+ name: "Bash",
26
+ input: { command: "npm run test:unit" },
27
+ },
28
+ ],
29
+ },
30
+ },
31
+ {
32
+ type: "user",
33
+ message: {
34
+ content: [
35
+ {
36
+ type: "tool_result",
37
+ tool_use_id: "t1",
38
+ is_error: false,
39
+ content: "711/711 tests passed\n",
40
+ },
41
+ ],
42
+ },
43
+ },
44
+ ]);
45
+ const receipt = extractVerificationReceiptFromLog(logPath, { headShaHint: SHA, now: () => "2026-09-17T00:00:00.000Z" });
46
+ assert.ok(receipt);
47
+ assert.equal(receipt.headSha, SHA);
48
+ assert.equal(receipt.commands.length, 1);
49
+ assert.equal(receipt.commands[0].outcome, "passed");
50
+ assert.equal(receipt.commands[0].detail, "711/711");
51
+ assert.match(receipt.commands[0].command, /npm run test:unit/);
52
+ });
53
+ test("extracts Cursor shellToolCall completed with exitCode 0", () => {
54
+ const logPath = writeLog([
55
+ {
56
+ type: "tool_call",
57
+ subtype: "completed",
58
+ tool_call: {
59
+ shellToolCall: {
60
+ args: { command: "npm run typecheck" },
61
+ result: { exitCode: 0, stdout: "ok\n" },
62
+ },
63
+ },
64
+ },
65
+ ]);
66
+ const receipt = extractVerificationReceiptFromLog(logPath, { headShaHint: SHA });
67
+ assert.ok(receipt);
68
+ assert.equal(receipt.commands[0].outcome, "passed");
69
+ assert.match(receipt.commands[0].command, /typecheck/);
70
+ });
71
+ test("a commit after a green suite invalidates the receipt", () => {
72
+ const logPath = writeLog([
73
+ {
74
+ type: "assistant",
75
+ message: {
76
+ content: [{ type: "tool_use", id: "t1", name: "Bash", input: { command: "npm test" } }],
77
+ },
78
+ },
79
+ {
80
+ type: "user",
81
+ message: {
82
+ content: [{ type: "tool_result", tool_use_id: "t1", is_error: false, content: "ok" }],
83
+ },
84
+ },
85
+ {
86
+ type: "assistant",
87
+ message: {
88
+ content: [
89
+ {
90
+ type: "tool_use",
91
+ id: "t2",
92
+ name: "Bash",
93
+ input: { command: "git commit -m 'wip'" },
94
+ },
95
+ ],
96
+ },
97
+ },
98
+ {
99
+ type: "user",
100
+ message: {
101
+ content: [{ type: "tool_result", tool_use_id: "t2", is_error: false, content: "[main abc] wip" }],
102
+ },
103
+ },
104
+ ]);
105
+ assert.equal(extractVerificationReceiptFromLog(logPath, { headShaHint: SHA }), null);
106
+ });
107
+ test("git -c user.* commit after a green suite invalidates the receipt", () => {
108
+ const logPath = writeLog([
109
+ {
110
+ type: "assistant",
111
+ message: {
112
+ content: [{ type: "tool_use", id: "t1", name: "Bash", input: { command: "npm test" } }],
113
+ },
114
+ },
115
+ {
116
+ type: "user",
117
+ message: {
118
+ content: [{ type: "tool_result", tool_use_id: "t1", is_error: false, content: "ok" }],
119
+ },
120
+ },
121
+ {
122
+ type: "assistant",
123
+ message: {
124
+ content: [
125
+ {
126
+ type: "tool_use",
127
+ id: "t2",
128
+ name: "Bash",
129
+ input: {
130
+ command: "git -c user.email=agent@test -c user.name=Agent commit -q -m 'wip'",
131
+ },
132
+ },
133
+ ],
134
+ },
135
+ },
136
+ {
137
+ type: "user",
138
+ message: {
139
+ content: [{ type: "tool_result", tool_use_id: "t2", is_error: false, content: "[main abc] wip" }],
140
+ },
141
+ },
142
+ ]);
143
+ assert.equal(extractVerificationReceiptFromLog(logPath, { headShaHint: SHA }), null);
144
+ });
145
+ test("git-yubikey-commit after a green suite invalidates the receipt", () => {
146
+ const logPath = writeLog([
147
+ {
148
+ type: "assistant",
149
+ message: {
150
+ content: [{ type: "tool_use", id: "t1", name: "Bash", input: { command: "npm run test:unit" } }],
151
+ },
152
+ },
153
+ {
154
+ type: "user",
155
+ message: {
156
+ content: [{ type: "tool_result", tool_use_id: "t1", is_error: false, content: "# pass 10\n" }],
157
+ },
158
+ },
159
+ {
160
+ type: "assistant",
161
+ message: {
162
+ content: [
163
+ {
164
+ type: "tool_use",
165
+ id: "t2",
166
+ name: "Bash",
167
+ input: { command: "git-yubikey-commit -F /tmp/msg.txt" },
168
+ },
169
+ ],
170
+ },
171
+ },
172
+ {
173
+ type: "user",
174
+ message: {
175
+ content: [{ type: "tool_result", tool_use_id: "t2", is_error: false, content: "[main abc] signed" }],
176
+ },
177
+ },
178
+ ]);
179
+ assert.equal(extractVerificationReceiptFromLog(logPath, { headShaHint: SHA }), null);
180
+ });
181
+ test("batched parallel tool_use records every verification command and invalidates on batched commit", () => {
182
+ const logPath = writeLog([
183
+ {
184
+ type: "assistant",
185
+ message: {
186
+ content: [
187
+ { type: "tool_use", id: "t1", name: "Bash", input: { command: "npm run typecheck" } },
188
+ { type: "tool_use", id: "t2", name: "Bash", input: { command: "npm run test:unit" } },
189
+ ],
190
+ },
191
+ },
192
+ {
193
+ type: "user",
194
+ message: {
195
+ content: [
196
+ { type: "tool_result", tool_use_id: "t1", is_error: false, content: "ok" },
197
+ { type: "tool_result", tool_use_id: "t2", is_error: false, content: "# pass 836\n# fail 0\n" },
198
+ ],
199
+ },
200
+ },
201
+ ]);
202
+ const receipt = extractVerificationReceiptFromLog(logPath, { headShaHint: SHA });
203
+ assert.ok(receipt);
204
+ assert.equal(receipt.commands.length, 2);
205
+ assert.ok(receipt.commands.some((c) => /typecheck/.test(c.command)));
206
+ assert.ok(receipt.commands.some((c) => /test:unit/.test(c.command) && c.detail === "836 passed"));
207
+ const withCommit = writeLog([
208
+ {
209
+ type: "assistant",
210
+ message: {
211
+ content: [
212
+ { type: "tool_use", id: "t1", name: "Bash", input: { command: "npm run test:unit" } },
213
+ {
214
+ type: "tool_use",
215
+ id: "t2",
216
+ name: "Bash",
217
+ input: { command: "git -C . commit -m 'wip'" },
218
+ },
219
+ ],
220
+ },
221
+ },
222
+ {
223
+ type: "user",
224
+ message: {
225
+ content: [
226
+ { type: "tool_result", tool_use_id: "t1", is_error: false, content: "ok" },
227
+ { type: "tool_result", tool_use_id: "t2", is_error: false, content: "[main abc] wip" },
228
+ ],
229
+ },
230
+ },
231
+ ]);
232
+ assert.equal(extractVerificationReceiptFromLog(withCommit, { headShaHint: SHA }), null);
233
+ });
234
+ test("observed rev-parse SHA that disagrees with tip drops the receipt", () => {
235
+ const logPath = writeLog([
236
+ {
237
+ type: "assistant",
238
+ message: {
239
+ content: [
240
+ { type: "tool_use", id: "t0", name: "Bash", input: { command: "git rev-parse HEAD" } },
241
+ ],
242
+ },
243
+ },
244
+ {
245
+ type: "user",
246
+ message: {
247
+ content: [{ type: "tool_result", tool_use_id: "t0", is_error: false, content: `${SHA}\n` }],
248
+ },
249
+ },
250
+ {
251
+ type: "assistant",
252
+ message: {
253
+ content: [{ type: "tool_use", id: "t1", name: "Bash", input: { command: "npm test" } }],
254
+ },
255
+ },
256
+ {
257
+ type: "user",
258
+ message: {
259
+ content: [{ type: "tool_result", tool_use_id: "t1", is_error: false, content: "ok" }],
260
+ },
261
+ },
262
+ ]);
263
+ assert.equal(extractVerificationReceiptFromLog(logPath, { headShaHint: SHA2 }), null);
264
+ });
265
+ test("failed-only suites are not persisted as skip evidence", () => {
266
+ const logPath = writeLog([
267
+ {
268
+ type: "assistant",
269
+ message: {
270
+ content: [{ type: "tool_use", id: "t1", name: "Bash", input: { command: "npm test" } }],
271
+ },
272
+ },
273
+ {
274
+ type: "user",
275
+ message: {
276
+ content: [{ type: "tool_result", tool_use_id: "t1", is_error: true, content: "failed" }],
277
+ },
278
+ },
279
+ ]);
280
+ assert.equal(extractVerificationReceiptFromLog(logPath, { headShaHint: SHA }), null);
281
+ });
282
+ test("exploration commands that merely mention a script name are not verification", () => {
283
+ assert.equal(isVerificationCommand("rg -n typecheck package.json"), false);
284
+ assert.equal(isVerificationCommand("grep -r test:unit ."), false);
285
+ assert.equal(isVerificationCommand("cat scripts/test:ci.sh"), false);
286
+ assert.equal(isVerificationCommand("npm run test:unit"), true);
287
+ assert.equal(isVerificationCommand("cd packages/server && npm run typecheck"), true);
288
+ const logPath = writeLog([
289
+ {
290
+ type: "assistant",
291
+ message: {
292
+ content: [
293
+ { type: "tool_use", id: "t1", name: "Bash", input: { command: "rg -n typecheck package.json" } },
294
+ ],
295
+ },
296
+ },
297
+ {
298
+ type: "user",
299
+ message: {
300
+ content: [{ type: "tool_result", tool_use_id: "t1", is_error: false, content: "ok" }],
301
+ },
302
+ },
303
+ ]);
304
+ assert.equal(extractVerificationReceiptFromLog(logPath, { headShaHint: SHA }), null);
305
+ });
306
+ test("detailFromOutput prefers runner-shaped counts over bare n/m", () => {
307
+ const logPath = writeLog([
308
+ {
309
+ type: "assistant",
310
+ message: {
311
+ content: [{ type: "tool_use", id: "t1", name: "Bash", input: { command: "npm run test:unit" } }],
312
+ },
313
+ },
314
+ {
315
+ type: "user",
316
+ message: {
317
+ content: [
318
+ {
319
+ type: "tool_result",
320
+ tool_use_id: "t1",
321
+ is_error: false,
322
+ content: "coverage 9/15\n# pass 836\n# fail 0\n",
323
+ },
324
+ ],
325
+ },
326
+ },
327
+ ]);
328
+ const receipt = extractVerificationReceiptFromLog(logPath, { headShaHint: SHA });
329
+ assert.ok(receipt);
330
+ assert.equal(receipt.commands[0].detail, "836 passed");
331
+ });
332
+ test("receiptForCurrentHead drops when HEAD moved", () => {
333
+ const receipt = parseVerificationReceipt({
334
+ headSha: SHA,
335
+ commands: [{ command: "npm test", outcome: "passed" }],
336
+ recordedAt: "2026-09-17T00:00:00.000Z",
337
+ });
338
+ assert.ok(receipt);
339
+ assert.equal(receiptForCurrentHead(receipt, SHA), receipt);
340
+ assert.equal(receiptForCurrentHead(receipt, SHA2), null);
341
+ });
342
+ test("shouldCarryVerificationReceipt excludes checks_failed retries", () => {
343
+ assert.equal(shouldCarryVerificationReceipt("Developer session failed or crashed."), true);
344
+ assert.equal(shouldCarryVerificationReceipt("Developer session timed out."), true);
345
+ assert.equal(shouldCarryVerificationReceipt("Developer's PR checks failed."), false);
346
+ assert.equal(shouldCarryVerificationReceipt(undefined), false);
347
+ });
348
+ test("receiptSupersededByFailedChecks matches CI failure at the same tip SHA", () => {
349
+ const receipt = parseVerificationReceipt({
350
+ headSha: SHA,
351
+ commands: [{ command: "npm test", outcome: "passed" }],
352
+ recordedAt: "2026-09-17T00:00:00.000Z",
353
+ });
354
+ assert.equal(receiptSupersededByFailedChecks(receipt, { snapshot: "failure", headSha: SHA }), true);
355
+ assert.equal(receiptSupersededByFailedChecks(receipt, { snapshot: "failure", headSha: SHA2 }), false);
356
+ assert.equal(receiptSupersededByFailedChecks(receipt, { snapshot: "success", headSha: SHA }), false);
357
+ });
358
+ test("formatVerificationReceiptSection names the tip and keeps skip optional", () => {
359
+ const section = formatVerificationReceiptSection({
360
+ headSha: SHA,
361
+ commands: [{ command: "npm run test:unit", outcome: "passed", detail: "711/711" }],
362
+ recordedAt: "2026-09-17T00:00:00.000Z",
363
+ }).join("\n");
364
+ assert.match(section, /Prior verification receipt/);
365
+ assert.match(section, /aaaaaaaa/);
366
+ assert.match(section, /npm run test:unit.*passed \(711\/711\)/);
367
+ assert.match(section, /HEAD is unchanged/);
368
+ assert.match(section, /evidence, not an instruction to skip/i);
369
+ assert.match(section, /Do not re-run an unchanged green suite by default/);
370
+ });
371
+ test("formatVerificationReceiptSection scopes skip guidance when outcomes are mixed", () => {
372
+ const section = formatVerificationReceiptSection({
373
+ headSha: SHA,
374
+ commands: [
375
+ { command: "npm run typecheck", outcome: "passed" },
376
+ { command: "npm run test:unit", outcome: "failed" },
377
+ ],
378
+ recordedAt: "2026-09-17T00:00:00.000Z",
379
+ }).join("\n");
380
+ assert.doesNotMatch(section, /Do not re-run an unchanged green suite by default/);
381
+ assert.match(section, /Treat only commands marked passed as already green/);
382
+ });