@provos/ironcurtain 0.10.0 → 0.12.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 (660) hide show
  1. package/README.md +68 -14
  2. package/dist/auth/oauth-provider.js +1 -4
  3. package/dist/auth/oauth-provider.js.map +1 -1
  4. package/dist/cli-help.d.ts +12 -0
  5. package/dist/cli-help.js +21 -0
  6. package/dist/cli-help.js.map +1 -1
  7. package/dist/cli.js +46 -6
  8. package/dist/cli.js.map +1 -1
  9. package/dist/config/agent-model-guard.d.ts +14 -0
  10. package/dist/config/agent-model-guard.js +24 -0
  11. package/dist/config/agent-model-guard.js.map +1 -0
  12. package/dist/config/config-command.js +277 -7
  13. package/dist/config/config-command.js.map +1 -1
  14. package/dist/config/first-start.js +62 -7
  15. package/dist/config/first-start.js.map +1 -1
  16. package/dist/config/generated/compiled-policy.json +528 -161
  17. package/dist/config/generated/test-scenarios.json +4810 -170
  18. package/dist/config/generated/tool-annotations.json +34 -47
  19. package/dist/config/index.d.ts +58 -0
  20. package/dist/config/index.js +97 -15
  21. package/dist/config/index.js.map +1 -1
  22. package/dist/config/mcp-servers.json +2 -2
  23. package/dist/config/model-provider.d.ts +9 -0
  24. package/dist/config/model-provider.js +13 -0
  25. package/dist/config/model-provider.js.map +1 -1
  26. package/dist/config/paths.d.ts +217 -0
  27. package/dist/config/paths.js +363 -14
  28. package/dist/config/paths.js.map +1 -1
  29. package/dist/config/types.d.ts +7 -1
  30. package/dist/config/user-config.d.ts +101 -6
  31. package/dist/config/user-config.js +145 -31
  32. package/dist/config/user-config.js.map +1 -1
  33. package/dist/config/validate-policy-dir.d.ts +10 -0
  34. package/dist/config/validate-policy-dir.js +59 -0
  35. package/dist/config/validate-policy-dir.js.map +1 -0
  36. package/dist/cron/job-commands.js +22 -2
  37. package/dist/cron/job-commands.js.map +1 -1
  38. package/dist/cron/types.d.ts +16 -2
  39. package/dist/cron/types.js +1 -3
  40. package/dist/cron/types.js.map +1 -1
  41. package/dist/daemon/daemon-command.js +30 -7
  42. package/dist/daemon/daemon-command.js.map +1 -1
  43. package/dist/daemon/ironcurtain-daemon.d.ts +62 -1
  44. package/dist/daemon/ironcurtain-daemon.js +231 -8
  45. package/dist/daemon/ironcurtain-daemon.js.map +1 -1
  46. package/dist/daemon-client/daemon-client.d.ts +123 -0
  47. package/dist/daemon-client/daemon-client.js +324 -0
  48. package/dist/daemon-client/daemon-client.js.map +1 -0
  49. package/dist/docker/adapters/claude-code.js +194 -20
  50. package/dist/docker/adapters/claude-code.js.map +1 -1
  51. package/dist/docker/adapters/codex.d.ts +9 -0
  52. package/dist/docker/adapters/codex.js +297 -0
  53. package/dist/docker/adapters/codex.js.map +1 -0
  54. package/dist/docker/adapters/goose.d.ts +12 -11
  55. package/dist/docker/adapters/goose.js +80 -27
  56. package/dist/docker/adapters/goose.js.map +1 -1
  57. package/dist/docker/agent-adapter.d.ts +183 -5
  58. package/dist/docker/agent-adapter.js +16 -0
  59. package/dist/docker/agent-adapter.js.map +1 -1
  60. package/dist/docker/agent-registry.js +5 -0
  61. package/dist/docker/agent-registry.js.map +1 -1
  62. package/dist/docker/apple-container-manager.d.ts +49 -0
  63. package/dist/docker/apple-container-manager.js +464 -0
  64. package/dist/docker/apple-container-manager.js.map +1 -0
  65. package/dist/docker/code-mode-proxy.d.ts +38 -0
  66. package/dist/docker/code-mode-proxy.js +6 -1
  67. package/dist/docker/code-mode-proxy.js.map +1 -1
  68. package/dist/docker/container-lifecycle.d.ts +2 -2
  69. package/dist/docker/container-lifecycle.js.map +1 -1
  70. package/dist/docker/container-runtime.d.ts +29 -0
  71. package/dist/docker/container-runtime.js +76 -0
  72. package/dist/docker/container-runtime.js.map +1 -0
  73. package/dist/docker/docker-agent-session.d.ts +113 -62
  74. package/dist/docker/docker-agent-session.js +271 -294
  75. package/dist/docker/docker-agent-session.js.map +1 -1
  76. package/dist/docker/docker-infrastructure.d.ts +428 -26
  77. package/dist/docker/docker-infrastructure.js +1005 -70
  78. package/dist/docker/docker-infrastructure.js.map +1 -1
  79. package/dist/docker/docker-manager.d.ts +58 -3
  80. package/dist/docker/docker-manager.js +378 -33
  81. package/dist/docker/docker-manager.js.map +1 -1
  82. package/dist/docker/docker-probe.d.ts +45 -0
  83. package/dist/docker/docker-probe.js +86 -0
  84. package/dist/docker/docker-probe.js.map +1 -0
  85. package/dist/docker/docker-progress-sink.d.ts +43 -0
  86. package/dist/docker/docker-progress-sink.js +299 -0
  87. package/dist/docker/docker-progress-sink.js.map +1 -0
  88. package/dist/docker/mitm-proxy.d.ts +135 -1
  89. package/dist/docker/mitm-proxy.js +611 -25
  90. package/dist/docker/mitm-proxy.js.map +1 -1
  91. package/dist/docker/network-topology.d.ts +81 -0
  92. package/dist/docker/network-topology.js +160 -0
  93. package/dist/docker/network-topology.js.map +1 -0
  94. package/dist/docker/oauth-credentials.d.ts +59 -3
  95. package/dist/docker/oauth-credentials.js +181 -14
  96. package/dist/docker/oauth-credentials.js.map +1 -1
  97. package/dist/docker/oauth-token-manager.js +2 -2
  98. package/dist/docker/oauth-token-manager.js.map +1 -1
  99. package/dist/docker/orientation.d.ts +1 -1
  100. package/dist/docker/package-types.d.ts +1 -1
  101. package/dist/docker/provider-config.d.ts +78 -3
  102. package/dist/docker/provider-config.js +295 -31
  103. package/dist/docker/provider-config.js.map +1 -1
  104. package/dist/docker/provision-lock.d.ts +46 -0
  105. package/dist/docker/provision-lock.js +152 -0
  106. package/dist/docker/provision-lock.js.map +1 -0
  107. package/dist/docker/proxy-tools.d.ts +0 -2
  108. package/dist/docker/proxy-tools.js +2 -7
  109. package/dist/docker/proxy-tools.js.map +1 -1
  110. package/dist/docker/pty-session.d.ts +64 -1
  111. package/dist/docker/pty-session.js +306 -104
  112. package/dist/docker/pty-session.js.map +1 -1
  113. package/dist/docker/pty-types.d.ts +0 -2
  114. package/dist/docker/pty-types.js +0 -2
  115. package/dist/docker/pty-types.js.map +1 -1
  116. package/dist/docker/registry-proxy.d.ts +46 -0
  117. package/dist/docker/registry-proxy.js +255 -1
  118. package/dist/docker/registry-proxy.js.map +1 -1
  119. package/dist/docker/resource-limits.d.ts +134 -0
  120. package/dist/docker/resource-limits.js +203 -0
  121. package/dist/docker/resource-limits.js.map +1 -0
  122. package/dist/docker/spawn-with-idle-timeout.d.ts +39 -0
  123. package/dist/docker/spawn-with-idle-timeout.js +152 -0
  124. package/dist/docker/spawn-with-idle-timeout.js.map +1 -0
  125. package/dist/docker/sse-extractor.d.ts +51 -0
  126. package/dist/docker/sse-extractor.js +303 -0
  127. package/dist/docker/sse-extractor.js.map +1 -0
  128. package/dist/docker/token-stream-bus.d.ts +83 -0
  129. package/dist/docker/token-stream-bus.js +98 -0
  130. package/dist/docker/token-stream-bus.js.map +1 -0
  131. package/dist/docker/token-stream-types.d.ts +71 -0
  132. package/dist/docker/token-stream-types.js +8 -0
  133. package/dist/docker/token-stream-types.js.map +1 -0
  134. package/dist/docker/trajectory-capture.d.ts +79 -0
  135. package/dist/docker/trajectory-capture.js +521 -0
  136. package/dist/docker/trajectory-capture.js.map +1 -0
  137. package/dist/docker/trajectory-reassembler.d.ts +286 -0
  138. package/dist/docker/trajectory-reassembler.js +990 -0
  139. package/dist/docker/trajectory-reassembler.js.map +1 -0
  140. package/dist/docker/trajectory-tap.d.ts +100 -0
  141. package/dist/docker/trajectory-tap.js +410 -0
  142. package/dist/docker/trajectory-tap.js.map +1 -0
  143. package/dist/docker/trajectory-types.d.ts +187 -0
  144. package/dist/docker/trajectory-types.js +43 -0
  145. package/dist/docker/trajectory-types.js.map +1 -0
  146. package/dist/docker/types.d.ts +105 -12
  147. package/dist/doctor/checks.d.ts +171 -0
  148. package/dist/doctor/checks.js +598 -0
  149. package/dist/doctor/checks.js.map +1 -0
  150. package/dist/doctor/doctor-command.d.ts +29 -0
  151. package/dist/doctor/doctor-command.js +190 -0
  152. package/dist/doctor/doctor-command.js.map +1 -0
  153. package/dist/doctor/mcp-liveness.d.ts +28 -0
  154. package/dist/doctor/mcp-liveness.js +92 -0
  155. package/dist/doctor/mcp-liveness.js.map +1 -0
  156. package/dist/doctor/oauth-checks.d.ts +23 -0
  157. package/dist/doctor/oauth-checks.js +157 -0
  158. package/dist/doctor/oauth-checks.js.map +1 -0
  159. package/dist/doctor/output.d.ts +26 -0
  160. package/dist/doctor/output.js +82 -0
  161. package/dist/doctor/output.js.map +1 -0
  162. package/dist/escalation/listener-command.js +0 -3
  163. package/dist/escalation/listener-command.js.map +1 -1
  164. package/dist/event-bus/typed-event-bus.d.ts +26 -0
  165. package/dist/event-bus/typed-event-bus.js +35 -0
  166. package/dist/event-bus/typed-event-bus.js.map +1 -0
  167. package/dist/index.js +54 -21
  168. package/dist/index.js.map +1 -1
  169. package/dist/logger.js +31 -2
  170. package/dist/logger.js.map +1 -1
  171. package/dist/memory/auto-save.d.ts +18 -4
  172. package/dist/memory/auto-save.js +46 -6
  173. package/dist/memory/auto-save.js.map +1 -1
  174. package/dist/memory/memory-policy.d.ts +41 -0
  175. package/dist/memory/memory-policy.js +36 -0
  176. package/dist/memory/memory-policy.js.map +1 -0
  177. package/dist/mux/mux-app.d.ts +2 -0
  178. package/dist/mux/mux-app.js +1 -0
  179. package/dist/mux/mux-app.js.map +1 -1
  180. package/dist/mux/mux-command.d.ts +10 -1
  181. package/dist/mux/mux-command.js +74 -40
  182. package/dist/mux/mux-command.js.map +1 -1
  183. package/dist/mux/mux-renderer.js +2 -12
  184. package/dist/mux/mux-renderer.js.map +1 -1
  185. package/dist/mux/pty-bridge.d.ts +2 -0
  186. package/dist/mux/pty-bridge.js +3 -0
  187. package/dist/mux/pty-bridge.js.map +1 -1
  188. package/dist/mux/session-scanner.js +1 -2
  189. package/dist/mux/session-scanner.js.map +1 -1
  190. package/dist/{pipeline → observability}/llm-logger.d.ts +8 -3
  191. package/dist/{pipeline → observability}/llm-logger.js +8 -3
  192. package/dist/observability/llm-logger.js.map +1 -0
  193. package/dist/observe/observe-command.d.ts +12 -0
  194. package/dist/observe/observe-command.js +285 -0
  195. package/dist/observe/observe-command.js.map +1 -0
  196. package/dist/observe/observe-renderer.d.ts +24 -0
  197. package/dist/observe/observe-renderer.js +87 -0
  198. package/dist/observe/observe-renderer.js.map +1 -0
  199. package/dist/observe/observe-tui-rain.d.ts +42 -0
  200. package/dist/observe/observe-tui-rain.js +531 -0
  201. package/dist/observe/observe-tui-rain.js.map +1 -0
  202. package/dist/observe/observe-tui-text-panel.d.ts +46 -0
  203. package/dist/observe/observe-tui-text-panel.js +525 -0
  204. package/dist/observe/observe-tui-text-panel.js.map +1 -0
  205. package/dist/observe/observe-tui-types.d.ts +270 -0
  206. package/dist/observe/observe-tui-types.js +200 -0
  207. package/dist/observe/observe-tui-types.js.map +1 -0
  208. package/dist/observe/observe-tui-word-scorer.d.ts +143 -0
  209. package/dist/observe/observe-tui-word-scorer.js +572 -0
  210. package/dist/observe/observe-tui-word-scorer.js.map +1 -0
  211. package/dist/observe/observe-tui.d.ts +59 -0
  212. package/dist/observe/observe-tui.js +592 -0
  213. package/dist/observe/observe-tui.js.map +1 -0
  214. package/dist/persona/broad-policy-validator.d.ts +95 -0
  215. package/dist/persona/broad-policy-validator.js +171 -0
  216. package/dist/persona/broad-policy-validator.js.map +1 -0
  217. package/dist/persona/compile-persona-policy.d.ts +54 -2
  218. package/dist/persona/compile-persona-policy.js +29 -4
  219. package/dist/persona/compile-persona-policy.js.map +1 -1
  220. package/dist/persona/event-bus-progress-reporter.d.ts +57 -0
  221. package/dist/persona/event-bus-progress-reporter.js +66 -0
  222. package/dist/persona/event-bus-progress-reporter.js.map +1 -0
  223. package/dist/persona/memory-gate.d.ts +19 -0
  224. package/dist/persona/memory-gate.js +33 -0
  225. package/dist/persona/memory-gate.js.map +1 -0
  226. package/dist/persona/persona-command.js +77 -72
  227. package/dist/persona/persona-command.js.map +1 -1
  228. package/dist/persona/persona-compile-orchestrator.d.ts +101 -0
  229. package/dist/persona/persona-compile-orchestrator.js +504 -0
  230. package/dist/persona/persona-compile-orchestrator.js.map +1 -0
  231. package/dist/persona/persona-service.d.ts +137 -0
  232. package/dist/persona/persona-service.js +263 -0
  233. package/dist/persona/persona-service.js.map +1 -0
  234. package/dist/persona/policy-mutation-audit.d.ts +109 -0
  235. package/dist/persona/policy-mutation-audit.js +179 -0
  236. package/dist/persona/policy-mutation-audit.js.map +1 -0
  237. package/dist/persona/resolve.d.ts +22 -0
  238. package/dist/persona/resolve.js +45 -2
  239. package/dist/persona/resolve.js.map +1 -1
  240. package/dist/persona/rule-delta.d.ts +30 -0
  241. package/dist/persona/rule-delta.js +82 -0
  242. package/dist/persona/rule-delta.js.map +1 -0
  243. package/dist/persona/types.d.ts +28 -0
  244. package/dist/pipeline/annotate.js +10 -2
  245. package/dist/pipeline/annotate.js.map +1 -1
  246. package/dist/pipeline/compile.js +14 -2
  247. package/dist/pipeline/compile.js.map +1 -1
  248. package/dist/pipeline/constitution-compiler.d.ts +25 -14
  249. package/dist/pipeline/constitution-compiler.js +107 -38
  250. package/dist/pipeline/constitution-compiler.js.map +1 -1
  251. package/dist/pipeline/dynamic-list-types.d.ts +3 -6
  252. package/dist/pipeline/dynamic-list-types.js +3 -16
  253. package/dist/pipeline/dynamic-list-types.js.map +1 -1
  254. package/dist/pipeline/pipeline-runner.d.ts +63 -4
  255. package/dist/pipeline/pipeline-runner.js +242 -94
  256. package/dist/pipeline/pipeline-runner.js.map +1 -1
  257. package/dist/pipeline/pipeline-shared.d.ts +33 -4
  258. package/dist/pipeline/pipeline-shared.js +79 -19
  259. package/dist/pipeline/pipeline-shared.js.map +1 -1
  260. package/dist/pipeline/policy-verifier.js +16 -3
  261. package/dist/pipeline/policy-verifier.js.map +1 -1
  262. package/dist/pipeline/proxy-mcp-connections.js +17 -2
  263. package/dist/pipeline/proxy-mcp-connections.js.map +1 -1
  264. package/dist/pipeline/refresh-lists.js +3 -5
  265. package/dist/pipeline/refresh-lists.js.map +1 -1
  266. package/dist/pipeline/scenario-generator.d.ts +4 -8
  267. package/dist/pipeline/scenario-generator.js +13 -46
  268. package/dist/pipeline/scenario-generator.js.map +1 -1
  269. package/dist/sandbox/index.d.ts +29 -0
  270. package/dist/sandbox/index.js +351 -119
  271. package/dist/sandbox/index.js.map +1 -1
  272. package/dist/sandbox/ironcurtain-protocol.d.ts +52 -0
  273. package/dist/sandbox/ironcurtain-protocol.js +185 -0
  274. package/dist/sandbox/ironcurtain-protocol.js.map +1 -0
  275. package/dist/session/agent-session.d.ts +6 -0
  276. package/dist/session/agent-session.js +16 -5
  277. package/dist/session/agent-session.js.map +1 -1
  278. package/dist/session/cli-transport.js +0 -6
  279. package/dist/session/cli-transport.js.map +1 -1
  280. package/dist/session/index.d.ts +35 -14
  281. package/dist/session/index.js +333 -130
  282. package/dist/session/index.js.map +1 -1
  283. package/dist/session/preflight.d.ts +21 -13
  284. package/dist/session/preflight.js +227 -88
  285. package/dist/session/preflight.js.map +1 -1
  286. package/dist/session/prompts.d.ts +2 -5
  287. package/dist/session/prompts.js.map +1 -1
  288. package/dist/session/session-manager.d.ts +61 -6
  289. package/dist/session/session-manager.js +72 -10
  290. package/dist/session/session-manager.js.map +1 -1
  291. package/dist/session/session-metadata.d.ts +21 -5
  292. package/dist/session/session-metadata.js +34 -12
  293. package/dist/session/session-metadata.js.map +1 -1
  294. package/dist/session/types.d.ts +320 -5
  295. package/dist/session/types.js +57 -1
  296. package/dist/session/types.js.map +1 -1
  297. package/dist/signal/bot-command.js +6 -1
  298. package/dist/signal/bot-command.js.map +1 -1
  299. package/dist/signal/setup-signal.js +13 -21
  300. package/dist/signal/setup-signal.js.map +1 -1
  301. package/dist/signal/signal-bot-daemon.d.ts +16 -1
  302. package/dist/signal/signal-bot-daemon.js +39 -27
  303. package/dist/signal/signal-bot-daemon.js.map +1 -1
  304. package/dist/signal/signal-container.d.ts +3 -3
  305. package/dist/signal/signal-container.js +9 -3
  306. package/dist/signal/signal-container.js.map +1 -1
  307. package/dist/skills/discovery.d.ts +77 -0
  308. package/dist/skills/discovery.js +206 -0
  309. package/dist/skills/discovery.js.map +1 -0
  310. package/dist/skills/index.d.ts +4 -0
  311. package/dist/skills/index.js +3 -0
  312. package/dist/skills/index.js.map +1 -0
  313. package/dist/skills/staging.d.ts +28 -0
  314. package/dist/skills/staging.js +68 -0
  315. package/dist/skills/staging.js.map +1 -0
  316. package/dist/skills/types.d.ts +46 -0
  317. package/dist/skills/types.js +2 -0
  318. package/dist/skills/types.js.map +1 -0
  319. package/dist/trusted-process/approval-whitelist.d.ts +0 -2
  320. package/dist/trusted-process/approval-whitelist.js +17 -17
  321. package/dist/trusted-process/approval-whitelist.js.map +1 -1
  322. package/dist/trusted-process/async-mutex.d.ts +32 -0
  323. package/dist/trusted-process/async-mutex.js +50 -0
  324. package/dist/trusted-process/async-mutex.js.map +1 -0
  325. package/dist/trusted-process/audit-log.d.ts +38 -1
  326. package/dist/trusted-process/audit-log.js +82 -13
  327. package/dist/trusted-process/audit-log.js.map +1 -1
  328. package/dist/trusted-process/call-circuit-breaker.d.ts +6 -0
  329. package/dist/trusted-process/call-circuit-breaker.js +23 -3
  330. package/dist/trusted-process/call-circuit-breaker.js.map +1 -1
  331. package/dist/trusted-process/container-command.d.ts +35 -0
  332. package/dist/trusted-process/container-command.js +49 -0
  333. package/dist/trusted-process/container-command.js.map +1 -0
  334. package/dist/trusted-process/control-server.d.ts +101 -0
  335. package/dist/trusted-process/control-server.js +282 -0
  336. package/dist/trusted-process/control-server.js.map +1 -0
  337. package/dist/trusted-process/domain-utils.d.ts +6 -6
  338. package/dist/trusted-process/domain-utils.js +7 -23
  339. package/dist/trusted-process/domain-utils.js.map +1 -1
  340. package/dist/trusted-process/error-prefixes.d.ts +16 -0
  341. package/dist/trusted-process/error-prefixes.js +17 -0
  342. package/dist/trusted-process/error-prefixes.js.map +1 -0
  343. package/dist/trusted-process/index.d.ts +40 -23
  344. package/dist/trusted-process/index.js +131 -225
  345. package/dist/trusted-process/index.js.map +1 -1
  346. package/dist/trusted-process/list-matcher.d.ts +17 -0
  347. package/dist/trusted-process/list-matcher.js +31 -0
  348. package/dist/trusted-process/list-matcher.js.map +1 -0
  349. package/dist/trusted-process/mcp-client-manager.d.ts +35 -6
  350. package/dist/trusted-process/mcp-client-manager.js +40 -45
  351. package/dist/trusted-process/mcp-client-manager.js.map +1 -1
  352. package/dist/trusted-process/mcp-proxy-server.d.ts +31 -120
  353. package/dist/trusted-process/mcp-proxy-server.js +201 -660
  354. package/dist/trusted-process/mcp-proxy-server.js.map +1 -1
  355. package/dist/trusted-process/policy-engine.d.ts +16 -4
  356. package/dist/trusted-process/policy-engine.js +103 -47
  357. package/dist/trusted-process/policy-engine.js.map +1 -1
  358. package/dist/trusted-process/policy-roots.d.ts +6 -0
  359. package/dist/trusted-process/policy-roots.js +20 -0
  360. package/dist/trusted-process/policy-roots.js.map +1 -1
  361. package/dist/trusted-process/sandbox-integration.d.ts +12 -0
  362. package/dist/trusted-process/sandbox-integration.js +18 -0
  363. package/dist/trusted-process/sandbox-integration.js.map +1 -1
  364. package/dist/trusted-process/tcp-server-transport.d.ts +12 -1
  365. package/dist/trusted-process/tcp-server-transport.js +9 -1
  366. package/dist/trusted-process/tcp-server-transport.js.map +1 -1
  367. package/dist/trusted-process/tool-call-coordinator.d.ts +283 -0
  368. package/dist/trusted-process/tool-call-coordinator.js +592 -0
  369. package/dist/trusted-process/tool-call-coordinator.js.map +1 -0
  370. package/dist/trusted-process/tool-call-pipeline.d.ts +213 -0
  371. package/dist/trusted-process/tool-call-pipeline.js +832 -0
  372. package/dist/trusted-process/tool-call-pipeline.js.map +1 -0
  373. package/dist/trusted-process/tool-description-hints.d.ts +1 -1
  374. package/dist/types/argument-roles.d.ts +1 -1
  375. package/dist/types/argument-roles.js +14 -1
  376. package/dist/types/argument-roles.js.map +1 -1
  377. package/dist/types/audit.d.ts +9 -0
  378. package/dist/{session → types}/errors.d.ts +8 -2
  379. package/dist/{session → types}/errors.js +8 -2
  380. package/dist/types/errors.js.map +1 -0
  381. package/dist/types/reserved-servers.d.ts +11 -0
  382. package/dist/types/reserved-servers.js +12 -0
  383. package/dist/types/reserved-servers.js.map +1 -0
  384. package/dist/types/server-listing.d.ts +14 -0
  385. package/dist/types/server-listing.js +2 -0
  386. package/dist/types/server-listing.js.map +1 -0
  387. package/dist/types/slug.d.ts +2 -0
  388. package/dist/types/slug.js +2 -0
  389. package/dist/types/slug.js.map +1 -1
  390. package/dist/types/url-normalize.d.ts +16 -0
  391. package/dist/types/url-normalize.js +33 -0
  392. package/dist/types/url-normalize.js.map +1 -0
  393. package/dist/util/atomic-write.d.ts +31 -0
  394. package/dist/util/atomic-write.js +43 -0
  395. package/dist/util/atomic-write.js.map +1 -0
  396. package/dist/utils/error-message.d.ts +8 -0
  397. package/dist/utils/error-message.js +11 -0
  398. package/dist/utils/error-message.js.map +1 -0
  399. package/dist/utils/exec-error.d.ts +19 -0
  400. package/dist/utils/exec-error.js +16 -0
  401. package/dist/utils/exec-error.js.map +1 -0
  402. package/dist/utils/is-plain-object.d.ts +7 -0
  403. package/dist/utils/is-plain-object.js +12 -0
  404. package/dist/utils/is-plain-object.js.map +1 -0
  405. package/dist/utils/preflight-checks.d.ts +16 -0
  406. package/dist/utils/preflight-checks.js +178 -0
  407. package/dist/utils/preflight-checks.js.map +1 -0
  408. package/dist/web-ui/__tests__/json-rpc-dispatch.test.d.ts +7 -0
  409. package/dist/web-ui/__tests__/json-rpc-dispatch.test.js +725 -0
  410. package/dist/web-ui/__tests__/json-rpc-dispatch.test.js.map +1 -0
  411. package/dist/web-ui/conn-registry.d.ts +14 -0
  412. package/dist/web-ui/conn-registry.js +25 -0
  413. package/dist/web-ui/conn-registry.js.map +1 -0
  414. package/dist/web-ui/dispatch/escalation-dispatch.d.ts +7 -0
  415. package/dist/web-ui/dispatch/escalation-dispatch.js +52 -0
  416. package/dist/web-ui/dispatch/escalation-dispatch.js.map +1 -0
  417. package/dist/web-ui/dispatch/job-dispatch.d.ts +8 -0
  418. package/dist/web-ui/dispatch/job-dispatch.js +83 -0
  419. package/dist/web-ui/dispatch/job-dispatch.js.map +1 -0
  420. package/dist/web-ui/dispatch/persona-dispatch.d.ts +50 -0
  421. package/dist/web-ui/dispatch/persona-dispatch.js +269 -0
  422. package/dist/web-ui/dispatch/persona-dispatch.js.map +1 -0
  423. package/dist/web-ui/dispatch/session-dispatch.d.ts +9 -0
  424. package/dist/web-ui/dispatch/session-dispatch.js +219 -0
  425. package/dist/web-ui/dispatch/session-dispatch.js.map +1 -0
  426. package/dist/web-ui/dispatch/token-stream-dispatch.d.ts +15 -0
  427. package/dist/web-ui/dispatch/token-stream-dispatch.js +55 -0
  428. package/dist/web-ui/dispatch/token-stream-dispatch.js.map +1 -0
  429. package/dist/web-ui/dispatch/types.d.ts +54 -0
  430. package/dist/web-ui/dispatch/types.js +72 -0
  431. package/dist/web-ui/dispatch/types.js.map +1 -0
  432. package/dist/web-ui/dispatch/workflow-dispatch.d.ts +103 -0
  433. package/dist/web-ui/dispatch/workflow-dispatch.js +1036 -0
  434. package/dist/web-ui/dispatch/workflow-dispatch.js.map +1 -0
  435. package/dist/web-ui/json-rpc-dispatch.d.ts +12 -0
  436. package/dist/web-ui/json-rpc-dispatch.js +34 -0
  437. package/dist/web-ui/json-rpc-dispatch.js.map +1 -0
  438. package/dist/web-ui/state-graph.d.ts +13 -0
  439. package/dist/web-ui/state-graph.js +82 -0
  440. package/dist/web-ui/state-graph.js.map +1 -0
  441. package/dist/web-ui/token-stream-bridge.d.ts +76 -0
  442. package/dist/web-ui/token-stream-bridge.js +265 -0
  443. package/dist/web-ui/token-stream-bridge.js.map +1 -0
  444. package/dist/web-ui/web-event-bus.d.ts +176 -0
  445. package/dist/web-ui/web-event-bus.js +19 -0
  446. package/dist/web-ui/web-event-bus.js.map +1 -0
  447. package/dist/web-ui/web-session-transport.d.ts +31 -0
  448. package/dist/web-ui/web-session-transport.js +109 -0
  449. package/dist/web-ui/web-session-transport.js.map +1 -0
  450. package/dist/web-ui/web-ui-server.d.ts +90 -0
  451. package/dist/web-ui/web-ui-server.js +477 -0
  452. package/dist/web-ui/web-ui-server.js.map +1 -0
  453. package/dist/web-ui/web-ui-types.d.ts +418 -0
  454. package/dist/web-ui/web-ui-types.js +32 -0
  455. package/dist/web-ui/web-ui-types.js.map +1 -0
  456. package/dist/web-ui/ws-utils.d.ts +5 -0
  457. package/dist/web-ui/ws-utils.js +12 -0
  458. package/dist/web-ui/ws-utils.js.map +1 -0
  459. package/dist/web-ui-static/assets/index-BD5U6ILh.css +1 -0
  460. package/dist/web-ui-static/assets/index-FMsHWEfK.js +79 -0
  461. package/dist/web-ui-static/assets/orbitron-latin-700-normal-4jsRXGGJ.woff2 +0 -0
  462. package/dist/web-ui-static/assets/orbitron-latin-700-normal-DmYOh-W9.woff +0 -0
  463. package/dist/web-ui-static/index.html +14 -0
  464. package/dist/workflow/artifacts.d.ts +59 -0
  465. package/dist/workflow/artifacts.js +188 -0
  466. package/dist/workflow/artifacts.js.map +1 -0
  467. package/dist/workflow/checkpoint-selection.d.ts +34 -0
  468. package/dist/workflow/checkpoint-selection.js +48 -0
  469. package/dist/workflow/checkpoint-selection.js.map +1 -0
  470. package/dist/workflow/checkpoint.d.ts +35 -0
  471. package/dist/workflow/checkpoint.js +84 -0
  472. package/dist/workflow/checkpoint.js.map +1 -0
  473. package/dist/workflow/cli-shared.d.ts +62 -0
  474. package/dist/workflow/cli-shared.js +117 -0
  475. package/dist/workflow/cli-shared.js.map +1 -0
  476. package/dist/workflow/cli-support.d.ts +92 -0
  477. package/dist/workflow/cli-support.js +421 -0
  478. package/dist/workflow/cli-support.js.map +1 -0
  479. package/dist/workflow/container-snapshots.d.ts +29 -0
  480. package/dist/workflow/container-snapshots.js +137 -0
  481. package/dist/workflow/container-snapshots.js.map +1 -0
  482. package/dist/workflow/daemon-gate-commands.d.ts +21 -0
  483. package/dist/workflow/daemon-gate-commands.js +599 -0
  484. package/dist/workflow/daemon-gate-commands.js.map +1 -0
  485. package/dist/workflow/definition-loader.d.ts +32 -0
  486. package/dist/workflow/definition-loader.js +56 -0
  487. package/dist/workflow/definition-loader.js.map +1 -0
  488. package/dist/workflow/discovery.d.ts +107 -0
  489. package/dist/workflow/discovery.js +263 -0
  490. package/dist/workflow/discovery.js.map +1 -0
  491. package/dist/workflow/errors.d.ts +66 -0
  492. package/dist/workflow/errors.js +49 -0
  493. package/dist/workflow/errors.js.map +1 -0
  494. package/dist/workflow/guards.d.ts +8 -0
  495. package/dist/workflow/guards.js +38 -0
  496. package/dist/workflow/guards.js.map +1 -0
  497. package/dist/workflow/lane-template.d.ts +89 -0
  498. package/dist/workflow/lane-template.js +141 -0
  499. package/dist/workflow/lane-template.js.map +1 -0
  500. package/dist/workflow/lint-integration.d.ts +64 -0
  501. package/dist/workflow/lint-integration.js +56 -0
  502. package/dist/workflow/lint-integration.js.map +1 -0
  503. package/dist/workflow/lint.d.ts +72 -0
  504. package/dist/workflow/lint.js +593 -0
  505. package/dist/workflow/lint.js.map +1 -0
  506. package/dist/workflow/machine-builder.d.ts +178 -0
  507. package/dist/workflow/machine-builder.js +678 -0
  508. package/dist/workflow/machine-builder.js.map +1 -0
  509. package/dist/workflow/message-log.d.ts +99 -0
  510. package/dist/workflow/message-log.js +40 -0
  511. package/dist/workflow/message-log.js.map +1 -0
  512. package/dist/workflow/orchestrator.d.ts +954 -0
  513. package/dist/workflow/orchestrator.js +3594 -0
  514. package/dist/workflow/orchestrator.js.map +1 -0
  515. package/dist/workflow/prompt-builder.d.ts +39 -0
  516. package/dist/workflow/prompt-builder.js +243 -0
  517. package/dist/workflow/prompt-builder.js.map +1 -0
  518. package/dist/workflow/run-state-command.d.ts +9 -0
  519. package/dist/workflow/run-state-command.js +414 -0
  520. package/dist/workflow/run-state-command.js.map +1 -0
  521. package/dist/workflow/run-state-debug-capture.d.ts +40 -0
  522. package/dist/workflow/run-state-debug-capture.js +101 -0
  523. package/dist/workflow/run-state-debug-capture.js.map +1 -0
  524. package/dist/workflow/status-parser.d.ts +68 -0
  525. package/dist/workflow/status-parser.js +274 -0
  526. package/dist/workflow/status-parser.js.map +1 -0
  527. package/dist/workflow/types.d.ts +598 -0
  528. package/dist/workflow/types.js +85 -0
  529. package/dist/workflow/types.js.map +1 -0
  530. package/dist/workflow/validate.d.ts +80 -0
  531. package/dist/workflow/validate.js +788 -0
  532. package/dist/workflow/validate.js.map +1 -0
  533. package/dist/workflow/workflow-command.d.ts +9 -0
  534. package/dist/workflow/workflow-command.js +670 -0
  535. package/dist/workflow/workflow-command.js.map +1 -0
  536. package/dist/workflow/workflow-discovery.d.ts +27 -0
  537. package/dist/workflow/workflow-discovery.js +121 -0
  538. package/dist/workflow/workflow-discovery.js.map +1 -0
  539. package/dist/workflow/workflow-manager.d.ts +156 -0
  540. package/dist/workflow/workflow-manager.js +359 -0
  541. package/dist/workflow/workflow-manager.js.map +1 -0
  542. package/dist/workflow/workflows/design-and-code/README.md +50 -0
  543. package/dist/workflow/workflows/design-and-code/workflow.yaml +169 -0
  544. package/dist/workflow/workflows/deterministic-eval-smoke/scripts/format_report.js +29 -0
  545. package/dist/workflow/workflows/deterministic-eval-smoke/scripts/package.json +5 -0
  546. package/dist/workflow/workflows/deterministic-eval-smoke/scripts/requirements.txt +1 -0
  547. package/dist/workflow/workflows/deterministic-eval-smoke/scripts/run_eval.py +41 -0
  548. package/dist/workflow/workflows/deterministic-eval-smoke/workflow.yaml +57 -0
  549. package/dist/workflow/workflows/deterministic-verdict-smoke/scripts/classify.py +18 -0
  550. package/dist/workflow/workflows/deterministic-verdict-smoke/workflow.yaml +51 -0
  551. package/dist/workflow/workflows/evolve/scripts/LICENSE +201 -0
  552. package/dist/workflow/workflows/evolve/scripts/README.md +314 -0
  553. package/dist/workflow/workflows/evolve/scripts/evolve-brief +16 -0
  554. package/dist/workflow/workflows/evolve/scripts/evolve-cognition +16 -0
  555. package/dist/workflow/workflows/evolve/scripts/evolve-db +16 -0
  556. package/dist/workflow/workflows/evolve/scripts/evolve-eval +16 -0
  557. package/dist/workflow/workflows/evolve/scripts/evolve-files +16 -0
  558. package/dist/workflow/workflows/evolve/scripts/evolve-summary +16 -0
  559. package/dist/workflow/workflows/evolve/scripts/evolve_core/__init__.py +14 -0
  560. package/dist/workflow/workflows/evolve/scripts/evolve_core/algorithms/__init__.py +17 -0
  561. package/dist/workflow/workflows/evolve/scripts/evolve_core/algorithms/base.py +21 -0
  562. package/dist/workflow/workflows/evolve/scripts/evolve_core/algorithms/factory.py +90 -0
  563. package/dist/workflow/workflows/evolve/scripts/evolve_core/algorithms/greedy.py +17 -0
  564. package/dist/workflow/workflows/evolve/scripts/evolve_core/algorithms/island.py +316 -0
  565. package/dist/workflow/workflows/evolve/scripts/evolve_core/algorithms/random.py +18 -0
  566. package/dist/workflow/workflows/evolve/scripts/evolve_core/algorithms/ucb1.py +55 -0
  567. package/dist/workflow/workflows/evolve/scripts/evolve_core/best_snapshot.py +62 -0
  568. package/dist/workflow/workflows/evolve/scripts/evolve_core/cli.py +752 -0
  569. package/dist/workflow/workflows/evolve/scripts/evolve_core/cognition.py +127 -0
  570. package/dist/workflow/workflows/evolve/scripts/evolve_core/database.py +218 -0
  571. package/dist/workflow/workflows/evolve/scripts/evolve_core/diff.py +57 -0
  572. package/dist/workflow/workflows/evolve/scripts/evolve_core/embedding.py +84 -0
  573. package/dist/workflow/workflows/evolve/scripts/evolve_core/file_lock.py +45 -0
  574. package/dist/workflow/workflows/evolve/scripts/evolve_core/run_state.py +343 -0
  575. package/dist/workflow/workflows/evolve/scripts/evolve_core/sampling_config.py +173 -0
  576. package/dist/workflow/workflows/evolve/scripts/evolve_core/structures.py +93 -0
  577. package/dist/workflow/workflows/evolve/scripts/evolve_core/vector_index.py +191 -0
  578. package/dist/workflow/workflows/evolve/scripts/evolve_result.py +1503 -0
  579. package/dist/workflow/workflows/evolve/scripts/requirements.txt +2 -0
  580. package/dist/workflow/workflows/evolve/workflow.yaml +646 -0
  581. package/dist/workflow/workflows/test-email-summary/skills/email-formatting/SKILL.md +55 -0
  582. package/dist/workflow/workflows/test-email-summary/skills/gmail-query-syntax/SKILL.md +63 -0
  583. package/dist/workflow/workflows/test-email-summary/skills/json-schema-emails/SKILL.md +41 -0
  584. package/dist/workflow/workflows/test-email-summary/skills/json-schema-emails/schema.json +34 -0
  585. package/dist/workflow/workflows/test-email-summary/skills/summary-style-guide/SKILL.md +51 -0
  586. package/dist/workflow/workflows/test-email-summary/workflow.yaml +90 -0
  587. package/dist/workflow/workflows/vuln-discovery/skills/harness-design-fuzzing/SKILL.md +206 -0
  588. package/dist/workflow/workflows/vuln-discovery/skills/memory-safety-c-cpp/SKILL.md +167 -0
  589. package/dist/workflow/workflows/vuln-discovery/skills/vulnerability-triage/SKILL.md +189 -0
  590. package/dist/workflow/workflows/vuln-discovery/workflow.yaml +925 -0
  591. package/dist/workflow/worktree.d.ts +43 -0
  592. package/dist/workflow/worktree.js +106 -0
  593. package/dist/workflow/worktree.js.map +1 -0
  594. package/docker/Dockerfile.base +31 -0
  595. package/docker/Dockerfile.base.arm64 +27 -0
  596. package/docker/Dockerfile.claude-code +3 -1
  597. package/docker/Dockerfile.codex +17 -0
  598. package/docker/Dockerfile.goose +3 -1
  599. package/docker/entrypoint-claude-code.sh +7 -0
  600. package/docker/entrypoint-codex.sh +42 -0
  601. package/docker/entrypoint-goose.sh +6 -0
  602. package/docker/entrypoint-uid-remap.sh +93 -0
  603. package/package.json +27 -11
  604. package/packages/web-ui/README.md +113 -0
  605. package/packages/web-ui/docs/workflow-state-machine.png +0 -0
  606. package/src/config/generated/compiled-policy.json +528 -161
  607. package/src/config/generated/test-scenarios.json +4810 -170
  608. package/src/config/generated/tool-annotations.json +34 -47
  609. package/src/config/mcp-servers.json +2 -2
  610. package/src/workflow/workflows/design-and-code/README.md +50 -0
  611. package/src/workflow/workflows/design-and-code/workflow.yaml +169 -0
  612. package/src/workflow/workflows/deterministic-eval-smoke/scripts/format_report.js +29 -0
  613. package/src/workflow/workflows/deterministic-eval-smoke/scripts/package.json +5 -0
  614. package/src/workflow/workflows/deterministic-eval-smoke/scripts/requirements.txt +1 -0
  615. package/src/workflow/workflows/deterministic-eval-smoke/scripts/run_eval.py +41 -0
  616. package/src/workflow/workflows/deterministic-eval-smoke/workflow.yaml +57 -0
  617. package/src/workflow/workflows/deterministic-verdict-smoke/scripts/classify.py +18 -0
  618. package/src/workflow/workflows/deterministic-verdict-smoke/workflow.yaml +51 -0
  619. package/src/workflow/workflows/evolve/scripts/LICENSE +201 -0
  620. package/src/workflow/workflows/evolve/scripts/README.md +314 -0
  621. package/src/workflow/workflows/evolve/scripts/evolve-brief +16 -0
  622. package/src/workflow/workflows/evolve/scripts/evolve-cognition +16 -0
  623. package/src/workflow/workflows/evolve/scripts/evolve-db +16 -0
  624. package/src/workflow/workflows/evolve/scripts/evolve-eval +16 -0
  625. package/src/workflow/workflows/evolve/scripts/evolve-files +16 -0
  626. package/src/workflow/workflows/evolve/scripts/evolve-summary +16 -0
  627. package/src/workflow/workflows/evolve/scripts/evolve_core/__init__.py +14 -0
  628. package/src/workflow/workflows/evolve/scripts/evolve_core/algorithms/__init__.py +17 -0
  629. package/src/workflow/workflows/evolve/scripts/evolve_core/algorithms/base.py +21 -0
  630. package/src/workflow/workflows/evolve/scripts/evolve_core/algorithms/factory.py +90 -0
  631. package/src/workflow/workflows/evolve/scripts/evolve_core/algorithms/greedy.py +17 -0
  632. package/src/workflow/workflows/evolve/scripts/evolve_core/algorithms/island.py +316 -0
  633. package/src/workflow/workflows/evolve/scripts/evolve_core/algorithms/random.py +18 -0
  634. package/src/workflow/workflows/evolve/scripts/evolve_core/algorithms/ucb1.py +55 -0
  635. package/src/workflow/workflows/evolve/scripts/evolve_core/best_snapshot.py +62 -0
  636. package/src/workflow/workflows/evolve/scripts/evolve_core/cli.py +752 -0
  637. package/src/workflow/workflows/evolve/scripts/evolve_core/cognition.py +127 -0
  638. package/src/workflow/workflows/evolve/scripts/evolve_core/database.py +218 -0
  639. package/src/workflow/workflows/evolve/scripts/evolve_core/diff.py +57 -0
  640. package/src/workflow/workflows/evolve/scripts/evolve_core/embedding.py +84 -0
  641. package/src/workflow/workflows/evolve/scripts/evolve_core/file_lock.py +45 -0
  642. package/src/workflow/workflows/evolve/scripts/evolve_core/run_state.py +343 -0
  643. package/src/workflow/workflows/evolve/scripts/evolve_core/sampling_config.py +173 -0
  644. package/src/workflow/workflows/evolve/scripts/evolve_core/structures.py +93 -0
  645. package/src/workflow/workflows/evolve/scripts/evolve_core/vector_index.py +191 -0
  646. package/src/workflow/workflows/evolve/scripts/evolve_result.py +1503 -0
  647. package/src/workflow/workflows/evolve/scripts/requirements.txt +2 -0
  648. package/src/workflow/workflows/evolve/workflow.yaml +646 -0
  649. package/src/workflow/workflows/test-email-summary/skills/email-formatting/SKILL.md +55 -0
  650. package/src/workflow/workflows/test-email-summary/skills/gmail-query-syntax/SKILL.md +63 -0
  651. package/src/workflow/workflows/test-email-summary/skills/json-schema-emails/SKILL.md +41 -0
  652. package/src/workflow/workflows/test-email-summary/skills/json-schema-emails/schema.json +34 -0
  653. package/src/workflow/workflows/test-email-summary/skills/summary-style-guide/SKILL.md +51 -0
  654. package/src/workflow/workflows/test-email-summary/workflow.yaml +90 -0
  655. package/src/workflow/workflows/vuln-discovery/skills/harness-design-fuzzing/SKILL.md +206 -0
  656. package/src/workflow/workflows/vuln-discovery/skills/memory-safety-c-cpp/SKILL.md +167 -0
  657. package/src/workflow/workflows/vuln-discovery/skills/vulnerability-triage/SKILL.md +189 -0
  658. package/src/workflow/workflows/vuln-discovery/workflow.yaml +925 -0
  659. package/dist/pipeline/llm-logger.js.map +0 -1
  660. package/dist/session/errors.js.map +0 -1
@@ -0,0 +1,925 @@
1
+ name: vuln-discovery
2
+ description: 'Orchestrator-driven vulnerability discovery: analyze code, build tiered harnesses, and confirm vulnerabilities through execution'
3
+ initial: orchestrator
4
+
5
+ settings:
6
+ mode: docker
7
+ dockerAgent: claude-code
8
+ sharedContainer: true
9
+ model: anthropic:claude-opus-4-8
10
+ maxRounds: 12
11
+ maxSessionSeconds: 10800
12
+ unversionedArtifacts:
13
+ - journal
14
+ systemPrompt: |
15
+ Workflow artifacts go in `.workflow/`. The harness and all build modifications go directly in the workspace.
16
+
17
+ IMPORTANT: The task description is the source of truth for:
18
+ - **Scope**: what code is being investigated (a file, a directory, a module, a subsystem)
19
+ - **Goal**: what kind of vulnerability to find (memory corruption, local privilege escalation, auth bypass, logic flaw, info disclosure, etc.)
20
+ - **Threat model**: who the attacker is and how inputs reach the code (external network, local user, co-tenant, malicious file, etc.)
21
+
22
+ Every agent must re-read the task description and let it override defaults in these prompts. Where a prompt says "target file" or "external entry point," substitute what the task actually specifies. Where a prompt says "externally exploitable vulnerability," substitute the vulnerability class and threat model from the task.
23
+
24
+ IMPORTANT: The investigation journal at `.workflow/journal/journal.md` is the source of truth for what has been tried, what worked, and what failed. Read it before starting any work. Only the orchestrator updates it.
25
+
26
+ states:
27
+ analyze:
28
+ type: agent
29
+ description: 'Structural code analysis — maps call chains, entry points, and build system'
30
+ persona: global
31
+ skills:
32
+ - memory-safety-c-cpp
33
+ prompt: |
34
+ You are a code analyst mapping the structure of code in scope for a vulnerability investigation. Read the task description first — it defines the scope (file, directory, module, subsystem) and the threat model (who can reach this code and how). You have access to a source tree at the workspace root.
35
+
36
+ **First action**: if a "Scoping from the previous agent" section is present at the top of this message, read its Directive and Notes subsections — they tell you what specifically to focus on this round (e.g., "prior analysis missed X, look deeper at Y"). If the scoping section is absent on cold start that is expected; otherwise, if it is missing or vague, STOP and report back with an `escalate`-style agent_status rather than improvising from the task description alone.
37
+
38
+ Your job is STRUCTURAL ANALYSIS. Map the code's architecture, data flow, and assumptions. Do NOT claim specific code paths are vulnerable, propose exploits, or assess severity — later agents handle that with execution-based tooling.
39
+
40
+ 1. **Code role and data flow.** Read the in-scope code. What does it do? What data does it process? What trust boundaries does it sit on? Where does input from the attacker (as defined by the task's threat model) enter?
41
+
42
+ 2. **Call graph to attacker-reachable entry points.** Trace backward from the in-scope functions: what calls them? Keep tracing until you reach an entry point the attacker can reach under the task's threat model (network socket, file input, IPC, CLI argument, HTTP handler, setuid binary, shared library boundary, etc.). Document every call chain from attacker-controlled input to the in-scope code.
43
+
44
+ 3. **Language, build system, and tooling.** What language and build system? What coverage instrumentation is available? What sanitizers or runtime checkers apply?
45
+
46
+ 4. **Existing test infrastructure.** Are there tests, fuzzers, or harnesses that exercise this code? What do they cover and what do they miss?
47
+
48
+ 5. **Protocol and interaction sequences.** What multi-step interactions are required to reach the target code (handshakes, authentication, session setup, state machine transitions)?
49
+
50
+ 6. **Function catalog with assumption inventory.** Every function reachable from the entry-point call graph (mapped in item 2) must be accounted for in writing — either with a full entry below or a one-line "examined and dismissed because <reason>" note. Silent omission is forbidden; functions raised in the call graph but missing from the catalog are implicit blind spots. Dispatch-family variants (per-bps, per-codec, per-message-type, per-mode) each get their own entry — "same as variant X" is not a sufficient dismissal because variants commonly have differing per-element arithmetic. For each function with a full entry, document:
51
+ - Signature, parameters, and return type
52
+ - Data types used — record details that matter for the bug-class vocabulary the surface admits (for native C/C++ memory safety, that means type widths, signed/unsigned distinctions, casts between types, and any macros or wrappers around comparisons or arithmetic; consult the relevant surface skill for the canonical list).
53
+ - Stated invariants: assertions, comments like "should never happen," documented preconditions
54
+ - Unstated assumptions: values that are used without validation, conditions that are assumed true but never checked, state that is expected to be non-null or within bounds without guards
55
+ - Where external input reaches this function and what validation (if any) occurs before it arrives
56
+
57
+ 7. **Cross-cutting data flow analysis.** After completing the function catalog, trace bug-class patterns ACROSS function boundaries. The most important bugs live at the intersection of separately-correct functions. The patterns themselves — what bug classes apply, what arithmetic / lifetime / dispatch shapes to look for — are surface-specific; consult the relevant skill for the canonical taxonomy and the high-yield search patterns. For native C/C++ memory safety, see the `memory-safety-c-cpp` skill.
58
+
59
+ Apply every pattern from the surface skill that plausibly fits the code — do not stop at the first match. Surfaces that admit multiple bug classes deserve documented findings across each. Tunnel-visioning on one bug class is the most common failure mode of this state; if the skill names five patterns and you only walked the code for one, the analysis is incomplete.
60
+
61
+ For each pattern instance found, document: the source location (file:line), the data flow path, the types involved, the boundary value where the pattern activates, the bug-class identifier from the surface skill's taxonomy, and a **Reachability sketch** — a short trace from attacker-controlled input to the pattern site naming each guard between (file:line of the guard, what it checks, what value range it admits). If prior journal rounds have evidence that a guard along the path is impassable or has been bypassed, note that. Reachability is structural, not dynamic — do not validate by execution; just document what the code shows. Do not assess exploitability beyond reachability; that is downstream agents' job.
62
+
63
+ The function catalog and cross-cutting analysis are the most important outputs. They give downstream agents the raw material to form hypotheses. Focus on precision: exact type names, exact macro definitions, exact check conditions. Do not editorialize about what might be exploitable.
64
+
65
+ Write your analysis to `.workflow/analysis/analysis.md`.
66
+ inputs: []
67
+ outputs:
68
+ - analysis
69
+ transitions:
70
+ - to: orchestrator
71
+
72
+ orchestrator:
73
+ type: agent
74
+ description: 'Investigation strategy and routing — decides what to do next'
75
+ persona: global
76
+ skills: none
77
+ prompt: |
78
+ You are the investigation orchestrator for a vulnerability discovery workflow. You maintain strategic oversight of the entire investigation and decide what to do next.
79
+
80
+ ## Your role: ROUTER (not analyst)
81
+
82
+ Your job is to read the journal and artifacts produced by other agents, assess progress, and decide which agent to dispatch next. You are NOT the analyst, and you do NOT write code. You MAY read a few source files when it materially changes a routing decision — validating a quoted claim, locating a function the directive will name, or disambiguating between routing alternatives. If you need a structural read of the code, that's `analyze`'s job — route there with a `reanalyze` verdict.
83
+
84
+ ## Cold start
85
+
86
+ If the journal at `.workflow/journal/journal.md` does not exist, this is a cold start: create the journal with the initial header and set verdict to `reanalyze`. Your directive to analyze should be minimal — just name the target code from the task description. Don't re-specify what analysis to do; analyze's prompt covers that.
87
+
88
+ ## Output format
89
+
90
+ Structure your response as follows:
91
+
92
+ 1. **Directive for next agent** (FIRST in your output, REQUIRED): A clear, actionable instruction for whichever agent you are dispatching to. This section is what scopes the next agent's round — it is forwarded verbatim into the next agent's prompt as the authoritative "Directive" subsection. Be specific: tell it exactly what target to examine, what hypothesis to test, what tier to build at, and WHY. Aim for 1,500–4,000 characters of concrete scoping, not a one-liner.
93
+
94
+ **The directive is REQUIRED.** If you cannot write a concrete, specific directive — if the best you can produce is "continue investigating" or "pick something reasonable" — the investigation is stalled. Set verdict to `escalate` and in the escalation describe what's unclear: hypothesis space feels exhausted, prior evidence is contradictory, need the human to pick a direction. Do NOT route to the next agent with a weak directive.
95
+
96
+ 2. **Journal update** (SECOND): Your reasoning about the investigation state. This provides context but the directive above is what matters to the next agent.
97
+
98
+ 3. **Status block** (LAST): The agent_status YAML block with your verdict.
99
+
100
+ The `notes` field inside the status block is a **1–2 sentence summary of your directive**, intended for journal readability. It is NOT a substitute for the directive — it is forwarded as a short "Notes" subsection alongside the directive, not instead of it. If you find yourself trying to pack real scoping content into `notes` because you didn't write a proper directive, that's the signal to either write a full directive or escalate.
101
+
102
+ ## Standard procedure
103
+
104
+ On every invocation after cold start, FIRST read the investigation journal at `.workflow/journal/journal.md`. Then read any new artifacts from the state that just completed.
105
+
106
+ ## Journal format
107
+
108
+ The journal is a markdown file at `.workflow/journal/journal.md`. It has two parts:
109
+
110
+ **Status header** (lines 1-10, updated in-place each round):
111
+ ```
112
+ # Investigation Journal
113
+ ## Status
114
+ - **Phase**: [analysis | hypothesis testing | harness building | live discovery | triage]
115
+ - **Active hypothesis**: [current hypothesis or "none"]
116
+ - **Round**: [N]
117
+ - **Blocker**: [current blocker or "none"]
118
+ ```
119
+
120
+ **Append-only log** (everything after the header, never edited, only appended):
121
+ ```
122
+ ## Round N — Post [state_name]
123
+ ### Evidence
124
+ - **Artifacts written**: [list of file paths the completed agent wrote or modified]
125
+ - **Key findings**: [specific facts from the agent's output — what was tested, what values were used, what code paths were hit, what mitigations were found and where]
126
+ - **Harness tier**: [tier N, or N/A if not a harness state]
127
+ - **Hypothesis scope**: [single-function | cross-component (N functions) | full-system — describes how many components the current hypothesis involves]
128
+ ### Assessment
129
+ [your interpretation of the evidence — why the results support or refute the hypothesis, what gaps remain]
130
+ ### Decision
131
+ Verdict: [verdict]
132
+ Directive: [what the next agent should focus on]
133
+ ```
134
+
135
+ **What to capture in Evidence.** Extract specific details from the completed agent's artifacts — functions tested, input values, coverage observed, mitigations cited (with source locations), error messages. A good evidence section lets someone reconstruct WHY the agent reached its verdict without re-reading the full artifact; a bad one says "the agent found it was mitigated."
136
+
137
+ On each invocation: update the status header in-place, then APPEND a new round section. Never edit or delete previous round sections — they are the investigation's permanent record.
138
+
139
+ Every downstream state has its own detailed prompt covering its methodology. Your directive must NOT re-specify that methodology; it should supply what is SPECIFIC to this invocation — target, hypothesis, focus area, human feedback, and WHY this state is being routed to now.
140
+
141
+ ## Routing decisions
142
+
143
+ One subsection per state the orchestrator can dispatch to. `harness_build` and `harness_validate` are NOT listed — the orchestrator does not route to them directly; they run inside the harness pipeline and hand control back to the orchestrator only after `harness_validate` approves.
144
+
145
+ ### `analyze` — verdict: `reanalyze`
146
+
147
+ - **When:** Need the structural analysis expanded.
148
+ - **Directive must supply:** Target code path, any specific focus (e.g., "the initial analysis missed X, look deeper at Y").
149
+
150
+ ### `harness_design` — verdict: `harness_design`
151
+
152
+ - **When:** Build or upgrade a harness. Directive MUST specify the tier (see Tiered harness strategy below) and the design class (see Design class below).
153
+ - **Directive must supply:** Tier, design class (trigger-driven OR coverage-driven), the target (a hypothesis with named violation site for trigger-driven; a named under-exercised code region with dispatch axes for coverage-driven), component scope, WHY this tier and class. Do NOT prescribe specific test cases, fixed input values, or enumerated scenarios (T1, T2, T3...) — that is `harness_design`'s job. Trust the downstream agent to pick the sweep strategy given what you describe.
154
+ - **Bundling multiple hypotheses.** A trigger-driven directive may scope a single hypothesis or a related set when they share input space and code region. Bundle when consolidating doesn't dilute the sweep (e.g., three integer-overflow hypotheses on the same allocator → one harness). Keep separate when input distributions conflict (one wants huge dimensions, another wants small) or observation points differ enough that one harness can't see both.
155
+ - **Audit on return** (after `harness_validate` approves and control comes back to the orchestrator): confirm validation showed (a) the target function reached with non-zero coverage, AND (b) the fuzzer's self-reported feedback metric (e.g., libFuzzer `cov:` / AFL++ edge count / the stack's equivalent) recorded in `validation.md` passed the fuzzer-feedback-reach threshold of `harness_validate`. If (b) is absent from an `approved` validation report, that is a prompt-conformance failure in `harness_validate` itself — do NOT bounce back to it. The state's own prompt forbids approval without (b), so repeat invocations are unlikely to self-correct, and `harness_validate`'s approved transition is declared before the visit-cap escalation guard, so the guard never fires on approved verdicts. Instead, set verdict `escalate` and route to `human_escalation` so a human can diagnose whether the issue is a tool-output-parsing edge case, a model quirk, or a missing field in the design that validate glossed over.
156
+
157
+ ### `discover` — verdict: `discover`
158
+
159
+ - **When:** Run hypothesis-driven research using an existing working harness.
160
+ - **Directive must supply:** The hypothesis to test and any specific trigger conditions from prior rounds.
161
+ - **Audit on return** depends on the verdict discover produced:
162
+
163
+ **If discover returned `approved` (confirmed):** route to `triage` if the evidence is solid, or back to `discover` for more evidence if it is thin.
164
+
165
+ **If discover returned `blocked` (mitigated or exhausted):** the "mitigated" claim is authoritative only when ALL of:
166
+ - The tier matched the hypothesis scope — cross-component hypotheses require Tier 2+.
167
+ - Evidence shows execution, not reasoning: coverage of the mitigation code AND a completed input sweep over the hypothesis's value range (fuzzing for value-dependent claims, bounded enumeration for discrete claims).
168
+ - The mitigation was exercised, not just present. If non-impact is attributed to an invariant the harness had to disable to reach the site (a concurrency/serialization contract, an upstream guard, a runtime/allocator check, or a first-abort that shadows a later sink), the claim holds only if discover ran the trigger under a configuration where that invariant is present and showed the effect absorbed there. An invariant-only mitigation backed by source reading is the masked-finding pattern — non-terminal.
169
+
170
+ If the evidence is text-only ("the code looks safe") or the mitigation rests on an un-exercised invariant, re-route to `discover` to exercise it under an invariant-present config, or to `harness_design` at a higher tier if the harness cannot. If no invariant-present config that still reaches the site can be built, treat the evidence as insufficient and keep the hypothesis open — do NOT record it as a terminal `harness-tested-and-rejected`; route to `harness_design` for an alternative reachability approach, or defer the hypothesis with an explicit reason. ("Insufficient" here describes the evidence; it is not a routing verdict to emit.)
171
+
172
+ **If discover returned `rejected` (couldn't run):**
173
+ - Coverage shows the target function IS reached but the condition doesn't fire → `differential_validate` (values need adjustment for the live context: protocol encoding, byte order, framing).
174
+ - Coverage shows the target function is NOT reached → `harness_design` at a higher tier (reachability problem).
175
+ - Discover agent couldn't run the harness at all → `harness_design` (infrastructure problem).
176
+
177
+ ### `differential_validate` — verdict: `differential_validate`
178
+
179
+ - **When:** Harness test diverges from expected behavior.
180
+ - **Directive must supply:** What diverged and what hypothesis to test.
181
+
182
+ ### `human_escalation` — verdict: `escalate`
183
+
184
+ - **When:** Investigation is stuck.
185
+ - **Directive must supply:** Describe what was tried and what you need from the human.
186
+ - **Return:** via human events (APPROVE / FORCE_REVISION / ABORT), not verdicts.
187
+
188
+ ### `triage` — verdict: `triage`
189
+
190
+ - **When:** at least one `.workflow/discoveries/findings.hN.md` exists from an `approved` discover round. Tier-1 detector output from `harness_validate` — whether a C/C++ sanitizer, a fuzz crash, a static analyzer hit, a tainted-flow report, or an assertion failure — is NOT sufficient evidence to route here. That output proves the detector fired, not that the attacker sees an impact. Route to `discover` first to demonstrate the effect with attacker-maximal parameters.
191
+ - **Directive must supply:** Nothing extra — reads findings.
192
+ - **Audit on return** depends on the verdict triage produced:
193
+
194
+ **If triage returned `approved`:** the demonstrated evidence supports the severity assigned. Proceed to `conclude` (verdict `complete`) or continue the investigation loop as usual.
195
+
196
+ **If triage returned `insufficient`:** the triage agent found discover's evidence too thin for the severity it implied (e.g., a confidentiality claim anchored on zeroed padding instead of observed heap contents, or a theoretical OOB extent without a demonstration at adversary-maximal parameters). Route back to `discover` with a directive naming the **specific gap** triage identified. Do NOT retry triage without new discover output — that just reproduces the same insufficient assessment.
197
+
198
+ **If triage returned `escalate`:** evidence is contradictory or the severity question needs human judgment — route to `human_escalation`.
199
+
200
+ ### `conclude` — verdict: `complete`
201
+
202
+ - **When:** Investigation finished — vulnerability confirmed and triaged, OR thorough search completed with no findings.
203
+ - **Directive must supply:** Nothing extra — reads all artifacts.
204
+ - **Return:** routes to `review` (LLM report reviewer), then `report_review` (human gate). The orchestrator does NOT dispatch `review` directly.
205
+
206
+ ### Returns from `review` (LLM report reviewer)
207
+
208
+ `conclude` routes to `review`; `review` may bounce back here with `rejected`. Treat it as a FORCE_REVISION: read `.workflow/report_review/review.md` for the failed checklist items, then route per the standard rules — `complete` for writeup-only fixes the existing artifacts support, `discover`/`triage`/`reanalyze` for evidence gaps, `escalate` for threat-model policy calls.
209
+
210
+ **Per-hypothesis report split — directive scoping.** The conclude state writes `.workflow/report/report.md` (index) plus one `.workflow/report/report.hN.md` per hypothesis. When you route back to `conclude` after a `review` rejection or a human FORCE_REVISION, your directive MUST carry two sentinel lines so conclude and the next review pass can scope to the dirty subset:
211
+
212
+ - `Dirty hypotheses: <list>` — comma-separated `report.hN.md` filenames (e.g., `Dirty hypotheses: report.h2.md, report.h5.md`), OR `Dirty hypotheses: none` when no hypothesis file content changes, OR `Dirty hypotheses: all` when you cannot tell from `report_review/review.md` which files are dirty (request the full re-author rather than guessing).
213
+ - `Index update required: <yes|no>` — `yes` when the rejection touches the index file (`report.md`) itself; `no` otherwise.
214
+
215
+ The index-only revision case is the combination `Dirty hypotheses: none` + `Index update required: yes` — the rejection is about cross-cutting content (executive summary, ranking, recommendations) but no hypothesis file content changes. Both lines are required on every revision route to `conclude`; do not pack filenames and free text into one sentinel. Downstream `review` (and `conclude` itself) reads these to decide whether to do a full re-read or scope to the dirty subset, and to know whether to refresh the index.
216
+
217
+ **Feedback-driven directives.** When human feedback or prior-round evidence changes the focus, your directive should point the next agent AT the specific thing to investigate, not prescribe how to investigate it. Example shape: "Re-analyze focusing on the interaction between [function A] and [data structure B] — prior round missed [the specific case]." Let the agent's prompt drive HOW.
218
+
219
+ ## Harness directives — required vocabulary
220
+
221
+ Every `harness_design` directive MUST name a tier (1, 2, or 3) and a design class (trigger-driven OR coverage-driven). Both terms are defined in the `harness-design-fuzzing` skill that `harness_design` loads — your job here is to pick them, not to redefine them.
222
+
223
+ Tier is a function of hypothesis scope: ONE function → Tier 1; value flowing across 2+ functions or shared types/sentinels across components → Tier 2 minimum; real initialization, protocol framing, or global state required → Tier 3. Tier 1 for a cross-component hypothesis is wrong; if in doubt, go higher.
224
+
225
+ Design class is a function of what analyze produced: a falsifiable claim with a named violation site → trigger-driven; an under-exercised dispatch surface (file/function set + dispatch axes) without a specific theory → coverage-driven. Coverage-driven is the right pick when prior trigger-driven rounds against the same region have been mitigated by upstream guards but the region itself is untested by existing fuzz infrastructure.
226
+
227
+ ## Strategic principles
228
+
229
+ **Human feedback overrides agent verdicts.** When report_review feedback contradicts an agent's "mitigated" or "ruled_out" verdict, the human's assessment takes precedence. Route to harness_design at a higher tier — never re-run the tier the human disputed. If the human describes a cross-component interaction, go to Tier 2 or 3.
230
+
231
+ **Detector evidence vs. impact evidence.** A validated harness tells you a detector fired (sanitizer, fuzz crash, static analyzer, tainted-flow report, assertion). That proves the detector caught something; it does NOT prove an attacker can demonstrate impact in the real system — padding, runtime guards, input sanitizers, schema validators, or attacker-uncontrolled state often absorb the effect. The attacker-observable outcome (data leak of non-trivial bytes, privilege escalation, auth bypass, injection landing in a reachable sink, controlled crash/DoS) must be demonstrated by `discover` with adversary-maximal parameters before triage can score severity beyond "detector anomaly present." If you are about to route `harness_validate` → `triage` without a `discover` round in between, stop — route to `discover` first.
232
+
233
+ **Completing the investigation.** `complete` is OR-shaped by default — a single confirmed finding is tempting to treat as sufficient — but multi-hypothesis investigations close prematurely if you route to `conclude` after the first hit while other analyze-produced hypotheses remain untested. To prevent that, `complete` requires every hypothesis from `.workflow/analysis/analysis.md` (cross-cutting analysis section) to be in one of three terminal states. Before you can route to `conclude` with `complete`, your status block's `notes` field MUST contain a hypothesis ledger of the following form:
234
+
235
+ ```
236
+ Hypothesis status:
237
+ - h1: confirmed (triage verdict: approved, severity: <severity>)
238
+ - h2: harness-tested-and-rejected (rejected at <discover|differential_validate>, round <N>)
239
+ - h3: deferred (reason: <reason>)
240
+ ```
241
+
242
+ Use stable hypothesis IDs (`h1`, `h2`, ...) — assign them on first ledger emission by walking the analyze artifact's cross-cutting pattern instances in order, then keep those IDs stable across rounds (they are the same IDs that flow into `report.hN.md` filenames at `conclude`). Every hypothesis the analysis enumerates MUST appear in the ledger; silent omission is forbidden.
243
+
244
+ Terminal states:
245
+ - **confirmed** — `discover` returned `approved` for this hypothesis AND `triage` produced a verdict (approved, insufficient, or escalate). Confirmed-but-insufficient counts as terminal because triage made the call.
246
+ - **harness-tested-and-rejected** — `discover` returned `blocked` with execution evidence per the `discover` audit rules above, OR `differential_validate` concluded the hypothesis is not exploitable. Text-only "looks safe" is NOT terminal — that hypothesis is still open and you must route to `harness_design` / `discover`, not defer. "Execution evidence" requires the harness corpus to have actually hit the hypothesis's named code site (function/file/line) — `discover`'s coverage report or `harness_validate`'s per-function coverage must cite per-site hits. A sanitizer-clean run where the targeted site received zero coverage is NOT terminal evidence; the corpus did not exercise the hypothesis. Extend the corpus through `harness_design` / `harness_build` and re-run `discover`, or defer with reason (a) if the surface is genuinely out of task scope. Do not mark `harness-tested-and-rejected` on aggregate-corpus statistics ("N rounds, no fire") that bypass the named site.
247
+ - **deferred** — orchestrator's documented choice. Acceptable reasons, named inline in the ledger entry: (a) **out-of-scope per task description** — the task description's scope or threat model excludes this hypothesis; (b) **intrinsic capability the workflow cannot acquire** — e.g., kernel debugger, hardware attack surface (firmware, side channels, physical access), network-only stack the workflow cannot exercise, or a target environment the harness fundamentally cannot replicate (different OS/architecture, proprietary toolchain). This reason does NOT cover fixable harness-build problems: missing apt-installable libraries, missing pip packages, missing build flags, missing codec libraries that exist as packages, or any dependency the harness build could install. For those, route back through `harness_design` with directive to address the environment fix — do not defer; (c) **duplicate of another confirmed hypothesis** — the same primitive at the same site, already covered by hN.
248
+
249
+ `complete` is FORBIDDEN when:
250
+ - any hypothesis is unaccounted-for in the ledger, OR
251
+ - any `deferred` entry omits a reason or names a reason outside the (a)–(c) list, OR
252
+ - any `deferred (b)` entry's underlying issue is a fixable harness-build problem (missing apt/pip dependency, missing build flag, missing codec library that exists as a package) rather than an intrinsic limitation — route through `harness_design` with an environment-fix directive instead.
253
+
254
+ When the ledger is fully terminal — every hypothesis is `confirmed`, `harness-tested-and-rejected`, or `deferred` with a valid reason — set verdict to `complete`. Escalation is for being stuck; completion is for being done. "No exploitable vulnerability found" is a valid and valuable outcome, but it must be backed by a ledger showing every hypothesis was actually tested or explicitly deferred — not merely by the absence of a confirmed finding.
255
+
256
+ **Stall detection.** If the same state has been visited 3+ times without progress, results contradict across layers, or you are uncertain about strategy, set verdict `escalate` rather than continuing.
257
+ inputs:
258
+ - analysis?
259
+ - harness_design?
260
+ - harness_build?
261
+ - harness_validate?
262
+ - discoveries?
263
+ - differential?
264
+ - triage?
265
+ - report_review?
266
+ outputs:
267
+ - journal
268
+ transitions:
269
+ - to: harness_design
270
+ when:
271
+ verdict: harness_design
272
+ actions:
273
+ - type: resetVisitCounts
274
+ stateIds: [harness_design, harness_design_review, harness_build, harness_validate]
275
+ - to: discover
276
+ when:
277
+ verdict: discover
278
+ - to: differential_validate
279
+ when:
280
+ verdict: differential_validate
281
+ - to: human_escalation
282
+ when:
283
+ verdict: escalate
284
+ - to: triage
285
+ when:
286
+ verdict: triage
287
+ - to: conclude
288
+ when:
289
+ verdict: complete
290
+ - to: analyze
291
+ when:
292
+ verdict: reanalyze
293
+ - to: done
294
+ guard: isRoundLimitReached
295
+
296
+ human_escalation:
297
+ type: human_gate
298
+ description: Human review when investigation is stuck
299
+ acceptedEvents:
300
+ - APPROVE
301
+ - FORCE_REVISION
302
+ - ABORT
303
+ present:
304
+ - journal
305
+ - analysis
306
+ - discoveries?
307
+ transitions:
308
+ - to: orchestrator
309
+ event: APPROVE
310
+ - to: orchestrator
311
+ event: FORCE_REVISION
312
+ - to: aborted
313
+ event: ABORT
314
+
315
+ harness_design:
316
+ type: agent
317
+ description: Designs instrumented test harness specifications
318
+ persona: global
319
+ skills:
320
+ - harness-design-fuzzing
321
+ prompt: |
322
+ You are a security tooling architect. Read the analysis in `.workflow/analysis/analysis.md` and the investigation journal at `.workflow/journal/journal.md`.
323
+
324
+ **First action**: read the "Scoping from the previous agent" section at the top of this message. Its Directive subsection is the orchestrator's authoritative description of what to design — the tier, the design class (trigger-driven or coverage-driven), the target (a specific hypothesis OR a named under-covered surface), the component scope, and WHY this tier. The Notes subsection is a short summary. If the scoping section is missing or the directive does not specify both a tier and a design class with its target, STOP and report back with an `escalate`-style agent_status — do not pick those by guessing from the analysis or journal alone.
325
+
326
+ ## Your scope: DESIGN ONLY
327
+
328
+ You produce a design document. You do NOT write the harness code, compile it, or run it. A separate `harness_build` agent implements what you design, and a `harness_validate` agent runs the experiments. Your output is a specification — detailed enough that the build agent can implement it without guessing, but NOT the implementation itself.
329
+
330
+ The methodology — design classes, harness tiers, systematic input exploration, the two-coverage distinction, existing-fuzzer selection, seed-corpus discipline, diagnostic checkpoints, and the design-document scope rules — lives in the `harness-design-fuzzing` skill. Apply it. Your job here is to instantiate the skill against the directive's tier, class, and target.
331
+
332
+ Do NOT include:
333
+ - Full C/Python/etc. source code for the harness
334
+ - Build scripts or compile commands with full flag lists (reference them abstractly: "compile with ASAN+UBSAN, integer checks in recover/warn-only mode")
335
+ - Test execution results or tables of "expected outcomes" — you haven't run anything yet
336
+
337
+ DO include:
338
+ - What source files to link and why
339
+ - What types and structs to preserve
340
+ - What to sweep and over what range — hypothesis input variables (trigger-driven) or dispatch axes (coverage-driven)
341
+ - What conditions constitute a positive finding — specific violation pattern (trigger-driven) or any sanitizer error within the named region (coverage-driven)
342
+ - What instrumentation (sanitizers, fuzzer-feedback mechanism + named metric field, audit-coverage tool, fallback tool on a different mechanism) to use
343
+ - Diagnostic checkpoints with concrete thresholds
344
+ - Infrastructure assumptions and fallbacks
345
+
346
+ Keep the design document concise. Aim for something the build agent can read and act on without wading through reference implementation. Be concrete: name specific source files, function names, struct fields, and sanitizer flags — but as specifications, not implementations.
347
+
348
+ Write your design to `.workflow/harness_design/design.md`. Include the tier number prominently at the top.
349
+ inputs:
350
+ - analysis
351
+ - journal
352
+ outputs:
353
+ - harness_design
354
+ transitions:
355
+ - to: harness_design_review
356
+
357
+ harness_design_review:
358
+ type: agent
359
+ description: LLM review of harness design before implementation
360
+ persona: global
361
+ maxVisits: 3
362
+ skills:
363
+ - harness-design-fuzzing
364
+ prompt: |
365
+ You are a harness design reviewer. Read the design in `.workflow/harness_design/design.md`, the analysis in `.workflow/analysis/analysis.md`, and the investigation journal at `.workflow/journal/journal.md`.
366
+
367
+ Your job: pre-screen the design for concrete errors before the build agent invests effort. Be strict — each item below either passes or fails. Rubber-stamping a design that fails a checklist item wastes downstream effort.
368
+
369
+ The methodology — what makes a tier match, what counts as a concrete sweep range, the two-coverage distinction, the seed-corpus rule, and the design-document scope boundary — lives in the `harness-design-fuzzing` skill. The checklist below names the dimensions to verify; for each, the skill defines the bar.
370
+
371
+ ## Checklist
372
+
373
+ For each item, the design either concretely satisfies the corresponding section of the skill, or it does not. "Stated vaguely" is a fail; the skill's bar is concrete file/function/value-level specificity.
374
+
375
+ 1. **Tier matches target scope.** Skill section: *Harness tiers — Picking the right tier*. Tier 1 for a cross-component target is an automatic fail in either class.
376
+
377
+ 2. **Design class and target are stated precisely.** Skill section: *Design class — pick first*. A trigger-driven design names specific functions, value range, and expected violation site. A coverage-driven design names a concrete under-exercised code region (file/function set) and the dispatch axes to sweep. "There may be memory corruption here" or "fuzz the project" is a fail.
378
+
379
+ 3. **Input sweep range is concrete.** Skill section: *Systematic input exploration*. Named input variables (or dispatch axes), explicit ranges with boundary points, specified search strategy. "Test a few values" or pre-enumerated `Test A / Test B / Test C` is a fail.
380
+
381
+ 4. **Observables and positive-finding condition are specific.** Skill section: *Systematic input exploration*. For trigger-driven: a specific violation pattern at a named site. For coverage-driven: "any sanitizer error within <named code region>" where the region is a concrete file/function set, not "somewhere in the target." "A crash happens" is a fail.
382
+
383
+ 5. **Coverage targets the right code — both kinds.** Skill section: *Coverage instrumentation — two distinct concerns*. The design must separately name (a) the fuzzer-feedback mechanism, (b) the audit-coverage tool, (c) the exact metric field name the validator will read (e.g., libFuzzer `cov:`, AFL++ `edges_found`), and (d) which units of code receive feedback instrumentation — specifically the target, not just the harness wrapper. A design missing a fallback path on a *different* instrumentation mechanism is also a fail.
384
+
385
+ 6. **Existing fuzzer used when appropriate.** Skill section: *Existing fuzzer selection*. For large or structured input spaces, the design specifies libFuzzer / AFL++ / a stack-equivalent rather than a hand-rolled loop. For small discrete spaces, bounded enumeration is fine — but the design must state the choice and why.
386
+
387
+ 7. **Seed corpus matches dispatch surface.** Skill section: *Seed corpus and dispatch surface*. Structured dispatcher → programmatic per-arm seeds. Small discrete target → N/A with a one-line reason.
388
+
389
+ 8. **Design stays within scope.** Skill section: *Design-document scope*. A design is a specification, not an implementation — no full source code, no compile commands with full flag lists, no pre-computed "expected outcomes." For multi-hypothesis bundles, the design must briefly justify the bundling; >3 bundled hypotheses without strong shared-input-space rationale is a fail.
390
+
391
+ 9. **Stub audit.** Skill section: *Delegate library realism*. The design enumerates every stub it introduces and classifies each as Class A (I/O / allocator — acceptable) or Class B (validator / parser / decoder — forbidden). Any Class B stub is an automatic fail. A design with no stub inventory — even when the design plausibly introduces stubs — is also a fail. The design also enumerates the build-or-install-time configuration of every third-party library/package the harness depends on (build flags, build tags, optional extras, conditional-compilation gates — the dependency's *own* configuration, not the consumer's choice of which dependency to pick) and confirms parity with the production-default. A library/package without parity is a Class C fail.
392
+
393
+ ## Verdicts
394
+
395
+ - **approved**: All applicable checklist items pass. Write a brief note to `.workflow/harness_design_review/review.md` listing what you verified.
396
+ - **rejected**: One or more items fail. Write `.workflow/harness_design_review/review.md` listing each failure by checklist number, the specific problem, and what the designer must change. Be concrete — "item 3 fails: input range for the named index variable is not specified; design must specify the sweep covering the full representable range of its type plus boundary points" beats "item 3: needs more detail."
397
+
398
+ Do NOT modify the design document yourself. The designer iterates on their own output.
399
+ inputs:
400
+ - analysis
401
+ - harness_design
402
+ - journal
403
+ outputs:
404
+ - harness_design_review
405
+ transitions:
406
+ - to: harness_build
407
+ when:
408
+ verdict: approved
409
+ # Ordered before the rejection transition so the cap wins on the Nth visit.
410
+ - to: harness_design_escalation
411
+ guard: isStateVisitLimitReached
412
+ - to: harness_design
413
+ when:
414
+ verdict: rejected
415
+
416
+ harness_design_escalation:
417
+ type: human_gate
418
+ description: Human escalation when the design-review loop hits its cap — human decides whether the design is good enough to build
419
+ acceptedEvents:
420
+ - APPROVE
421
+ - FORCE_REVISION
422
+ - ABORT
423
+ present:
424
+ - analysis
425
+ - harness_design
426
+ - harness_design_review?
427
+ - journal
428
+ transitions:
429
+ - to: harness_build
430
+ event: APPROVE
431
+ # The human accepted the design despite the reviewer's objections.
432
+ # Reset the harness loop counters so the build/validate cycle starts fresh.
433
+ actions:
434
+ - type: resetVisitCounts
435
+ stateIds: [harness_design, harness_design_review, harness_build, harness_validate]
436
+ - to: orchestrator
437
+ event: FORCE_REVISION
438
+ - to: aborted
439
+ event: ABORT
440
+
441
+ harness_validate_escalation:
442
+ type: human_gate
443
+ description: Human escalation when the validate loop hits its cap — human decides whether the built harness is usable despite validation failures
444
+ acceptedEvents:
445
+ - APPROVE
446
+ - FORCE_REVISION
447
+ - ABORT
448
+ present:
449
+ - analysis
450
+ - harness_design
451
+ - harness_build?
452
+ - harness_validate
453
+ - journal
454
+ transitions:
455
+ - to: orchestrator
456
+ event: APPROVE
457
+ # The human accepted the harness despite validation failures and is
458
+ # telling the orchestrator to proceed. Reset the harness loop counters
459
+ # so a future orchestrator-driven re-entry into the harness cycle
460
+ # starts with fresh counters.
461
+ actions:
462
+ - type: resetVisitCounts
463
+ stateIds: [harness_design, harness_design_review, harness_build, harness_validate]
464
+ - to: orchestrator
465
+ event: FORCE_REVISION
466
+ - to: aborted
467
+ event: ABORT
468
+
469
+ harness_build:
470
+ type: agent
471
+ description: Implements harness from design specification
472
+ persona: global
473
+ skills: none
474
+ prompt: |
475
+ You are a security tooling engineer. Read the harness design in `.workflow/harness_design/design.md`, the analysis in `.workflow/analysis/analysis.md`, and the investigation journal at `.workflow/journal/journal.md`.
476
+
477
+ **Core rule.** The fuzzer-feedback instrumentation named in the design MUST actually land on the target code (library, package, module, classpath — whichever the design names), not only on the harness wrapper. A harness that loads a prebuilt or uninstrumented target produces blind random mutation even though everything compiles cleanly. Verify instrumentation placement before declaring the build complete.
478
+
479
+ Implement the harness exactly as specified:
480
+
481
+ 1. Build system modifications: apply the design's fuzzer-feedback mechanism to every unit the design names (including the target). Apply audit-coverage tooling separately — it is for post-hoc audit, not fuzzer feedback.
482
+ 2. The driver: code that sends external inputs through real entry points.
483
+ 3. The session API: a script or program for multi-step interactions and results.
484
+ 4. Reset mechanism: implement and verify session reset.
485
+ 5. Coverage extraction in the specified format.
486
+
487
+ The harness should be invocable as a CLI tool or script. The discovery agent needs to: start a session, perform multiple steps, observe coverage, and reset cleanly.
488
+
489
+ **Bounded harness execution (required).** Every command that runs the built harness — reset check, smoke/baseline run, instrumentation probe, any burst — MUST be wrapped in a hard-kill wall-clock guard: `timeout -s KILL <seconds> <command>`. Plain `timeout` sends a catchable SIGTERM that a wedged harness can defer or ignore; only `-s KILL` (or `-k`) guarantees termination. This is tool-agnostic — it holds whether the harness is a fuzzer, a bounded enumerator, an analyzer, or a one-shot driver.
490
+
491
+ **Instrumentation verification (required before approved).** Confirm fuzzer-feedback is present on the target, not only the harness. Method depends on the mechanism:
492
+ - Compile-time (libFuzzer / sanitizer-coverage): `nm` / `readelf` the target archive and expect symbols like `__sanitizer_cov_trace_pc_guard` in the target's object files.
493
+ - Compile-wrapper (AFL++): instrumented objects contain `__afl_area_ptr`; `afl-showmap` on a baseline reports non-trivial map density (≫100 edges).
494
+ - Runtime agent (managed stacks): the fuzzer's startup output lists instrumented classes/modules; verify the target appears.
495
+
496
+ Record the verification command and output in `.workflow/harness_build/README.md` alongside the build instructions. If the target shows no instrumentation, STOP — this is a build defect, not an accepted outcome.
497
+
498
+ Install all required dependencies eagerly. If anything cannot be installed, report verdict rejected with the specific error — do NOT report approved if fuzzer-feedback instrumentation is missing from the target code.
499
+
500
+ Write build/run instructions to `.workflow/harness_build/README.md`.
501
+ inputs:
502
+ - analysis
503
+ - harness_design
504
+ - journal
505
+ outputs:
506
+ - harness_build
507
+ transitions:
508
+ - to: harness_validate
509
+
510
+ harness_validate:
511
+ type: agent
512
+ description: Verifies the harness runs and produces coverage; classifies failures by cause
513
+ persona: global
514
+ maxVisits: 4
515
+ skills: none
516
+ prompt: |
517
+ You are a harness validation engineer. Your job is to verify the harness ACTUALLY WORKS by running it, and if it doesn't, classify the cause so the workflow routes to the right fix.
518
+
519
+ **Core rule.** Approval requires evidence that the fuzzer's evolutionary feedback loop is reaching the target code — not just that an audit-coverage tool reports a line-coverage percentage. Audit coverage (`llvm-cov`, `gcov`, or the stack's equivalent) is orthogonal to fuzzer-feedback (libFuzzer's PC-guard counter, AFL++'s coverage map, or a runtime agent's instrumented-class report). A harness whose wrapper is the only unit with fuzzer-feedback instrumentation will report healthy line coverage AND burn millions of iterations on blind random mutation. Gate on the fuzzer's own self-reported feedback metric.
520
+
521
+ Read the build/run instructions in `.workflow/harness_build/README.md` and the design in `.workflow/harness_design/design.md`.
522
+
523
+ Perform these validation steps:
524
+
525
+ 1. **Reset.** Run the harness reset command. Verify it exits cleanly.
526
+ 2. **Linkage audit (gating).** Run `ldd` (Linux), `otool -L` (macOS), or the platform equivalent against the test binary. Confirm no shared library resolves to a project-internal stub directory (typical names: `*-stub/`, `*_mock/`, `mock-*/`, `fake-*/`). If linkage resolves to a project-internal stub of any component performing trust-boundary validation in production (parser, validator, decoder, signature checker, schema validator), return `rejected_design` regardless of coverage and crash signals. Cite the specific link path and stub component in the rejection. The linkage check is gating: it runs before the coverage and crash gates and short-circuits on failure.
527
+ 3. **Baseline run.** Run the harness with a simple benign input. Verify it exits without errors.
528
+ 4. **Target function reached.** Run the harness coverage command. Verify the **target function** (not just the target file) appears with non-zero hits. If the file has coverage but the function does not, the call chain is broken — fail.
529
+ 5. **Fuzzer-feedback reach (load-bearing).** Run a short representative fuzz burst (30–60s) from the seed corpus. Run it in the FOREGROUND under a hard kill guard — `timeout -s KILL <burst+30s>` wrapping the binary — and pass the fuzzer's own watchdogs (libFuzzer: `-max_total_time=<burst> -timeout=20 -rss_limit_mb=4096`; AFL++: the `-V` / `-t` / `-m` equivalents). Plain `timeout` sends a catchable SIGTERM that a slow fuzzer defers between iterations — it will not stop a wedged burst. Do NOT background the burst and poll with a `pgrep`/`sleep` loop: `pgrep -af <name>` matches the poll command's own arguments, so the loop never exits. Capture the fuzzer's own reported coverage metric:
530
+ - libFuzzer (also the tools that wrap its output: jazzer.js, atheris, cargo-fuzz, Jazzer): stderr prints `#N ... cov: C ft: F`. Record `C`.
531
+ - AFL++: `afl-fuzz` reports `map density` / `edges found`; `afl-showmap` on a sample prints non-zero map entries. Record the edge count.
532
+ - For any other tool, the design must name the exact metric field (see harness_design coverage-instrumentation rules). If the design does not name it, that is a design defect — route `rejected_design` with a directive "design.md must specify the fuzzer-feedback metric field name for the chosen tool."
533
+ Fail approval unless ALL of:
534
+ (a) Target-code fuzzer-feedback count ≥ **1000** (default; configurable via directive). A count in the tens is the signature of wrapper-only instrumentation. For tools reporting in different units, interpret the threshold as "several orders of magnitude larger than a thin wrapper's footprint" and state the equivalent number used.
535
+ (b) Audit-coverage line coverage of the target source file ≥ **20%** (default; configurable via directive) after the burst.
536
+ (c) Burst throughput is not pathological: record `exec/s` and the slowest-unit time. `exec/s` in the single digits, or any single unit exceeding ~10s, means the harness is too slow to fuzz — a per-input heavyweight-setup defect that both wedges the burst's own time limit and would wedge `discover`.
537
+ If (a) fails: target code was not instrumented for fuzzer feedback — diagnose build vs. design.
538
+ If (b) fails but (a) passes: seed corpus is too narrow — route to `harness_design` for seed-corpus revision.
539
+ If (c) fails: route `rejected_design` with directive "cap input size (`-max_len`) and move per-input heavyweight setup (full re-init, process spawn, multi-connection sweep) out of the fuzz entry point."
540
+ 6. **Stateful session sanity.** If the harness supports multi-step sessions, run a short sequence; verify state persists.
541
+ 7. **Branch-level audit coverage exists.** Confirm the audit tool reports branch data, not just line counts.
542
+
543
+ Do NOT report `approved` with caveats. A harness whose fuzzer-feedback does not reach the target code is not validated — that is exactly the silent failure this step exists to catch.
544
+
545
+ ## Verdicts
546
+
547
+ - **approved**: All seven steps passed, including the linkage audit and the fuzzer-feedback thresholds.
548
+ - **rejected_build**: Implementation bugs the build agent can fix without changing the design — compile errors, driver defects, session reset broken, coverage extraction not wired up, fuzzer-feedback flags in the design but missing from the actual compile invocation. Route: back to `harness_build`.
549
+ - **rejected_design**: Design flaws the build agent cannot fix — wrong call chain, wrong tooling, tier insufficient, target linked as a prebuilt artifact that cannot be rebuilt with instrumentation, seed corpus too narrow. Route: back to `harness_design`.
550
+
551
+ When the fuzzer-feedback (a) check fails, classify by cross-referencing `design.md` — the same observed symptom (wrapper-only instrumentation) can be either build or design depending on what the design asked for:
552
+ - Design specified rebuilding the target with feedback flags AND the build's compile invocation actually did that, but flags didn't land (e.g., wrong `CFLAGS` scope) → `rejected_build` (implementation gap).
553
+ - Design specified the flags correctly and the build's compile invocation omits them → `rejected_build`.
554
+ - Design accepted a prebuilt target / system library / vendored archive as the attack surface and provided no rebuild step → `rejected_design`, switch to a tool that handles full-binary instrumentation or rebuild from source.
555
+ - Project build system resists whole-target rebuild AND the design named no fallback on a different mechanism → `rejected_design`, reconsider tool choice and pick the design's fallback (or add one if missing).
556
+
557
+ Write validation results — including the recorded fuzzer-feedback metric values — to `.workflow/harness_validate/validation.md`.
558
+ inputs:
559
+ - harness_design
560
+ - harness_build
561
+ outputs:
562
+ - harness_validate
563
+ transitions:
564
+ - to: orchestrator
565
+ when:
566
+ verdict: approved
567
+ # Ordered before the rejection transitions so the cap wins on the Nth visit.
568
+ - to: harness_validate_escalation
569
+ guard: isStateVisitLimitReached
570
+ - to: harness_build
571
+ when:
572
+ verdict: rejected_build
573
+ - to: harness_design
574
+ when:
575
+ verdict: rejected_design
576
+
577
+ discover:
578
+ type: agent
579
+ description: Hypothesis-driven vulnerability research using the harness
580
+ persona: global
581
+ skills:
582
+ - memory-safety-c-cpp
583
+ prompt: |
584
+ You are a vulnerability researcher. You have a working instrumented harness for the code in scope. Read the task description, the analysis, harness build instructions, and investigation journal.
585
+
586
+ **First action**: read the "Scoping from the previous agent" section at the top of this message. Its Directive subsection is the orchestrator's authoritative description of WHAT to test this round — specific hypothesis, trigger conditions, input values to sweep, component scope. If the scoping section is missing or the directive is vague ("try something reasonable", generic exploration without a hypothesis), STOP and report back with an `escalate`-style agent_status — do not improvise hypotheses from the analysis or journal alone. Your value comes from executing scoped experiments the orchestrator asked for, not from re-deriving scope each round.
587
+
588
+ Your goal: find a vulnerability matching the task's scope, vulnerability class, and threat model, by running experiments through the harness and observing results. The task defines what counts as a finding — for example, "externally reachable memory corruption in this file" is very different from "local privilege escalation via any user-reachable path in this directory" or "authentication bypass in these endpoints." Let the task's definition drive what inputs you craft, what behaviors you watch for, and what counts as exploitable.
589
+
590
+ ## First action: verify the harness
591
+
592
+ Before forming hypotheses, run the harness with a baseline input and read the coverage output. Confirm:
593
+ - The harness executes without errors
594
+ - The target function appears in the coverage report
595
+ - You can observe which branches were taken
596
+
597
+ If the harness doesn't work, set verdict to rejected immediately. Do not attempt to find vulnerabilities by reading code alone — that is not your job. Your value comes from execution-based evidence.
598
+
599
+ ## Hypothesis-driven discovery
600
+
601
+ The orchestrator's directive tells you what to test. Use the harness to:
602
+
603
+ 1. **Design an input sequence** that exercises the hypothesized code path. Use the analysis's function catalog for guidance on types, boundaries, and assumptions.
604
+ 2. **Send it through the harness** and observe results. Read coverage to see which lines and branches were hit. Check for sanitizer output, crashes, and invariant violations. If you run ANY work in the background (fuzz campaign, build, etc.), wait on the launched PID (`cmd & PID=$!; wait "$PID"`); never poll with a `pgrep -f <name>`/`sleep` loop (`pgrep -f` matches the waiter's own command line and your prompt-bearing agent process, so the loop never exits) and never poll a hardcoded log/output path you did not capture from that same launch.
605
+ 3. **Refine or pivot.** If coverage shows you missed the target path, adjust inputs to hit the right branch. If you reached it with no violation, try variations before moving to a new hypothesis.
606
+ 4. **Repeat systematically.** Work through the hypotheses in the orchestrator's directive.
607
+
608
+ Adversary-maximal evidence is not "the named site fired." It includes characterizing the primitive's extent — exercising each scaling axis named in the relevant surface skill's primitive-extent taxonomy (for native C/C++ memory-safety primitives, see the `memory-safety-c-cpp` skill's *Primitive-extent scaling axes* section; the canonical per-class enumeration also appears in `vulnerability-triage`'s *Primitive extent* section, which triage uses to score). For each axis the directive scopes, run the trigger at least at one boundary point that demonstrates the primitive's behavior at that axis's value, not just the minimum. Document what was exercised and what was not — the findings doc must record the demonstrated extent per axis, so triage can score on what was actually run.
609
+
610
+ The minimum PoC that fires the named site is necessary but not sufficient. A finding with only minimum-trigger evidence will route back from triage with an `insufficient` verdict naming the un-explored axis.
611
+
612
+ ## Verdicts
613
+
614
+ - **approved**: An exploitable condition fired — sanitizer error, controlled crash, invariant violation, or direct evidence the hypothesized bug triggered. Document the reproduction input, coverage proof, and severity.
615
+ - **rejected**: The harness is insufficient — can't reach the target, missing protocol feature, broken infrastructure.
616
+ - **blocked**: Hypothesis did not fire despite a completed search. You may only return `blocked` if (a) coverage shows the target code was executed, AND (b) you swept the input range relevant to the hypothesis — fuzzing for value-dependent claims (sizes, offsets, indices, counters), bounded enumeration for discrete claims (protocol states, flag combinations, auth paths). "I read the code and it looks mitigated" is not `blocked` — it's `rejected` with a request to redesign the harness. If neither fuzzing nor enumeration applies to this hypothesis, name the reason explicitly in your findings doc. **Invariant-only mitigations are not `blocked`.** If you attribute non-impact to an invariant your harness had to disable to reach the site — a lock/serialization or single-threaded dispatch contract, an upstream guard, a runtime/allocator check, or an earlier sanitizer abort that prevents reaching a downstream sink — you may return `blocked` only if you ALSO ran the trigger under a configuration where that invariant is present and showed the effect absorbed there. Disabling the invariant to reach the site and then citing it as the mitigation is circular; if you cannot build an invariant-present config that still reaches the site, return `blocked` only for the part you exercised and name the un-exercised invariant explicitly.
617
+
618
+ Write per-hypothesis findings to `.workflow/discoveries/findings.<id>.md` where `<id>` is the orchestrator's hypothesis ID — e.g. `findings.h1.md`, `findings.h2.md`. Do not collapse hypotheses into one file. Append after each experiment (hypothesis, input, harness command, result) before starting the next one — do not batch writes to the end, so a crash or rate-limit preserves the round's work. If a hypothesis's findings file already exists from a prior run, read it first and skip experiments whose results are already recorded; do not re-read other hypotheses' files unless the directive scopes you to them.
619
+ inputs:
620
+ - analysis
621
+ - harness_build
622
+ - journal
623
+ - discoveries?
624
+ outputs:
625
+ - discoveries
626
+ transitions:
627
+ - to: orchestrator
628
+
629
+ differential_validate:
630
+ type: agent
631
+ description: Diagnoses divergence between isolated tests and live system
632
+ persona: global
633
+ skills: none
634
+ prompt: |
635
+ You are a systems debugging specialist. A lower-tier harness produced different results than a higher-tier harness or the full system, OR the harness behaves differently than expected based on code analysis.
636
+
637
+ **First action**: read the "Scoping from the previous agent" section at the top of this message. Its Directive subsection tells you which divergence to diagnose and what hypothesis to test. If the scoping section is missing or the directive does not identify a specific divergence, STOP and report back with an `escalate`-style agent_status rather than picking a divergence to chase.
638
+
639
+ Read the investigation journal at `.workflow/journal/journal.md` and the per-hypothesis discovery findings in `.workflow/discoveries/findings.h*.md`.
640
+
641
+ Your job: determine WHY results diverge between tiers or between the harness and expected behavior.
642
+
643
+ Instrument the live system to observe actual behavior at the vulnerability site. Common divergences:
644
+ - **Input transformation**: firewalls stripping options, proxies rewriting headers, serializers normalizing data
645
+ - **State differences**: prior operations, initialization, caching, connection state
646
+ - **Timing/ordering**: async processing, buffering, interrupt handling
647
+ - **Type/encoding**: byte order, padding, alignment, type promotion differences
648
+ - **Environment**: missing dependencies, different compiler flags, optimization levels
649
+
650
+ Use the harness coverage data to confirm whether the vulnerable function is even reached. If coverage shows zero hits on the target function, the problem is reachability, not values.
651
+
652
+ Add debug output (printf/logging) to the vulnerable function if needed to observe intermediate values.
653
+
654
+ Write your diagnosis to `.workflow/differential/diagnosis.md`. Include: the specific divergence found, which layer is wrong (lower-tier test assumptions vs higher-tier behavior), and what needs to change.
655
+ inputs:
656
+ - journal
657
+ - discoveries
658
+ - harness_build
659
+ outputs:
660
+ - differential
661
+ transitions:
662
+ - to: orchestrator
663
+
664
+ triage:
665
+ type: agent
666
+ description: Independent validation and severity assessment of findings
667
+ persona: global
668
+ skills:
669
+ - vulnerability-triage
670
+ - memory-safety-c-cpp
671
+ prompt: |
672
+ You are a senior vulnerability triager. Your job is to **interpret** what `discover` demonstrated and translate it into threat-model language. You do NOT run fresh experiments — you may reproduce discover's experiments to confirm them, but if evidence is too thin for the severity a finding would warrant, set verdict `insufficient` and name the specific gap for discover to close next. Read the per-hypothesis `.workflow/discoveries/findings.h*.md` files (one per hypothesis) and `.workflow/journal/journal.md`. Read only the `findings.hN.md` files for the hypotheses you are triaging this round.
673
+
674
+ **First action**: read the "Scoping from the previous agent" section at the top of this message. If it is absent, triage every hypothesis with a `.workflow/discoveries/findings.hN.md` file. If it is present but vague, STOP and report back with an `escalate`-style agent_status.
675
+
676
+ The methodology — the core anchoring rule, detector-vs-impact distinction, the eleven-item interpretation rubric, delegation transparency, primitive-extent scaling, the disqualifier taxonomy (D-0..D-4), CVSS Achievable / Environmental scoring conventions, hedging-language elimination, falsification asymmetry, common pitfalls, and the insufficient-verdict gap-statement bar — lives in the `vulnerability-triage` skill. Apply it.
677
+
678
+ ## Verdicts
679
+
680
+ - **approved**: Evidence supports a concrete severity. All eleven rubric items completed with consistent conclusions.
681
+ - **insufficient**: Evidence does not support the severity the finding would warrant, OR `discover` has not demonstrated the attacker-visible effect at all. Notes MUST name the specific gap per the skill's insufficient-verdict discipline (parameter to sweep, mitigation to exercise, adjacency to verify, hardening profile to rebuild under, disqualifier label where applicable). The orchestrator needs a directive it can hand to `discover` verbatim.
682
+ - **escalate**: Evidence is contradictory, scope is unclear, or severity requires human judgment (known class with possible in-flight CVE, policy question about threat model).
683
+
684
+ Write the per-hypothesis triage report to `.workflow/triage/triage.<id>.md` where `<id>` matches the hypothesis ID from `findings.<id>.md` — e.g. `triage.h1.md`, `triage.h2.md`. Each file must present all eleven rubric items; the verdict must be traceable to them. Do not collapse hypotheses into one file.
685
+ inputs:
686
+ - discoveries
687
+ - harness_build
688
+ - journal
689
+ outputs:
690
+ - triage
691
+ transitions:
692
+ - to: orchestrator
693
+
694
+ conclude:
695
+ type: agent
696
+ description: Writes final investigation report from all artifacts
697
+ persona: global
698
+ skills: none
699
+ prompt: |
700
+ You are a security report writer. You **translate** investigation artifacts into a final report — you do not re-assess severity, re-interpret evidence, or manufacture framing that isn't already present in the inputs.
701
+
702
+ Read these artifacts:
703
+ - `.workflow/journal/journal.md` — the investigation journal
704
+ - `.workflow/analysis/analysis.md` — structural analysis
705
+ - `.workflow/discoveries/` — live findings (if present)
706
+ - `.workflow/differential/` — differential analysis (if present)
707
+ - `.workflow/triage/` — triage results (if present)
708
+
709
+ ## Hard rules (read before writing)
710
+
711
+ **Severity fidelity.** Severity for each finding MUST match triage's assessment in the corresponding `.workflow/triage/triage.hN.md` verbatim — same CVSS vector, same numeric score, same justification. Do not re-score. Do not strengthen "Medium" into "Medium-High" for emphasis. Do not add confidentiality-impact claims triage did not make. If triage did not assign a severity (e.g., finding returned `insufficient` or escalate), the report does not assign one either.
712
+
713
+ **Exploitability requires demonstration.** A finding may be called "exploitable" only when the hypothesis's `discoveries/findings.hN.md` documents an observation of the attacker-visible effect AND triage confirmed it. Detector output from an isolated harness (sanitizer, fuzz crash, static analyzer hit, tainted-flow report, assertion failure) proves the detector caught an anomaly — not exploitability. Findings backed only by detector evidence go under "Findings without demonstrated impact," NOT "Confirmed findings."
714
+
715
+ **Contradiction handling.** When evidence across rounds disagrees — a Tier-1 detector fires but the Tier-3 effect is absorbed by padding / a schema validator / a sanitizing middleware, or discover observed round-tripped attacker input rather than target state — call out the contradiction in the finding's "Evidence chain" subsection AND score at the **weaker** demonstrated level. Do not reconcile with "the vulnerability is real, the sanitizer confirmed it" — that is papering over. If the weaker observation is "no attacker-visible impact," the finding belongs under "Findings without demonstrated impact."
716
+
717
+ **No invented framing.** Phrases like "three-tier evidence chain," "fully validated," or "directly observable" may appear in the report only if they appear in the source artifacts AND the underlying evidence supports them. Write gaps as gaps.
718
+
719
+ **Executive summary discipline.** Claims in the summary must be traceable line-by-line to findings below. If the summary says "three vulnerabilities confirmed exploitable," three findings must meet the bar above. If fewer, fix the summary — do not promote findings up from "Findings without demonstrated impact."
720
+
721
+ ## Report layout — split index + per-hypothesis files
722
+
723
+ The report is split into a thin index file and one self-contained file per hypothesis. All files live in `.workflow/report/`.
724
+
725
+ - `.workflow/report/report.md` — the index. Cross-cutting only: executive summary, ranked hypothesis table, cross-cutting recommendations, links to the per-hypothesis files. The index does NOT duplicate per-hypothesis evidence chains, CVSS justifications, or reproduction steps — those live in the hypothesis files.
726
+ - `.workflow/report/report.hN.md` — one file per hypothesis, where `N` is the hypothesis ID that flowed through triage and the journal (`h1`, `h2`, ...). Each file is self-contained: a reader who opens only `report.h2.md` should be able to understand the finding without consulting `report.md` or other hypothesis files.
727
+
728
+ You MUST write the index AND every hypothesis file the artifacts cover (one `report.hN.md` per hypothesis with a `triage/triage.hN.md` or `discoveries/findings.hN.md`). Do not collapse multiple hypotheses into one file.
729
+
730
+ ### Index file (`report.md`)
731
+
732
+ The first line of `report.md` MUST be a single `Updated:` line listing the per-hypothesis files authored or modified in this run, comma-separated, in the form `Updated: report.h1.md, report.h3.md` (or `Updated: none` if only the index changed). This line is the on-disk source of truth that downstream `review` and `report_review` consult to scope incremental re-reads. On a fresh first authoring it lists every hypothesis file you created. On revision re-entry it lists only the subset you re-authored. Do NOT omit it — when missing, downstream review must fall back to a full re-read of every hypothesis file.
733
+
734
+ After the `Updated:` line, the index has the following sections:
735
+
736
+ #### Executive Summary
737
+
738
+ One paragraph: what was investigated, what was found (or not found), what severity was demonstrated (not theoretical). Must be faithful to the per-hypothesis files — no claims a hypothesis file does not support.
739
+
740
+ #### Hypothesis ranking
741
+
742
+ One table, one row per hypothesis. Columns: `ID` (h1, h2, ...), `Title` (short noun phrase), `Severity` (verbatim from triage, or "n/a" if triage did not assign one), `Status` (one of: `confirmed`, `no demonstrated impact`, `mitigated`, `insufficient`), `File` (link to `report.hN.md`).
743
+
744
+ Sort by severity then by status (confirmed first, then no-demonstrated-impact, then mitigated, then insufficient). The status values map to the same bars as the per-hypothesis sections below.
745
+
746
+ #### Cross-cutting recommendations
747
+
748
+ Recommendations that apply across multiple hypotheses or to the codebase as a whole — defense-in-depth additions, missing build/test infrastructure, hardening profiles to enable. Per-hypothesis fixes belong in the hypothesis files, not here.
749
+
750
+ #### Investigation coverage
751
+
752
+ The first element of this section MUST be a per-hypothesis ledger; the supporting bullets sit alongside it, not in place of it.
753
+
754
+ ##### Hypothesis ledger
755
+
756
+ List every analyze-produced hypothesis (`h1`, `h2`, ...) with its terminal status — one of `confirmed`, `harness-tested-and-rejected`, or `deferred (reason: ...)`. The ledger here mirrors the orchestrator's notes-channel ledger and uses the same allowed deferral reasons enumerated in the orchestrator's "Completing the investigation" paragraph: (a) out-of-scope per task description, (b) intrinsic capability the workflow cannot acquire (does NOT cover fixable harness-build dependencies — those route through `harness_design`), (c) duplicate of another confirmed hypothesis.
757
+
758
+ Coherence rules (a missing or violated rule is a fail and triggers FORCE_REVISION at review):
759
+
760
+ - The IDs in this ledger MUST match the `report.hN.md` filenames. A `confirmed` hypothesis without a corresponding `report.hN.md` is a fail, and a `report.hN.md` without a ledger entry is a fail.
761
+ - A `deferred (b)` entry whose underlying issue was a missing apt-installable dependency, missing pip package, missing build flag, missing codec library that exists as a package, or any other fixable harness-build problem is a fail (incoherent — should have routed through `harness_design` for the environment fix instead of deferring).
762
+ - Every hypothesis the analysis enumerates MUST appear in the ledger; silent omission is forbidden.
763
+
764
+ ##### Supporting coverage notes
765
+
766
+ - Code paths exercised via harness, including coverage percentages as reported
767
+ - What was NOT tested and why (time limits, infrastructure gaps, skipped states like discover)
768
+ - If the workflow skipped states that the routing rules call for (e.g., `discover` was never invoked), call that out here — it is a coverage gap even when the state machine accepted the path.
769
+
770
+ ### Per-hypothesis file (`report.hN.md`)
771
+
772
+ Each hypothesis file MUST classify itself with a top-line `Status:` matching one of the four bars below, then include the relevant subsections. Keep the file self-contained — re-state the hypothesis title, location, and trigger mechanism rather than referring readers to the index.
773
+
774
+ Hard rules apply per-file (severity fidelity, exploitability bar, contradiction handling, no invented framing — see the rules above). Section headings:
775
+
776
+ - `Status:` line — `confirmed` | `no demonstrated impact` | `mitigated` | `insufficient`
777
+ - Triage verdict and rationale (verbatim from this hypothesis's `triage/triage.hN.md`). For `mitigated` and `insufficient` files, triage may not have run at all — in that case state "Triage not invoked" and explain why (typically: discover returned `blocked` with execution evidence, so the orchestrator routed to `conclude` without dispatching `triage`).
778
+ - Evidence summary — what each round contributed for this hypothesis, what it demonstrated, contradictions across rounds (resolved at the weaker level)
779
+ - Primitive characterization — what kind of primitive the bug yields, and the demonstrated extent along each axis discover exercised (per `discover`'s adversary-maximal-evidence requirement)
780
+ - **Severity section — varies by status.** `confirmed` and `no demonstrated impact` files carry an *Achievable severity* subsection: the CVSS vector, score, and justification from triage, verbatim. `mitigated` and `insufficient` files OMIT *Achievable severity* entirely and instead carry a *Why no demonstrated severity* subsection explaining why the file does not anchor a score (mitigation absorbed the primitive before attacker-visible effect; evidence too thin for a severity assessment; etc.). Do NOT assign a severity for these statuses — they have not earned one. The hypothesis-ranking row in `report.md` shows `n/a` for severity in these cases.
781
+ - Mitigations — code-level fixes for this hypothesis, and any caller-side / configuration-level mitigations that apply. From triage when present, otherwise from `discover` or `analyze` artifacts. Not re-derived.
782
+ - References — file paths (file:line), CWE classification, related hypotheses if applicable
783
+
784
+ The four status bars (apply per-hypothesis, in the file's `Status:` line):
785
+
786
+ - **confirmed** — this hypothesis's `discoveries/findings.hN.md` documents an attacker-visible-effect observation at the severity triage assigned, AND its `triage.hN.md` returned `approved`. Without both, the file is NOT confirmed.
787
+ - **no demonstrated impact** — a detector flagged something (sanitizer, fuzz crash, static analyzer hit, tainted-flow report, assertion failure) but `discover` did not demonstrate an attacker-visible effect, or `triage` returned `insufficient`. The Mitigations section here describes the code-quality fix; the Achievable severity section names the gap that would need to be closed (parameter to sweep, mitigation to exercise, etc.) for this to become `confirmed`.
788
+ - **mitigated** — `discover` returned `blocked` with execution evidence the mitigation holds: coverage of the mitigation code AND a completed input sweep over the hypothesis's value range. Mitigations section identifies the mitigation's location and call-site coverage. The file OMITS *Achievable severity* and instead carries *Why no demonstrated severity*. Triage may not have been invoked for `mitigated` hypotheses (the orchestrator routes blocked-with-evidence cases straight to `conclude`), so do not require triage output for these. Hypotheses whose mitigation rests on text-only reasoning are NOT `mitigated` — they are `no demonstrated impact` with the mitigation analysis flagged as unverified.
789
+ - **insufficient** — triage returned `insufficient` or `escalate`, and the hypothesis is still open. The file OMITS *Achievable severity* and instead carries *Why no demonstrated severity*. Evidence summary names the specific gap.
790
+
791
+ ## Revision discipline (FORCE_REVISION re-entry)
792
+
793
+ When you re-enter `conclude` after a FORCE_REVISION, the orchestrator's directive in the "Scoping from the previous agent" section names which hypotheses changed and what to fix. ALSO read `.workflow/report_review/review.md` if present — it contains the LLM reviewer's per-checklist failure detail and any human-gate rejection text the orchestrator's directive may have summarized away. The directive scopes WHAT to re-author; `report_review/review.md` carries the specific gap descriptions you need to fix. Re-author ONLY the named `report.hN.md` files. Update `report.md` only if the change affects the executive summary, hypothesis ranking, or cross-cutting recommendations — leave it unchanged otherwise. Whatever set you re-author, refresh the `Updated:` line at the top of `report.md` to list exactly that subset (or `Updated: none` if only `report.md` itself changed).
794
+
795
+ Even on first authoring, your `agent_status` `notes` field MUST list the per-hypothesis files written or updated this round, in the form `report.hN.md` (comma-separated). On a fresh run that is every hypothesis file you created; on revision it is the subset you re-authored. Downstream review uses this list to scope incremental review, so omitting it forces a full re-read.
796
+
797
+ ## Verdict
798
+
799
+ Set verdict to `complete`. Your agent_status `notes` field should be a 1–2 sentence factual summary of the report's conclusion (mirror the Executive Summary, do not exaggerate), followed on a separate line by `Updated: report.h1.md, report.h3.md` listing the per-hypothesis files written or updated this round. This `notes` line is a convenience mirror for the next state's prompt context — the authoritative `Updated:` line lives at the top of `report.md` itself (see Index file requirements above), and downstream review reads the on-disk line, not `notes`.
800
+ inputs:
801
+ - journal
802
+ - analysis
803
+ - discoveries?
804
+ - differential?
805
+ - triage?
806
+ - report_review?
807
+ outputs:
808
+ - report
809
+ transitions:
810
+ - to: review
811
+
812
+ review:
813
+ type: agent
814
+ description: LLM review of the final report before the human gate — catches scope drift, evidence-headline contradictions, and writeup errata
815
+ persona: global
816
+ maxVisits: 2
817
+ skills: none
818
+ prompt: |
819
+ You are the report reviewer. The report is split into an index file (`.workflow/report/report.md`) and one self-contained file per hypothesis (`.workflow/report/report.hN.md`).
820
+
821
+ ## What to read
822
+
823
+ Always read: `.workflow/report/report.md` (index), `.workflow/journal/journal.md`, `.workflow/analysis/analysis.md`, plus the per-hypothesis `.workflow/discoveries/findings.h*.md` and `.workflow/triage/triage.h*.md` and `.workflow/differential/diagnosis.md` when present.
824
+
825
+ Per-hypothesis file scoping:
826
+
827
+ Read `.workflow/report/report.md` FIRST. The first line is an `Updated:` line listing the per-hypothesis files authored or modified in the most recent `conclude` round. This on-disk line — not the directive — is the authoritative source for scoping re-reads on the `conclude → review` hop (the orchestrator does not sit between those states, so `Dirty hypotheses:` and the propagated `Notes` may not reach you).
828
+
829
+ - **First-ever review** (the index's `Updated:` line is missing, or this is the first run and every hypothesis file is freshly authored): read EVERY `.workflow/report/report.h*.md` file. Full review.
830
+ - **Re-review after a rewrite** (the index's `Updated:` line names a subset of `report.hN.md` files): read ONLY those `report.hN.md` files plus the index. The unchanged hypothesis files were not touched and do not need re-review.
831
+ - **Edge case** (`Updated: none`, meaning the index changed but no hypothesis file did): re-read only the index.
832
+
833
+ If `Updated:` is malformed or ambiguous, fall back to reading all hypothesis files.
834
+
835
+ List the `.workflow/report/` directory at the start of every run to confirm which hypothesis files exist. The index's hypothesis-ranking table should name one `report.hN.md` per row; if a row points at a missing file, that's an errata fail (item 7).
836
+
837
+ If `discoveries/` is absent, no hypothesis file may carry `Status: confirmed` and the index's Investigation coverage section must call out that `discover` was not invoked.
838
+
839
+ Your job is to catch the failures that have historically reached human review and required FORCE_REVISION. You are NOT re-doing the investigation. You are NOT re-scoring severity. You ARE checking that the report's claims match the artifacts the report cites.
840
+
841
+ ## Checklist
842
+
843
+ Apply each item to the index file and to every hypothesis file in scope (full set on a fresh run, named subset on incremental review). Each item passes or fails on its own. ONE failure means the report is not ready.
844
+
845
+ 1. **Headline matches the artifacts.** The Executive Summary's load-bearing claim and the hypothesis-ranking table agree with the per-hypothesis `discoveries/findings.h*.md` and `triage/triage.h*.md`. A hypothesis with execution evidence in `discoveries/` (POC, sanitizer trace, observed attacker-visible effect, runtime sink reached) whose `report.hN.md` carries `Status: no demonstrated impact` or whose row is omitted from the ranking table is a fail — the canonical "buried CVE" pattern. If the report narrows the threat model to exclude an in-`discoveries/` hypothesis, the narrowing must be stated explicitly in the index AND the excluded hypothesis must still appear in the ranking table, not buried.
846
+
847
+ 2. **No contradiction-papering.** When `discoveries/`, `differential/`, `triage/`, or earlier rounds disagree with a hypothesis file's claims, the file calls out the contradiction in its Evidence summary AND scores at the weaker demonstrated level (per `conclude`'s "Contradiction handling" hard rule). Reconciliation by rhetoric ("the vulnerability is real, the sanitizer confirmed it") is a fail.
848
+
849
+ 3. **Severity matches triage verbatim.** Each `report.hN.md` with `Status: confirmed` or `Status: no demonstrated impact` must carry the CVSS vector, score, and justification from the corresponding `triage/triage.hN.md` exactly. Files with `Status: mitigated` or `Status: insufficient` MUST NOT carry an *Achievable severity* subsection — they carry *Why no demonstrated severity* instead, and their hypothesis-ranking row in the index shows `n/a`. Triage may legitimately not have been invoked for `mitigated` files (orchestrator routes blocked-with-evidence cases straight to `conclude`); for those, "no triage" does not block the status. For `confirmed` and `no demonstrated impact` files, triage must have run — if `triage` was never invoked, those statuses cannot stand. Re-derived severity, rounded scores, or strengthened wording ("Medium-High" where triage said "Medium") is a fail. The index's Hypothesis ranking severity column must match the per-hypothesis files (with `n/a` for the `mitigated` / `insufficient` rows).
850
+
851
+ 4. **Exploitability bar respected.** A hypothesis file may carry `Status: confirmed` only when BOTH a documented attacker-visible-effect observation in the corresponding `discoveries/findings.hN.md` AND an `approved` triage verdict apply to that hypothesis. Detector-only evidence — any tool that flagged something without a discover-demonstrated effect (sanitizer, fuzz crash, static analyzer, tainted-flow tracer, schema-validator alert, assertion failure, etc.) — must carry `Status: no demonstrated impact`; promoting it is a fail.
852
+
853
+ 5. **Out-of-scope side hypotheses stay honest.** Every hypothesis the report demotes (e.g., omitted from the ranking table, marked out-of-scope inside its file) is actually outside the task's scope as stated by the task description. A hypothesis moved out of the ranking purely to keep the headline clean — when the task description, threat model, or triage would treat it as in-scope — is a fail. When in doubt, lean toward in-scope: the human will rather see an extra hypothesis promoted than a real one demoted.
854
+
855
+ 6. **Coverage gaps stated as gaps.** The index's Investigation coverage section names what was NOT tested and why. Skipped routing states (e.g., `discover` never invoked, `triage` never invoked) are called out per `conclude`'s "Investigation coverage" guidance — silently omitting is a fail.
856
+
857
+ 6a. **Hypothesis ledger present and coherent.** The index's Investigation coverage section MUST carry a per-hypothesis ledger covering every analyze-produced hypothesis (`h1`, `h2`, ...) with a terminal status — `confirmed`, `harness-tested-and-rejected`, or `deferred (reason: ...)`. Every `deferred` entry MUST name a reason from the orchestrator's allowed set: (a) out-of-scope per task description, (b) intrinsic capability the workflow cannot acquire, (c) duplicate of another confirmed hypothesis. A `deferred (b)` entry whose underlying issue was a fixable harness-build problem (missing apt/pip dependency, missing build flag, missing codec library that exists as a package) is a fail (incoherent — should have routed through `harness_design` for the environment fix). A hypothesis enumerated in `analysis.md`'s cross-cutting section but missing from the ledger is a fail (silent drop). This is the canonical "premature complete on first hit" failure: a confirmed h1 plus an unmentioned h2 is a fail, not an approval. FORCE_REVISION when this item fails — it is the human gate's stand-in too.
858
+
859
+ 7. **Errata clean.** Round numbers, role-visit counts, item-ordering inside redirection lists, file paths (container vs. host layout), and cross-section references between the index and hypothesis files agree with each other and with the journal. Hypothesis-ranking rows that link to non-existent `report.hN.md` files, hypothesis files whose `Status:` line disagrees with the index ranking, off-by-one round numbers, and path strings the reader cannot resolve in their environment are fails — these have all reached prior human reviewers.
860
+
861
+ ## Verdicts
862
+
863
+ - **approved**: All seven items pass for every file in scope. Write `.workflow/report_review/review.md` listing what you verified (one bullet per checklist item) and which `report.hN.md` files were reviewed this round (full set vs. named subset). Concise.
864
+ - **rejected**: One or more items fail. Write `.workflow/report_review/review.md` listing each failure by checklist number, the specific file (`report.md` or `report.hN.md`), the specific problem, and what the fix needs. The fix instruction MUST name which `report.hN.md` files need re-authoring (and whether `report.md` itself needs an update) so the orchestrator can route a scoped revision. Be concrete enough that the orchestrator can route per its rules without re-deriving the gap.
865
+
866
+ ## Visit-cap handling
867
+
868
+ The state machine routes review → `report_review` (the human gate) when the visit cap fires, even if your most recent verdict was `rejected`. This is structurally identical to the `approved` transition, so the human gate cannot tell from the transition alone whether the loop terminated cleanly or was forced out. To prevent the human from mistaking a cap-out for an approval, when you are on your final allowed visit AND would emit `rejected`, write a short "review-cap-reached" header at the top of `.workflow/report_review/review.md` BEFORE the per-checklist failure list. State explicitly that the review loop ran out of visits with unresolved failures, that the report was NOT approved, and that the human is being asked to decide whether to FORCE_REVISION (return to orchestrator with the unresolved failures) or APPROVE (accept the report despite the open items). Keep the rest of the rejection content exactly as a normal `rejected` would write it.
869
+
870
+ Do NOT modify any file under `.workflow/report/` yourself. The orchestrator routes the rejection.
871
+ inputs:
872
+ - report
873
+ - discoveries?
874
+ - differential?
875
+ - triage?
876
+ - journal
877
+ - analysis
878
+ outputs:
879
+ - report_review
880
+ transitions:
881
+ - to: report_review
882
+ when:
883
+ verdict: approved
884
+ # Ordered before the rejection transition so the cap wins on the Nth visit;
885
+ # a deadlocked review loop lands in the human gate rather than spinning further.
886
+ - to: report_review
887
+ guard: isStateVisitLimitReached
888
+ - to: orchestrator
889
+ when:
890
+ verdict: rejected
891
+
892
+ report_review:
893
+ type: human_gate
894
+ description: |
895
+ Human review of the final vulnerability report. The report is split: read `.workflow/report/report.md` (the index) for the executive summary and hypothesis ranking, then drill into the per-hypothesis writeups at `.workflow/report/report.hN.md`. The first line of `report.md` is an `Updated:` line naming the `report.hN.md` files authored or modified in the latest `conclude` round — that on-disk line is the authoritative scoping signal (the human gate sees only artifacts, not orchestrator directives). On a fresh review, read every hypothesis file. On revision, read the index plus the `report.hN.md` files named in the index's `Updated:` line (cross-referenced with the LLM reviewer's `report_review/review.md`) — unchanged hypothesis files were not touched. When emitting FORCE_REVISION, your feedback prompt SHOULD name which `report.hN.md` files need re-authoring (and whether the index itself needs an update) so the orchestrator's next directive can scope the conclude re-entry to those files. Before approving, verify the index's "Investigation coverage" section contains a per-hypothesis ledger covering every analyze-produced hypothesis with a terminal status (`confirmed` / `harness-tested-and-rejected` / `deferred (reason: ...)`) — and FORCE_REVISION when the ledger is missing, when it contains silent omissions (a hypothesis enumerated in `analysis.md`'s cross-cutting section but not in the ledger), or when it carries an incoherent `deferred (b)` entry whose underlying issue was a fixable harness-build problem (missing apt/pip dependency, missing build flag, missing codec library that exists as a package).
896
+ acceptedEvents:
897
+ - APPROVE
898
+ - FORCE_REVISION
899
+ - ABORT
900
+ present:
901
+ - report
902
+ - report_review?
903
+ - discoveries
904
+ - triage
905
+ - journal
906
+ transitions:
907
+ - to: done
908
+ event: APPROVE
909
+ - to: orchestrator
910
+ event: FORCE_REVISION
911
+ - to: aborted
912
+ event: ABORT
913
+
914
+ done:
915
+ type: terminal
916
+ description: Investigation complete
917
+ outputs:
918
+ - report
919
+ - triage
920
+ - discoveries
921
+ - journal
922
+
923
+ aborted:
924
+ type: terminal
925
+ description: Investigation aborted