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