humanish 0.106.1 → 0.108.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 (668) hide show
  1. package/ARCHITECTURE.md +38 -32
  2. package/CHANGELOG.md +28 -0
  3. package/CONTEXT.md +51 -41
  4. package/README.md +152 -174
  5. package/SECURITY.md +16 -26
  6. package/TELEMETRY.md +8 -6
  7. package/dist/actors/affordance.d.ts +4 -4
  8. package/dist/actors/affordance.js +12 -12
  9. package/dist/actors/agent-session.d.ts +2 -2
  10. package/dist/actors/agent-session.js +7 -7
  11. package/dist/actors/codex/app-server-actor-trace.d.ts +1 -1
  12. package/dist/actors/codex/app-server-actor-trace.js +1 -1
  13. package/dist/actors/codex/app-server-client.js +2 -1
  14. package/dist/actors/codex/app-server-trace.d.ts +0 -1
  15. package/dist/actors/codex/app-server-trace.js +1 -3
  16. package/dist/actors/codex/app-server-ui.js +1 -1
  17. package/dist/actors/codex/app-server.js +2 -1
  18. package/dist/actors/codex/codex-admission.d.ts +91 -0
  19. package/dist/actors/codex/codex-admission.js +150 -0
  20. package/dist/actors/codex/protocol-compat.d.ts +27 -0
  21. package/dist/actors/codex/protocol-compat.js +357 -0
  22. package/dist/actors/codex/protocol-contract.d.ts +61 -0
  23. package/dist/actors/codex/protocol-contract.js +471 -0
  24. package/dist/actors/codex/restricted-executable.d.ts +28 -4
  25. package/dist/actors/codex/restricted-executable.js +104 -29
  26. package/dist/actors/codex/restricted-launch.d.ts +9 -2
  27. package/dist/actors/codex/restricted-launch.js +43 -4
  28. package/dist/actors/codex/restricted-notifications.d.ts +101 -0
  29. package/dist/actors/codex/restricted-notifications.js +277 -0
  30. package/dist/actors/codex/restricted-participant.d.ts +30 -0
  31. package/dist/actors/codex/restricted-participant.js +66 -6
  32. package/dist/actors/codex/restricted-policy.d.ts +11 -3
  33. package/dist/actors/codex/restricted-policy.js +2 -2
  34. package/dist/actors/codex/restricted-session.d.ts +16 -3
  35. package/dist/actors/codex/restricted-session.js +60 -22
  36. package/dist/actors/codex/restricted-transport.d.ts +29 -0
  37. package/dist/actors/codex/restricted-transport.js +139 -27
  38. package/dist/actors/codex/restricted-turn.d.ts +19 -4
  39. package/dist/actors/codex/restricted-turn.js +43 -30
  40. package/dist/actors/computer-use/actor.d.ts +17 -17
  41. package/dist/actors/computer-use/actor.js +6 -6
  42. package/dist/actors/computer-use/admission-limit.d.ts +1 -1
  43. package/dist/actors/computer-use/admission-limit.js +1 -1
  44. package/dist/actors/computer-use/loop/actions.d.ts +3 -3
  45. package/dist/actors/computer-use/loop/actions.js +10 -10
  46. package/dist/actors/computer-use/loop/backstop.d.ts +3 -3
  47. package/dist/actors/computer-use/loop/backstop.js +15 -13
  48. package/dist/actors/computer-use/loop/ending.d.ts +7 -7
  49. package/dist/actors/computer-use/loop/ending.js +9 -9
  50. package/dist/actors/computer-use/loop/observation.d.ts +2 -2
  51. package/dist/actors/computer-use/loop/observation.js +9 -9
  52. package/dist/actors/computer-use/loop/provider-call.d.ts +1 -1
  53. package/dist/actors/computer-use/loop/provider-call.js +1 -1
  54. package/dist/actors/computer-use/loop/session.js +3 -3
  55. package/dist/actors/computer-use/loop/spend.d.ts +2 -2
  56. package/dist/actors/computer-use/loop/spend.js +4 -4
  57. package/dist/actors/computer-use/loop/trace.js +11 -10
  58. package/dist/actors/computer-use/loop/types.d.ts +37 -26
  59. package/dist/actors/computer-use/loop/usage.js +2 -2
  60. package/dist/actors/computer-use/loop.js +4 -4
  61. package/dist/actors/computer-use/openai-context.d.ts +37 -0
  62. package/dist/actors/computer-use/openai-context.js +214 -0
  63. package/dist/actors/computer-use/openai-provider.d.ts +12 -9
  64. package/dist/actors/computer-use/openai-provider.js +47 -36
  65. package/dist/actors/computer-use/openai-requests.d.ts +36 -10
  66. package/dist/actors/computer-use/openai-requests.js +82 -15
  67. package/dist/actors/computer-use/openai-wire.d.ts +19 -7
  68. package/dist/actors/computer-use/openai-wire.js +36 -34
  69. package/dist/actors/contract.d.ts +86 -49
  70. package/dist/actors/contract.js +29 -23
  71. package/dist/actors/local-agent/claude-session.d.ts +1 -1
  72. package/dist/actors/local-agent/claude-session.js +10 -9
  73. package/dist/actors/local-agent/cli.d.ts +8 -6
  74. package/dist/actors/local-agent/cli.js +24 -28
  75. package/dist/actors/local-agent/readiness.d.ts +2 -2
  76. package/dist/actors/local-agent/readiness.js +4 -4
  77. package/dist/actors/reasoning-effort.js +6 -6
  78. package/dist/actors/registry.d.ts +10 -10
  79. package/dist/actors/registry.js +18 -17
  80. package/dist/actors/scripted-browser/actor.d.ts +8 -8
  81. package/dist/actors/scripted-browser/actor.js +16 -16
  82. package/dist/actors/scripted-browser/browser-command.js +1 -1
  83. package/dist/actors/scripted-browser/journey.js +15 -4
  84. package/dist/actors/scripted-browser/steps.js +19 -1
  85. package/dist/actors/scripted-browser/types.d.ts +12 -2
  86. package/dist/actors/stop-conditions.d.ts +1 -1
  87. package/dist/actors/stop-conditions.js +1 -3
  88. package/dist/analysis/automatic-completion.d.ts +13 -8
  89. package/dist/analysis/automatic-completion.js +14 -8
  90. package/dist/analysis/automatic-config.d.ts +1 -1
  91. package/dist/analysis/automatic-config.js +2 -2
  92. package/dist/analysis/automatic.d.ts +10 -4
  93. package/dist/analysis/automatic.js +17 -13
  94. package/dist/analysis/codex-config.d.ts +6 -2
  95. package/dist/analysis/codex-config.js +22 -14
  96. package/dist/analysis/diagnostics.d.ts +23 -0
  97. package/dist/analysis/diagnostics.js +112 -0
  98. package/dist/analysis/evidence-sources.js +2 -2
  99. package/dist/analysis/evidence.js +6 -6
  100. package/dist/analysis/execute.d.ts +7 -1
  101. package/dist/analysis/execute.js +39 -12
  102. package/dist/analysis/job.d.ts +8 -5
  103. package/dist/analysis/job.js +15 -14
  104. package/dist/analysis/load.d.ts +1 -1
  105. package/dist/analysis/load.js +5 -5
  106. package/dist/analysis/provider.d.ts +9 -1
  107. package/dist/analysis/restricted-codex.d.ts +2 -0
  108. package/dist/analysis/restricted-codex.js +10 -1
  109. package/dist/analysis/service.d.ts +2 -0
  110. package/dist/analysis/service.js +35 -16
  111. package/dist/analysis/sharing.d.ts +2 -2
  112. package/dist/analysis/sharing.js +5 -5
  113. package/dist/analysis/store-executions.d.ts +5 -5
  114. package/dist/analysis/store-executions.js +12 -12
  115. package/dist/analysis/store.d.ts +4 -4
  116. package/dist/analysis/store.js +13 -13
  117. package/dist/cli/commands/analysis-signals.d.ts +2 -1
  118. package/dist/cli/commands/analysis-signals.js +4 -1
  119. package/dist/cli/commands/analyze.js +23 -18
  120. package/dist/cli/commands/codex.js +9 -9
  121. package/dist/cli/commands/comms.js +62 -34
  122. package/dist/cli/commands/feedback.js +20 -21
  123. package/dist/cli/commands/lab-format.d.ts +5 -4
  124. package/dist/cli/commands/lab-format.js +35 -23
  125. package/dist/cli/commands/lab-route-computer-use.d.ts +0 -2
  126. package/dist/cli/commands/lab-route-computer-use.js +32 -21
  127. package/dist/cli/commands/lab-route-preview.d.ts +1 -3
  128. package/dist/cli/commands/lab-route-preview.js +7 -7
  129. package/dist/cli/commands/lab-route-run.d.ts +6 -4
  130. package/dist/cli/commands/lab-route-run.js +6 -2
  131. package/dist/cli/commands/lab-route-scripted.d.ts +0 -2
  132. package/dist/cli/commands/lab-route-scripted.js +0 -1
  133. package/dist/cli/commands/lab-route-shared-world.d.ts +0 -2
  134. package/dist/cli/commands/lab-route-shared-world.js +3 -4
  135. package/dist/cli/commands/lab-route-terminal.d.ts +0 -2
  136. package/dist/cli/commands/lab-route-terminal.js +0 -1
  137. package/dist/cli/commands/lab-run.js +84 -54
  138. package/dist/cli/commands/lab-scorer.d.ts +2 -4
  139. package/dist/cli/commands/lab-scorer.js +1 -1
  140. package/dist/cli/commands/migrate.d.ts +4 -0
  141. package/dist/cli/commands/migrate.js +65 -0
  142. package/dist/cli/commands/observe.js +168 -107
  143. package/dist/cli/commands/run-command.d.ts +10 -0
  144. package/dist/cli/commands/run-command.js +85 -0
  145. package/dist/cli/commands/run-signals.d.ts +30 -0
  146. package/dist/cli/commands/run-signals.js +146 -0
  147. package/dist/cli/commands/runs.js +137 -136
  148. package/dist/cli/commands/setup.d.ts +2 -2
  149. package/dist/cli/commands/setup.js +26 -21
  150. package/dist/cli/commands/study.d.ts +3 -0
  151. package/dist/cli/commands/study.js +182 -0
  152. package/dist/cli/commands/tui.d.ts +4 -4
  153. package/dist/cli/commands/tui.js +21 -21
  154. package/dist/cli/commands/watch.js +64 -77
  155. package/dist/cli/deprecations.d.ts +18 -0
  156. package/dist/cli/deprecations.js +48 -0
  157. package/dist/cli/doctor.d.ts +18 -5
  158. package/dist/cli/doctor.js +111 -48
  159. package/dist/cli/first-run-path.d.ts +12 -10
  160. package/dist/cli/first-run-path.js +30 -28
  161. package/dist/cli/io.d.ts +46 -10
  162. package/dist/cli/io.js +109 -30
  163. package/dist/cli/observer-follow.d.ts +2 -2
  164. package/dist/cli/observer-follow.js +5 -6
  165. package/dist/cli/orientation.d.ts +6 -3
  166. package/dist/cli/orientation.js +43 -39
  167. package/dist/cli/product-sentence.d.ts +6 -0
  168. package/dist/cli/product-sentence.js +6 -0
  169. package/dist/cli/program.d.ts +6 -1
  170. package/dist/cli/program.js +112 -43
  171. package/dist/cli/telemetry.d.ts +17 -17
  172. package/dist/cli/telemetry.js +49 -41
  173. package/dist/cli.js +1 -1
  174. package/dist/comms/capture-surface.d.ts +14 -13
  175. package/dist/comms/capture-surface.js +23 -22
  176. package/dist/comms/catch-host.d.ts +8 -8
  177. package/dist/comms/catch-host.js +14 -14
  178. package/dist/comms/connections.js +4 -4
  179. package/dist/comms/email-catch.d.ts +3 -3
  180. package/dist/comms/email-catch.js +8 -8
  181. package/dist/comms/external-evidence.d.ts +27 -0
  182. package/dist/comms/external-evidence.js +93 -0
  183. package/dist/comms/extract.js +2 -2
  184. package/dist/comms/fake-inbox.d.ts +2 -2
  185. package/dist/comms/fake-inbox.js +2 -2
  186. package/dist/comms/lease-store.js +7 -7
  187. package/dist/comms/receiving-render.d.ts +1 -1
  188. package/dist/comms/receiving-render.js +2 -2
  189. package/dist/comms/sandbox-catch-script.d.ts +6 -6
  190. package/dist/comms/sandbox-catch-script.js +6 -6
  191. package/dist/comms/sandbox-catch.d.ts +36 -37
  192. package/dist/comms/sandbox-catch.js +34 -35
  193. package/dist/comms/setup.js +42 -8
  194. package/dist/comms/thread-evidence.d.ts +1 -1
  195. package/dist/comms/thread-evidence.js +6 -6
  196. package/dist/comms/types.d.ts +6 -6
  197. package/dist/comms/types.js +4 -4
  198. package/dist/evidence/browser-hygiene.d.ts +1 -1
  199. package/dist/evidence/browser-hygiene.js +1 -1
  200. package/dist/evidence/frame-signature.d.ts +1 -1
  201. package/dist/evidence/frame-signature.js +4 -4
  202. package/dist/evidence/redaction.d.ts +3 -3
  203. package/dist/evidence/redaction.js +5 -5
  204. package/dist/evidence/secret-scrub.d.ts +9 -0
  205. package/dist/evidence/secret-scrub.js +96 -0
  206. package/dist/feedback/draft.js +41 -85
  207. package/dist/feedback/export-bundle.js +5 -5
  208. package/dist/feedback/export.d.ts +5 -1
  209. package/dist/feedback/export.js +16 -17
  210. package/dist/feedback/feedback.d.ts +3 -3
  211. package/dist/feedback/feedback.js +21 -3
  212. package/dist/guest/chromium-text.js +2 -2
  213. package/dist/guest/desktop-native.d.ts +1 -1
  214. package/dist/guest/runtime.js +1 -1
  215. package/dist/index.d.ts +14 -15
  216. package/dist/index.js +10 -11
  217. package/dist/keys/key-resolution.d.ts +7 -5
  218. package/dist/keys/key-resolution.js +40 -38
  219. package/dist/lab/adapter-extension.d.ts +35 -13
  220. package/dist/lab/adapter-extension.js +50 -134
  221. package/dist/lab/adapter-scorer-loader.d.ts +32 -12
  222. package/dist/lab/adapter-scorer-loader.js +56 -32
  223. package/dist/lab/composition-rules.js +58 -58
  224. package/dist/lab/config.js +85 -43
  225. package/dist/lab/deprecated-fields.d.ts +10 -0
  226. package/dist/lab/deprecated-fields.js +24 -0
  227. package/dist/lab/device-presets.d.ts +10 -10
  228. package/dist/lab/device-presets.js +11 -11
  229. package/dist/lab/discover.d.ts +13 -9
  230. package/dist/lab/discover.js +118 -60
  231. package/dist/lab/doctor.d.ts +53 -11
  232. package/dist/lab/doctor.js +193 -47
  233. package/dist/lab/init-plan.js +1 -3
  234. package/dist/lab/init-templates.js +147 -147
  235. package/dist/lab/init.d.ts +1 -1
  236. package/dist/lab/init.js +41 -9
  237. package/dist/lab/keys.d.ts +2 -0
  238. package/dist/lab/keys.js +43 -9
  239. package/dist/lab/lab-deps.d.ts +80 -0
  240. package/dist/lab/lab-deps.js +4 -0
  241. package/dist/lab/parse/actors.js +17 -16
  242. package/dist/lab/parse/comms.js +8 -7
  243. package/dist/lab/parse/execution.js +15 -14
  244. package/dist/lab/parse/study-v3.d.ts +23 -0
  245. package/dist/lab/parse/study-v3.js +259 -0
  246. package/dist/lab/parse/subject-state.d.ts +7 -7
  247. package/dist/lab/parse/subject-state.js +18 -17
  248. package/dist/lab/parse/subject.js +30 -29
  249. package/dist/lab/parse/values.d.ts +1 -2
  250. package/dist/lab/parse/values.js +3 -6
  251. package/dist/lab/persona-resolve.d.ts +7 -7
  252. package/dist/lab/persona-resolve.js +8 -8
  253. package/dist/lab/persona.js +1 -3
  254. package/dist/lab/plan-base.d.ts +0 -2
  255. package/dist/lab/plan-base.js +6 -4
  256. package/dist/lab/plan-participants.d.ts +7 -7
  257. package/dist/lab/plan-participants.js +2 -2
  258. package/dist/lab/plan-types.d.ts +26 -8
  259. package/dist/lab/plan.d.ts +4 -18
  260. package/dist/lab/plan.js +24 -79
  261. package/dist/lab/preflight-probes.js +11 -11
  262. package/dist/lab/preflight-result.d.ts +2 -1
  263. package/dist/lab/preflight-result.js +5 -6
  264. package/dist/lab/preflight.d.ts +7 -8
  265. package/dist/lab/preflight.js +6 -6
  266. package/dist/lab/requirements.d.ts +20 -0
  267. package/dist/lab/requirements.js +54 -0
  268. package/dist/lab/route-inputs.d.ts +23 -12
  269. package/dist/lab/route-inputs.js +48 -51
  270. package/dist/lab/routing.d.ts +42 -41
  271. package/dist/lab/routing.js +66 -68
  272. package/dist/lab/run-lab-events.d.ts +3 -11
  273. package/dist/lab/run-lab-events.js +2 -16
  274. package/dist/lab/run-lab-homes.d.ts +76 -0
  275. package/dist/lab/run-lab-homes.js +7 -0
  276. package/dist/lab/run-lab-options.d.ts +12 -66
  277. package/dist/lab/run-lab-options.js +58 -285
  278. package/dist/lab/summary.d.ts +15 -9
  279. package/dist/lab/summary.js +24 -17
  280. package/dist/lab/tasks.d.ts +15 -15
  281. package/dist/lab/tasks.js +14 -14
  282. package/dist/lab/types.d.ts +201 -195
  283. package/dist/lab/types.js +2 -0
  284. package/dist/lab/validation.d.ts +21 -21
  285. package/dist/lab/validation.js +61 -59
  286. package/dist/lab/warnings.d.ts +10 -0
  287. package/dist/lab/warnings.js +70 -41
  288. package/dist/library-aliases.d.ts +56 -0
  289. package/dist/library-aliases.js +22 -0
  290. package/dist/observer/artifact.d.ts +2 -2
  291. package/dist/observer/artifact.js +9 -9
  292. package/dist/observer/data.d.ts +6 -6
  293. package/dist/observer/data.js +7 -6
  294. package/dist/observer/exposure.d.ts +1 -1
  295. package/dist/observer/exposure.js +11 -11
  296. package/dist/observer/http.js +1 -1
  297. package/dist/observer/library.d.ts +1 -1
  298. package/dist/observer/library.js +4 -2
  299. package/dist/observer/listen.js +5 -4
  300. package/dist/observer/render.d.ts +1 -1
  301. package/dist/observer/render.js +12 -7
  302. package/dist/observer/run-routes.d.ts +4 -4
  303. package/dist/observer/run-routes.js +6 -6
  304. package/dist/observer/serve.d.ts +17 -0
  305. package/dist/observer/serve.js +41 -6
  306. package/dist/observer/tunnel.js +1 -1
  307. package/dist/observer-app.html +4 -4
  308. package/dist/routes/computer-use/bundle-parts.d.ts +9 -9
  309. package/dist/routes/computer-use/bundle-parts.js +24 -17
  310. package/dist/routes/computer-use/bundle.d.ts +4 -11
  311. package/dist/routes/computer-use/bundle.js +12 -18
  312. package/dist/routes/computer-use/diagnostics.d.ts +1 -1
  313. package/dist/routes/computer-use/diagnostics.js +1 -1
  314. package/dist/routes/computer-use/e2b-desktop/comms.d.ts +11 -11
  315. package/dist/routes/computer-use/e2b-desktop/comms.js +24 -27
  316. package/dist/routes/computer-use/e2b-desktop/desktop.d.ts +2 -2
  317. package/dist/routes/computer-use/e2b-desktop/desktop.js +8 -11
  318. package/dist/routes/computer-use/e2b-desktop/fidelity.d.ts +16 -15
  319. package/dist/routes/computer-use/e2b-desktop/fidelity.js +20 -19
  320. package/dist/routes/computer-use/e2b-desktop/prepare.d.ts +3 -3
  321. package/dist/routes/computer-use/e2b-desktop/prepare.js +40 -43
  322. package/dist/routes/computer-use/e2b-desktop/start.d.ts +1 -1
  323. package/dist/routes/computer-use/e2b-desktop/start.js +32 -30
  324. package/dist/routes/computer-use/e2b-desktop/state.d.ts +5 -4
  325. package/dist/routes/computer-use/e2b-desktop/state.js +5 -3
  326. package/dist/routes/computer-use/e2b-desktop/teardown.d.ts +1 -1
  327. package/dist/routes/computer-use/e2b-desktop/teardown.js +33 -20
  328. package/dist/routes/computer-use/external-comms.d.ts +4 -4
  329. package/dist/routes/computer-use/external-comms.js +19 -43
  330. package/dist/routes/computer-use/fanout-bundle.d.ts +3 -3
  331. package/dist/routes/computer-use/fanout-bundle.js +38 -42
  332. package/dist/routes/computer-use/{fanout-lanes.js → fanout-records.js} +33 -28
  333. package/dist/routes/computer-use/in-process-desktop.d.ts +3 -10
  334. package/dist/routes/computer-use/in-process-desktop.js +3 -9
  335. package/dist/routes/computer-use/live-flush.d.ts +7 -5
  336. package/dist/routes/computer-use/live-flush.js +20 -28
  337. package/dist/routes/computer-use/{run-lanes.d.ts → live-phase.d.ts} +4 -2
  338. package/dist/routes/computer-use/{run-lanes.js → live-phase.js} +17 -16
  339. package/dist/routes/computer-use/local-tree-pack.d.ts +3 -3
  340. package/dist/routes/computer-use/local-tree-pack.js +4 -5
  341. package/dist/routes/computer-use/local-vm.d.ts +11 -8
  342. package/dist/routes/computer-use/local-vm.js +40 -67
  343. package/dist/routes/computer-use/participant-desktop.d.ts +8 -23
  344. package/dist/routes/computer-use/participant-desktop.js +5 -25
  345. package/dist/routes/computer-use/{lanes.d.ts → participant-execution.d.ts} +11 -11
  346. package/dist/routes/computer-use/{lanes.js → participant-execution.js} +39 -26
  347. package/dist/routes/computer-use/participant-facts.d.ts +7 -1
  348. package/dist/routes/computer-use/participant-facts.js +12 -1
  349. package/dist/routes/computer-use/participant-model.d.ts +25 -11
  350. package/dist/routes/computer-use/participant-model.js +76 -34
  351. package/dist/routes/computer-use/participant-prompt.d.ts +13 -13
  352. package/dist/routes/computer-use/participant-prompt.js +8 -8
  353. package/dist/routes/computer-use/participant-runs.d.ts +11 -11
  354. package/dist/routes/computer-use/participant-runs.js +18 -18
  355. package/dist/routes/computer-use/plan.d.ts +21 -29
  356. package/dist/routes/computer-use/plan.js +70 -78
  357. package/dist/routes/computer-use/preflight.d.ts +3 -5
  358. package/dist/routes/computer-use/preflight.js +40 -36
  359. package/dist/routes/computer-use/rerun-selection.js +11 -7
  360. package/dist/routes/computer-use/result.d.ts +1 -1
  361. package/dist/routes/computer-use/result.js +48 -38
  362. package/dist/routes/computer-use/route.d.ts +5 -5
  363. package/dist/routes/computer-use/route.js +36 -33
  364. package/dist/routes/computer-use/self-report.d.ts +8 -8
  365. package/dist/routes/computer-use/self-report.js +33 -33
  366. package/dist/routes/computer-use/setup.d.ts +9 -15
  367. package/dist/routes/computer-use/setup.js +106 -83
  368. package/dist/routes/computer-use/single-bundle.d.ts +13 -16
  369. package/dist/routes/computer-use/single-bundle.js +46 -53
  370. package/dist/routes/computer-use/subject-projection.d.ts +6 -8
  371. package/dist/routes/computer-use/subject-projection.js +24 -23
  372. package/dist/routes/computer-use/types.d.ts +195 -238
  373. package/dist/routes/computer-use/types.js +21 -13
  374. package/dist/routes/preview.d.ts +8 -2
  375. package/dist/routes/preview.js +8 -5
  376. package/dist/routes/scripted/bundle.d.ts +5 -8
  377. package/dist/routes/scripted/bundle.js +23 -27
  378. package/dist/routes/scripted/plan.d.ts +6 -8
  379. package/dist/routes/scripted/plan.js +15 -13
  380. package/dist/routes/scripted/result.d.ts +4 -1
  381. package/dist/routes/scripted/result.js +15 -11
  382. package/dist/routes/scripted/route.js +15 -17
  383. package/dist/routes/scripted/scenario.d.ts +1 -1
  384. package/dist/routes/scripted/scenario.js +3 -3
  385. package/dist/routes/scripted/setup.d.ts +3 -2
  386. package/dist/routes/scripted/setup.js +27 -26
  387. package/dist/routes/scripted/subject.d.ts +8 -3
  388. package/dist/routes/scripted/subject.js +10 -6
  389. package/dist/routes/scripted/surface-records.js +4 -4
  390. package/dist/routes/scripted/surface-sessions.d.ts +2 -2
  391. package/dist/routes/scripted/surface-sessions.js +4 -4
  392. package/dist/routes/scripted/types.d.ts +21 -40
  393. package/dist/routes/scripted/types.js +2 -3
  394. package/dist/routes/shared-world/{seat-records.js → bundle-records.js} +17 -16
  395. package/dist/routes/shared-world/bundle.d.ts +7 -7
  396. package/dist/routes/shared-world/bundle.js +60 -62
  397. package/dist/routes/shared-world/checkpoints.d.ts +5 -5
  398. package/dist/routes/shared-world/checkpoints.js +5 -5
  399. package/dist/routes/shared-world/comms.d.ts +8 -7
  400. package/dist/routes/shared-world/comms.js +38 -51
  401. package/dist/routes/shared-world/external-public.d.ts +6 -5
  402. package/dist/routes/shared-world/external-public.js +28 -25
  403. package/dist/routes/shared-world/handoff.d.ts +10 -9
  404. package/dist/routes/shared-world/handoff.js +89 -81
  405. package/dist/routes/shared-world/lobby-code.d.ts +7 -7
  406. package/dist/routes/shared-world/lobby-code.js +22 -22
  407. package/dist/routes/shared-world/{seats.d.ts → participant-specs.d.ts} +15 -12
  408. package/dist/routes/shared-world/{seats.js → participant-specs.js} +65 -63
  409. package/dist/routes/shared-world/plan.d.ts +2 -6
  410. package/dist/routes/shared-world/plan.js +8 -9
  411. package/dist/routes/shared-world/provenance.d.ts +4 -4
  412. package/dist/routes/shared-world/provenance.js +4 -4
  413. package/dist/routes/shared-world/provisioned.d.ts +3 -2
  414. package/dist/routes/shared-world/provisioned.js +55 -48
  415. package/dist/routes/shared-world/result.js +47 -36
  416. package/dist/routes/shared-world/route.js +54 -57
  417. package/dist/routes/shared-world/setup.d.ts +6 -5
  418. package/dist/routes/shared-world/setup.js +16 -18
  419. package/dist/routes/shared-world/types.d.ts +70 -141
  420. package/dist/routes/shared-world/types.js +5 -5
  421. package/dist/routes/terminal/adapter.d.ts +10 -10
  422. package/dist/routes/terminal/adapter.js +32 -129
  423. package/dist/routes/terminal/bundle.d.ts +10 -15
  424. package/dist/routes/terminal/bundle.js +43 -52
  425. package/dist/routes/terminal/credentials.d.ts +4 -4
  426. package/dist/routes/terminal/credentials.js +20 -20
  427. package/dist/routes/terminal/dry-run.d.ts +1 -1
  428. package/dist/routes/terminal/dry-run.js +13 -15
  429. package/dist/routes/terminal/encoding.d.ts +1 -1
  430. package/dist/routes/terminal/encoding.js +4 -4
  431. package/dist/routes/terminal/ledger.d.ts +8 -8
  432. package/dist/routes/terminal/ledger.js +22 -22
  433. package/dist/routes/terminal/live-finish.d.ts +1 -2
  434. package/dist/routes/terminal/live-finish.js +24 -23
  435. package/dist/routes/terminal/live-sandbox.d.ts +3 -2
  436. package/dist/routes/terminal/live-sandbox.js +31 -32
  437. package/dist/routes/terminal/participant-text.d.ts +23 -0
  438. package/dist/routes/terminal/participant-text.js +165 -0
  439. package/dist/routes/terminal/persona.js +1 -1
  440. package/dist/routes/terminal/plan.d.ts +2 -6
  441. package/dist/routes/terminal/plan.js +23 -19
  442. package/dist/routes/terminal/recorder.d.ts +6 -1
  443. package/dist/routes/terminal/recorder.js +12 -4
  444. package/dist/routes/terminal/result.js +12 -12
  445. package/dist/routes/terminal/route.js +30 -31
  446. package/dist/routes/terminal/runtime.d.ts +3 -2
  447. package/dist/routes/terminal/runtime.js +3 -3
  448. package/dist/routes/terminal/sandbox.d.ts +4 -4
  449. package/dist/routes/terminal/sandbox.js +10 -10
  450. package/dist/routes/terminal/session.js +22 -19
  451. package/dist/routes/terminal/token-usage.d.ts +1 -1
  452. package/dist/routes/terminal/token-usage.js +10 -11
  453. package/dist/routes/terminal/trace.d.ts +7 -10
  454. package/dist/routes/terminal/trace.js +11 -66
  455. package/dist/routes/terminal/types.d.ts +104 -114
  456. package/dist/routes/terminal/types.js +12 -7
  457. package/dist/run/active-runs.d.ts +13 -0
  458. package/dist/run/active-runs.js +12 -0
  459. package/dist/run/bundle-shape.d.ts +2 -1
  460. package/dist/run/bundle-shape.js +7 -7
  461. package/dist/run/bundle.d.ts +99 -92
  462. package/dist/run/bundle.js +10 -8
  463. package/dist/run/cost-summary.js +6 -5
  464. package/dist/run/costs.d.ts +6 -6
  465. package/dist/run/costs.js +17 -11
  466. package/dist/run/detail.d.ts +9 -9
  467. package/dist/run/detail.js +10 -10
  468. package/dist/run/dry-run-inputs.js +1 -1
  469. package/dist/run/dry-run.js +23 -30
  470. package/dist/run/{study-files.d.ts → evidence-files.d.ts} +4 -4
  471. package/dist/run/{study-files.js → evidence-files.js} +5 -5
  472. package/dist/run/feedback-shape.js +1 -1
  473. package/dist/run/judge.d.ts +34 -16
  474. package/dist/run/judge.js +41 -14
  475. package/dist/run/locate.d.ts +2 -0
  476. package/dist/run/locate.js +3 -1
  477. package/dist/run/outcomes.d.ts +3 -3
  478. package/dist/run/outcomes.js +3 -3
  479. package/dist/run/participant-records.d.ts +2 -2
  480. package/dist/run/participant-records.js +2 -2
  481. package/dist/run/paths.d.ts +1 -1
  482. package/dist/run/paths.js +2 -2
  483. package/dist/run/pricing.d.ts +30 -15
  484. package/dist/run/pricing.js +49 -30
  485. package/dist/run/projection.d.ts +27 -27
  486. package/dist/run/projection.js +23 -23
  487. package/dist/run/reclaim.d.ts +6 -0
  488. package/dist/run/reclaim.js +15 -5
  489. package/dist/run/results.d.ts +5 -5
  490. package/dist/run/run-index.d.ts +9 -7
  491. package/dist/run/run-index.js +24 -25
  492. package/dist/run/run-not-found.d.ts +5 -0
  493. package/dist/run/run-not-found.js +15 -0
  494. package/dist/run/run.d.ts +7 -4
  495. package/dist/run/run.js +10 -6
  496. package/dist/run/sandbox-receipts.d.ts +5 -5
  497. package/dist/run/sandbox-receipts.js +6 -6
  498. package/dist/run/shared-world-evidence.d.ts +67 -66
  499. package/dist/run/shared-world-evidence.js +4 -4
  500. package/dist/run/shared-world-shape.d.ts +3 -3
  501. package/dist/run/shared-world-shape.js +16 -16
  502. package/dist/run/stats.d.ts +17 -11
  503. package/dist/run/stats.js +124 -66
  504. package/dist/run/status.d.ts +21 -32
  505. package/dist/run/status.js +34 -33
  506. package/dist/run/stored-runs.js +2 -1
  507. package/dist/run/stream-shape.js +1 -1
  508. package/dist/run/streams.d.ts +29 -22
  509. package/dist/run/study-provenance.d.ts +47 -0
  510. package/dist/run/study-provenance.js +63 -0
  511. package/dist/run/study-result.d.ts +16 -0
  512. package/dist/run/study-result.js +9 -0
  513. package/dist/run/terminal-contract.d.ts +5 -5
  514. package/dist/run/terminal-contract.js +4 -4
  515. package/dist/run/text.d.ts +2 -0
  516. package/dist/run/text.js +4 -0
  517. package/dist/run/transient-comms-secrets.d.ts +5 -0
  518. package/dist/run/transient-comms-secrets.js +12 -0
  519. package/dist/run-lab.d.ts +40 -38
  520. package/dist/run-lab.js +42 -40
  521. package/dist/study/convert.d.ts +33 -0
  522. package/dist/study/convert.js +432 -0
  523. package/dist/study/field-paths.d.ts +12 -0
  524. package/dist/study/field-paths.js +126 -0
  525. package/dist/study/files.d.ts +26 -0
  526. package/dist/study/files.js +43 -0
  527. package/dist/study/migrate.d.ts +48 -0
  528. package/dist/study/migrate.js +408 -0
  529. package/dist/study/owned-files.d.ts +38 -0
  530. package/dist/study/owned-files.js +182 -0
  531. package/dist/subject/clone.d.ts +1 -1
  532. package/dist/subject/desktop-cli.d.ts +2 -2
  533. package/dist/subject/desktop-cli.js +3 -3
  534. package/dist/subject/local-tree-archive.d.ts +1 -1
  535. package/dist/subject/local-tree-archive.js +5 -5
  536. package/dist/subject/local-tree.d.ts +3 -3
  537. package/dist/subject/local-tree.js +1 -1
  538. package/dist/subject/node-bootstrap.d.ts +1 -1
  539. package/dist/subject/node-bootstrap.js +4 -4
  540. package/dist/subject/runtime.js +10 -11
  541. package/dist/subject/serve.d.ts +1 -1
  542. package/dist/subject/serve.js +4 -4
  543. package/dist/subject/state.d.ts +1 -1
  544. package/dist/subject/steps.d.ts +8 -8
  545. package/dist/subject/steps.js +7 -7
  546. package/dist/substrates/command-failure.d.ts +3 -3
  547. package/dist/substrates/command-failure.js +3 -3
  548. package/dist/substrates/detached.d.ts +6 -6
  549. package/dist/substrates/detached.js +7 -7
  550. package/dist/substrates/e2b/cdp-probe.d.ts +11 -11
  551. package/dist/substrates/e2b/cdp-probe.js +15 -18
  552. package/dist/substrates/e2b/desktop-browser.d.ts +5 -5
  553. package/dist/substrates/e2b/desktop-browser.js +12 -12
  554. package/dist/substrates/e2b/desktop-cdp.d.ts +15 -15
  555. package/dist/substrates/e2b/desktop-cdp.js +11 -11
  556. package/dist/substrates/e2b/desktop-executor.d.ts +1 -1
  557. package/dist/substrates/e2b/desktop-executor.js +1 -1
  558. package/dist/substrates/e2b/desktop-geometry.d.ts +6 -6
  559. package/dist/substrates/e2b/desktop-geometry.js +9 -9
  560. package/dist/substrates/e2b/desktop-media.d.ts +1 -1
  561. package/dist/substrates/e2b/desktop-media.js +3 -3
  562. package/dist/substrates/e2b/desktop-screenshot-cleanup.d.ts +3 -3
  563. package/dist/substrates/e2b/desktop-screenshot-cleanup.js +3 -3
  564. package/dist/substrates/e2b/sandbox.d.ts +1 -1
  565. package/dist/substrates/e2b/sandbox.js +2 -2
  566. package/dist/substrates/e2b/sdk.d.ts +11 -11
  567. package/dist/substrates/e2b/sdk.js +8 -8
  568. package/dist/substrates/local/runtime-host.d.ts +2 -0
  569. package/dist/substrates/local/runtime-host.js +6 -0
  570. package/dist/tui/actions.d.ts +8 -6
  571. package/dist/tui/actions.js +31 -22
  572. package/dist/tui/contract.d.ts +16 -16
  573. package/dist/tui/contract.js +10 -10
  574. package/dist/tui/launch.d.ts +4 -4
  575. package/dist/tui/launch.js +18 -19
  576. package/dist/tui/project.js +5 -5
  577. package/dist/tui-app.js +83 -83
  578. package/dist/verify/actor.d.ts +14 -14
  579. package/dist/verify/actor.js +45 -45
  580. package/dist/verify/artifacts.d.ts +3 -3
  581. package/dist/verify/artifacts.js +6 -6
  582. package/dist/verify/context-growth.d.ts +9 -0
  583. package/dist/verify/context-growth.js +62 -0
  584. package/dist/verify/costs.d.ts +7 -7
  585. package/dist/verify/costs.js +9 -9
  586. package/dist/verify/liveness.d.ts +1 -1
  587. package/dist/verify/liveness.js +4 -1
  588. package/dist/verify/rerun.d.ts +1 -1
  589. package/dist/verify/rerun.js +1 -1
  590. package/dist/verify/shared-world-concurrent.d.ts +2 -2
  591. package/dist/verify/shared-world-concurrent.js +79 -76
  592. package/dist/verify/shared-world.d.ts +2 -2
  593. package/dist/verify/shared-world.js +18 -18
  594. package/dist/verify/subject.d.ts +4 -4
  595. package/dist/verify/subject.js +18 -18
  596. package/dist/verify/verify.js +98 -49
  597. package/docs/README.md +26 -33
  598. package/docs/architecture/actor-contract.md +105 -98
  599. package/docs/architecture/desktop-sessions.md +14 -14
  600. package/docs/architecture/external-public-shared-world.md +66 -59
  601. package/docs/architecture/github-feedback-loop.md +1 -4
  602. package/docs/architecture/guest-desktop.md +2 -2
  603. package/docs/architecture/observer.md +37 -37
  604. package/docs/architecture/restricted-codex-analysis.md +70 -30
  605. package/docs/architecture/serve.md +34 -37
  606. package/docs/architecture/state-driven-executor.md +55 -59
  607. package/docs/architecture/task-protocol-support.md +7 -9
  608. package/docs/architecture/{terminal-product-lane.md → terminal-product-route.md} +83 -115
  609. package/docs/contracts/README.md +24 -0
  610. package/docs/contracts/adapter-admission.md +3 -3
  611. package/docs/contracts/adapter-fixtures.md +2 -5
  612. package/docs/contracts/core.md +1 -3
  613. package/docs/contracts/errors.md +62 -0
  614. package/docs/contracts/feedback.md +8 -9
  615. package/docs/contracts/policy.md +7 -12
  616. package/docs/contracts/run-bundle.md +54 -51
  617. package/docs/contracts/schemas.md +505 -390
  618. package/docs/contracts/study-analysis.md +26 -2
  619. package/docs/contracts/study-costs.md +11 -7
  620. package/docs/decisions/0001-run-bundle-is-the-source-of-truth.md +1 -1
  621. package/docs/decisions/0002-observer-is-one-self-contained-file.md +2 -2
  622. package/docs/decisions/0003-contained-paths-and-cleanup-authority.md +3 -3
  623. package/docs/decisions/0004-participant-desktops-stay-in-the-route.md +2 -1
  624. package/docs/principles/actor-fidelity.md +4 -4
  625. package/docs/principles/engineering.md +3 -3
  626. package/docs/principles/invariants-and-defaults.md +38 -38
  627. package/docs/principles/self-driving-harness.md +1 -5
  628. package/docs/principles/three-roles.md +1 -1
  629. package/docs/product/automatic-analysis.md +8 -7
  630. package/examples/participant/README.md +5 -5
  631. package/examples/participant/run.mjs +11 -10
  632. package/examples/scorer/README.md +3 -3
  633. package/examples/scorer/run.mjs +11 -12
  634. package/package.json +16 -17
  635. package/skills/humanish/SKILL.md +63 -60
  636. package/skills/humanish/agents/openai.yaml +1 -1
  637. package/AGENTS.md +0 -99
  638. package/CONTRIBUTING.md +0 -148
  639. package/dist/actors/codex/qualified-versions.d.ts +0 -45
  640. package/dist/actors/codex/qualified-versions.js +0 -87
  641. package/dist/cli/commands/lab.d.ts +0 -3
  642. package/dist/cli/commands/lab.js +0 -193
  643. package/dist/deprecated.d.ts +0 -90
  644. package/dist/deprecated.js +0 -106
  645. package/dist/lab/bag-overrides.d.ts +0 -18
  646. package/dist/lab/bag-overrides.js +0 -121
  647. package/dist/observer/static.d.ts +0 -39
  648. package/dist/observer/static.js +0 -369
  649. package/dist/routes/computer-use/legacy-lane-spec.d.ts +0 -62
  650. package/dist/routes/computer-use/legacy-lane-spec.js +0 -39
  651. package/docs/architecture/comms-inbox.md +0 -122
  652. package/docs/architecture/desktop-recording.md +0 -80
  653. package/docs/architecture/local-browser-runtime.md +0 -178
  654. package/docs/architecture/observer-review.md +0 -144
  655. package/docs/architecture/participant-media.md +0 -90
  656. package/docs/architecture/project-layout.md +0 -62
  657. package/docs/architecture/real-email-receiving.md +0 -136
  658. package/docs/assets/humanish-drawdb-hero.png +0 -0
  659. package/docs/assets/humanish-observer-hero.png +0 -0
  660. package/docs/goals/current.md +0 -207
  661. package/docs/product/lobby-trivia-3player-external-public.md +0 -77
  662. package/docs/product/open-source-install-experience.md +0 -240
  663. package/docs/ramp/README.md +0 -294
  664. package/docs/release/open-source-readiness.md +0 -222
  665. package/docs/release/public-readiness-standard.md +0 -210
  666. package/docs/roadmap/world-class-open-source-v0.md +0 -311
  667. /package/dist/routes/computer-use/{fanout-lanes.d.ts → fanout-records.d.ts} +0 -0
  668. /package/dist/routes/shared-world/{seat-records.d.ts → bundle-records.d.ts} +0 -0
@@ -1,12 +1,9 @@
1
1
  # Contract Schema Index
2
2
 
3
- Date: 2026-06-02 (current-state note updated 2026-07-14)
4
-
5
- Status: reference map for the major contracts shipped through source version
6
- `0.106.0`; it is not an exhaustive inventory of command/result envelopes. Exported types,
7
- schema constants, parsers, and validators in `src/` are authoritative. Rows
8
- marked "reserved" name layering intent only; no code emits or validates them
9
- yet. Do not emit a reserved schema.
3
+ This page maps the shipped contracts; it does not list every command or result envelope. The
4
+ exported types, schema constants, parsers and validators in `src/` are the source of truth. A row
5
+ marked "reserved" names layering intent only: no code emits or validates it yet, so do not emit a
6
+ reserved schema.
10
7
 
11
8
  ## Purpose
12
9
 
@@ -31,7 +28,7 @@ workflow without leaking private upstream truth into core.
31
28
  | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------- |
32
29
  | Run bundle | `humanish.run-bundle.v1` | `synthetic-run-bundle` |
33
30
  | Adapter | `humanish.adapter.v1` | `synthetic-cli-adapter` |
34
- | Lab | `humanish.lab.v2` | `first-run` |
31
+ | Study file | `humanish.study.v3` | `first-run` |
35
32
  | Persona | `humanish.persona.v1` | `synthetic-maintainer` |
36
33
  | Scenario | `humanish.scenario.v1` | `first-run-smoke` |
37
34
  | Actor trace | `humanish.actor-trace.v1` | `synthetic-actor-trace` |
@@ -55,32 +52,72 @@ workflow without leaking private upstream truth into core.
55
52
  | Adapter score | `humanish.adapter-score.v1` (`RunBundle.adapterScore`; namespaced; route-specific acceptance semantics) | see Product-Adapter Extension Seam below |
56
53
  | Adapter artifact | `humanish.adapter-artifact.v1` (`RunBundle.adapterArtifacts[]`; namespaced; local relative proof references) | see Product-Adapter Extension Seam below |
57
54
  | Shared-world evidence | `humanish.shared-world.v1` (additive `RunBundle.sharedWorld` + `RunBundle.attributionClass`; `topologyMode: sequential \| concurrent`) | see Shared-World Evidence below |
58
- | Comms thread | `humanish.comms-thread.v1` (off-app email/SMS the app sent, captured; a `kind: log` run-dir artifact of DIGESTS only — from/to/subject/link digests + an OTP count, never raw) | see `comms` under Lab Manifest |
55
+ | Comms thread | `humanish.comms-thread.v1` (off-app email/SMS the app sent, captured; a `kind: log` run-dir artifact of digests only: from/to/subject/link digests + one OTP count, never raw) | see `comms` under Study File |
59
56
  | Serve result | `humanish.serve-result.v1` (`src/observer/serve.ts` is authoritative) | none (command result envelope; see Serve Result below) |
60
57
  | Serve control plane | reserved (`/_humanish/api/*` answers `501` `HUMANISH_SERVE_CONTROL_PLANE_DISABLED` in v1) | none |
61
58
 
62
- ## Lab Manifest
63
-
64
- Schema: `humanish.lab.v2` (types in `src/lab/types.ts`, parsed by `src/lab/config.ts`). There
65
- is deliberately no v1 compatibility: v1 (`kind`, top-level `sims`) had zero real users and was
66
- deleted when labs became config.
67
-
68
- A lab is a composition over code primitives, not a hardcoded kind:
59
+ ## Study File
60
+
61
+ Schema: `humanish.study.v3`. `src/lab/parse/study-v3.ts` rewrites a v3 document into the
62
+ normalized config in `src/lab/types.ts`, which `src/lab/config.ts` parses. A `humanish.lab.v2`
63
+ file still parses in 0.108 with one warning that names `humanish migrate`; 0.109 removes it.
64
+
65
+ A study declares its route and composes code primitives; it is not a hardcoded kind. The
66
+ top-level keys:
67
+
68
+ | Key | Meaning |
69
+ | ----------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
70
+ | `schema`, `id`, `title`, `description` | the document's identity |
71
+ | `route` | required: `preview`, `computer-use`, `shared-world`, `terminal` or `scripted` |
72
+ | `mode` | `dry-run` (the default) or `live` |
73
+ | `actor` | who drives it, one object: `type`, `model`, `localAgent`, `persona`, `mission`, `tasks`, `maxOutputTokens`, `reasoningEffort`, `stopWhen`, `dwell` |
74
+ | `participants` | a count, a homogeneous `{ count, instruction }`, or a list of entries (below) |
75
+ | `surfaces` | scripted only: `[desktop]` or `[desktop, mobile]` |
76
+ | `caps` | the route's budget (below) |
77
+ | `scenario` | scripted only: a committed scenario id or path |
78
+ | `subject`, `execution`, `policies`, `review`, `defaults`, `comms` | as below |
79
+
80
+ The parser reads only the declared route's keys. A key that route never reads is an error that
81
+ names the route, such as "route: computer-use does not read scenario. Remove it.", and an
82
+ unknown key is an error that lists the known ones; both are `HUMANISH_STUDY_INVALID`.
83
+
84
+ `participants` takes the forms its route accepts:
85
+
86
+ | Route | Forms | Default |
87
+ | -------------- | ------------------------------------------------------------------------------ | -------------- |
88
+ | `preview` | a count | 4 |
89
+ | `computer-use` | a count, a homogeneous object, or a list (a `local-app` subject takes no list) | 1 |
90
+ | `shared-world` | a list of at least two entries | none: required |
91
+ | `scripted` | none; `surfaces` instead | |
92
+ | `terminal` | none; one agent | |
93
+
94
+ `caps` keeps each route's meaning, and a route refuses the keys it does not read:
95
+ `computer-use` and `shared-world` read `maxUsd` and `maxTotalUsd`; `terminal` reads `maxUsd`,
96
+ `maxJobs` and `maxMinutes`; `preview` and `scripted` take no `caps`.
97
+
98
+ A v2 file maps as follows: `actors[0]` is `actor`; `lanes` is the `participants` list; a
99
+ `roster` group is an entry with `count`; `count` is the count; `count` with `laneFocus` is the
100
+ homogeneous object; `execution.caps` (computer-use, shared-world) or `scenario.caps`
101
+ (terminal) is `caps`; `scenario.mode` is `mode`; `scenario.ref` is `scenario`; and
102
+ `subject.topology: shared-world` is `route: shared-world`. A key the v2 inert-field table
103
+ marks inert for the route is not in v3.
104
+
105
+ The rest of this section names keys by their v3 spelling:
69
106
 
70
107
  - `subject`: what the run acts on: `this-repo`, `clone` (owner/repo slugs,
71
108
  optional in-sandbox `serve` + env var names + `state`), `local-tree` (the
72
109
  operator's own working tree, packed on the host and provisioned in-sandbox
73
110
  in place of a clone; see below), `app-url`
74
111
  (loopback unless `policies.allowPublicTargets` declares an owned deployment),
75
- `local-app` (an already-running LOCAL dev server driven IN-PROCESS via a
76
- custom `CuaExecutor`, NO clone and NO E2B desktop, always loopback),
112
+ `local-app` (an already-running local dev server driven in-process via a
113
+ custom `ComputerUseExecutor`, no clone and no E2B desktop, always loopback),
77
114
  `terminal-product` (a CLI/product a real autonomous terminal agent studies
78
- from PUBLIC surfaces only; see below), or `desktop-cli` (a computer-use
115
+ from public surfaces only; see below), or `desktop-cli` (a computer-use
79
116
  participant using a CLI in a terminal on a hosted desktop). A
80
117
  `local-app` subject pairs a computer-use actor with `execution.target: local`
81
118
  (or absent) and is library-assisted: the caller supplies
82
- `RunLabOptions.inProcess.executor` + `createProvider`; with neither the engine fails
83
- closed (`HUMANISH_CUA_LAB_LOCAL_APP_NO_EXECUTOR`), never a desktop attempt. See
119
+ `RunStudyOptions.inProcess.executor` + `createProvider`; with neither the engine fails
120
+ closed (`HUMANISH_COMPUTER_USE_LOCAL_APP_NO_EXECUTOR`), never a desktop attempt. See
84
121
  [`docs/architecture/state-driven-executor.md`](../architecture/state-driven-executor.md);
85
122
  - `subject.localTree` (`local-tree` subjects, computer-use route): pack/upload
86
123
  knobs for the packed working tree: `exclude[]` (extra archive excludes on
@@ -93,8 +130,8 @@ A lab is a composition over code primitives, not a hardcoded kind:
93
130
  `maxArchiveBytes` (upload size cap override; default 256 MiB). Routing requires `execution.target: e2b-desktop` and a
94
131
  computer-use actor; `subject.serve`/`env`/`state` apply exactly as they do
95
132
  on the clone route (identical install/build/start/state semantics). The
96
- packed root is the lab resolution cwd; there is no path field, by design
97
- (an absolute path in a lab manifest would be a machine-specific,
133
+ packed root is the study resolution cwd; there is no path field, by design
134
+ (an absolute path in a study file would be a machine-specific,
98
135
  unshareable, leak-prone artifact). Enumeration is git-aware when the root
99
136
  is a git work tree (`git ls-files --cached --others --exclude-standard`,
100
137
  honoring `.gitignore`) or a denylist-only recursive walk otherwise; an
@@ -104,101 +141,98 @@ A lab is a composition over code primitives, not a hardcoded kind:
104
141
  `src/subject/local-tree-archive.ts`)
105
142
  applies in both modes and is not overridable. The denylist matches names,
106
143
  not contents; a secret in a file it does not name packs like any other
107
- file, so review the pack summary line and use `localTree.exclude`. The lab packs
108
- ONCE per run and uploads the identical archive to every fan-out participant. The
144
+ file, so review the pack summary line and use `localTree.exclude`. The study packs
145
+ once per run and uploads the identical archive to every fan-out participant. The
109
146
  in-sandbox commit refresh clone subjects use is skipped: `.git` is never
110
147
  uploaded, so identity comes from the host-side archive digest instead. See
111
- [`docs/goals/local-tree-subject/goal.md`](https://github.com/danielgwilson/humanish/blob/main/docs/goals/local-tree-subject/goal.md);
148
+ [`docs/history/goals/local-tree-subject/goal.md`](https://github.com/danielgwilson/humanish/blob/main/docs/history/goals/local-tree-subject/goal.md);
112
149
  - `subject.product` (terminal-product subjects): the product the agent studies.
113
- `product.name` is a public-safe token (committed fixtures use a NEUTRAL mock
150
+ `product.name` is a public-safe token (committed fixtures use a neutral mock
114
151
  name); `product.publicSurfaces[]` is the list of http(s) URLs (docs, llms.txt,
115
- skill manifest) that are the ONLY world the agent sees. The lab does not
116
- clone/provision the product, so its provenance is recorded UNPINNED
117
- (invariant 5). `serve`/`clone`/`state`/`repos`/`appUrl` are rejected on a
152
+ skill manifest) that are the only world the agent sees. The study does not
153
+ clone/provision the product, so its provenance is recorded
154
+ unpinned. `serve`/`clone`/`state`/`repos`/`appUrl` are rejected on a
118
155
  terminal-product subject (a field that cannot act on the route is a parse
119
156
  error, not silently dropped). See
120
- [`docs/architecture/terminal-product-lane.md`](../architecture/terminal-product-lane.md);
157
+ [`docs/architecture/terminal-product-route.md`](../architecture/terminal-product-route.md);
121
158
  - `subject.state` (clone or local-tree subjects, computer-use route): the
122
159
  subject's state story. `state.seed[]` declares ordered, bounded
123
160
  seed/migration/fixture steps (`{ name, command, when: before-build |
124
161
  before-start | after-ready, timeoutMs }`) executed in-sandbox around the
125
- serve sequence; `state.external[]` declares env var NAMES (each must also
126
- appear in `subject.env`) pointing at state the lab does not control,
127
- recorded as UNPINNED in provenance. Commands persist in evidence as
162
+ serve sequence; `state.external[]` declares env var names (each must also
163
+ appear in `subject.env`) pointing at state the study does not control,
164
+ recorded as unpinned in provenance. Commands persist in evidence as
128
165
  sha256-16 digests only, never as text;
129
- - `actors`: who drives it. On computer-use (including shared-world),
130
- scripted-browser, and terminal-product routes, `actors[0].type` is a real
131
- dispatch key resolved against the actor registry. On the synthetic route it
132
- remains a descriptive label (e.g. `synthetic-persona`). The
166
+ - `actor`: who drives it. On the computer-use, shared-world, scripted and
167
+ terminal routes, `actor.type` is a real dispatch key resolved against the
168
+ actor registry. On the preview route it remains a descriptive label (e.g.
169
+ `synthetic-persona`). The
133
170
  `codex-exec` descriptor's direct `runSession` member is a fail-closed
134
- compatibility entry, not the live runner; the terminal-product lab route
171
+ compatibility entry, not the live runner; the terminal route
135
172
  owns the live sandbox, auth, cap, evidence, and cleanup lifecycle.
136
- `actors[0].count` carries route-specific meanings: synthetic route participant
137
- count (simCount); scripted-browser route surface roster (1 = desktop,
138
- 2 = desktop + mobile, default 1); computer-use **E2B** route the HOMOGENEOUS
139
- fan-out participant count (N identical participants, each its own E2B desktop: separate
140
- worlds, cap 16). The in-process/local-app computer-use route stays single
141
- participant (no E2B to fan out);
142
- - `actors[0].lanes[]` (computer-use E2B route): a DIFFERENTIATED fan-out roster,
143
- each `{ id?, actorType?, surface?, caseGroup?, persona?, device?,
173
+ A `participants` count is the preview route's participant count (simCount),
174
+ and on the computer-use **E2B** route the homogeneous fan-out count (N
175
+ identical participants, each its own E2B desktop: separate worlds, cap 16).
176
+ The scripted route's `surfaces` is its surface list (`[desktop]`, the default,
177
+ or `[desktop, mobile]`). The in-process/local-app computer-use route stays
178
+ single participant (no E2B to fan out);
179
+ - a `participants` list (computer-use E2B route): a differentiated fan-out,
180
+ each entry `{ id?, actorType?, surface?, caseGroup?, persona?, device?,
144
181
  instruction?, target?, entry?, host?, reasoningEffort?, stopWhen?, dwell? }` becoming one independent E2B desktop (or, on the
145
182
  shared-world routes, one participant against the shared plane). `actorType`,
146
183
  `surface`, and `caseGroup` are adapter-owned public-safe labels for grouping
147
184
  simulated users; they are not core enums, and `actorType` is deliberately
148
- separate from the execution dispatch key `actors[0].type`. `lanes` is XOR with
149
- `count` (declare a roster OR a homogeneous count) and XOR with
150
- `actors[0].laneFocus` (a roster's per-participant `instruction` is the steer);
151
- `lanes[].device` is XOR with a raw `execution.desktop.resolution`. Participant ids
185
+ separate from the execution dispatch key `actor.type`. A list sets the count,
186
+ so `--count` does not apply to it, and each entry's `instruction` is the steer;
187
+ an entry's `device` is XOR with a raw `execution.desktop.resolution`. Participant ids
152
188
  default `lane-01`..`lane-NN` (`role-01`..`role-NN` for shared-world participants), must be unique, and name per-participant evidence paths
153
- (`actors/<streamId>.json`, `screenshots/<laneId>/`). Cap 16 participants. On other
154
- routes `lanes` is inert (warned), except the scripted-browser route, which rejects it. `subject.clone.fanout` is REJECTED on
155
- the cua, shared-world and scripted-browser routes (declare fan-out via
156
- `count`/`lanes`). No current route reads `clone.fanout`;
157
- - `actors[0].lanes[].target` (app-url × computer-use E2B route only): an
189
+ (`actors/<streamId>.json`, `screenshots/<laneId>/`). Cap 16 participants. The other
190
+ routes refuse a list (table above). `subject.clone.fanout` is rejected on the
191
+ computer-use, shared-world and scripted routes (declare fan-out with
192
+ `participants`). No current route reads `clone.fanout`;
193
+ - `participants[].target` (app-url × computer-use E2B route only): an
158
194
  absolute browser URL that participant opens instead of `subject.appUrl`. This is the
159
- setup-produced-target handoff for crawler/swarm labs: an adapter may start any
195
+ setup-produced-target handoff for crawler/swarm studies: an adapter may start any
160
196
  topology it needs, then declare exactly which target each actor should drive.
161
- If any participant declares `target`, every participant in that roster must declare one.
197
+ If any participant declares `target`, every entry in the list must declare one.
162
198
  Public/non-loopback targets still require `policies.allowPublicTargets: true`.
163
199
  `target` is mutually exclusive with `entry`: `target` is an absolute app-url
164
200
  browser target; `entry` is a shared-world participant's same-origin path;
165
- - `actors[0].roster[]` (computer-use E2B route): compact authoring sugar for
166
- repeated participant groups, each `{ id, count, actorType?, surface?, caseGroup?,
167
- persona?, device?, instruction?, target?, entry?, host?, reasoningEffort?, stopWhen?,
168
- dwell? }`. The parser expands it into
169
- deterministic `lanes[]` before the engine runs (`viewer-01`, `viewer-02`,
170
- ...), so the runtime and run bundle keep one normalized participant shape. `roster`
171
- is XOR with explicit `lanes`, homogeneous `count`, and `laneFocus`;
201
+ - a `participants` entry with `count: n` (computer-use E2B route): a group of
202
+ repeated participants. It needs an `id` and the parser expands it to
203
+ `<id>-01` … `<id>-NN`, even when n is 1, before the engine runs (`viewer-01`,
204
+ `viewer-02`, ...), so the runtime and run bundle keep one normalized participant
205
+ shape. Ids are checked for collisions after expansion;
172
206
  - `execution.concurrency` (computer-use E2B routes, including shared-world): a
173
- CAP on participants in flight at once. When omitted, every participant runs
207
+ cap on participants in flight at once. When omitted, every participant runs
174
208
  simultaneously: independent participants resolve it from the final participant count,
175
- after any `--count` override, and the parser fills `concurrency = laneCount`
176
- for multi-participant shared-world labs. Total sessions and spend are identical either way; only wall-clock
209
+ after any `--count` override, and the parser fills `concurrency = participantCount`
210
+ for multi-participant shared-world studies. Total sessions and spend are identical either way; only wall-clock
177
211
  and simultaneity differ. Declaring a value below the participant count runs participants in
178
212
  waves and emits a warning saying so, because a green waved run is otherwise
179
- indistinguishable from the all-live run the author meant. Shared-world labs
213
+ indistinguishable from the all-live run the author meant. Shared-world studies
180
214
  need at least 2 (the host and a follower are live together); `concurrency: 1`
181
215
  there is refused at parse, since the sequential turn-taking route was removed
182
216
  in 0.106.0. The env override
183
- `HUMANISH_CUA_MAX_CONCURRENCY` may only LOWER the effective bound, never
184
- raise concurrent paid desktops (invariant 3), and a lowering is recorded on
217
+ `HUMANISH_CUA_MAX_CONCURRENCY` may only lower the effective bound, never
218
+ raise concurrent paid desktops, and a lowering is recorded on
185
219
  the plan (`envLoweredConcurrencyFrom`). Inert (warned) on other routes.
186
- `execution.timeoutMs` is the PER-PARTICIPANT session budget on this route (semantics
220
+ `execution.timeoutMs` is the per-participant session budget on this route (semantics
187
221
  change: it was the single-session budget pre-fan-out); there is no run-level
188
222
  wall clock. `policies.allowPublicTargets` cannot combine with N>1 against one
189
223
  implicit public `subject.appUrl` (ambiguous shared-world-ish topology); it may
190
- combine with N>1 only when the roster declares explicit `lanes[].target` for
191
- every participant, OR when `subject.topology: shared-world` is ALSO declared. That
192
- routes N>1 against one public target to the EXTERNAL-PUBLIC shared-world plane
224
+ combine with N>1 only when every `participants` entry declares `target`, or when
225
+ the study declares `route: shared-world`. That
226
+ routes N>1 against one public target to the external-public shared-world plane
193
227
  (see Shared-World Evidence below), not separate worlds;
194
228
  - `subject.publicTarget: { owner, authorized: true }` (external-public shared-world
195
- route ONLY): the operator's REQUIRED ownership attestation when a real PUBLIC
196
- deployment (`source: app-url` + `topology: shared-world` + `allowPublicTargets` +
229
+ route only): the operator's required ownership attestation when a real public
230
+ deployment (`source: app-url` + `route: shared-world` + `allowPublicTargets` +
197
231
  `concurrency > 1`) is used directly as the shared plane. The harness neither
198
232
  provisions nor exposes the target, so it cannot attest synthetic data; the operator
199
- MUST attest they own/operate it. Author-trust (unverifiable); rejected on non-app-url
200
- subjects and inert (warned) on other app-url routes. `actors[0].lanes[].host: true` marks the single designated host participant there;
201
- - `subject.appUrl`: a loopback http(s) URL a computer-use actor drives, OR (on the
233
+ must attest they own/operate it. Author-trust (unverifiable); rejected on non-app-url
234
+ subjects and inert (warned) on other app-url routes. `participants[].host: true` marks the single designated host participant there;
235
+ - `subject.appUrl`: a loopback http(s) URL a computer-use actor drives, or (on the
202
236
  external-public shared-world route) the non-loopback public deployment used as the
203
237
  shared plane (with `publicTarget` + `allowPublicTargets`);
204
238
  - `execution`: where it runs (`local`, `e2b-desktop`, or `e2b-terminal`), plus
@@ -210,18 +244,18 @@ dwell? }`. The parser expands it into
210
244
  computer-use and scripted-browser routes refuse it when a library caller skips
211
245
  the parser;
212
246
  - `execution.desktop.template` (e2b-desktop computer-use and clone scripted-browser routes): a custom E2B
213
- desktop TEMPLATE (image) NAME or ID the run launches on, for a subject that
247
+ desktop template (image) name or ID the run launches on, for a subject that
214
248
  needs runtimes the stock `desktop` image lacks (e.g. node/bun/a local Postgres
215
249
  baked into an adopter-maintained image). Any non-empty string is a valid
216
250
  name/id (no allowlist); a blank/whitespace value is rejected. Threaded to the
217
- SDK's `Sandbox.create(template, opts)` on EVERY desktop-creating route (the
218
- single-participant and fan-out cua participants, and the shared-world subject AND every actor
251
+ SDK's `Sandbox.create(template, opts)` on every desktop-creating route (the
252
+ single-participant and fan-out cua participants, and the shared-world subject and every actor
219
253
  sandbox); when absent the call
220
254
  stays the byte-stable `Sandbox.create(opts)` default (the stock template). The
221
255
  template actually used is recorded in the run bundle as `desktopTemplate`
222
256
  (public-safe, since a template name is not a secret). Inert (warned) on every route
223
257
  that creates no desktop, incl. the in-process `local-app` cua route, so it is
224
- never silently ignored (invariant 6). Custom images need the
258
+ never silently ignored. Custom images need the
225
259
  Desktop SDK's `xdotool` input support, `mktemp`, and the `C.UTF-8` locale:
226
260
  every typed text is written to a private 0600 file and typed with
227
261
  `LC_ALL=C.UTF-8 xdotool type --file` in one attempt. Without that locale a
@@ -238,79 +272,76 @@ dwell? }`. The parser expands it into
238
272
  when known. Inert (warned) where this route-specific browser launcher is not
239
273
  used;
240
274
  - `execution.terminal` + `execution.runtimeAuth` (terminal-product route):
241
- `terminal.transport` is `exec-stream`: captured NON-interactive exec output
275
+ `terminal.transport` is `exec-stream`: captured non-interactive exec output
242
276
  (stdin disabled); `pty` is rejected because labeling captured exec output as
243
- an interactive PTY would overstate the mechanism (invariant 6; a true duplex
244
- PTY transport does not ship). `terminal.stdin` defaults to `disabled`
277
+ an interactive PTY would overstate the mechanism (a duplex PTY
278
+ transport does not ship). `terminal.stdin` defaults to `disabled`
245
279
  (`sent`/assisted input is rejected until the interventions ledger + a
246
280
  non-comparable marker exist). `runtimeAuth` (`openai-env`, the default, or `openai-egress`)
247
- declares the agent's runtime-auth channel, recorded as NAMES ONLY. On a live run, the engine
281
+ declares the agent's runtime-auth channel, recorded as names only. On a live run, the engine
248
282
  resolves the registered terminal descriptor and requires
249
283
  `keyPlacement: in-sandbox-command-scoped` before creating a sandbox. With `openai-env`
250
284
  the key is passed only to the agent command, never to `Sandbox.create` or
251
285
  metadata; with `openai-egress` it stays in the host-side E2B header transform
252
286
  set at sandbox creation, and the agent gets a placeholder. A dry-run neither
253
287
  reads nor injects it;
254
- - `scenario`: `mode: dry-run` (contract evidence, no spend) or `live`.
255
- `scenario.ref` is CONSUMED (and REQUIRED) on the scripted-browser route: it
256
- resolves a committed scenario (`humanish/scenarios/<ref>.yaml` or a repo
257
- path) whose `browser.steps` ARE what the actor executes, digest-pinned into
258
- bundle provenance; on other routes `ref`/`inline` stay forward-declared
259
- warnings. On the scripted route `live` gates real browser ACTUATION against
260
- the declared app. Provider spend stays $0 by mechanism (no model runs);
261
- - `scenario.caps` (terminal-product route): `{ maxUsd, maxJobs, maxMinutes }`,
288
+ - `mode`: `dry-run` (a synthetic bundle, no spend; the default) or `live`. On the
289
+ scripted route `live` gates real browser actuation against the declared app.
290
+ Provider spend stays $0 there by mechanism (no model runs);
291
+ - `scenario` (scripted route, required): a committed scenario id or path
292
+ (`humanish/scenarios/<id>.yaml` or a repo path) whose `browser.steps` are what
293
+ the actor executes, digest-pinned into bundle provenance. Every other route
294
+ refuses it;
295
+ - `caps` on the terminal route: `{ maxUsd, maxJobs, maxMinutes }`,
262
296
  all non-negative numbers (0 = no-spend, the default). This budget bounds the
263
297
  in-sandbox live key by mechanism: the live key is never exercised without a
264
298
  fail-closed cap in force. `maxMinutes` is the
265
299
  wall-clock kill; `maxUsd`/`maxJobs` are enforced fail-closed against the cost
266
- ledger after the session (a run whose KNOWN spend exceeds the cap fails closed,
267
- `HUMANISH_TERMINAL_LAB_CAPS_EXCEEDED`). Core records Codex provider spend as
300
+ ledger after the session (a run whose known spend exceeds the cap fails closed,
301
+ `HUMANISH_TERMINAL_CAPS_EXCEEDED`). Core records Codex provider spend as
268
302
  unpriced tokens, so without a cost probe no line can trip a positive `maxUsd`:
269
303
  a live run refuses `maxUsd > 0` before creating a sandbox
270
- (`HUMANISH_TERMINAL_LAB_UNPRICED_CAP`) unless the caller passes a `costProbe`
304
+ (`HUMANISH_TERMINAL_UNPRICED_CAP`) unless the caller passes a `costProbe`
271
305
  hook. The sandbox's server-side timeout is the steps before the
272
306
  codex command (Node bootstrap, runtime version check, and product setup when
273
307
  `subject.product.install` is declared) plus `maxMinutes` plus a 5-minute
274
308
  teardown buffer; a live run refuses a `maxMinutes` that would take it past
275
- E2B's one-hour limit (`HUMANISH_TERMINAL_LAB_CAPS_INVALID`). The no-spend proof is derived from that real ledger, never asserted (see
276
- Terminal Cost Ledger And No-Spend Proof).
277
- Inert (warned) on every other route, except that a positive
278
- `scenario.caps.maxUsd` or `scenario.caps.maxTotalUsd` on a computer-use lab
279
- is a parse error naming the matching `execution.caps` field, which is what
280
- that route enforces;
309
+ E2B's one-hour limit (`HUMANISH_TERMINAL_CAPS_INVALID`). The no-spend proof is derived from that real ledger, never asserted (see
310
+ Terminal Cost Ledger And No-Spend Proof). `maxTotalUsd` is refused on the
311
+ terminal route;
281
312
  - `policies`: `redactRepos`, `redactScreenshots`, `allowPublicTargets`,
282
313
  `mediaPermission` (`prompt` | `granted`), and the
283
314
  terminal-product credential-boundary booleans `allowPrivateRepoAccess`,
284
315
  `allowProviderCredentials`, `allowPaymentCredentials`, `allowGitHubMutation`
285
- (all DEFAULT FALSE, deny-by-default; only the runtime LLM key enters, and
316
+ (all default false, deny-by-default; only the runtime LLM key enters, and
286
317
  only command-scoped). The scripted-browser route is loopback-only and rejects
287
318
  `redactScreenshots: true` (blur unimplemented there) and
288
319
  `allowPublicTargets: true` fail-closed rather than ignoring them.
289
- - `comms` (#297; hosted on the clone/local-tree computer-use route and the
320
+ - `comms` (hosted on the clone/local-tree computer-use route and the
290
321
  shared-world getHost plane, or connected to an external catch on
291
322
  app-url/operator-provided subjects): off-app
292
- email the app itself sends, made a persona-driven testable surface. Lab
323
+ email the app itself sends, made a persona-driven testable surface. Study
293
324
  configuration rejects `comms.sms` and unknown channel names; message-bus SMS
294
325
  types do not imply a supported SMS execution route. SMTP capture is supported
295
326
  on provisioned routes with separate worlds and rejected for shared-world studies.
296
327
  `comms.email` = `{ kind: fake, injectEnv?, port?, smtp?, recipients?, linkOrigin?, external? }`.
297
- `injectEnv` is the ADOPTER-NAMED env var the app reads for its email-API base
328
+ `injectEnv` is the adopter-named env var the app reads for its email-API base
298
329
  URL (e.g. `RESEND_API_URL`); the harness sets it to an in-sandbox catch (so it
299
- is NOT declared in `subject.env`) that captures the app's sends without touching
330
+ is not declared in `subject.env`) that captures the app's sends without touching
300
331
  the internet. Verify the app actually reads that variable, because a run whose
301
332
  catch captured zero sends warns at teardown for exactly that. `kind` must be
302
333
  `fake`; a real inbox names a saved `comms.email.connection` instead; `port` ≤ 65534
303
334
  (the catch reserves `port+1` for the read-only inbox listener the shared-world
304
- route getHost-exposes). `recipients[]` = `{ lane, address? }`: OMIT the list
335
+ route getHost-exposes). `recipients[]` = `{ lane, address? }`: omit the list
305
336
  and the parser fills one deterministic address per participant
306
- (`<laneId>@example.test`) so every participant can do email (#351). When declared, a
307
- `lane` must be one of the lab's real participant ids (roster ids, or the generated
308
- `lane-01..lane-NN` under `count`). An unknown participant id is a hard parse error
337
+ (`<laneId>@example.test`) so every participant can do email. When declared, a
338
+ `lane` must be one of the study's real participant ids (list or group ids, or the
339
+ generated `lane-01..lane-NN` under a count). An unknown participant id is a hard parse error
309
340
  listing them, zero addressed participants is a hard error, partial coverage warns
310
341
  with the uncovered participants. Each addressed participant's actor prompt is extended with
311
342
  the full handoff: its address ("enter exactly that"), the inbox URL, and the
312
343
  wait steering ("waiting for an email is normal, not a blocker").
313
- `external` (#328) switches the funnel to an ADOPTER-HOSTED catch, which is what
344
+ `external` switches the funnel to an adopter-hosted catch, which is what
314
345
  makes comms work on planes humanish does not provision (app-url /
315
346
  operator-provisioned): `{ catchBaseUrl, inboxBaseUrl?, authTokenEnv? }`. The
316
347
  operator runs the catch and points their own app's email-API base URL at it.
@@ -323,14 +354,19 @@ dwell? }`. The parser expands it into
323
354
  `humanish-comms-catch` marker, so a proxy answering 200 for everything cannot
324
355
  pass for a catch), the teardown drain over `GET /deliveries`, and the same
325
356
  digest-only evidence. `authTokenEnv` names an env var holding a bearer token
326
- for the drain read. The NAME is recorded as evidence; the value never
327
- persists. Declaring `external` on a harness-provisioned subject warns: two
357
+ for the drain read. The name is recorded as evidence; the value never
358
+ persists. A live run refuses a token shorter than 16 characters, or not
359
+ well-formed Unicode, before it probes the catch
360
+ (`HUMANISH_COMPUTER_USE_COMMS_TOKEN_INVALID`,
361
+ `HUMANISH_SHARED_WORLD_COMMS_TOKEN_INVALID`), as
362
+ `humanish comms catch --token` does, and scrubs the token and its encoded
363
+ forms from every comms warning. Declaring `external` on a harness-provisioned subject warns: two
328
364
  catches would exist and the app would point at humanish's own. `linkOrigin`
329
365
  is an optional operator-declared origin the app bakes into links when it
330
366
  differs from the serve origin; the harness rewrites captured links through it
331
367
  so a clicked link resolves to a reachable host. Captured mail is drained into a
332
- digest-only `humanish.comms-thread.v1` artifact (from/to/subject/link DIGESTS +
333
- an OTP COUNT; no raw address/link/code persists). The READABLE proof a
368
+ digest-only `humanish.comms-thread.v1` artifact (from/to/subject/link digests +
369
+ an OTP count; no raw address/link/code persists). The readable proof a
334
370
  persona saw the email is its screenshots of the inbox page. Requires `python3`
335
371
  in the subject sandbox (the stock E2B desktop template has it).
336
372
 
@@ -356,7 +392,7 @@ Adding an existing name with different settings refuses to overwrite it.
356
392
  profiles and local key presence/source, not provider authentication or delivery.
357
393
  `comms providers --json` returns `humanish.comms-providers.v1` with explicit
358
394
  setup/receiving availability. The key store accepts `humanish keys set agentmail`.
359
- Saving a connection neither modifies a lab nor creates a provider resource;
395
+ Saving a connection neither modifies a study nor creates a provider resource;
360
396
  a supported study explicitly selects `comms.email: { connection: agentmail }`.
361
397
  Optional `allowedOrigins` lists exact additional HTTP(S) origins; `linkOrigin`
362
398
  participates in the existing provisioned-subject origin rewrite. Connection
@@ -374,15 +410,15 @@ acquisition and cleanup status. It contains no raw addresses, bodies, provider I
374
410
  or content digests. `run.json` embeds the final projection as `commsReceiving` and
375
411
  carries `publication.restrictions: [real-communications]`. Verification keeps the
376
412
  run local-only regardless of screenshot redaction. See the
377
- [receiving contract](../architecture/real-email-receiving.md).
378
-
379
- Lab backends report results in their own schemas (`humanish.run-result.v1`,
380
- `humanish.cua-lab-result.v2`, `humanish.scripted-lab-result.v1`,
381
- `humanish.terminal-lab-result.v1`,
382
- `humanish.concurrent-shared-world-lab-result.v1`); the evidence record stays
383
- `humanish.run-bundle.v1` in every case. The computer-use result bumped to v2 for
384
- fan-out: it carries `plan` (the pre-flight participant table: concurrency, waves,
385
- per-participant session budget, worst-case sandbox-minutes), `lanes[]` (ALWAYS present,
413
+ [receiving contract](https://humanish.dev/docs/email-receiving).
414
+
415
+ A study's result has one schema on every route, `humanish.study-result.v1`
416
+ (`src/run/study-result.ts`), with `route`, `studyId` and, until 0.109, a deprecated `labId`
417
+ holding the same value. Route-specific fields stay on their route's result. `humanish run`
418
+ with no study, `observe`, and a refusal made before a route is picked keep
419
+ `humanish.run-result.v1`. The evidence record stays `humanish.run-bundle.v1` in every case.
420
+ The computer-use result carries `plan` (the pre-flight participant table: concurrency, waves,
421
+ per-participant session budget, worst-case sandbox-minutes), `lanes[]` (always present,
386
422
  length 1 at N=1; per-participant status/session/sandbox/subject), and `laneSummary`
387
423
  (passed/skipped/harnessErrors/hollow counts). The top-level `session`/`sandbox`
388
424
  mirror the first participant and `subject.commit` is unanimity-gated across participants
@@ -391,39 +427,46 @@ the run bundle is byte-stable with the pre-fan-out output; only the result
391
427
  projection changed. A fan-out run records a `cua-lab.fanout.plan` bundle event
392
428
  (and a `cua-lab.fanout.fail-fast` event when a harness error skips queued participants);
393
429
  `ok = observer.ok ∧ every participant ok ∧ no failed adapter or declared-scorer verdict`,
394
- where a dry-run participant is ok as contract evidence and a live participant must pass
430
+ where a dry-run participant is ok as synthetic and a live participant must pass
395
431
  `participantPassed` (`src/run/judge.ts`): not skipped, status `passed`, no harness
396
432
  error, engaged, and no self-reported blocker.
397
433
 
398
434
  Explicit failed-participant reruns are supported on the CUA fan-out route via
399
- `humanish lab run <lab> --rerun-failed-from <run-id> [--lanes lane-a,lane-b]`.
400
- The source run must be a live CUA fan-out bundle. humanish creates a NEW run for
435
+ `humanish run <study> --rerun-failed-from <run-id> [--participants <ids>]`, where a
436
+ participant id is a declared `participants` entry id, or `lane-01`, `lane-02`, … by position.
437
+ The source run must be a live CUA fan-out bundle. humanish creates a new run for
401
438
  the selected failed/blocked/timed-out/hollow participants (or explicit participant ids), leaves
402
439
  the source verdict unchanged, and records lineage as `run.rerun` plus a
403
440
  `cua-lab.fanout.rerun` event: source run id, selected participant ids, and previous participant
404
441
  statuses/reasons. This is intentionally not automatic retry; a passing rerun is a
405
442
  nondeterminism candidate for human/product scoring, not a rewrite of the old run.
443
+ Without `--participants`, a participant is selected when its stream's `judgedStatus` is not `passed`.
444
+ That field is the judge's status (`judgedStatus` in `src/run/judge.ts`), so a goal_satisfied
445
+ session that reported a blocker counts as blocked. A source bundle without `judgedStatus` falls back
446
+ to the stream's trace status, its actor status and completion reason, and a zero-action check.
406
447
 
407
- Manifests are human-authored `.yaml` source under `humanish/labs/*.yaml` for
408
- committed public-safe labs, or ignored `.humanish/labs/*.yaml` /
409
- `.humanish/local/labs/*.yaml` for private local dogfood. Fields the engine does
410
- not yet consume are accepted but reported as warnings (`humanish lab inspect`
411
- shows them), so a manifest never silently claims behavior that did not run.
448
+ Study files are human-authored `.yaml` source under `humanish/studies/*.yaml` for
449
+ committed public-safe studies, or ignored `.humanish/studies/*.yaml` /
450
+ `.humanish/local/studies/*.yaml` for private local dogfood. A v3 key the declared route
451
+ does not read is an error, so a study file never silently claims behavior that did not run.
452
+ A v2 file keeps its inert-field warnings until 0.109 (`humanish study show` shows them).
412
453
 
413
- Committed fixture (`humanish/labs/first-run.yaml`):
454
+ Committed fixture (`humanish/studies/first-run.yaml`):
414
455
 
415
456
  ```yaml
416
- schema: humanish.lab.v2
457
+ schema: humanish.study.v3
417
458
  id: first-run
418
459
  title: First-run synthetic Observer
419
- description: Public-safe starter lab that generates a synthetic run bundle and Observer without provider spend.
460
+ description: >-
461
+ Writes a preview run: a synthetic run bundle and Observer for four participants. Needs no browser,
462
+ model or keys, and costs nothing. Run it with humanish run first-run.
463
+ route: preview
464
+ mode: dry-run
420
465
  subject:
421
466
  source: this-repo
422
- actors:
423
- - type: synthetic-persona
424
- count: 4
425
- scenario:
426
- mode: dry-run
467
+ actor:
468
+ type: synthetic-persona
469
+ participants: 4
427
470
  defaults:
428
471
  open: true
429
472
  ```
@@ -452,38 +495,40 @@ Core-owned fields:
452
495
  - `artifacts`
453
496
  - `review`
454
497
  - `feedbackCandidates`
455
- - `lab` (optional, additive): which manifest produced the run,
456
- `{ id, path?, origin? }`, where `origin` is `committed` (humanish/labs),
498
+ - `study` (optional, additive): which study file produced the run,
499
+ `{ id, path?, origin? }`, where `origin` is `committed` (`humanish/studies/`, or
500
+ `humanish/labs/` for a v2 file),
457
501
  `ignored` (a local overlay), or `explicit` (a path the operator passed).
458
- Absent on bundles written before this contract and on library callers who
459
- hand a `LabConfig` directly. Such a run has no lab, and nothing guesses
460
- one. Readers wanting attribution for an older bundle may fall back to
461
- `inferLegacyLabId`, which reads only the historical
462
- `persona.source = "lab:<id>"` convention and nothing else.
502
+ `lab` holds the same value until 0.109 removes it, so a 0.107 reader keeps working.
503
+ Both are absent on bundles written before this contract and on library callers who
504
+ hand a `StudyConfig` directly; such a run has no study. Readers go through
505
+ `studyProvenanceOf` (`src/run/study-provenance.ts`): a valid `study`, else a valid `lab`,
506
+ else `inferLegacyStudyId`, which reads the `persona.source` or `scenario.source`
507
+ convention `study:<id>`, or `lab:<id>` from runs written before 0.108, and nothing else.
463
508
  - `subject` (optional, additive): structured subject provenance,
464
509
  `{ source: clone | app-url | local-tree, repo?, commit?, archiveSha256?,
465
510
  dirty?, envNames?, state }` where `state` is `{ provenance: seeded |
466
511
  unpinned | declared-not-run | undeclared | external-public, seed?: [{ name, when,
467
512
  commandDigest, ok?, exitCode?, timedOut?, durationMs? }], externalEnvNames?
468
- }`. Emitted by the computer-use and shared-world backends and the clone
513
+ }`. Emitted by the computer-use and shared-world routes and the clone
469
514
  scripted-browser route; absent on pre-existing and other bundles. `repo`/`commit` are clone-route fields; `archiveSha256`
470
515
  (64-hex sha256, the local-tree provenance pin) and `dirty` (host git
471
516
  porcelain status at pack time) are local-tree-route fields, additive under
472
517
  `humanish.run-bundle.v1`: a dirty working tree cannot be commit-pinned, so
473
518
  the archive content digest stands in for it. `commandDigest` is the
474
519
  sha256-16 of the exact seed command; command text and env values never
475
- appear. `humanish verify` fails closed when a LIVE `local-tree` bundle carries
520
+ appear. `humanish verify` fails closed when a live `local-tree` bundle carries
476
521
  no well-formed `archiveSha256`, in addition to the existing `subject state
477
522
  provenance` check.
478
- - `desktopTemplate` (optional, additive): the custom E2B desktop TEMPLATE (image)
523
+ - `desktopTemplate` (optional, additive): the custom E2B desktop template (image)
479
524
  the run's sandbox(es) launched on, from `execution.desktop.template`, so the
480
- evidence shows WHICH image ran. Present only when a template was configured;
525
+ evidence shows which image ran. Present only when a template was configured;
481
526
  absent == the stock `desktop` template, so every pre-existing bundle is
482
527
  byte-stable. Public-safe (a template name is not a secret).
483
528
  - `attributionClass` (optional, additive): `isolated | shared-world`. Absent ==
484
529
  `isolated`, so every existing bundle is byte-stable. It is the
485
- interaction-attribution axis (#164), independent of the persona-sampling
486
- evidence classes. Set to `shared-world` by the shared-world backend, paired
530
+ interaction-attribution axis, independent of the persona-sampling
531
+ evidence classes. Set to `shared-world` by the shared-world route, paired
487
532
  with `sharedWorld`.
488
533
  - `sharedWorld` (optional, additive): the shared-world evidence block
489
534
  (`humanish.shared-world.v1`); see [Shared-World Evidence](#shared-world-evidence)
@@ -557,43 +602,43 @@ feedbackCandidates: []
557
602
 
558
603
  ## Shared-World Evidence
559
604
 
560
- The shared-world topology (#164) is the DECLARED override of the `per-lane-worlds`
561
- default: N distinct actor ROLES drive ONE provisioned, mutable service plane (one
562
- app + one seeded DB) so their actions interact through shared state. The ONE
605
+ The shared-world topology is the declared override of the `per-lane-worlds`
606
+ default: N distinct participants drive one provisioned, mutable service plane (one
607
+ app + one seeded DB) so their actions interact through shared state. The one
563
608
  subject plane is provisioned via `subject.source: clone` (a fresh `git clone`) or
564
609
  `subject.source: local-tree` (the operator's own working tree, packed on the host
565
610
  and provisioned in-sandbox in place of a clone - see `subject.localTree` above);
566
611
  both sources are accepted on the shared-world route.
567
612
 
568
- Current runs write only the CONCURRENT shape below. The SEQUENTIAL shape came from the
613
+ Current runs write only the concurrent shape below. The sequential shape came from the
569
614
  sequential shared-world route, which 0.106.0 removed. Only bundles written before
570
615
  0.106.0 carry it, and `humanish verify` still checks them
571
- (`tests/run/legacy-sequential-shared-world.test.ts`). The SEQUENTIAL rules below apply
616
+ (`tests/run/legacy-sequential-shared-world.test.ts`). The sequential rules below apply
572
617
  to those bundles.
573
618
 
574
- A shared-world bundle adds TWO additive, optional fields to `humanish.run-bundle.v1`
619
+ A shared-world bundle adds two additive, optional fields to `humanish.run-bundle.v1`
575
620
  (absent on every other bundle, so they stay byte-stable):
576
621
 
577
622
  - `attributionClass: isolated | shared-world`: a separate attribution axis
578
- ("how well did the run attribute INTERACTION?"), distinct from the persona-sampling
623
+ ("how well did the run attribute interaction?"), distinct from the persona-sampling
579
624
  evidence classes ("how representative is the actor?"). Absent == `isolated`.
580
- - `sharedWorld` (`humanish.shared-world.v1`): TWO variants discriminated by
625
+ - `sharedWorld` (`humanish.shared-world.v1`): two variants discriminated by
581
626
  `topologyMode: "sequential" | "concurrent"` (`sharedWorldEvidenceFindings` in `src/verify/shared-world.ts` branches on
582
- it FIRST; unknown/missing or a mismatched shape fails closed). Common fields:
627
+ it first; unknown/missing or a mismatched shape fails closed). Common fields:
583
628
  - `topology: shared-world`
584
629
  - `topologyMode: sequential | concurrent`
585
- - `roleCount`: the DECLARED number of participants.
586
- - `plane: { commit?, seedDigest, envNames, hostDigest?, exposure?, publicOriginDigest?, declaredOriginDigest? }`: the ONE
630
+ - `roleCount`: the declared number of participants.
631
+ - `plane: { commit?, seedDigest, envNames, hostDigest?, exposure?, publicOriginDigest?, declaredOriginDigest? }`: the one
587
632
  shared-plane provenance. `seedDigest` is the sha256-16 of the ordered seed-step
588
- command digests (the seed RECIPE identity, not the runtime state); `envNames` are
589
- NAMES only. `hostDigest`/`exposure` are CONCURRENT provisioned-getHost-only, and
590
- `publicOriginDigest` is CONCURRENT external-public-only (below).
633
+ command digests (the seed recipe identity, not the runtime state); `envNames` are
634
+ names only. `hostDigest`/`exposure` are concurrent provisioned-getHost-only, and
635
+ `publicOriginDigest` is concurrent external-public-only (below).
591
636
  - `planeClass?: provisioned-getHost | external-public` and `lobbyConvergenceDigest?` are
592
- CONCURRENT-only (below); both absent on the sequential shape.
637
+ concurrent-only (below); both absent on the sequential shape.
593
638
  - `attributionLimits: [...]`: the verify-enforced attribution ceiling (the set
594
639
  differs per `topologyMode`, below).
595
640
 
596
- SEQUENTIAL shape (`topologyMode: sequential`, #164 PR1; only in bundles written before
641
+ Sequential shape (`topologyMode: sequential`; only in bundles written before
597
642
  0.106.0):
598
643
  - `sequence: [roleId, …]`: the role ids that actually took a turn, in declared order.
599
644
  - `skippedTail` (optional, live sequential only): `{ afterRoleId, roles,
@@ -603,128 +648,128 @@ cause, maxTotalUsd?, estimatedTotalUsd? }`. Each ordered `roles` entry names
603
648
  predecessor must have a matching `harness_error`, explicit `session_error`,
604
649
  `usage_unreported`, or measured `study_spend_limit`. Only the last cause
605
650
  carries budget figures, using the same per-participant estimates as the
606
- tracker. Blocked participants need matching simulation, stream and blocked-event
607
- evidence, with no actor, trace, screenshot or invented timeline turn.
651
+ tracker. Blocked participants need a matching `simulations[]` entry, stream and
652
+ blocked-event evidence, with no actor, trace, screenshot or invented timeline turn.
608
653
  Historical bundles without this field still require every role in the timeline.
609
654
  - `timeline: (checkpoint | turn)[]`: a harness-clocked, strictly alternating
610
655
  timeline that starts `cp-baseline`, alternates checkpoint → turn → checkpoint,
611
656
  and ends on a checkpoint:
612
657
  - checkpoint = `{ kind: checkpoint, name, digest, deltaFromPrev }`; `digest` is
613
- sha256-16(scrub+redact(probe stdout)); the record is DIGEST-ONLY (no
658
+ sha256-16(scrub+redact(probe stdout)); the record is digest-only (no
614
659
  value-shaped field). `deltaFromPrev` is true when the observed state changed
615
660
  across the intervening turn.
616
661
  - turn = `{ kind: turn, roleId, simId, streamId, commit?, seedDigest }` references
617
662
  a real `RunBundle["simulations"][number]` and `RunBundle["streams"][number]`; carries the
618
663
  plane provenance it observed
619
664
  (identical across turns by construction; this is the single-plane proof).
620
- - Sequential `attributionLimits` MUST contain `sequential-only`, `no-concurrent-races`,
665
+ - Sequential `attributionLimits` must contain `sequential-only`, `no-concurrent-races`,
621
666
  and `delta-attributed-to-turn-not-action`.
622
667
 
623
- CONCURRENT shape (`topologyMode: concurrent`, #164 phase 2: N personas drive ONE
624
- getHost-exposed plane AT ONCE; NO `timeline`/`sequence`):
625
- - `plane.hostDigest`: sha256-16 of the harness-minted `getHost` ORIGIN every actor
626
- drove (a first-class provisioned-subject target; invariant 2). A DIGEST, not the raw
668
+ Concurrent shape (`topologyMode: concurrent`: N personas drive one
669
+ getHost-exposed plane at once; no `timeline`/`sequence`):
670
+ - `plane.hostDigest`: sha256-16 of the harness-minted `getHost` origin every actor
671
+ drove (a first-class provisioned-subject target). A digest, not the raw
627
672
  URL: a getHost URL embeds the live sandbox id and matches the publish-safety e2b-URL
628
673
  redaction, so it never lands raw in a published bundle (the raw tokenless URL is
629
- surfaced only on the ephemeral lab result). The orchestrator confirms the URL is
630
- TOKENLESS (no authKey; invariant 1) before digesting.
631
- - `plane.exposure: synthetic`: the REQUIRED author attestation that the subject behind
674
+ surfaced only on the ephemeral study result). The orchestrator confirms the URL is
675
+ tokenless (no authKey) before digesting.
676
+ - `plane.exposure: synthetic`: the required author attestation that the subject behind
632
677
  the internet-reachable getHost URL is synthetic seeded data (author-trust + a
633
- provenance gate, NOT a no-real-data guarantee).
678
+ provenance gate, not a no-real-data guarantee).
634
679
  - `laneWindows: [{ roleId, simId, streamId, startedAt, endedAt, verdict, routeHostDigest,
635
- commit?, seedDigest }]`: one harness-clocked window per actor; OVERLAPPING windows
680
+ commit?, seedDigest }]`: one harness-clocked window per actor; overlapping windows
636
681
  prove ≥2 personas were active simultaneously. `routeHostDigest` == `plane.hostDigest`
637
682
  (every actor drove exactly the harness-minted host).
638
683
  - `stateSeries: [{ timestamp, digest }]`: cadence digests of the shared world under
639
- load (baseline + periodic + final). DIGEST-ONLY: the allowed-keys tripwire permits
640
- ONLY `timestamp` + `digest` (no per-delta→actor field, because causation under concurrency
684
+ load (baseline + periodic + final). Digest-only: the allowed-keys tripwire permits
685
+ only `timestamp` + `digest` (no per-delta→actor field, because causation under concurrency
641
686
  is structurally inexpressible).
642
687
  - `outcomes: [{ roleId, simId, streamId, status, completionReason?, ok }]`: per-persona
643
- OUTCOME (the "M of N succeeded" headline).
644
- - Concurrent `attributionLimits` MUST contain `concurrent`,
688
+ outcome (the "M of N succeeded" headline).
689
+ - Concurrent `attributionLimits` must contain `concurrent`,
645
690
  `best-effort-causal-attribution`, `non-deterministic-shared-state`,
646
691
  `window-and-snapshot-granularity`, `contention-observed-not-proven-safe`,
647
- `state-change-not-isolated-to-actors`, and MUST NOT contain `sequential-only` or
692
+ `state-change-not-isolated-to-actors`, and must not contain `sequential-only` or
648
693
  `no-concurrent-races` (a sequential guarantee on a concurrent run is an overclaim).
649
694
 
650
695
  The `shared-world evidence` check in `humanish verify` is fail-closed (live runs only;
651
- dry-run contract bundles are skipped). It dispatches on `topologyMode` FIRST.
652
- SEQUENTIAL: the timeline must be well-formed (start `cp-baseline`, strictly alternate,
696
+ dry-run contract bundles are skipped). It dispatches on `topologyMode` first.
697
+ Sequential: the timeline must be well-formed (start `cp-baseline`, strictly alternate,
653
698
  end on a checkpoint, turn order == sequence, sequence length == roleCount == turn
654
699
  count, no `laneWindows`); every turn's simId/streamId resolves; every checkpoint digest
655
- is sha256-16 with NO value-shaped field; all turns share ONE plane provenance; the
656
- mandatory limits are present; and a PASSED run shows ≥1 checkpoint `deltaFromPrev` (the
657
- delta-on-pass gate). CONCURRENT: no `timeline`; laneWindows and outcomes each
658
- cover exactly roleCount and stateSeries is present; the required limits are present AND the forbidden ones absent;
659
- `plane.hostDigest` present and every `routeHostDigest` equals it (invariant 2);
660
- `plane.exposure == synthetic` AND `subject.state.provenance == seeded` (the
700
+ is sha256-16 with no value-shaped field; all turns share one plane provenance; the
701
+ mandatory limits are present; and a passed run shows ≥1 checkpoint `deltaFromPrev` (the
702
+ delta-on-pass gate). Concurrent: no `timeline`; laneWindows and outcomes each
703
+ cover exactly roleCount and stateSeries is present; the required limits are present and the forbidden ones absent;
704
+ `plane.hostDigest` present and every `routeHostDigest` equals it;
705
+ `plane.exposure == synthetic` and `subject.state.provenance == seeded` (the
661
706
  synthetic-subject gate); stateSeries snapshots are digest-only (allowed-keys tripwire);
662
- all laneWindows share ONE plane provenance; and the CONCURRENCY-ON-PASS gate: a PASSED
663
- run MUST show ≥2 overlapping laneWindows AND a stateSeries delta whose timestamp is
707
+ all laneWindows share one plane provenance; and the concurrency-on-pass gate: a passed
708
+ run must show ≥2 overlapping laneWindows and a stateSeries delta whose timestamp is
664
709
  AT/AFTER an overlap interval start (otherwise it was not actually concurrent, or the
665
- world never changed under load). The per-role no-engagement guard applies to both.
666
- Checkpoints / stateSeries persist digest-only by DEFAULT until the #108 PII/PHI
667
- detector lands.
668
-
669
- WHAT THE BUNDLE CAN / CANNOT CLAIM. SEQUENTIAL: each role's own behavior at full
670
- fidelity; the OBSERVED system outcome as an ordered DIGEST sequence; and the
671
- SEQUENCED-INTERACTION proof (role B entered a world already containing role A's mutation;
672
- the checkpoint after A strictly precedes B's turn). It CANNOT claim action-granular
673
- causation, concurrency/races (sequential-only), or exact-state determinism. CONCURRENT:
674
- each persona's own behavior at full fidelity; per-persona OUTCOME against the contended
675
- world ("M of N"); PROVEN CONCURRENCY (overlapping windows); and system-state evolution
676
- under load (the stateSeries) with best-effort temporal correlation. It CANNOT claim
710
+ world never changed under load). The per-participant no-engagement guard applies to both.
711
+ Checkpoints / stateSeries persist digest-only by default until the
712
+ [PII/PHI detector](https://github.com/danielgwilson/humanish/issues/108) lands.
713
+
714
+ What the bundle can / cannot claim. Sequential: each role's own behavior at full
715
+ fidelity; the observed system outcome as an ordered digest sequence; and the
716
+ sequenced-interaction proof (role B entered a world already containing role A's mutation;
717
+ the checkpoint after A strictly precedes B's turn). It cannot claim action-granular
718
+ causation, concurrency/races (sequential-only), or exact-state determinism. Concurrent:
719
+ each persona's own behavior at full fidelity; per-persona outcome against the contended
720
+ world ("M of N"); proven concurrency (overlapping windows); and system-state evolution
721
+ under load (the stateSeries) with best-effort temporal correlation. It cannot claim
677
722
  strict causal attribution of a delta to an actor (concurrent ⇒ ambiguous), determinism
678
- of exact state, per-action granularity, or concurrency-SAFETY (races are OBSERVED, never
679
- PROVEN absent). HONESTY: the deterministic $0 gate proves the plumbing + the
723
+ of exact state, per-action granularity, or concurrency-safety (races are observed, never
724
+ proven absent). Limits: the deterministic $0 gate proves the plumbing + the
680
725
  attribution contract. A kept 2026-06-17 live receipt separately proves one
681
726
  bounded three-persona trial against a synthetic plane. Neither the deterministic
682
727
  gate nor that receipt proves scale, repeatability, or adopter-harness replacement.
683
728
 
684
- ### Concurrent plane classes: provisioned-getHost vs external-public (#164 phase 2, 0.20.0)
729
+ ### Concurrent plane classes: provisioned and external-public
685
730
 
686
- The CONCURRENT shape carries a PLANE-class discriminator, `sharedWorld.planeClass:
731
+ The concurrent shape carries a plane-class discriminator, `sharedWorld.planeClass:
687
732
  "provisioned-getHost" | "external-public"`. Absent == `provisioned-getHost` (every existing
688
- concurrent bundle byte-stable). It gates EVERY getHost-specific verify assertion, so a getHost
733
+ concurrent bundle byte-stable). It gates every getHost-specific verify assertion, so a getHost
689
734
  claim can never leak onto the external-public class (or vice versa).
690
735
 
691
736
  - `provisioned-getHost`: the historical plane described above: a `clone`/`local-tree` subject the
692
- harness serves + getHost-exposes in-sandbox. The harness MINTED the host, so it asserts the
737
+ harness serves + getHost-exposes in-sandbox. The harness minted the host, so it asserts the
693
738
  synthetic-seeded attestation (`plane.exposure: synthetic` + `subject.state.provenance == seeded`),
694
739
  the harness-minted host identity (`plane.hostDigest`, every `routeHostDigest == it`), and an
695
740
  authoritative in-sandbox checkpoint `stateSeries` with a delta-on-pass.
696
741
 
697
- - `external-public`: a real, operator-OWNED public deployment used DIRECTLY as the shared plane
698
- (`subject.source: app-url` + `topology: shared-world` + `policies.allowPublicTargets: true` +
699
- `execution.concurrency > 1`). NO getHost, NO clone, NO subject sandbox, NO seed. The subject
742
+ - `external-public`: a real, operator-owned public deployment used directly as the shared plane
743
+ (`subject.source: app-url` + `route: shared-world` + `policies.allowPublicTargets: true` +
744
+ `execution.concurrency > 1`). No getHost, no clone, no subject sandbox, no seed. The subject
700
745
  carries the ownership attestation `subject.publicTarget: { owner, authorized: true }` (the
701
- analog of `exposure: synthetic`: you cannot attest synthetic on a real site, but you MUST attest
746
+ analog of `exposure: synthetic`: you cannot attest synthetic on a real site, but you must attest
702
747
  you own/operate it; author-trust, unverifiable by the harness). Evidence deltas, all
703
748
  asserted-absent (never silently dropped):
704
- - `plane.publicOriginDigest`: sha256-16 of the one OBSERVED origin the participants converged on. Every
705
- `laneWindow.routeHostDigest` (computed from that participant's CDP-OBSERVED final-URL origin) equals it.
749
+ - `plane.publicOriginDigest`: sha256-16 of the one observed origin the participants converged on. Every
750
+ `laneWindow.routeHostDigest` (computed from that participant's CDP-observed final-URL origin) equals it.
706
751
  `plane.declaredOriginDigest` records the operator-declared origin as evidence only; a redirect
707
- can make the two differ. This proves inter-participant CONVERGENCE on one origin, NOT harness control of the plane
708
- (WEAKER than getHost's `hostDigest`, and disclosed).
709
- - `plane.exposure` is ABSENT (verify fails closed if `synthetic` appears, since that
752
+ can make the two differ. This proves inter-participant convergence on one origin, not harness control of the plane
753
+ (weaker than getHost's `hostDigest`, and disclosed).
754
+ - `plane.exposure` is absent (verify fails closed if `synthetic` appears, since that
710
755
  attestation would be false on a real site);
711
- `plane.hostDigest` is ABSENT (the harness minted no host).
756
+ `plane.hostDigest` is absent (the harness minted no host).
712
757
  - `subject.state.provenance == "external-public"` (never `seeded`, `unpinned` or `undeclared`).
713
- - `stateSeries` is OMITTED (Option A): no in-sandbox filesystem to authoritatively digest, so there
714
- is NO authoritative shared-state proof. Concurrency-on-pass is RELAXED to temporal co-occupancy
715
- ONLY (≥2 overlapping `laneWindows`); there is no state-delta requirement.
716
- - `lobbyConvergenceDigest` (optional, strong, cheap): sha256-16 of the shared `/lobby/CODE` PATH
717
- all participants' CDP URLs converged on; present only when every participant converged on ONE code. Digest-only
758
+ - `stateSeries` is omitted (Option A): no in-sandbox filesystem to authoritatively digest, so there
759
+ is no authoritative shared-state proof. Concurrency-on-pass is relaxed to temporal co-occupancy
760
+ only (≥2 overlapping `laneWindows`); there is no state-delta requirement.
761
+ - `lobbyConvergenceDigest` (optional, strong, cheap): sha256-16 of the shared `/lobby/CODE` path
762
+ all participants' CDP URLs converged on; present only when every participant converged on one code. Digest-only
718
763
  (the raw 6-char code and full URLs are runtime-only and never land).
719
- - `attributionLimits` MUST contain the concurrent family PLUS `external-public-plane`,
764
+ - `attributionLimits` must contain the concurrent family plus `external-public-plane`,
720
765
  `operator-attested-target-not-harness-controlled`, `no-synthetic-attestation`,
721
- `no-authoritative-shared-state-proof`, `concurrency-by-temporal-co-occupancy-only`; and MUST NOT
766
+ `no-authoritative-shared-state-proof`, `concurrency-by-temporal-co-occupancy-only`; and must not
722
767
  contain `sequential-only`, `no-concurrent-races`, or any `seeded`/`synthetic` limit.
723
768
 
724
- The getHost synthetic gate is deliberately NOT reachable from the app-url branch, and why: that gate
725
- exists because getHost is internet-reachable AND harness-owned (real data behind a harness-exposed
769
+ The getHost synthetic gate is deliberately not reachable from the app-url branch, and why: that gate
770
+ exists because getHost is internet-reachable and harness-owned (real data behind a harness-exposed
726
771
  URL is the hazard). A public site the harness neither provisioned nor exposed has neither property,
727
- so the gate's hazard does not exist there. Attribution stays `shared-world` (N participants, ONE plane); the
772
+ so the gate's hazard does not exist there. Attribution stays `shared-world` (N participants, one plane); the
728
773
  plane-control claim degrades from "harness-controlled" to "operator-attested, observed-only".
729
774
 
730
775
  ## Adapter
@@ -819,7 +864,7 @@ Legacy profiles retain medium defaults. `none_declared`/`none` access needs are
819
864
  omitted. `keyboard_first` allows pointer fallback; `keyboard_only` does not.
820
865
  These are prompt instructions, not evidence that a participant complied.
821
866
 
822
- `humanish lab inspect` adds `personas: [{ id, resolved, brief? }]`. A brief contains
867
+ `humanish study show` adds `personas: [{ id, resolved, brief? }]`. A brief contains
823
868
  `compilerVersion`, persona-section `text`, its pre-redaction `digest`, `redacted`,
824
869
  and an optional source-file `sourceDigest`. It excludes task criteria and runtime
825
870
  grants. This is authored context; whether a selected actor consumes it depends on
@@ -848,7 +893,7 @@ Actors execute or simulate the trial. Actor evidence is the provider-neutral
848
893
  computer-use cycles, scripted browser steps, and in-sandbox terminal-agent exec
849
894
  output all map onto one `ActorTrace`. Registered actors live in
850
895
  `src/actors/registry.ts` (`codex-app-server`, `openai-computer-use`,
851
- `local-agent`, `scripted-browser`, `codex-exec`). A lab that names
896
+ `local-agent`, `scripted-browser`, `codex-exec`). A study that names
852
897
  `pi-agent-core` or `claude-agent-sdk` fails to parse. There is no
853
898
  `humanish.actor.v1`; that name never shipped.
854
899
 
@@ -860,7 +905,7 @@ Core-owned fields:
860
905
  `terminal-exec`)
861
906
  - `lane` (`code` | `computer-use` | `scripted-browser` | `terminal`)
862
907
  - `persona` (`id`, `traitsApplied`, `promptDigest`)
863
- - `capabilities.keyPlacement` (`external` | `in-sandbox-command-scoped`): WHERE
908
+ - `capabilities.keyPlacement` (`external` | `in-sandbox-command-scoped`): where
864
909
  the actor's runtime key lives. It is registry metadata the engine enforces. The
865
910
  terminal agent declares `in-sandbox-command-scoped` (the agent-under-test runs
866
911
  inside the sandbox); every other actor is `external` (absent === external).
@@ -871,7 +916,7 @@ Core-owned fields:
871
916
  meaning the subject failed the script while the harness executed faithfully; and
872
917
  `budget_reached`: a computer-use session ended by a limit. The loop
873
918
  (`src/actors/computer-use/loop/ending.ts`) emits it when the time budget runs
874
- out after at least one material action, when `execution.caps.maxUsd` or the
919
+ out after at least one material action, when `caps.maxUsd` or the
875
920
  study's shared spend threshold is crossed, when the adapter reports a local
876
921
  admission limit, or when the provider hits its output or context token limit.
877
922
  Every `budget_reached` session has status `incomplete`
@@ -883,24 +928,24 @@ Core-owned fields:
883
928
  may carry `cacheWriteInput` (tokens billed at the provider's cache-write rate,
884
929
  OpenAI 5.6+) and `turns[]` (per provider-request usage, the recorded fact
885
930
  long-context tier pricing needs). Both are additive and absent on
886
- producers that do not report them (#334). Items may
931
+ producers that do not report them. Items may
887
932
  carry `at` (ISO-8601 recording stamp from the loop's clock) and, on
888
- click-like `ui_action` items, structured `coord` (`x`/`y`). Both are additive
889
- (#441): absent on older bundles and non-stamping producers, and absence means
933
+ click-like `ui_action` items, structured `coord` (`x`/`y`). Both are additive:
934
+ absent on older bundles and non-stamping producers, and absence means
890
935
  "timing/position unrecorded", never zero
891
- - optional `modelSettings` (`humanish.model-settings.v1`, #497): HOW the model was
936
+ - optional `modelSettings` (`humanish.model-settings.v1`): how the model was
892
937
  asked to run, alongside `ids.model` which says which model it was.
893
- `reasoningEffort` records the value the request ACTUALLY carried,
894
- including the provider default when a lab declared nothing. The resolved
938
+ `reasoningEffort` records the value the request actually carried,
939
+ including the provider default when a study declared nothing. The resolved
895
940
  value is what produced the trace, and recording "unset" would misdescribe
896
941
  the run. Absent when a provider declares no settings and on every
897
942
  pre-existing bundle; tolerated by verify. It exists because effort was
898
- unreachable from a lab, which made every run take the default silently. Effort
899
- is part of WHO the participant was rather than of how the instrument was tuned
943
+ unreachable from a study, which made every run take the default silently. Effort
944
+ is part of who the participant was rather than of how the instrument was tuned
900
945
  (docs/principles/actor-fidelity.md), so a trace without it is a result missing
901
946
  half its sample description
902
947
  - optional `modelSettings.maxOutputTokens`: the declared positive integer
903
- `actors[0].maxOutputTokens`, passed to every first-party OpenAI CUA response
948
+ `actor.maxOutputTokens`, passed to every first-party OpenAI CUA response
904
949
  request, including reasoning output. Absent when undeclared. The first request
905
950
  of a session and closing reports use `min(maxOutputTokens, 1024)`, or 1024 when
906
951
  undeclared; the first request also carries the opening screenshot as an
@@ -913,17 +958,37 @@ Core-owned fields:
913
958
  “provider reply cut off by the output limit; asking again” notice. A second
914
959
  cut-off reply to the same request ends the session with
915
960
  `stopCause: provider_output_limit`.
916
- - optional `affordanceUse` (`humanish.affordance-use.v1`): which KIND of route this
961
+ - optional `conversation`: how the first-party OpenAI computer-use provider
962
+ carried the conversation between requests. `mode` is `threaded` (the server
963
+ kept it through `previous_response_id`) or `explicit_context` (the server kept
964
+ none). `explicitReason` says why: `configured` (`zeroDataRetention`) or
965
+ `zdr_rejection` (the organization rejected server-side state; `switchedAt` and
966
+ `switchedAtRequest` say when). In `explicit_context` each request carries the
967
+ conversation from the client, with `store: false` and the reasoning returned
968
+ in encrypted form. It carries the opening message, every exchange of a reply
969
+ with the outputs that answered it, and the latest reply. Past an estimated
970
+ 64,000 input tokens the opening screenshot is dropped first, then the oldest
971
+ exchanges become lines of a note, sent as an assistant message, that keeps
972
+ their reasoning summaries, messages and actions as text. The two newest
973
+ exchanges are always carried whole, even when they alone pass the budget. `summarizedTurns` counts the earlier exchanges the latest
974
+ `explicit_context` request no longer carried whole: lines of the note, or
975
+ only counted once the note reached its cap.
976
+ `requests[]` has one entry per participant request: its `mode` and, in
977
+ `explicit_context`, `carriedExchanges`, `carriedScreenshots` and
978
+ `estimatedInputTokens`. Billed input per request is in `tokenUsage.turns`. A
979
+ participant that ran in `explicit_context` gets a run warning. Absent on other
980
+ providers and on earlier bundles
981
+ - optional `affordanceUse` (`humanish.affordance-use.v1`): which kind of route this
917
982
  actor took (see Affordance Use below)
918
983
  - optional `estimatedCost` (`humanish.actor-estimated-cost.v1`): a token-derived
919
- cost ESTIMATE for this participant (see Run Cost Summary And Estimated Actor Cost).
920
- It is deliberately a DIFFERENT field from `tokenUsage.costUsd`: a bare
921
- `costUsd` is RESERVED for a real provider-returned charge, while
922
- `estimatedCost.estimatedCostUsd` is a rate-table multiply, named honestly as
923
- an estimate so a reader can never confuse the two (invariant 6). Absent on
984
+ cost estimate for this participant (see Run Cost Summary And Estimated Actor Cost).
985
+ It is deliberately a different field from `tokenUsage.costUsd`: a bare
986
+ `costUsd` is reserved for a real provider-returned charge, while
987
+ `estimatedCost.estimatedCostUsd` is a rate-table multiply, named as an
988
+ estimate so a reader can never confuse the two. Absent on
924
989
  the Codex app-server and scripted routes and on every pre-existing bundle; the
925
990
  terminal route records a `null` estimate. A `null`
926
- `estimatedCostUsd` is DECLARED ABSENT (unknown rate / no usage), never 0.
991
+ `estimatedCostUsd` is declared absent (unknown rate / no usage), never 0.
927
992
 
928
993
  Unexpected actor-loop diagnostics live inside `items[]` as
929
994
  `kind: notice`, `status: error` rows. They are public-safe evidence, not crash
@@ -966,12 +1031,12 @@ items: []
966
1031
 
967
1032
  Reserved: `humanish.substrate.v1` is named here for layering intent but has
968
1033
  never shipped; no code emits or validates it. Substrate truth today lives
969
- inside run bundles (per-stream transport and status) and lab execution config
1034
+ inside run bundles (per-stream transport and status) and study execution config
970
1035
  (`execution.target: local | e2b-desktop | e2b-terminal`). Do not emit this schema.
971
1036
 
972
1037
  ## Serve Result And Reserved Control-Plane Namespace
973
1038
 
974
- `humanish serve` reports `humanish.serve-result.v1`. The exported `ServeResult`
1039
+ `humanish observe --all` reports `humanish.serve-result.v1`. The exported `ServeResult`
975
1040
  type and `SERVE_SCHEMA` constant in `src/observer/serve.ts` are authoritative:
976
1041
  mode (`loopback | exposed | share-safe-open`), the loopback host/port,
977
1042
  `publicUrl`, the `tunnel` provider/url, an `oauth` echo (`provider`,
@@ -1012,10 +1077,10 @@ discipline** that distinguishes three states and never conflates them:
1012
1077
  - `usd: null` is **not measured**: no spend signal exists for the category on
1013
1078
  this run. `null` is written explicitly (never `undefined`-omitted, never guessed
1014
1079
  to `0`). A line with `null` says "this category exists but we did not measure
1015
- it"; the no-spend proof reports it as unmeasured and does NOT claim it is zero.
1080
+ it"; the no-spend proof reports it as unmeasured and does not claim it is zero.
1016
1081
  - an **absent** line is **not applicable** (n/a) to the participant or run.
1017
1082
 
1018
- `knownTotalUsd` sums ONLY the non-null lines (a `null` line contributes nothing
1083
+ `knownTotalUsd` sums only the non-null lines (a `null` line contributes nothing
1019
1084
  and is never coerced to `0`); `fullyMeasured` is true only when no line is null.
1020
1085
  Core meters only the `provider` line, populated from the actor trace's
1021
1086
  `tokenUsage.costUsd` when present (else `null`); `product`/`media`/`payment`
@@ -1024,7 +1089,7 @@ cost-probe seam. The terminal route records the model as `codex` and does not pi
1024
1089
  model, so a measured token count stays `usd: null` with `source: unpriced-token-usage`, and
1025
1090
  `knownTotalUsd: 0` with `fullyMeasured: false` means no line of this ledger was priced. E2B
1026
1091
  time for the terminal sandbox is not a line of this ledger, whose lines are checked against
1027
- `scenario.caps.maxUsd`. The run bundle's `cost` summary prices it as a `desktop-minutes`
1092
+ `caps.maxUsd`. The run bundle's `cost` summary prices it as a `desktop-minutes`
1028
1093
  line (see Run Cost Summary And Estimated Actor Cost below).
1029
1094
 
1030
1095
  ```yaml
@@ -1039,10 +1104,10 @@ knownTotalUsd: 0
1039
1104
  fullyMeasured: false
1040
1105
  ```
1041
1106
 
1042
- The no-spend proof (`humanish.terminal-no-spend-proof.v1`) is DERIVED from the
1107
+ The no-spend proof (`humanish.terminal-no-spend-proof.v1`) is derived from the
1043
1108
  ledger. It vouches only for what it measured: `knownZeroLines` (proven zero),
1044
1109
  `knownNonZeroLines` (break `satisfied`), and `unmeasuredLines` (the `null` lines
1045
- it explicitly CANNOT vouch for). `satisfied` is true only when every KNOWN line
1110
+ it explicitly cannot vouch for). `satisfied` is true only when every known line
1046
1111
  is within `maxUsd` (for a no-spend run, `maxUsd: 0` ⇒ every known line is `0`).
1047
1112
  Unmeasured lines never break it, so a ledger whose lines are all `null` is
1048
1113
  `satisfied` with nothing proven. The `statement` says which case applies: it
@@ -1062,9 +1127,9 @@ knownTotalUsd: 0
1062
1127
  statement: "No-spend proof not established for maxUsd=0: no spend line was measured. …"
1063
1128
  ```
1064
1129
 
1065
- **Full caps enforcement (fail-closed, not advisory).** `scenario.caps.maxUsd`
1066
- is enforced against the ledger: if the observed KNOWN spend exceeds `maxUsd`, the
1067
- run fails closed (`HUMANISH_TERMINAL_LAB_CAPS_EXCEEDED`); `maxJobs` likewise when
1130
+ **Full caps enforcement (fail-closed, not advisory).** `caps.maxUsd`
1131
+ is enforced against the ledger: if the observed known spend exceeds `maxUsd`, the
1132
+ run fails closed (`HUMANISH_TERMINAL_CAPS_EXCEEDED`); `maxJobs` likewise when
1068
1133
  a known job count is present; `maxMinutes` is the wall-clock kill (unchanged).
1069
1134
  Unknowns (`null`) never trip a cap (we cannot claim a violation we did not
1070
1135
  measure) and never grant a green pass (they surface as unmeasured). `verifyRun`
@@ -1074,33 +1139,58 @@ proof claims zero on a `null` line, or when known spend exceeds the declared cap
1074
1139
  ## Run Status (identity + liveness index)
1075
1140
 
1076
1141
  `humanish.run-status.v1` is `status.json`, written inside each run directory by
1077
- every backend at run start, refreshed on a fixed cadence while the run is
1142
+ every route at run start, refreshed on a fixed cadence while the run is
1078
1143
  alive, and finalized when it ends: `{ schema, runId, state: running |
1079
- finished, mode, lab?, pid, startedAt, updatedAt, completedAt?, outcome? }`.
1144
+ finished | interrupted, mode, study?, lab?, pid, startedAt, updatedAt, completedAt?,
1145
+ signal?, outcome? }`. `lab` repeats `study` until 0.109.
1080
1146
  `outcome` carries the bundle's `verdict`, `participants` and `estimatedCostUsd`
1081
1147
  when the run finishes, then the result's `ok` and `execution: { succeeded,
1082
- failures: [{ kind, message }] }` once the Observer has rendered. The verdict is
1083
- what the participants experienced; `execution` is whether the run worked as an
1084
- execution (`kind` is `harness`, `provider-cleanup`, `sandbox-cleanup`,
1085
- `evidence`, `cap` or `run`), and `ok` reads both under the route's policy
1086
- (`OUTCOME_POLICIES` in `src/run/judge.ts`).
1087
-
1088
- It answers two questions the filesystem could not answer before: **which lab**
1148
+ failures: [{ kind, message }], warnings? }` once the Observer has rendered. The
1149
+ verdict is what the participants experienced; `execution` is whether the run
1150
+ worked as an execution (`kind` is `harness`, `provider-cleanup`,
1151
+ `provider-policy`, `sandbox-cleanup`, `evidence`, `cap` or `run`), and `ok`
1152
+ reads both under the route's policy (`OUTCOME_POLICIES` in `src/run/judge.ts`).
1153
+ `provider-policy` names a computer-use or shared-world participant whose Codex
1154
+ app-server reported a disallowed item at any point in the session, or whose
1155
+ output could not be checked (a byte, frame or event limit, a malformed line, or
1156
+ a last frame cut off by anything but humanish's own signal, which is a run
1157
+ warning with its byte count). It is recorded even when a request already failed for it,
1158
+ because a step that tolerates a failed request (the debrief) would otherwise
1159
+ absorb it, and it fails the run on every route.
1160
+ `warnings`, in the same shape, holds the failures the policy lets warn and is
1161
+ omitted when empty: on computer-use, shared-world and scripted runs, a
1162
+ `sandbox-cleanup` entry for each sandbox whose release was not confirmed, with
1163
+ the participant (or `subject`), the release warning and the
1164
+ `humanish reclaim --run <id>` command. A local VM writes no receipt, so its
1165
+ entry names the container and the `docker rm --force --volumes <container>`
1166
+ command that removes it (through `limactl shell` on a Mac).
1167
+
1168
+ `interrupted` with `signal` (`SIGINT`, `SIGTERM` or `SIGHUP`) is written by
1169
+ the CLI's run command when that signal stops a live run outside post-run
1170
+ analysis. It then kills the E2B sandboxes the run's `sandbox-receipts.ndjson`
1171
+ names, writes `reclaim-receipt.json` as `humanish reclaim` does, and exits
1172
+ 128+n. The bundle is not finished: run.json keeps its last live flush, and no
1173
+ `outcome` is written. A `running` record that stopped refreshing still reads as
1174
+ interrupted once stale. A library caller of `runStudy` gets no signal handling. A
1175
+ humanish older than this field rejects the state and falls back to the bundle's
1176
+ own liveness.
1177
+
1178
+ It answers two questions the filesystem could not answer before: **which study**
1089
1179
  a run belongs to, and **whether it is still alive**, including for runs an
1090
- agent launched (`lab run --json`) or that were detached, which previously wrote
1180
+ agent launched (`run --json`) or that were detached, which previously wrote
1091
1181
  nothing at all until they completed.
1092
1182
 
1093
- It is a DERIVED INDEX, not evidence. `run.json` remains the evidence-of-record;
1183
+ It is a derived index, not evidence. `run.json` remains the evidence-of-record;
1094
1184
  `verify` never gates on `status.json`, nothing in it is a claim about what a
1095
1185
  participant did, and when the two disagree the bundle wins and the record is
1096
1186
  rebuildable from it. A `running` record whose `updatedAt` is older than three
1097
- touch intervals is INTERRUPTED, not alive. A dropped connection or a killed
1187
+ touch intervals is interrupted, not alive. A dropped connection or a killed
1098
1188
  terminal leaves exactly that shape, so the record reads as interrupted
1099
1189
  (`classifyRunStatus` is the one shared definition). Fields are
1100
1190
  public-safe by construction: no hostname and no user paths, because a run
1101
1191
  directory may be shared.
1102
1192
 
1103
- ## Run Index, Run Detail, And The Terminal Surface (#455)
1193
+ ## Run Index, Run Detail, And The Terminal Surface
1104
1194
 
1105
1195
  Three derived projections that exist so a surface can list, classify and watch
1106
1196
  runs without opening evidence for all of them. None is authoritative: `run.json`
@@ -1110,17 +1200,17 @@ them is a claim about what a participant did.
1110
1200
  `humanish.run-index.v1` is the listing projection. One entry per run, read
1111
1201
  cheapest-source-first: the `status.json` record, else the bundle, else the run
1112
1202
  directory alone. `{ runId, derivedFrom: status | bundle | directory, liveness,
1113
- mode?, pid?, lab?, startedAt?, updatedAt?, completedAt?, verdict?,
1203
+ mode?, pid?, study?, startedAt?, updatedAt?, completedAt?, verdict?,
1114
1204
  participants?, estimatedCostUsd?, durationMs? }`. The point is cost: walking
1115
1205
  every run tree and parsing every bundle measured 167ms on a 25-run project,
1116
1206
  against 16ms cold and 2.8ms warm here, which is what makes a surface that
1117
1207
  refreshes on a cadence affordable. `derivedFrom` is reported so a surprising row
1118
1208
  can be traced to the file it came from. A run with receipts and no outcome is
1119
1209
  `interrupted` (the shape a dropped connection leaves), and so is an
1120
- IN-PROGRESS bundle reached without a status record: there is no freshness to
1210
+ in-progress bundle reached without a status record: there is no freshness to
1121
1211
  judge, and nothing in it says the run finished.
1122
1212
 
1123
- `humanish.run-detail.v1` is the watching projection, for ONE run. Who is in it
1213
+ `humanish.run-detail.v1` is the watching projection, for one run. Who is in it
1124
1214
  and what they are thinking: `{ runId, participants: [{ id, label, personaId?,
1125
1215
  traits, status?, completionReason?, thought?, turns?, actions?, thoughts?,
1126
1216
  estimatedCostUsd? }], observerPath?, automaticAnalysis? }`. It reads the actor trace from
@@ -1128,7 +1218,7 @@ estimatedCostUsd? }], observerPath?, automaticAnalysis? }`. It reads the actor t
1128
1218
  finished, preferring the live one, so a single screen renders a run the whole
1129
1219
  way through. A reasoning item still being written is skipped rather than quoted
1130
1220
  half-finished, and the text is carried verbatim: a surface may wrap it, and
1131
- nothing paraphrases it. Unlike the index this DOES open the bundle, which is affordable
1221
+ nothing paraphrases it. Unlike the index this does open the bundle, which is affordable
1132
1222
  only because it is asked for the one run being watched.
1133
1223
 
1134
1224
  `humanish.tui-result.v1` is what `humanish tui` emits when it refuses:
@@ -1137,23 +1227,23 @@ only because it is asked for the one run being watched.
1137
1227
  `HUMANISH_TUI_BUNDLE_MISSING`. `HUMANISH_TUI_AGENT_SESSION` is the one a TTY
1138
1228
  check could not catch: `codex exec` allocates a PTY for the commands it runs, so
1139
1229
  both streams are terminals and the surface used to open. A study watched an
1140
- agent move through the labs list and start a run it did not mean to start
1141
- (humanish/labs/handed-a-human-surface.yaml). The refusal names the environment variable
1230
+ agent move through the studies list and start a run it did not mean to start
1231
+ (humanish/studies/handed-a-human-surface.yaml). The refusal names the environment variable
1142
1232
  that identified the runner, so the reader can check the claim, and `--force` is
1143
1233
  the escape for a person who really is at that keyboard. Every other
1144
1234
  command is built for an agent to drive; this one takes the screen and waits for
1145
1235
  a person, so a non-interactive stdin or stdout fails closed naming the commands
1146
- that DO answer the question rather than rendering escape codes into a pipe.
1236
+ that do answer the question rather than rendering escape codes into a pipe.
1147
1237
 
1148
1238
  ## Run Cost Summary And Estimated Actor Cost
1149
1239
 
1150
- The computer-use (CUA) route surfaces an ADVISORY, additive cost ESTIMATE. It is
1240
+ The computer-use (CUA) route surfaces an advisory, additive cost estimate. It is
1151
1241
  never authoritative: every dollar figure is a rate-table multiply, labeled
1152
- "estimated (rates as of `<date>`)", and is NEVER presented as a provider charge
1153
- (invariant 6). Three new `.v1` schema tags ship, all additive and optional so
1242
+ "estimated (rates as of `<date>`)", and is never presented as a provider charge.
1243
+ Three new `.v1` schema tags ship, all additive and optional so
1154
1244
  `humanish.run-bundle.v1` stays v1 and every pre-existing bundle is byte-stable:
1155
1245
 
1156
- - `humanish.pricing.v1`: the OPERATOR-EDITABLE rate table in `src/run/pricing.ts`:
1246
+ - `humanish.pricing.v1`: the operator-editable rate table in `src/run/pricing.ts`:
1157
1247
  dated per-model input/output USD-per-token rates and E2B CPU/GiB-second
1158
1248
  rates, each with a public pricing-page `source` and an `asOf`
1159
1249
  date. A prominent banner says these are estimates to update when providers
@@ -1163,16 +1253,18 @@ never authoritative: every dollar figure is a rate-table multiply, labeled
1163
1253
  request; priced exactly only from the trace's per-request `turns` ledger;
1164
1254
  totals alone never re-tier, which errs toward under-estimating). Some
1165
1255
  entries are `placeholder: true` stand-ins (the legacy desktop helper's
1166
- 8-vCPU/8-GiB planning assumption); an operator MUST confirm them before trusting
1256
+ 8-vCPU/8-GiB planning assumption); an operator must confirm them before trusting
1167
1257
  the magnitude; the flag propagates into every estimate so a stand-in is never
1168
- mistaken for a live rate. An UNKNOWN model/desktop rate is DECLARED ABSENT
1258
+ mistaken for a live rate. An unknown model/desktop rate is declared absent
1169
1259
  (`estimatedCostUsd: null` + a `reason`), never guessed.
1170
1260
  - `humanish.actor-estimated-cost.v1` (`ActorTrace.estimatedCost`): one participant's
1171
1261
  token-derived model cost, with `estimatedCostUsd` (or `null` + `reason`
1172
1262
  `no_rate_for_model`/`no_token_usage`/`account_billing_unknown`), `ratesAsOf`, `source`, `modelId`,
1173
- optional `placeholder`, and a `breakdown`.
1263
+ optional `placeholder`, and a `breakdown`. `basis: aggregated_turns_base_rate` marks usage that
1264
+ arrived as turn totals, such as Codex `turn.completed`, priced at the base tier because no single
1265
+ request's size is known; the run summary's `model-tokens` line carries the same `basis`.
1174
1266
  - `humanish.run-cost-summary.v1` (`RunBundle.cost`): the sum of every participant's
1175
- `model-tokens` lines PLUS `desktop-minutes` lines. New independent CUA runs
1267
+ `model-tokens` lines plus `desktop-minutes` lines. New independent CUA runs
1176
1268
  price each owned desktop separately; concurrent shared-world runs add one line per participant
1177
1269
  and a `laneId: subject` desktop line for a provisioned plane; older single aggregate
1178
1270
  lines remain readable.
@@ -1184,23 +1276,24 @@ never authoritative: every dollar figure is a rate-table multiply, labeled
1184
1276
  unconfirmed allocation adds `desktop_lifetime_incomplete` as a second null line.
1185
1277
  Live terminal-product runs emit the same summary: one `desktop-minutes` line for
1186
1278
  the E2B shell sandbox (its acquired-to-cleanup span and `e2b.getInfo` size) and
1187
- one `model-tokens` line for the Codex participant, which stays `null` with
1188
- `no_rate_for_model` (model `codex`) or `no_token_usage`. The terminal trace
1189
- records that same null `estimatedCost`. The sandbox line is not part of the
1190
- terminal cost ledger, whose lines are checked against `scenario.caps.maxUsd`.
1191
-
1192
- The summary follows the SAME null discipline as the terminal cost ledger above.
1193
- `estimatedTotalUsd` sums ONLY the non-null `breakdown` lines and is `null` iff
1194
- EVERY line is null (never coerced to `0`); a present-but-unpriceable line stays
1279
+ one `model-tokens` line for the Codex participant, priced from the model the route passed
1280
+ to Codex (`gpt-5.6-sol` unless the study declares one) with `basis:
1281
+ aggregated_turns_base_rate`, or `null` with `no_token_usage`. Older terminal bundles record
1282
+ `no_rate_for_model` (model `codex`). The terminal trace records the same `estimatedCost`. The sandbox line is not part of the
1283
+ terminal cost ledger, whose lines are checked against `caps.maxUsd`.
1284
+
1285
+ The summary follows the same null discipline as the terminal cost ledger above.
1286
+ `estimatedTotalUsd` sums only the non-null `breakdown` lines and is `null` iff
1287
+ every line is null (never coerced to `0`); a present-but-unpriceable line stays
1195
1288
  in `breakdown` with `estimatedCostUsd: null` + a `reason` (it records that we
1196
1289
  tried and could not price it) and contributes nothing. `fullyEstimated` is
1197
1290
  `false` when any applicable line is null (the total is then a lower bound). A spend-free
1198
1291
  live run has no lines and records `estimatedTotalUsd: 0` with `ratesAsOf: null`; verify
1199
1292
  asks for a rates date only when a line is priced.
1200
1293
  `placeholder` is true when any contributing rate is a stand-in; `ratesAsOf` is
1201
- the MIN (oldest) `asOf` across contributing rates. An aggregate is only as fresh
1202
- as its stalest input, so MAX would overclaim freshness (each `breakdown` line
1203
- keeps its own true `asOf`). `desktopMinutes` is a HOST-SIDE
1294
+ the min (oldest) `asOf` across contributing rates. An aggregate is only as fresh
1295
+ as its stalest input, so max would overclaim freshness (each `breakdown` line
1296
+ keeps its own true `asOf`). `desktopMinutes` is a host-side
1204
1297
  acquired-handle→cleanup span, excluding allocation/startup before handle acquisition.
1205
1298
  It approximates E2B's server-side billed lifetime. Plan fees, credits, and negotiated
1206
1299
  prices are excluded; unknown remaining lifetime makes `fullyEstimated` false.
@@ -1234,17 +1327,17 @@ desktopMinutes: 1
1234
1327
  note: "Estimated 11.60167 USD total…"
1235
1328
  ```
1236
1329
 
1237
- `verifyRun` asserts LABELING/provenance, never MAGNITUDE. Absence PASSES
1330
+ `verifyRun` asserts labeling and provenance, never magnitude. Absence passes
1238
1331
  (fail-open on display): a bundle with no cost, a null estimate that carries a
1239
- `reason` and a null `ratesAsOf`, or a participant without `estimatedCost` verifies fine. A CLAIMED number FAILS closed when it lacks its
1332
+ `reason` and a null `ratesAsOf`, or a participant without `estimatedCost` verifies fine. A claimed number fails closed when it lacks its
1240
1333
  `ratesAsOf` date or `source`, or when `estimatedTotalUsd` does not equal the
1241
1334
  rounded sum of its non-null lines (a null line coerced to 0 is a mechanism
1242
1335
  mismatch). A correctly-labeled huge estimate still passes. Cost is neither a
1243
1336
  secret nor a share-blocker: its magnitude never affects `shareSafety`. A labeling
1244
1337
  failure fails verify, which blocks sharing like any other failed check.
1245
1338
 
1246
- **Computer-use spend thresholds.** `execution.caps.maxUsd` applies independently
1247
- to each participant. `execution.caps.maxTotalUsd` shares an estimated model-spend ledger
1339
+ **Computer-use spend thresholds.** `caps.maxUsd` applies independently
1340
+ to each participant. `caps.maxTotalUsd` shares an estimated model-spend ledger
1248
1341
  across the study's participants. Either, both, or neither may be declared. Without a
1249
1342
  shared threshold, N participants each have their own `maxUsd`; N × `maxUsd` is the sum
1250
1343
  of those thresholds, not a guaranteed total spending ceiling.
@@ -1288,10 +1381,10 @@ threshold is not proof of task completion.
1288
1381
  An absent threshold is uncapped. A zero threshold can still permit a paid model
1289
1382
  request before reported usage trips it; `maxUsd: 0` is not a no-provider-call
1290
1383
  mode. Use the keyless `humanish run first-run` preview or an explicit
1291
- `humanish lab run <lab> --dry-run` for a path without provider calls. A declared
1384
+ `humanish run <study> --dry-run` for a path without provider calls. A declared
1292
1385
  threshold on a model `src/run/pricing.ts` cannot price is refused at preflight
1293
- (`HUMANISH_CUA_LAB_UNPRICED_CAP`, or `HUMANISH_CONCURRENT_SHARED_WORLD_LAB_UNPRICED_CAP` on a
1294
- shared-world lab) before sandbox allocation. This rate-availability
1386
+ (`HUMANISH_COMPUTER_USE_UNPRICED_CAP`, or `HUMANISH_SHARED_WORLD_UNPRICED_CAP` on a
1387
+ shared-world study) before sandbox allocation. This rate-availability
1295
1388
  check is separate from the post-response spend check.
1296
1389
 
1297
1390
  Library callers can make a capped session stricter by composing it from the loop and the default
@@ -1321,9 +1414,9 @@ on, and any failed or stalled request stops the session; both stop with `harness
1321
1414
  `stopCause: usage_unreported`, and the label “provider usage unavailable.” It is not recorded as a
1322
1415
  crossed threshold. `singleDispatch: true` disables the OpenAI adapter's HTTP and
1323
1416
  policy-negotiation retries, so each turn makes at most one request.
1324
- `runCuaActorSession({ requireReportedUsageForSpendCap: true })` sets `singleDispatch` itself and
1325
- is deprecated in this minor. It also attaches `trace.estimatedCost` for an injected
1326
- account-billed provider, which the composition does not. The loop cancels its owned request signal when a
1417
+ This composition replaces `runCuaActorSession`, which set `singleDispatch` itself and was removed
1418
+ in 0.107.0. Unlike `runCuaActorSession`, it attaches no `trace.estimatedCost` for an injected
1419
+ account-billed provider. The loop cancels its owned request signal when a
1327
1420
  request ends or its timeout wins; injected providers must honor cancellation
1328
1421
  and remain responsible for their own internal dispatch. Known usage remains in
1329
1422
  the trace alongside an explicit unknown. Reported zero input and output counts
@@ -1332,19 +1425,19 @@ original interruption cause first, with any missing usage still recorded as
1332
1425
  unknown.
1333
1426
 
1334
1427
  These computer-use rules do not replace the terminal route's separate
1335
- `scenario.caps` cost-ledger and product-spend rules described above.
1428
+ `caps` cost-ledger and product-spend rules described above.
1336
1429
 
1337
1430
  ## Affordance Use
1338
1431
 
1339
- `humanish.affordance-use.v1` (additive `ActorTrace.affordanceUse`) records WHICH
1340
- KIND of route an actor took, per dispatched action, as counts by class:
1432
+ `humanish.affordance-use.v1` (additive `ActorTrace.affordanceUse`) records which
1433
+ kind of route an actor took, per dispatched action, as counts by class:
1341
1434
 
1342
1435
  | Class | What it covers |
1343
1436
  | ------------------ | ------------------------------------------------------------------------------------------------------------- |
1344
1437
  | `pointer` | click, double-click, drag, scroll: interaction with what is rendered |
1345
1438
  | `keyboard` | typed text and key presses into the page |
1346
1439
  | `speech` | spoken input from a `speak` action |
1347
- | `url-navigation` | typing a URL (the `nav` subset); a HUMAN affordance, see below |
1440
+ | `url-navigation` | typing a URL (the `nav` subset); a human affordance, see below |
1348
1441
  | `script-execution` | a `javascript:` or `data:` URL; not a human affordance |
1349
1442
  | `devtools` | developer tooling opened by keyboard chord |
1350
1443
  | `browser-internal` | `chrome://`, `about:`, `view-source:`, `devtools:`, `file:`, `edge:`, `brave:` (the browser, not the product) |
@@ -1355,45 +1448,49 @@ rate; `shortcutTotal` rolls up `script-execution` + `devtools` +
1355
1448
  `browser-internal`, and a value of `0` is a meaningful result rather than an
1356
1449
  absence.
1357
1450
 
1358
- Direct URL navigation is deliberately its OWN class and is grouped with the
1451
+ Direct URL navigation is deliberately its own class and is grouped with the
1359
1452
  naturalistic classes, not with script execution: `load(url)` appears in 99.4% of
1360
1453
  2,337 real human web demonstrations, so address-bar use is ordinary human
1361
1454
  behavior and classifying it as a shortcut would make an ordinary human participant look
1362
1455
  unfaithful. See [`docs/principles/actor-fidelity.md`](../principles/actor-fidelity.md)
1363
1456
  for the evidence and the scoping of what a fidelity claim can mean.
1364
1457
 
1365
- The record carries a CLASS and at most a scheme-shaped signal (`javascript:`,
1458
+ The record carries a class and at most a scheme-shaped signal (`javascript:`,
1366
1459
  `https:`, a devtools chord), never the typed text, which can be a password, a
1367
1460
  session token, or an identifying URL path. Classification runs at dispatch
1368
1461
  because the text exists only there: the trace's own action label deliberately
1369
1462
  renders `type [N chars]`.
1370
1463
 
1371
- The harness states NO verdict about a class. Whether an affordance invalidates a
1464
+ The harness states no verdict about a class. Whether an affordance invalidates a
1372
1465
  study depends on the population that study declares, which is product semantics
1373
1466
  and belongs to the adopter's scorer. That scorer already receives the full trace,
1374
1467
  so `affordanceUse` needs no extra wiring to reach it. Present on computer-use participants
1375
1468
  that dispatched at least one action; absent elsewhere and on every pre-existing
1376
1469
  bundle, and its absence is tolerated by verify.
1377
1470
 
1378
- ## Library options (`RunLabOptions`)
1379
-
1380
- `runLab(config, options)` checks its options against the lab's route before anything runs. A
1381
- refusal comes back in the route's own result envelope, with no run directory, receipt or sandbox.
1382
-
1383
- | Option | What it does | Routes |
1384
- | ---------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1385
- | `env` | Keys and subject env for the run. Defaults to `process.env` | all |
1386
- | `scorer` | An `AdapterScorerModule` (`score`, `deriveFeedback`, and `deriveArtifacts` on browser routes) over the finished evidence | computer use, shared world, terminal |
1387
- | `createProvider(ctx)` | The participant's brain. `ctx` is `ProviderContext`: `config`, `participant { id, index, count }` and `executor` | computer use: E2B, local VM or in process |
1388
- | `inProcess.executor({ config, appUrl })` | Drives an `app-url` or `local-app` subject in process, one participant. Requires `createProvider` | computer use |
1389
- | `prepareDesktop(desktop, target)` | Runs on an E2B sandbox before provisioning. `target` is `{ kind: "subject" }` or `{ kind: "participant", participant }` | computer use on E2B (each participant); shared world (provisioned: the subject, then each participant; external-public: each participant); scripted clone (the subject) |
1390
- | `onEvent(event)` | Observes `LabEvent`s, below | all |
1391
- | `onStream(event)` | Awaited. `ready` after a participant's live stream starts; its `url` carries an auth key and must never be persisted. `ended` after the participant's sandbox is gone. A rejected `ready` becomes a run warning; an `ended` rejection is swallowed | computer use and shared world on E2B |
1392
- | `analysisSignal` | Cancels post-run analysis only | routes with post-run analysis |
1393
- | `onObserverReady(observer)` | Awaited before any participant desktop exists | live computer use; live shared world, where the provisioned subject sandbox is already billed |
1394
- | `rerun.participantIds` | Participants to rerun from `rerun.sourceRunId` | computer use |
1395
-
1396
- `LabEvent` has four types:
1471
+ ## Library options
1472
+
1473
+ `runStudy(config, options)` checks its `RunStudyOptions` against the study's route before anything
1474
+ runs. A refusal comes back in the route's own result envelope, with no run directory, receipt or
1475
+ sandbox. The 0.107 names (`runLab`, `RunLabOptions`, `parseLabConfig`, `LAB_CONFIG_SCHEMA`,
1476
+ `LabConfig`, the `Lab*` result and event types, the nine `Cua*` loop types, `CuaAdmissionLimitError`
1477
+ and `BrowserLabScoringContext`) are deprecated aliases with the same value or class until 0.109
1478
+ (`src/library-aliases.ts`).
1479
+
1480
+ | Option | What it does | Routes |
1481
+ | ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1482
+ | `env` | Keys and subject env for the run. Defaults to `process.env` | all |
1483
+ | `scorer` | An `AdapterScorerModule` (`score`, `deriveFeedback`, and `deriveArtifacts` on browser routes) over the finished evidence. A scorer written for one context passes through `browserScorer` or `terminalScorer` | computer use, shared world, terminal |
1484
+ | `createProvider(ctx)` | The participant's brain. `ctx` is `ProviderContext`: `config`, `participant { id, index, count }` and `executor` | computer use: E2B, local VM or in process |
1485
+ | `inProcess.executor({ config, appUrl })` | Drives an `app-url` or `local-app` subject in process, one participant. Requires `createProvider` | computer use |
1486
+ | `prepareDesktop(desktop, target)` | Runs on an E2B sandbox before provisioning. `target` is `{ kind: "subject" }` or `{ kind: "participant", participant }` | computer use on E2B (each participant); shared world (provisioned: the subject, then each participant; external-public: each participant); scripted clone (the subject) |
1487
+ | `onEvent(event)` | Observes `StudyEvent`s, below | all |
1488
+ | `onStream(event)` | Awaited. `ready` after a participant's live stream starts; its `url` carries an auth key and must never be persisted. `ended` after the participant's sandbox is gone. A rejected `ready` becomes a run warning; an `ended` rejection is swallowed. Each event carries `participantId`, `recordId` (the id of this participant's entry in `run.json` `simulations[]`, such as `sim-001`, which `streams[].simId` also holds) and `streamId`; `ready` adds `sandboxId` and `url`. `simId` is the older name for `recordId`: its first read prints a `DeprecationWarning` with code `HUMANISH_STREAM_EVENT_FIELD_DEPRECATED`, and the next minor removes it. | computer use and shared world on E2B |
1489
+ | `analysisSignal` | Cancels post-run analysis only | routes with post-run analysis |
1490
+ | `onObserverReady(observer)` | Awaited before any participant desktop exists | live computer use; live shared world, where the provisioned subject sandbox is already billed |
1491
+ | `rerun.participantIds` | Participants to rerun from `rerun.sourceRunId` | computer use |
1492
+
1493
+ `StudyEvent` has four types:
1397
1494
 
1398
1495
  - `plan`: the participants (`id`, `persona`, `device`, `instructionDigest`) before any sandbox
1399
1496
  or provider call. Computer use only; the other routes gain it later.
@@ -1408,43 +1505,61 @@ throw or a rejected promise becomes a redacted run warning.
1408
1505
 
1409
1506
  Refusals:
1410
1507
 
1411
- - `HUMANISH_LAB_OPTION_UNSUPPORTED`: the route cannot honor the option, for example `scorer` on
1412
- a preview or scripted lab, `createProvider` off computer use, or `prepareDesktop` where no E2B
1413
- desktop exists. The message names the option and the route.
1414
- - `HUMANISH_LAB_OPTION_CONFLICT`: a new option is set together with the older field it replaces.
1415
-
1416
- The older route hook bags (`cuaHooks`, `scriptedHooks`, `terminalHooks`, `sharedWorldHooks`) and
1417
- `rerun.laneIds` keep their behavior in this minor and are removed in the next. Each old field in
1418
- use emits one `DeprecationWarning` (code `HUMANISH_RUN_LAB_OPTION_DEPRECATED`) per process:
1419
-
1420
- | Old field | New home |
1421
- | -------------------------------------------------------------------------------------------------- | ---------------------- |
1422
- | `score`, `deriveFeedback` on `cuaHooks`, `sharedWorldHooks` or `terminalHooks` | `scorer` |
1423
- | `deriveArtifacts` on `cuaHooks` or `sharedWorldHooks` | `scorer` |
1424
- | `cuaHooks.buildProvider` | `createProvider` |
1425
- | `cuaHooks.buildExecutor` | `inProcess.executor` |
1426
- | `prepareDesktop` on `cuaHooks`, `scriptedHooks` or `sharedWorldHooks` | `prepareDesktop` |
1427
- | `cuaHooks.onPreflight`; `onPhase` on `cuaHooks` or `sharedWorldHooks`; `automaticAnalysis.onStart` | `onEvent` |
1428
- | `onRuntimeStreamReady`, `onRuntimeStreamEnded` on `cuaHooks` or `sharedWorldHooks` | `onStream` |
1429
- | `automaticAnalysis.deps.signal` | `analysisSignal` |
1430
- | `env` on any bag | `env` |
1431
- | `rerun.laneIds` | `rerun.participantIds` |
1432
- | `cuaHooks.createDesktopLane` | none |
1433
-
1434
- `cuaHooks.createDesktopLane` warns and goes in the next minor with no replacement: a run's desktop
1435
- is E2B, the local VM or in process. The bags' other fields are test seams with no public
1436
- replacement, and they do not warn. A
1437
- `scorer` passed through `RunLabOptions` behaves exactly like the same functions in the old bag,
1438
- including the route-specific verdict rules in the next section.
1508
+ - `HUMANISH_STUDY_OPTION_UNSUPPORTED`: the route cannot honor the option, for example `scorer` on
1509
+ a preview or scripted study, `createProvider` off computer use, or `prepareDesktop` where no E2B
1510
+ desktop exists. The message names the option and the route. The same code refuses a field
1511
+ `RunStudyOptions` no longer has (below); that message names the field and where its job went.
1512
+
1513
+ `rerun.laneIds`, the older name of `rerun.participantIds`, was removed in 0.107.0. A caller that
1514
+ still passes it is refused with `HUMANISH_STUDY_OPTION_UNSUPPORTED`; use `rerun.participantIds`.
1515
+
1516
+ The route hook bags (`cuaHooks`, `scriptedHooks`, `terminalHooks`, `sharedWorldHooks`,
1517
+ `automaticAnalysis`) were removed from `RunStudyOptions`, with the hook bag types and the four route
1518
+ runners that took them (`runCuaActorLab`, `runScriptedBrowserLab`, `runTerminalProductLab`,
1519
+ `runConcurrentSharedWorld`). `lab` and `scorerProvenance` are set only by the humanish CLI. A
1520
+ JavaScript caller that passes any of these fields is refused before anything runs. Where each
1521
+ bag field's job went:
1522
+
1523
+ | Removed field | Use |
1524
+ | -------------------------------------------------------------------------------------------------- | -------------------- |
1525
+ | `score`, `deriveFeedback` on `cuaHooks`, `sharedWorldHooks` or `terminalHooks` | `scorer` |
1526
+ | `deriveArtifacts` on `cuaHooks` or `sharedWorldHooks` | `scorer` |
1527
+ | `cuaHooks.buildProvider` | `createProvider` |
1528
+ | `cuaHooks.buildExecutor` | `inProcess.executor` |
1529
+ | `prepareDesktop` on `cuaHooks`, `scriptedHooks` or `sharedWorldHooks` | `prepareDesktop` |
1530
+ | `cuaHooks.onPreflight`; `onPhase` on `cuaHooks` or `sharedWorldHooks`; `automaticAnalysis.onStart` | `onEvent` |
1531
+ | `onRuntimeStreamReady`, `onRuntimeStreamEnded` on `cuaHooks` or `sharedWorldHooks` | `onStream` |
1532
+ | `automaticAnalysis.deps.signal` | `analysisSignal` |
1533
+ | `env` on any bag | `env` |
1534
+ | `cuaHooks.createDesktopLane` | none |
1535
+
1536
+ `cuaHooks.createDesktopLane` has no replacement: a run's desktop is E2B, the local VM or in
1537
+ process. The bags' other fields were test seams with no public replacement. `scorer` keeps the
1538
+ route-specific verdict rules in the next section.
1539
+
1540
+ 0.107.0 also removed these exports from `humanish`. A removed function no longer warns; an import
1541
+ of it fails.
1542
+
1543
+ | Removed export | Use |
1544
+ | ---------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1545
+ | `runDryRun`, `RunOptions`, `RunResult` | `runStudy(config, options)` on a `this-repo` study; its result is `StudyResult<"preview">` |
1546
+ | `runCuaActorSession` | `runComputerUseLoop` with `createOpenAiResponsesProvider({ singleDispatch: true })`, `redaction: defaultRedactionHooks` and `now: Date.now`, which `runCuaActorSession` defaulted (the composition above). A caller that passed `desktop` and `executorOptions` writes its own `ComputerUseExecutor`: the E2B desktop executor is not exported |
1547
+ | `CuaActorLabResult`, `ScriptedBrowserLabResult`, `TerminalProductLabResult`, `ConcurrentSharedWorldLabResult` | `StudyResult<"computer-use">`, `StudyResult<"scripted">`, `StudyResult<"terminal">`, `StudyResult<"shared-world">` |
1548
+ | `SubjectPhaseEvent` | the `subject-phase` `StudyEvent` through `onEvent`. Its `type` is `"subject-phase"`; the phase that was `type` is `name`, and `target` is `{ kind: "subject" }` or `{ kind: "participant", participant }`. `message`, `at`, `ok` and `durationMs` are unchanged |
1549
+ | `StudyOutcome.backend`, `LabBackend` | `outcome.route` |
1550
+ | `selectLabBackend`, the `routesTo*` predicates, `actorResolvesToTerminal` | `routeOf(config)` |
1551
+ | `resolveLabDryRun` | `RunStudyOptions.dryRun`; without it, `scenario.mode` decides |
1552
+ | `cuaLaneCount` | the `plan` `StudyEvent`, which lists every participant |
1553
+ | `resolveSeatUrl` | `parseStudy`, which refuses a participant entry that is not same-origin loopback; build the URL with `new URL(entry, serveUrl)` |
1554
+ | `cuaLaneValidationReason`, `sharedWorldValidationReason`, `concurrentSharedWorldValidationReason`, `externalPublicSharedWorldValidationReason` | `parseStudy`, which returns the same reason |
1555
+ | `MAX_CUA_LANES` | none; `parseStudy` refuses a roster of more than 16 participants |
1439
1556
 
1440
1557
  ## Product-Adapter Extension Seam
1441
1558
 
1442
1559
  The terminal-product and browser/computer-use routes let an adopter attach
1443
- product-specific scoring + feedback as a THIN in-repo extension WITHOUT forking
1444
- core. The seam is `RunLabOptions.scorer`, an `AdapterScorerModule`, for terminal-product,
1445
- computer-use and shared-world runs; the CLI loads the same module with `--scorer`. The older
1446
- `TerminalProductLabHooks`, `CuaActorLabHooks` and `SharedWorldLabHooks` fields still accept the
1447
- same functions in this minor. This is never a built-in product scorer (the adopter's scorecard
1560
+ product-specific scoring + feedback as a thin in-repo extension without forking
1561
+ core. The seam is `RunStudyOptions.scorer`, an `AdapterScorerModule`, for terminal-product,
1562
+ computer-use and shared-world runs; the CLI loads the same module with `--scorer`. This is never a built-in product scorer (the adopter's scorecard
1448
1563
  lives in the adopter's repo).
1449
1564
 
1450
1565
  Three product-agnostic carriers keep core's nouns closed while letting the adapter
@@ -1460,8 +1575,8 @@ status, score, summary, data? }`. Core never reads `data`, where the adopter's
1460
1575
  feedback-candidate shape; product-specific concepts (public CLI/product command
1461
1576
  observed, hosted product success-or-blocker, feedback id/draft, media/job/asset
1462
1577
  ids, explicit no-media/no-provider-spend proof, defection/friction risk) are
1463
- recorded ONLY under `adapter: { namespace, data }`, never as core enums. Core
1464
- validates the SHAPE (a non-empty `namespace` + a `data` record); the keys inside
1578
+ recorded only under `adapter: { namespace, data }`, never as core enums. Core
1579
+ validates the shape (a non-empty `namespace` + a `data` record); the keys inside
1465
1580
  `data` are the adapter's.
1466
1581
  - **Adapter artifacts** (`humanish.adapter-artifact.v1`,
1467
1582
  `RunBundle.adapterArtifacts[]`). A namespaced list of local relative artifact
@@ -1507,7 +1622,7 @@ Acceptance semantics are route-specific:
1507
1622
 
1508
1623
  - Terminal-product runs keep the mission-based `review` verdict unchanged for a
1509
1624
  library `scorer`; the adapter score is additive. A config-declared scorer
1510
- (`--scorer` or the lab's `review.scorer.ref`) that returns `status: fail`,
1625
+ (`--scorer` or the study's `review.scorer.ref`) that returns `status: fail`,
1511
1626
  throws, or returns a malformed score turns a pass-like verdict into `fail` and
1512
1627
  fails the run result.
1513
1628
  - Browser/computer-use runs treat `adapterScore.status: fail` as product-red:
@@ -1518,18 +1633,18 @@ Acceptance semantics are route-specific:
1518
1633
  evidence.
1519
1634
 
1520
1635
  The `e2b-terminal` substrate is added to `RunFeedbackCandidate.substrate` so a
1521
- terminal-agent candidate names its substrate honestly; browser candidates use
1522
- the existing `e2b-desktop` substrate. The routes invoke hooks over FULLY-ASSEMBLED,
1636
+ terminal-agent candidate names the substrate it ran on; browser candidates use
1637
+ the existing `e2b-desktop` substrate. The routes invoke hooks over fully-assembled,
1523
1638
  redacted evidence (`TerminalProductScoringContext` or
1524
- `BrowserLabScoringContext`: `bundle`, runtime-only `runDir`, run identifiers,
1525
- actor/backend metadata; all exported public types), scrub+redact returned
1526
- payloads, and DROP any malformed score, candidate, or artifact reference with a
1639
+ `BrowserScoringContext`: `bundle`, runtime-only `runDir`, run identifiers,
1640
+ actor and route metadata; all exported public types), scrub+redact returned
1641
+ payloads, and drop any malformed score, candidate, or artifact reference with a
1527
1642
  warning so a bad extension never poisons a verifiable bundle. On the terminal
1528
- route the context also carries `transcript`: the FULL normalized session
1643
+ route the context also carries `transcript`: the full normalized session
1529
1644
  transcript, source-scrubbed then shape-redacted and byte-identical to the
1530
1645
  persisted `terminal-transcript.txt` (the trace's `transcriptTail` is a ~2KB
1531
1646
  projection of it), so an adopter rubric can find command-tier evidence anywhere
1532
- in the session rather than only in the tail window (#341). Default behavior
1647
+ in the session, including outside the tail window. Default behavior
1533
1648
  (no hook) is unchanged. `verifyRun` re-checks the surviving shapes fail-closed,
1534
1649
  including existence for referenced adapter artifacts.
1535
1650
 
@@ -1615,10 +1730,10 @@ bundlePath: .humanish/runs/synthetic-run-bundle-2026-06-02t10-00-00-000z-proof/r
1615
1730
  checks:
1616
1731
  - name: run.json exists
1617
1732
  ok: true
1618
- message: run.json present
1733
+ message: run.json is present
1619
1734
  - name: redaction passed
1620
1735
  ok: true
1621
- message: redaction status must be passed
1736
+ message: redaction passed
1622
1737
  shareSafety:
1623
1738
  status: share_ready
1624
1739
  reasons: []