humanish 0.0.1 → 0.15.1

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 (205) hide show
  1. package/AGENTS.md +77 -0
  2. package/CONTRIBUTING.md +39 -0
  3. package/LICENSE +21 -0
  4. package/README.md +437 -8
  5. package/SECURITY.md +34 -0
  6. package/dist/actor-contract.d.ts +107 -0
  7. package/dist/actor-contract.js +286 -0
  8. package/dist/actor-contract.js.map +1 -0
  9. package/dist/actor-registry.d.ts +73 -0
  10. package/dist/actor-registry.js +93 -0
  11. package/dist/actor-registry.js.map +1 -0
  12. package/dist/adapter-extension.d.ts +54 -0
  13. package/dist/adapter-extension.js +214 -0
  14. package/dist/adapter-extension.js.map +1 -0
  15. package/dist/argv.d.ts +1 -0
  16. package/dist/argv.js +8 -0
  17. package/dist/argv.js.map +1 -0
  18. package/dist/artifact-reference.d.ts +22 -0
  19. package/dist/artifact-reference.js +47 -0
  20. package/dist/artifact-reference.js.map +1 -0
  21. package/dist/browser-evidence-hygiene.d.ts +7 -0
  22. package/dist/browser-evidence-hygiene.js +40 -0
  23. package/dist/browser-evidence-hygiene.js.map +1 -0
  24. package/dist/claude-agent-sdk.d.ts +94 -0
  25. package/dist/claude-agent-sdk.js +363 -0
  26. package/dist/claude-agent-sdk.js.map +1 -0
  27. package/dist/cli.d.ts +2 -0
  28. package/dist/cli.js +5 -0
  29. package/dist/cli.js.map +1 -0
  30. package/dist/codex-app-server-ui.d.ts +39 -0
  31. package/dist/codex-app-server-ui.js +357 -0
  32. package/dist/codex-app-server-ui.js.map +1 -0
  33. package/dist/codex-app-server.d.ts +144 -0
  34. package/dist/codex-app-server.js +754 -0
  35. package/dist/codex-app-server.js.map +1 -0
  36. package/dist/command-failure.d.ts +29 -0
  37. package/dist/command-failure.js +54 -0
  38. package/dist/command-failure.js.map +1 -0
  39. package/dist/computer-use-actor.d.ts +52 -0
  40. package/dist/computer-use-actor.js +48 -0
  41. package/dist/computer-use-actor.js.map +1 -0
  42. package/dist/computer-use.d.ts +232 -0
  43. package/dist/computer-use.js +615 -0
  44. package/dist/computer-use.js.map +1 -0
  45. package/dist/concurrency.d.ts +1 -0
  46. package/dist/concurrency.js +27 -0
  47. package/dist/concurrency.js.map +1 -0
  48. package/dist/concurrent-shared-world-lab.d.ts +115 -0
  49. package/dist/concurrent-shared-world-lab.js +1094 -0
  50. package/dist/concurrent-shared-world-lab.js.map +1 -0
  51. package/dist/core/git-state.d.ts +33 -0
  52. package/dist/core/git-state.js +277 -0
  53. package/dist/core/git-state.js.map +1 -0
  54. package/dist/core/git-workspace.d.ts +31 -0
  55. package/dist/core/git-workspace.js +447 -0
  56. package/dist/core/git-workspace.js.map +1 -0
  57. package/dist/core/index.d.ts +4 -0
  58. package/dist/core/index.js +3 -0
  59. package/dist/core/index.js.map +1 -0
  60. package/dist/core/run-primitives.d.ts +66 -0
  61. package/dist/core/run-primitives.js +120 -0
  62. package/dist/core/run-primitives.js.map +1 -0
  63. package/dist/cua-actor-lab.d.ts +621 -0
  64. package/dist/cua-actor-lab.js +2845 -0
  65. package/dist/cua-actor-lab.js.map +1 -0
  66. package/dist/device-presets.d.ts +67 -0
  67. package/dist/device-presets.js +50 -0
  68. package/dist/device-presets.js.map +1 -0
  69. package/dist/e2b-desktop-executor.d.ts +101 -0
  70. package/dist/e2b-desktop-executor.js +309 -0
  71. package/dist/e2b-desktop-executor.js.map +1 -0
  72. package/dist/e2b-desktop-launch.d.ts +144 -0
  73. package/dist/e2b-desktop-launch.js +59 -0
  74. package/dist/e2b-desktop-launch.js.map +1 -0
  75. package/dist/e2b-detached.d.ts +53 -0
  76. package/dist/e2b-detached.js +130 -0
  77. package/dist/e2b-detached.js.map +1 -0
  78. package/dist/e2b-terminal-lab.d.ts +345 -0
  79. package/dist/e2b-terminal-lab.js +1532 -0
  80. package/dist/e2b-terminal-lab.js.map +1 -0
  81. package/dist/env-file.d.ts +14 -0
  82. package/dist/env-file.js +108 -0
  83. package/dist/env-file.js.map +1 -0
  84. package/dist/feedback.d.ts +51 -0
  85. package/dist/feedback.js +351 -0
  86. package/dist/feedback.js.map +1 -0
  87. package/dist/image-evidence.d.ts +2 -0
  88. package/dist/image-evidence.js +33 -0
  89. package/dist/image-evidence.js.map +1 -0
  90. package/dist/index.d.ts +63 -0
  91. package/dist/index.js +33 -0
  92. package/dist/index.js.map +1 -0
  93. package/dist/init-templates.d.ts +12 -0
  94. package/dist/init-templates.js +369 -0
  95. package/dist/init-templates.js.map +1 -0
  96. package/dist/init.d.ts +26 -0
  97. package/dist/init.js +429 -0
  98. package/dist/init.js.map +1 -0
  99. package/dist/lab-app-runner.d.ts +78 -0
  100. package/dist/lab-app-runner.js +403 -0
  101. package/dist/lab-app-runner.js.map +1 -0
  102. package/dist/lab-config.d.ts +583 -0
  103. package/dist/lab-config.js +1789 -0
  104. package/dist/lab-config.js.map +1 -0
  105. package/dist/lab-engine.d.ts +77 -0
  106. package/dist/lab-engine.js +216 -0
  107. package/dist/lab-engine.js.map +1 -0
  108. package/dist/lab-preflight.d.ts +67 -0
  109. package/dist/lab-preflight.js +385 -0
  110. package/dist/lab-preflight.js.map +1 -0
  111. package/dist/labs.d.ts +53 -0
  112. package/dist/labs.js +382 -0
  113. package/dist/labs.js.map +1 -0
  114. package/dist/observer-assets.d.ts +2 -0
  115. package/dist/observer-assets.js +2630 -0
  116. package/dist/observer-assets.js.map +1 -0
  117. package/dist/observer-data.d.ts +63 -0
  118. package/dist/observer-data.js +145 -0
  119. package/dist/observer-data.js.map +1 -0
  120. package/dist/observer-static.d.ts +39 -0
  121. package/dist/observer-static.js +306 -0
  122. package/dist/observer-static.js.map +1 -0
  123. package/dist/observer.d.ts +46 -0
  124. package/dist/observer.js +550 -0
  125. package/dist/observer.js.map +1 -0
  126. package/dist/openai-responses-cu.d.ts +113 -0
  127. package/dist/openai-responses-cu.js +545 -0
  128. package/dist/openai-responses-cu.js.map +1 -0
  129. package/dist/oss-lab.d.ts +52 -0
  130. package/dist/oss-lab.js +344 -0
  131. package/dist/oss-lab.js.map +1 -0
  132. package/dist/oss-meta-lab-scoring.d.ts +15 -0
  133. package/dist/oss-meta-lab-scoring.js +145 -0
  134. package/dist/oss-meta-lab-scoring.js.map +1 -0
  135. package/dist/oss-meta-lab.d.ts +277 -0
  136. package/dist/oss-meta-lab.js +4529 -0
  137. package/dist/oss-meta-lab.js.map +1 -0
  138. package/dist/oss-remote-telemetry.d.ts +77 -0
  139. package/dist/oss-remote-telemetry.js +403 -0
  140. package/dist/oss-remote-telemetry.js.map +1 -0
  141. package/dist/persona.d.ts +35 -0
  142. package/dist/persona.js +133 -0
  143. package/dist/persona.js.map +1 -0
  144. package/dist/pi-agent-core.d.ts +77 -0
  145. package/dist/pi-agent-core.js +203 -0
  146. package/dist/pi-agent-core.js.map +1 -0
  147. package/dist/program.d.ts +54 -0
  148. package/dist/program.js +2349 -0
  149. package/dist/program.js.map +1 -0
  150. package/dist/redaction.d.ts +90 -0
  151. package/dist/redaction.js +292 -0
  152. package/dist/redaction.js.map +1 -0
  153. package/dist/run-paths.d.ts +56 -0
  154. package/dist/run-paths.js +340 -0
  155. package/dist/run-paths.js.map +1 -0
  156. package/dist/run.d.ts +794 -0
  157. package/dist/run.js +4844 -0
  158. package/dist/run.js.map +1 -0
  159. package/dist/scripted-browser-actor.d.ts +297 -0
  160. package/dist/scripted-browser-actor.js +1399 -0
  161. package/dist/scripted-browser-actor.js.map +1 -0
  162. package/dist/scripted-browser-lab.d.ts +107 -0
  163. package/dist/scripted-browser-lab.js +788 -0
  164. package/dist/scripted-browser-lab.js.map +1 -0
  165. package/dist/selected-output-paths.d.ts +49 -0
  166. package/dist/selected-output-paths.js +354 -0
  167. package/dist/selected-output-paths.js.map +1 -0
  168. package/dist/shared-world-lab.d.ts +208 -0
  169. package/dist/shared-world-lab.js +1049 -0
  170. package/dist/shared-world-lab.js.map +1 -0
  171. package/dist/source-archive.d.ts +82 -0
  172. package/dist/source-archive.js +408 -0
  173. package/dist/source-archive.js.map +1 -0
  174. package/dist/stop-conditions.d.ts +26 -0
  175. package/dist/stop-conditions.js +66 -0
  176. package/dist/stop-conditions.js.map +1 -0
  177. package/dist/terminal-agent-actor.d.ts +36 -0
  178. package/dist/terminal-agent-actor.js +23 -0
  179. package/dist/terminal-agent-actor.js.map +1 -0
  180. package/docs/architecture/actor-contract.md +431 -0
  181. package/docs/architecture/github-feedback-loop.md +189 -0
  182. package/docs/architecture/local-codex-tui-actor.md +213 -0
  183. package/docs/architecture/observer.md +118 -0
  184. package/docs/architecture/oss-lab-poc.md +250 -0
  185. package/docs/architecture/project-layout.md +166 -0
  186. package/docs/architecture/state-driven-executor.md +203 -0
  187. package/docs/architecture/terminal-product-lane.md +148 -0
  188. package/docs/contracts/adapter-fixtures.md +80 -0
  189. package/docs/contracts/core.md +71 -0
  190. package/docs/contracts/feedback.md +145 -0
  191. package/docs/contracts/policy.md +311 -0
  192. package/docs/contracts/run-bundle.md +358 -0
  193. package/docs/contracts/schemas.md +984 -0
  194. package/docs/goals/current.md +428 -0
  195. package/docs/principles/invariants-and-defaults.md +135 -0
  196. package/docs/principles/self-driving-harness.md +129 -0
  197. package/docs/product/open-source-install-experience.md +237 -0
  198. package/docs/ramp/README.md +207 -0
  199. package/docs/release/open-source-readiness.md +208 -0
  200. package/docs/release/public-readiness-standard.md +205 -0
  201. package/docs/roadmap/world-class-open-source-v0.md +311 -0
  202. package/package.json +102 -8
  203. package/skills/humanish/SKILL.md +244 -0
  204. package/skills/humanish/agents/openai.yaml +7 -0
  205. package/index.js +0 -1
@@ -0,0 +1,984 @@
1
+ # Contract Schema Index
2
+
3
+ Date: 2026-06-02 (updated 2026-06-24)
4
+
5
+ Status: schema map aligned to the shipped v0.6.x surface. Rows marked
6
+ "reserved" name layering intent only — no code emits or validates them yet.
7
+ Do not emit a reserved schema.
8
+
9
+ ## Purpose
10
+
11
+ This document names the core Humanish contracts before more implementation
12
+ lands. It is intentionally public-safe: examples use synthetic ids, local
13
+ relative artifact paths, env var names without values, and redacted evidence
14
+ notes.
15
+
16
+ Core contracts are reusable. Adapter contracts describe a target app, CLI, or
17
+ workflow without leaking private upstream truth into core.
18
+
19
+ ## Ownership Rule
20
+
21
+ | Layer | Owns | Does not own |
22
+ | --- | --- | --- |
23
+ | Core | Schema versions, run ids, artifact layout, lifecycle events, actor/substrate status, evidence shape, review, verification, redaction, feedback mechanics, latest/history indexes. | Product routes, real customer data, private screenshots, private transcripts, credential values, target-specific acceptance language. |
24
+ | Adapter | Product routes, scenario/persona choices, app topology, env var names, network allowlists, coverage vocabulary, milestones, fixture data, target-specific proof expectations. | Generic run bundle schema, public-safety gates, provider secret values, raw private artifacts, GitHub mutation authority. |
25
+
26
+ ## Contract Index
27
+
28
+ | Contract | Schema | Public-safe fixture |
29
+ | --- | --- | --- |
30
+ | Run bundle | `humanish.run-bundle.v1` | `synthetic-run-bundle` |
31
+ | Adapter | `humanish.adapter.v1` | `synthetic-cli-adapter` |
32
+ | Lab | `humanish.lab.v2` | `first-run` |
33
+ | Persona | `humanish.persona.v1` | `synthetic-maintainer` |
34
+ | Scenario | `humanish.scenario.v1` | `first-run-smoke` |
35
+ | Actor trace | `humanish.actor-trace.v1` | `synthetic-actor-trace` |
36
+ | Substrate | reserved (never shipped) | none |
37
+ | Evidence stream | reserved (streams live inside the run bundle) | see [`run-bundle.md`](run-bundle.md) |
38
+ | Review | `humanish.review.v1` | `contract-proof-review` |
39
+ | Verification | `humanish.verify-result.v1` | `five-check-verify` |
40
+ | Policy | `humanish.policy.v1` (fixture-only; not engine-validated) | `public-safety-policy` |
41
+ | Feedback | `humanish.feedback.v1` | `public-safe-feedback` |
42
+ | Terminal cost ledger | `humanish.terminal-cost-ledger.v1` | see Terminal Cost Ledger below |
43
+ | Terminal no-spend proof | `humanish.terminal-no-spend-proof.v1` | see Terminal Cost Ledger below |
44
+ | Adapter score | `humanish.adapter-score.v1` (`RunBundle.adapterScore`; namespaced; route-specific acceptance semantics) | see Product-Adapter Extension Seam below |
45
+ | Adapter artifact | `humanish.adapter-artifact.v1` (`RunBundle.adapterArtifacts[]`; namespaced; local relative proof references) | see Product-Adapter Extension Seam below |
46
+ | Shared-world evidence | `humanish.shared-world.v1` (additive `RunBundle.sharedWorld` + `RunBundle.attributionClass`; `topologyMode: sequential \| concurrent`) | see Shared-World Evidence below |
47
+
48
+ ## Lab Manifest
49
+
50
+ Schema: `humanish.lab.v2` (`src/lab-config.ts`). There is deliberately no v1
51
+ compatibility: v1 (`kind`, top-level `sims`) had zero real users and was
52
+ deleted when labs became config.
53
+
54
+ A lab is a composition over code primitives, not a hardcoded kind:
55
+
56
+ - `subject`: what the run acts on — `this-repo`, `clone` (owner/repo slugs,
57
+ optional in-sandbox `serve` + env var names + `state`), `local-tree` (the
58
+ operator's own working tree, packed on the host and provisioned in-sandbox
59
+ in place of a clone; see below), `app-url`
60
+ (loopback unless `policies.allowPublicTargets` declares an owned deployment),
61
+ `local-app` (an already-running LOCAL dev server driven IN-PROCESS via a
62
+ custom `CuaExecutor`, NO clone and NO E2B desktop — always loopback), or
63
+ `terminal-product` (a CLI/product a real autonomous terminal agent studies
64
+ from PUBLIC surfaces only — see below). A
65
+ `local-app` subject pairs a computer-use actor with `execution.target: local`
66
+ (or absent) and is library-assisted: the caller supplies
67
+ `cuaHooks.buildExecutor` + `buildProvider`; with no hooks the engine fails
68
+ closed (`HUMANISH_CUA_LAB_LOCAL_APP_NO_EXECUTOR`), never a desktop attempt. See
69
+ [`docs/architecture/state-driven-executor.md`](../architecture/state-driven-executor.md);
70
+ - `subject.localTree` (`local-tree` subjects, computer-use route): pack/upload
71
+ knobs for the packed working tree: `exclude[]` (extra archive excludes on
72
+ top of the always-on denylist; entries match as a repo-relative path
73
+ prefix or an exact basename, absolute paths and glob syntax are rejected
74
+ at parse time, and leading `./` / trailing `/` are normalized), `keep`
75
+ (preserve the sandbox on a failed lane for debugging, mirroring
76
+ `subject.clone.keep`; a kept local-tree sandbox holds the packed working
77
+ tree, including any file that survived the denylist), and
78
+ `maxArchiveBytes` (upload size cap override; default 256 MiB). Routing requires `execution.target: e2b-desktop` and a
79
+ computer-use actor; `subject.serve`/`env`/`state` apply exactly as they do
80
+ on the clone route (identical install/build/start/state semantics). The
81
+ packed root is the lab resolution cwd; there is no path field, by design
82
+ (an absolute path in a lab manifest would be a machine-specific,
83
+ unshareable, leak-prone artifact). Enumeration is git-aware when the root
84
+ is a git work tree (`git ls-files --cached --others --exclude-standard`,
85
+ honoring `.gitignore`) or a denylist-only recursive walk otherwise; an
86
+ always-on denylist (`.git`, `node_modules`, `.humanish`, `.env*`, key/cert
87
+ file patterns, and common credential-shaped names; the authoritative list
88
+ is `LOCAL_TREE_DENYLIST_BASENAME_PATTERNS` in `src/source-archive.ts`)
89
+ applies in both modes and is not overridable. The denylist matches names,
90
+ not contents; a secret in a file it does not name packs like any other
91
+ file, so review the pack summary line and use `localTree.exclude`. The lab packs
92
+ ONCE per run and uploads the identical archive to every fan-out lane. The
93
+ in-sandbox commit refresh clone subjects use is skipped: `.git` is never
94
+ uploaded, so identity comes from the host-side archive digest instead. See
95
+ [`docs/goals/local-tree-subject/goal.md`](../goals/local-tree-subject/goal.md);
96
+ - `subject.product` (terminal-product subjects): the product the agent studies.
97
+ `product.name` is a public-safe token (committed fixtures use a NEUTRAL mock
98
+ name); `product.publicSurfaces[]` is the list of http(s) URLs (docs, llms.txt,
99
+ skill manifest) that are the ONLY world the agent sees — the lab does not
100
+ clone/provision the product, so its provenance is recorded UNPINNED
101
+ (invariant 5). `serve`/`clone`/`state`/`repos`/`appUrl` are rejected on a
102
+ terminal-product subject (a field that cannot act on the route is a parse
103
+ error, not silently dropped). See
104
+ [`docs/architecture/terminal-product-lane.md`](../architecture/terminal-product-lane.md);
105
+ - `subject.state` (clone or local-tree subjects, computer-use route): the
106
+ subject's state story. `state.seed[]` declares ordered, bounded
107
+ seed/migration/fixture steps (`{ name, command, when: before-build |
108
+ before-start | after-ready, timeoutMs }`) executed in-sandbox around the
109
+ serve sequence; `state.external[]` declares env var NAMES (each must also
110
+ appear in `subject.env`) pointing at state the lab does not control,
111
+ recorded as UNPINNED in provenance. Commands persist in evidence as
112
+ sha256-16 digests only, never as text;
113
+ - `actors`: who drives it. On the computer-use and scripted-browser routes
114
+ `actors[0].type` is a real dispatch key resolved against the actor registry;
115
+ elsewhere it is a descriptive label (e.g. `synthetic-persona`).
116
+ `actors[0].count` carries route-specific meanings: synthetic route lane
117
+ count (simCount); scripted-browser route surface roster (1 = desktop,
118
+ 2 = desktop + mobile, default 1); computer-use **E2B** route the HOMOGENEOUS
119
+ fan-out lane count (N identical lanes, each its own E2B desktop — per-lane
120
+ worlds, cap 16). The in-process/local-app computer-use route stays single
121
+ lane (no E2B to fan out);
122
+ - `actors[0].lanes[]` (computer-use E2B route): a DIFFERENTIATED fan-out roster,
123
+ each `{ id?, actorType?, surface?, caseGroup?, persona?, device?,
124
+ instruction?, target?, entry? }` becoming one independent E2B desktop (or, on the
125
+ shared-world routes, one role/seat against the shared plane). `actorType`,
126
+ `surface`, and `caseGroup` are adapter-owned public-safe labels for grouping
127
+ simulated users; they are not core enums, and `actorType` is deliberately
128
+ separate from the execution dispatch key `actors[0].type`. `lanes` is XOR with
129
+ `count` (declare a roster OR a homogeneous count) and XOR with
130
+ `actors[0].laneFocus` (a roster's per-lane `instruction` is the steer);
131
+ `lanes[].device` is XOR with a raw `execution.desktop.resolution`. Lane ids
132
+ default `lane-01`..`lane-NN`, must be unique, and name per-lane evidence paths
133
+ (`actors/<streamId>.json`, `screenshots/<laneId>/`). Cap 16 lanes. On every
134
+ non-cua route `lanes` is inert (warned). `subject.clone.fanout` is REJECTED on
135
+ the cua route (declare fan-out via `count`/`lanes`; `clone.fanout` drives the
136
+ OSS smoke/meta routes only);
137
+ - `actors[0].lanes[].target` (app-url × computer-use E2B route only): an
138
+ absolute browser URL that lane opens instead of `subject.appUrl`. This is the
139
+ setup-produced-target handoff for crawler/swarm labs: an adapter may start any
140
+ topology it needs, then declare exactly which target each actor should drive.
141
+ If any lane declares `target`, every lane in that roster must declare one.
142
+ Public/non-loopback targets still require `policies.allowPublicTargets: true`.
143
+ `target` is mutually exclusive with `entry`: `target` is an absolute app-url
144
+ browser target; `entry` is a shared-world same-origin seat path;
145
+ - `actors[0].roster[]` (computer-use E2B route): compact authoring sugar for
146
+ repeated lane groups, each `{ id, count, actorType?, surface?, caseGroup?,
147
+ persona?, device?, instruction?, target?, entry? }`. The parser expands it into
148
+ deterministic `lanes[]` before the engine runs (`viewer-01`, `viewer-02`,
149
+ ...), so the runtime and run bundle keep one normalized lane shape. `roster`
150
+ is XOR with explicit `lanes`, homogeneous `count`, and `laneFocus`;
151
+ - `execution.concurrency` (computer-use E2B route): bounds in-flight (paid)
152
+ fan-out lanes; default `min(laneCount, 3)`. The env override
153
+ `HUMANISH_CUA_MAX_CONCURRENCY` may only LOWER the effective bound, never raise
154
+ concurrent paid desktops (invariant 3). Inert (warned) on other routes.
155
+ `execution.timeoutMs` is the PER-LANE session budget on this route (semantics
156
+ change: it was the single-session budget pre-fan-out); there is no run-level
157
+ wall clock. `policies.allowPublicTargets` cannot combine with N>1 against one
158
+ implicit public `subject.appUrl` (ambiguous shared-world-ish topology); it may
159
+ combine with N>1 only when the roster declares explicit `lanes[].target` for
160
+ every lane;
161
+ - `execution`: where it runs — `local`, `e2b-desktop`, or `e2b-terminal`, plus
162
+ desktop device/resolution and timeouts. app-url subjects pair `e2b-desktop`
163
+ with a computer-use actor, or `local` (or absent) with a scripted-browser
164
+ actor; terminal-product subjects pair `e2b-terminal` (or absent → implied)
165
+ with a registered terminal actor;
166
+ - `execution.desktop.template` (e2b-desktop computer-use routes): a custom E2B
167
+ desktop TEMPLATE (image) NAME or ID the run launches on — for a subject that
168
+ needs runtimes the stock `desktop` image lacks (e.g. node/bun/a local Postgres
169
+ baked into an adopter-maintained image). Any non-empty string is a valid
170
+ name/id (no allowlist); a blank/whitespace value is rejected. Threaded to the
171
+ SDK's `Sandbox.create(template, opts)` on EVERY desktop-creating route (the
172
+ single-lane + fan-out cua lanes, the sequential shared-world plane, and the
173
+ concurrent shared-world subject AND every actor sandbox); when absent the call
174
+ stays the byte-stable `Sandbox.create(opts)` default (the stock template). The
175
+ template actually used is recorded in the run bundle as `desktopTemplate`
176
+ (public-safe — a template name is not a secret). Inert (warned) on every route
177
+ that creates no desktop, incl. the in-process `local-app` cua route and the
178
+ meta route — never silently ignored (invariant 6);
179
+ - `execution.desktop.browser` (e2b-desktop computer-use/fan-out routes, plus
180
+ sequential and concurrent shared-world actor seats): optional browser family
181
+ preference: `default`, `chrome`, `chromium`, or `firefox`. Absent/default
182
+ preserves the historical desktop opener behavior. A concrete value means
183
+ launch that browser or fail closed; when configured, the bundle records
184
+ `desktopBrowser` with the requested value and the resolved in-sandbox command
185
+ when known. Inert (warned) where this route-specific browser launcher is not
186
+ used;
187
+ - `execution.terminal` + `execution.runtimeAuth` (terminal-product route):
188
+ `terminal.transport` is `exec-stream` — captured NON-interactive exec output
189
+ (stdin disabled); `pty` is rejected because labeling captured exec output as
190
+ an interactive PTY would overstate the mechanism (invariant 6; a true duplex
191
+ PTY transport is a deferred slice). `terminal.stdin` defaults to `disabled`
192
+ (`sent`/assisted input is rejected until the interventions ledger + a
193
+ non-comparable marker exist). `runtimeAuth: openai-env` declares the agent's
194
+ runtime-auth channel — recorded as NAMES ONLY; the command-scoped injection
195
+ (`keyPlacement: in-sandbox-command-scoped`) is enforced by the engine in a
196
+ later slice;
197
+ - `scenario`: `mode: dry-run` (contract evidence, no spend) or `live`.
198
+ `scenario.ref` is CONSUMED (and REQUIRED) on the scripted-browser route: it
199
+ resolves a committed scenario (`humanish/scenarios/<ref>.yaml` or a repo
200
+ path) whose `browser.steps` ARE what the actor executes, digest-pinned into
201
+ bundle provenance; on other routes `ref`/`inline` stay forward-declared
202
+ warnings. On the scripted route `live` gates real browser ACTUATION against
203
+ the declared app — provider spend stays $0 by mechanism (no model runs);
204
+ - `scenario.caps` (terminal-product route): `{ maxUsd, maxJobs, maxMinutes }`,
205
+ all non-negative numbers (0 = no-spend, the default). The blast-radius budget
206
+ that bounds the in-sandbox live key by MECHANISM, not by hope — the live key
207
+ is never exercised without a fail-closed cap in force. `maxMinutes` is the
208
+ wall-clock kill; `maxUsd`/`maxJobs` are enforced fail-closed against the cost
209
+ ledger (a run whose KNOWN spend exceeds the cap fails closed,
210
+ `HUMANISH_TERMINAL_LAB_CAPS_EXCEEDED`). The no-spend proof is derived from that
211
+ real ledger, never asserted (see Terminal Cost Ledger And No-Spend Proof).
212
+ Inert (warned) on every other route;
213
+ - `policies`: `redactRepos`, `redactScreenshots`, `allowPublicTargets`, and the
214
+ terminal-product credential-boundary booleans `allowPrivateRepoAccess`,
215
+ `allowProviderCredentials`, `allowPaymentCredentials`, `allowGitHubMutation`
216
+ (all DEFAULT FALSE — deny-by-default; only the runtime LLM key enters, and
217
+ only command-scoped). The scripted-browser route is loopback-only and rejects
218
+ `redactScreenshots: true` (blur unimplemented there) and
219
+ `allowPublicTargets: true` fail-closed rather than ignoring them.
220
+
221
+ Lab backends report results in their own schemas (`humanish.run-result.v1`,
222
+ `humanish.oss-lab-result.v1`, `humanish.oss-meta-lab-result.v1`,
223
+ `humanish.cua-lab-result.v2`, `humanish.scripted-lab-result.v1`,
224
+ `humanish.terminal-lab-result.v1`); the evidence record stays
225
+ `humanish.run-bundle.v1` in every case. The computer-use result bumped to v2 for
226
+ fan-out: it carries `plan` (the pre-flight lane table — concurrency, waves,
227
+ per-lane session budget, worst-case sandbox-minutes), `lanes[]` (ALWAYS present,
228
+ length 1 at N=1; per-lane status/session/sandbox/subject), and `laneSummary`
229
+ (passed/skipped/harnessError/hollow counts). The top-level `session`/`sandbox`
230
+ mirror the first lane and `subject.commit` is unanimity-gated across lanes
231
+ (omitted with a divergence warning when lanes resolve different commits). At N=1
232
+ the run bundle is byte-stable with the pre-fan-out output; only the result
233
+ projection changed. A fan-out run records a `cua-lab.fanout.plan` bundle event
234
+ (and a `cua-lab.fanout.fail-fast` event when a harness error skips queued lanes);
235
+ `ok = observer.ok ∧ no skipped lane ∧ all lanes terminal ∧ no harness error ∧ no
236
+ hollow lane`.
237
+
238
+ Explicit failed-lane reruns are supported on the CUA fan-out route via
239
+ `humanish lab run <lab> --rerun-failed-from <run-id> [--lanes lane-a,lane-b]`.
240
+ The source run must be a live CUA fan-out bundle. Humanish creates a NEW run for
241
+ the selected failed/blocked/timed-out/hollow lanes (or explicit lane ids), leaves
242
+ the source verdict unchanged, and records lineage as `run.rerun` plus a
243
+ `cua-lab.fanout.rerun` event: source run id, selected lane ids, and previous lane
244
+ statuses/reasons. This is intentionally not automatic retry; a passing rerun is a
245
+ nondeterminism candidate for human/product scoring, not a rewrite of the old run.
246
+
247
+ Manifests are human-authored `.yaml` source under `humanish/labs/*.yaml` for
248
+ committed public-safe labs, or ignored `.humanish/labs/*.yaml` /
249
+ `.humanish/local/labs/*.yaml` for private local dogfood. Fields the engine does
250
+ not yet consume are accepted but reported as warnings (`humanish lab inspect`
251
+ shows them), so a manifest never silently claims behavior that did not run.
252
+
253
+ Committed fixture (`humanish/labs/first-run.yaml`):
254
+
255
+ ```yaml
256
+ schema: humanish.lab.v2
257
+ id: first-run
258
+ title: First-run synthetic Observer
259
+ description: Public-safe starter lab that generates a synthetic run bundle and Observer without provider spend.
260
+ subject:
261
+ source: this-repo
262
+ actors:
263
+ - type: synthetic-persona
264
+ count: 4
265
+ scenario:
266
+ mode: dry-run
267
+ defaults:
268
+ open: true
269
+ ```
270
+
271
+ ## Run Bundle
272
+
273
+ Run bundles are the canonical evidence record. Observer data, review Markdown,
274
+ feedback drafts, and issue text are projections from the bundle.
275
+
276
+ Core-owned fields:
277
+
278
+ - `schema`
279
+ - `runId`
280
+ - `mode`
281
+ - `simCount`
282
+ - `createdAt`
283
+ - `artifactRoot`
284
+ - `source.git`
285
+ - `lifecycle`
286
+ - `simulations`
287
+ - `streams`
288
+ - `events`
289
+ - `redaction`
290
+ - `artifacts`
291
+ - `review`
292
+ - `feedbackCandidates`
293
+ - `subject` (optional, additive): structured subject provenance —
294
+ `{ source: clone | app-url | local-tree, repo?, commit?, archiveSha256?,
295
+ dirty?, envNames?, state }` where `state` is `{ provenance: seeded |
296
+ unpinned | declared-not-run | undeclared, seed?: [{ name, when,
297
+ commandDigest, ok?, exitCode?, timedOut?, durationMs? }], externalEnvNames?
298
+ }`. Emitted by the computer-use backend; absent on pre-existing and other
299
+ backends' bundles. `repo`/`commit` are clone-route fields; `archiveSha256`
300
+ (64-hex sha256, the local-tree provenance pin) and `dirty` (host git
301
+ porcelain status at pack time) are local-tree-route fields, additive under
302
+ `humanish.run-bundle.v1`: a dirty working tree cannot be commit-pinned, so
303
+ the archive content digest stands in for it. `commandDigest` is the
304
+ sha256-16 of the exact seed command — command text and env values never
305
+ appear. `humanish verify` fails closed when a LIVE `local-tree` bundle carries
306
+ no well-formed `archiveSha256`, in addition to the existing `subject state
307
+ provenance` check.
308
+ - `desktopTemplate` (optional, additive): the custom E2B desktop TEMPLATE (image)
309
+ the run's sandbox(es) launched on, from `execution.desktop.template` — so the
310
+ evidence shows WHICH image ran. Present only when a template was configured;
311
+ absent == the stock `desktop` template, so every pre-existing bundle is
312
+ byte-stable. Public-safe (a template name is not a secret).
313
+ - `attributionClass` (optional, additive): `isolated | shared-world`. Absent ==
314
+ `isolated`, so every existing bundle is byte-stable. The interaction-attribution
315
+ honesty axis (#164) — ORTHOGONAL to the persona-sampling evidence classes. Set
316
+ to `shared-world` by the shared-world backend, paired with `sharedWorld`.
317
+ - `sharedWorld` (optional, additive): the shared-world evidence block
318
+ (`humanish.shared-world.v1`) — see [Shared-World Evidence](#shared-world-evidence)
319
+ below. Present only on shared-world runs; verified fail-closed by the
320
+ `shared-world evidence` check in `humanish verify`.
321
+
322
+ Adapter-owned fields:
323
+
324
+ - `source.packageName`
325
+ - `source.humanishSource`
326
+ - `persona`
327
+ - `scenario`
328
+ - target-specific stream labels and public-safe summaries
329
+
330
+ Synthetic fixture:
331
+
332
+ ```yaml
333
+ schema: humanish.run-bundle.v1
334
+ runId: synthetic-run-bundle-2026-06-02t10-00-00-000z-proof
335
+ mode: dry-run
336
+ simCount: 1
337
+ createdAt: "2026-06-02T10:00:00.000Z"
338
+ artifactRoot: .humanish/runs/synthetic-run-bundle-2026-06-02t10-00-00-000z-proof
339
+ source:
340
+ packageName: fixture-app
341
+ humanishSource: present
342
+ git:
343
+ schema: humanish.git-state.v1
344
+ status: clean
345
+ capturedAt: "2026-06-02T10:00:00.000Z"
346
+ head:
347
+ shortSha: null
348
+ refState: unknown
349
+ changes:
350
+ staged: 0
351
+ unstaged: 0
352
+ untracked: 0
353
+ total: 0
354
+ note: public-safe synthetic fixture
355
+ persona:
356
+ id: synthetic-maintainer
357
+ name: Synthetic Maintainer
358
+ source: humanish/personas/synthetic-maintainer.yaml
359
+ sourceDigest: synthetic
360
+ scenario:
361
+ id: first-run-smoke
362
+ title: First-run smoke
363
+ goal: Prove setup and verification without private data.
364
+ source: humanish/scenarios/first-run-smoke.yaml
365
+ sourceDigest: synthetic
366
+ lifecycle:
367
+ - at: "2026-06-02T10:00:00.000Z"
368
+ event: run.created
369
+ message: Created synthetic contract fixture.
370
+ redaction:
371
+ status: passed
372
+ notes: Synthetic fixture only.
373
+ artifacts:
374
+ run: run.json
375
+ reviewJson: review.json
376
+ reviewMarkdown: review.md
377
+ observerData: observer/observer-data.json
378
+ events: events.ndjson
379
+ review:
380
+ schema: humanish.review.v1
381
+ verdict: contract_proof_only
382
+ summary: Synthetic contract fixture generated.
383
+ gaps: []
384
+ feedbackCandidates: []
385
+ ```
386
+
387
+ ## Shared-World Evidence
388
+
389
+ The shared-world topology (#164) is the DECLARED override of the per-lane-worlds
390
+ default: N distinct actor ROLES drive ONE provisioned, mutable service plane (one
391
+ app + one seeded DB) so their actions interact through shared state. The ONE
392
+ subject plane is provisioned via `subject.source: clone` (a fresh `git clone`) or
393
+ `subject.source: local-tree` (the operator's own working tree, packed on the host
394
+ and provisioned in-sandbox in place of a clone - see `subject.localTree` above);
395
+ both sources are accepted on the sequential AND concurrent shared-world routes. A
396
+ shared-world bundle adds TWO additive, optional fields to `humanish.run-bundle.v1`
397
+ (absent on every other bundle, so they stay byte-stable):
398
+
399
+ - `attributionClass: isolated | shared-world` — a new, ORTHOGONAL honesty axis
400
+ ("how well did the run attribute INTERACTION?"), distinct from the persona-sampling
401
+ evidence classes ("how representative is the actor?"). Absent == `isolated`.
402
+ - `sharedWorld` (`humanish.shared-world.v1`): TWO variants discriminated by
403
+ `topologyMode: "sequential" | "concurrent"` (validateSharedWorldEvidence branches on
404
+ it FIRST; unknown/missing or a mismatched shape fails closed). Common fields:
405
+ - `topology: shared-world`
406
+ - `topologyMode: sequential | concurrent`
407
+ - `roleCount` — the DECLARED number of role/persona seats.
408
+ - `plane: { commit?, seedDigest, envNames, hostDigest?, exposure? }` — the ONE
409
+ shared-plane provenance. `seedDigest` is the sha256-16 of the ordered seed-step
410
+ command digests (the seed RECIPE identity, not the runtime state); `envNames` are
411
+ NAMES only. `hostDigest`/`exposure` are CONCURRENT-only (below).
412
+ - `attributionLimits: [...]` — the verify-enforced attribution ceiling (the set
413
+ differs per `topologyMode`, below).
414
+
415
+ SEQUENTIAL shape (`topologyMode: sequential`, #164 PR1):
416
+ - `sequence: [roleId, …]` — the role ids that actually took a turn, in declared order.
417
+ - `timeline: (checkpoint | turn)[]` — a harness-clocked, strictly alternating
418
+ timeline that starts `cp-baseline`, alternates checkpoint → turn → checkpoint,
419
+ and ends on a checkpoint:
420
+ - checkpoint = `{ kind: checkpoint, name, digest, deltaFromPrev }` — `digest` is
421
+ sha256-16(scrub+redact(probe stdout)); the record is DIGEST-ONLY (no
422
+ value-shaped field). `deltaFromPrev` is true when the observed state changed
423
+ across the intervening turn.
424
+ - turn = `{ kind: turn, roleId, simId, streamId, commit?, seedDigest }` — references
425
+ a real RunSimulation/RunStream; carries the plane provenance it observed
426
+ (identical across turns by construction — the single-plane proof).
427
+ - Sequential `attributionLimits` MUST contain `sequential-only`, `no-concurrent-races`,
428
+ and `delta-attributed-to-turn-not-action`.
429
+
430
+ CONCURRENT shape (`topologyMode: concurrent`, #164 phase 2 — N personas drive ONE
431
+ getHost-exposed plane AT ONCE; NO `timeline`/`sequence`):
432
+ - `plane.hostDigest` — sha256-16 of the harness-minted `getHost` ORIGIN every actor
433
+ drove (a first-class provisioned-subject target — invariant 2). A DIGEST, not the raw
434
+ URL: a getHost URL embeds the live sandbox id and matches the publish-safety e2b-URL
435
+ redaction, so it never lands raw in a published bundle (the raw tokenless URL is
436
+ surfaced only on the ephemeral lab result). The orchestrator confirms the URL is
437
+ TOKENLESS (no authKey — invariant 1) before digesting.
438
+ - `plane.exposure: synthetic` — the REQUIRED author attestation that the subject behind
439
+ the internet-reachable getHost URL is synthetic seeded data (author-trust + a
440
+ provenance gate, NOT a no-real-data guarantee).
441
+ - `laneWindows: [{ roleId, simId, streamId, startedAt, endedAt, verdict, routeHostDigest,
442
+ commit?, seedDigest }]` — one harness-clocked window per actor; OVERLAPPING windows
443
+ prove ≥2 personas were active simultaneously. `routeHostDigest` == `plane.hostDigest`
444
+ (every actor drove exactly the harness-minted host).
445
+ - `stateSeries: [{ timestamp, digest }]` — cadence digests of the shared world under
446
+ load (baseline + periodic + final). DIGEST-ONLY: the allowed-keys tripwire permits
447
+ ONLY `timestamp` + `digest` (no per-delta→actor field — causation under concurrency is
448
+ structurally inexpressible).
449
+ - `outcomes: [{ roleId, simId, streamId, status, completionReason?, ok }]` — per-persona
450
+ OUTCOME (the "M of N succeeded" headline).
451
+ - Concurrent `attributionLimits` MUST contain `concurrent`,
452
+ `best-effort-causal-attribution`, `non-deterministic-shared-state`,
453
+ `window-and-snapshot-granularity`, `contention-observed-not-proven-safe`,
454
+ `state-change-not-isolated-to-actors`, and MUST NOT contain `sequential-only` or
455
+ `no-concurrent-races` (a sequential guarantee on a concurrent run is an overclaim).
456
+
457
+ The `shared-world evidence` check in `humanish verify` is fail-closed (live runs only;
458
+ dry-run contract bundles are skipped). It dispatches on `topologyMode` FIRST.
459
+ SEQUENTIAL: the timeline must be well-formed (start `cp-baseline`, strictly alternate,
460
+ end on a checkpoint, turn order == sequence, sequence length == roleCount == turn
461
+ count, no `laneWindows`); every turn's simId/streamId resolves; every checkpoint digest
462
+ is sha256-16 with NO value-shaped field; all turns share ONE plane provenance; the
463
+ mandatory limits are present; and a PASSED run shows ≥1 checkpoint `deltaFromPrev` (the
464
+ delta-on-pass gate). CONCURRENT: no `timeline`; laneWindows + stateSeries + outcomes
465
+ cover exactly roleCount; the required limits are present AND the forbidden ones absent;
466
+ `plane.hostDigest` present and every `routeHostDigest` equals it (invariant 2);
467
+ `plane.exposure == synthetic` AND `subject.state.provenance == seeded` (the
468
+ synthetic-subject gate); stateSeries snapshots are digest-only (allowed-keys tripwire);
469
+ all laneWindows share ONE plane provenance; and the CONCURRENCY-ON-PASS gate — a PASSED
470
+ run MUST show ≥2 overlapping laneWindows AND a stateSeries delta whose timestamp is
471
+ AT/AFTER an overlap interval start (otherwise it was not actually concurrent, or the
472
+ world never changed under load). The per-role no-engagement guard applies to both.
473
+ Checkpoints / stateSeries persist digest-only by DEFAULT until the #108 PII/PHI
474
+ detector lands.
475
+
476
+ WHAT THE BUNDLE CAN / CANNOT CLAIM. SEQUENTIAL: each role's own behavior at full
477
+ fidelity; the OBSERVED system outcome as an ordered DIGEST sequence; and the
478
+ SEQUENCED-INTERACTION proof (role B entered a world already containing role A's mutation
479
+ — the checkpoint after A strictly precedes B's turn). It CANNOT claim action-granular
480
+ causation, concurrency/races (sequential-only), or exact-state determinism. CONCURRENT:
481
+ each persona's own behavior at full fidelity; per-persona OUTCOME against the contended
482
+ world ("M of N"); PROVEN CONCURRENCY (overlapping windows); and system-state evolution
483
+ under load (the stateSeries) with best-effort temporal correlation. It CANNOT claim
484
+ strict causal attribution of a delta to an actor (concurrent ⇒ ambiguous), determinism
485
+ of exact state, per-action granularity, or concurrency-SAFETY (races are OBSERVED, never
486
+ PROVEN absent). HONESTY: the deterministic $0 gate proves the plumbing + the attribution
487
+ contract; the concurrency CAPABILITY at scale is backed only by a separately-authorized
488
+ live receipt.
489
+
490
+ ## Adapter
491
+
492
+ Adapters describe target-specific affordances without changing core contracts.
493
+
494
+ Core-owned fields:
495
+
496
+ - `schema`
497
+ - `id`
498
+ - normalized route/reference shape
499
+ - public-safety validation of adapter references
500
+
501
+ Adapter-owned fields:
502
+
503
+ - `name`
504
+ - `routes`
505
+ - route descriptions
506
+ - target-specific commands, paths, milestones, and vocabulary
507
+
508
+ Synthetic fixture:
509
+
510
+ ```yaml
511
+ schema: humanish.adapter.v1
512
+ id: synthetic-cli-adapter
513
+ name: Synthetic CLI Adapter
514
+ routes:
515
+ - id: help
516
+ path: synthetic-cli --help
517
+ description: Public-safe command discovery.
518
+ - id: dry-run
519
+ path: synthetic-cli run --dry-run --json
520
+ description: Generate a synthetic run bundle.
521
+ ```
522
+
523
+ ## Persona And Scenario
524
+
525
+ Personas and scenarios define trial intent. They are adapter-owned source
526
+ documents that core copies into run bundles by digest and id.
527
+
528
+ Core-owned fields:
529
+
530
+ - schema naming rules
531
+ - id/source/sourceDigest references inside run bundles
532
+ - redaction gates before persona/scenario text can appear in public feedback
533
+
534
+ Adapter-owned fields:
535
+
536
+ - persona traits
537
+ - scenario goals
538
+ - steps and expectations
539
+ - accessibility or workflow constraints
540
+
541
+ Synthetic fixture:
542
+
543
+ ```yaml
544
+ persona:
545
+ schema: humanish.persona.v1
546
+ id: synthetic-maintainer
547
+ name: Synthetic Maintainer
548
+ summary: Privacy-safe maintainer evaluating first-run clarity.
549
+ constraints:
550
+ - Do not use real personal data.
551
+ - Treat credentials as env var names only.
552
+ scenario:
553
+ schema: humanish.scenario.v1
554
+ id: first-run-smoke
555
+ title: First-run smoke
556
+ persona: synthetic-maintainer
557
+ goal: Prove setup, dry-run evidence, verification, and feedback drafting.
558
+ mode: dry-run
559
+ steps:
560
+ - name: Inspect help
561
+ expectation: Help explains setup and verification commands.
562
+ - name: Verify bundle
563
+ expectation: Verification passes without private data.
564
+ ```
565
+
566
+ ## Actor Trace
567
+
568
+ Actors execute or simulate the trial. Actor evidence is the provider-neutral
569
+ `humanish.actor-trace.v1` (`src/actor-contract.ts`): Codex app-server items,
570
+ Claude Agent SDK blocks, pi events, computer-use cycles, scripted browser
571
+ steps, and in-sandbox terminal-agent exec output all map onto one `ActorTrace`.
572
+ Registered actors live in
573
+ `src/actor-registry.ts` (`codex-app-server`, `pi-agent-core`,
574
+ `claude-agent-sdk`, `openai-computer-use`, `scripted-browser`, `codex-exec`).
575
+ There is no `humanish.actor.v1`; that name never shipped.
576
+
577
+ Core-owned fields:
578
+
579
+ - `schema`
580
+ - `provider` / `providerVersion`
581
+ - `protocol` (`json-rpc` | `json-stream` | `in-process-sdk` | `cua-loop` |
582
+ `scripted-steps` | `terminal-exec`)
583
+ - `lane` (`code` | `app` | `computer-use` | `scripted-browser` | `terminal`)
584
+ - `persona` (`id`, `traitsApplied`, `promptDigest`)
585
+ - `capabilities.keyPlacement` (`external` | `in-sandbox-command-scoped`): WHERE
586
+ the actor's runtime key lives — registry metadata the engine enforces. The
587
+ terminal agent declares `in-sandbox-command-scoped` (the agent-under-test runs
588
+ inside the sandbox); every other actor is `external` (absent === external).
589
+ - `redaction` (`status`, `screenshots: n/a|raw|blurred|ocr_scrubbed`, `notes`)
590
+ - `startedAt` / `completedAt` / `durationMs`
591
+ - `status` / `completionReason` / `reason` (`completionReason` includes
592
+ `step_failed`: a deterministic scripted step/expectation evaluated false —
593
+ the subject failed the script while the harness executed faithfully)
594
+ - `ids`, `counts`, `items[]`, optional `tokenUsage`, `capabilities`
595
+
596
+ Unexpected actor-loop diagnostics live inside `items[]` as
597
+ `kind: notice`, `status: error` rows. They are public-safe evidence, not crash
598
+ dumps: redacted message, coarse phase, optional error name, last normalized UI
599
+ action, and last screenshot reference. They must not carry raw stacks, env
600
+ values, target URLs, or unredacted provider payloads.
601
+
602
+ Adapter-owned fields:
603
+
604
+ - the prompt, mission, persona text, and lane focus that produced the trace
605
+ - product-specific acceptance notes
606
+
607
+ Synthetic fixture (abridged; see `src/actor-contract.ts` for the full type):
608
+
609
+ ```yaml
610
+ schema: humanish.actor-trace.v1
611
+ provider: codex-app-server
612
+ protocol: json-rpc
613
+ lane: code
614
+ persona:
615
+ id: synthetic-maintainer
616
+ traitsApplied: []
617
+ promptDigest: synthetic
618
+ redaction:
619
+ status: passed
620
+ screenshots: n/a
621
+ notes: Synthetic fixture only.
622
+ startedAt: "2026-06-02T10:00:00.000Z"
623
+ completedAt: "2026-06-02T10:00:01.000Z"
624
+ durationMs: 1000
625
+ status: passed
626
+ completionReason: turn_completed
627
+ reason: Synthetic dry-run fixture completed.
628
+ ids: {}
629
+ counts: {}
630
+ items: []
631
+ ```
632
+
633
+ ## Substrate
634
+
635
+ Reserved: `humanish.substrate.v1` is named here for layering intent but has
636
+ never shipped — no code emits or validates it. Substrate truth today lives
637
+ inside run bundles (per-stream transport and status) and lab execution config
638
+ (`execution.target: local | e2b-desktop`). Do not emit this schema.
639
+
640
+ ## Terminal Cost Ledger And No-Spend Proof
641
+
642
+ The terminal-product lane (`src/e2b-terminal-lab.ts`) places a real provider key
643
+ INSIDE the sandbox, so the no-spend claim must be REAL — derived from a ledger,
644
+ never asserted. The live run writes both to `terminal-ledgers.json` (a `cost`
645
+ block + a `noSpendProof` block, additive to `humanish.terminal-ledgers.v1`).
646
+
647
+ The cost ledger (`humanish.terminal-cost-ledger.v1`) has one line per category —
648
+ `product`, `media`, `payment`, `provider` — and follows a strict **null
649
+ discipline** that distinguishes three states and never conflates them:
650
+
651
+ - `usd: 0` — **known zero**: the category was metered and billed nothing.
652
+ - `usd: null` — **not measured**: no spend signal exists for the category this
653
+ slice. `null` is written explicitly (never `undefined`-omitted, never guessed
654
+ to `0`). A line with `null` says "this category exists but we did not measure
655
+ it"; the no-spend proof reports it as unmeasured and does NOT claim it is zero.
656
+ - line **absent** — **not applicable** (n/a) to the lane/run.
657
+
658
+ `knownTotalUsd` sums ONLY the non-null lines (a `null` line contributes nothing
659
+ and is never coerced to `0`); `fullyMeasured` is true only when no line is null.
660
+ This slice meters only the `provider` line, populated from the actor trace's
661
+ `tokenUsage.costUsd` when present (else `null`); `product`/`media`/`payment` are
662
+ `null` until the SLICE-4 adapter supplies them.
663
+
664
+ ```yaml
665
+ schema: humanish.terminal-cost-ledger.v1
666
+ currency: usd
667
+ lines:
668
+ product: { usd: null, count: null, source: unmeasured, note: "…no signal yet…" }
669
+ media: { usd: null, count: null, source: unmeasured, note: "…no signal yet…" }
670
+ payment: { usd: null, count: null, source: unmeasured, note: "…no signal yet…" }
671
+ provider: { usd: null, source: unmeasured, note: "…no tokenUsage.costUsd this run…" }
672
+ knownTotalUsd: 0
673
+ fullyMeasured: false
674
+ ```
675
+
676
+ The no-spend proof (`humanish.terminal-no-spend-proof.v1`) is DERIVED from the
677
+ ledger. It vouches only for what it measured: `knownZeroLines` (proven zero),
678
+ `knownNonZeroLines` (break `satisfied`), and `unmeasuredLines` (the `null` lines
679
+ it explicitly CANNOT vouch for). `satisfied` is true only when every KNOWN line
680
+ is within `maxUsd` (for a no-spend run, `maxUsd: 0` ⇒ every known line is `0`);
681
+ unmeasured lines never make it satisfied. A proof never claims zero on a `null`
682
+ line — verification fails closed if it does.
683
+
684
+ ```yaml
685
+ schema: humanish.terminal-no-spend-proof.v1
686
+ maxUsd: 0
687
+ satisfied: true
688
+ knownZeroLines: []
689
+ knownNonZeroLines: []
690
+ unmeasuredLines: [product, media, payment, provider]
691
+ knownTotalUsd: 0
692
+ statement: "No-spend proof SATISFIED for maxUsd=0: every MEASURED spend line is zero…"
693
+ ```
694
+
695
+ **Full caps enforcement (fail-closed, not advisory).** `scenario.caps.maxUsd`
696
+ is enforced against the ledger: if the observed KNOWN spend exceeds `maxUsd`, the
697
+ run fails closed (`HUMANISH_TERMINAL_LAB_CAPS_EXCEEDED`); `maxJobs` likewise when
698
+ a known job count is present; `maxMinutes` is the wall-clock kill (unchanged).
699
+ Unknowns (`null`) never trip a cap (we cannot claim a violation we did not
700
+ measure) and never grant a green pass (they surface as unmeasured). `verifyRun`
701
+ fails closed when a live bundle lacks the cost ledger or no-spend proof, when the
702
+ proof claims zero on a `null` line, or when known spend exceeds the declared cap.
703
+
704
+ ## Product-Adapter Extension Seam
705
+
706
+ The terminal-product and browser/computer-use lanes let an adopter attach
707
+ product-specific scoring + feedback as a THIN in-repo extension WITHOUT forking
708
+ core. The seam is the EXPORTED contract types plus DI hooks:
709
+ `TerminalProductLabHooks` for terminal-product runs, and the browser adapter
710
+ hooks inherited by `CuaActorLabHooks` / `SharedWorldLabHooks` for CUA,
711
+ sequential shared-world, and concurrent shared-world runs. This is never a
712
+ built-in product scorer (the adopter's scorecard lives in the adopter's repo).
713
+
714
+ Three product-agnostic carriers keep core's nouns closed while letting the adapter
715
+ record its own:
716
+
717
+ - **Adapter score** (`humanish.adapter-score.v1`, `RunBundle.adapterScore`).
718
+ A namespaced summary the adapter's `score` hook returns: `{ schema, namespace,
719
+ status, score, summary, data? }`. Core never reads `data` — the adopter's
720
+ component rubric rides there; `namespace` (an adopter slug) scopes the whole
721
+ record so a future inert-field audit never misfires.
722
+ - **Namespaced product-noun block** (`RunFeedbackCandidate.adapter`). The
723
+ adapter's `deriveFeedback` hook returns feedback candidates that satisfy core's
724
+ feedback-candidate shape; product-specific concepts (public CLI/product command
725
+ observed, hosted product success-or-blocker, feedback id/draft, media/job/asset
726
+ ids, explicit no-media/no-provider-spend proof, defection/friction risk) are
727
+ recorded ONLY under `adapter: { namespace, data }` — never as core enums. Core
728
+ validates the SHAPE (a non-empty `namespace` + a `data` record); the keys inside
729
+ `data` are the adapter's.
730
+ - **Adapter artifacts** (`humanish.adapter-artifact.v1`,
731
+ `RunBundle.adapterArtifacts[]`). A namespaced list of local relative artifact
732
+ references the adapter's `deriveArtifacts` hook returns after writing
733
+ product/state proof files under the ignored run directory. Core validates only
734
+ schema/namespace/label/path/kind/note and local-path safety, Observer links the
735
+ artifacts, and `verifyRun` fails closed if a referenced file is missing.
736
+
737
+ ```yaml
738
+ # RunBundle.adapterScore (namespaced; data is the adopter's, core never reads it)
739
+ schema: humanish.adapter-score.v1
740
+ namespace: adopter-slug
741
+ status: pass
742
+ score: 88
743
+ summary: Product study scored by the adopter's own rubric.
744
+ data: { productRubric: { discovery: 1, firstImage: 1 }, hostedProductSucceeded: true }
745
+ ```
746
+
747
+ ```yaml
748
+ # RunFeedbackCandidate.adapter — product nouns stay NON-core under the namespace
749
+ adapter:
750
+ namespace: adopter-slug
751
+ data:
752
+ publicCommandObserved: "product generate --prompt '…'"
753
+ hostedProductOutcome: success
754
+ feedbackId: null
755
+ mediaJobIds: []
756
+ noMediaSpendProof: { mediaUsd: null, providerUsd: 0 }
757
+ defectionFrictionRisk: low
758
+ ```
759
+
760
+ ```yaml
761
+ # RunBundle.adapterArtifacts — product/state proof payloads stay adapter-owned
762
+ - schema: humanish.adapter-artifact.v1
763
+ namespace: adopter-slug
764
+ label: Product state readback
765
+ path: adapter/product-state-readback.json
766
+ kind: state
767
+ note: Adapter-owned product/state proof.
768
+ ```
769
+
770
+ Acceptance semantics are route-specific:
771
+
772
+ - Terminal-product runs keep the mission-based `review` verdict unchanged; the
773
+ adapter score is additive because the route is a public-product study lane.
774
+ - Browser/computer-use runs treat `adapterScore.status: fail` as product-red:
775
+ the bundle keeps the adapter score, `review.verdict` becomes `fail` when it
776
+ was pass-like, a generic adapter gap is appended, and the route result returns
777
+ `ok: false`. This closes the false-positive class where a generic actor reaches
778
+ a terminal session but an adopter scorer finds no product-visible completion
779
+ evidence.
780
+
781
+ The `e2b-terminal` substrate is added to `RunFeedbackCandidate.substrate` so a
782
+ terminal-agent candidate names its substrate honestly; browser candidates use
783
+ the existing `e2b-desktop` substrate. Lanes invoke hooks over FULLY-ASSEMBLED,
784
+ redacted evidence (`TerminalProductScoringContext` or
785
+ `BrowserLabScoringContext`: `bundle`, runtime-only `runDir`, run identifiers,
786
+ actor/backend metadata; all exported public types), scrub+redact returned
787
+ payloads, and DROP any malformed score, candidate, or artifact reference with a
788
+ warning so a bad extension never poisons a verifiable bundle. Default behavior
789
+ (no hook) is unchanged. `verifyRun` re-checks the surviving shapes fail-closed,
790
+ including existence for referenced adapter artifacts.
791
+
792
+ ## Evidence Streams
793
+
794
+ Reserved: `humanish.evidence-stream.v1` has never shipped as a standalone
795
+ schema, and streams are not standalone artifacts. They are the `streams` array
796
+ inside `humanish.run-bundle.v1`, normalizing UI, browser, terminal, TUI,
797
+ code-agent UI, artifact, and summary lanes — each with transport, terminal
798
+ tail, completion, meaningful-use verdicts, and artifact pointers. See
799
+ [`run-bundle.md`](run-bundle.md#completion-and-meaningful-use-verdicts) for
800
+ the stream shape, the meaningful-use rubric, and hard-failure rules.
801
+
802
+ ## Review
803
+
804
+ Review summarizes whether evidence supports the claim. It does not replace
805
+ verification or maintainer acceptance.
806
+
807
+ Core-owned fields:
808
+
809
+ - `schema`
810
+ - `verdict`
811
+ - `summary`
812
+ - `gaps`
813
+
814
+ Adapter-owned fields:
815
+
816
+ - vocabulary labels
817
+ - milestone names
818
+ - product-specific gap language
819
+
820
+ Synthetic fixture:
821
+
822
+ ```yaml
823
+ schema: humanish.review.v1
824
+ verdict: contract_proof_only
825
+ summary: Synthetic dry-run proves bundle shape, not product behavior.
826
+ gaps:
827
+ - Live product behavior was not exercised.
828
+ ```
829
+
830
+ ## Verification
831
+
832
+ Verification checks bundles and evidence pointers. It fails closed when schema,
833
+ redaction, or artifacts are missing.
834
+
835
+ Core-owned fields:
836
+
837
+ - `schema`
838
+ - `ok`
839
+ - `run`
840
+ - `bundlePath`
841
+ - check names
842
+ - check booleans
843
+ - `warnings` (advisory postures, e.g. raw screenshots; never flip `ok`)
844
+ - machine-readable error codes
845
+
846
+ Adapter-owned fields:
847
+
848
+ - optional target-specific checks
849
+ - acceptance proof commands
850
+ - coverage-specific check names
851
+
852
+ Synthetic fixture:
853
+
854
+ ```yaml
855
+ schema: humanish.verify-result.v1
856
+ ok: true
857
+ run: synthetic-run-bundle-2026-06-02t10-00-00-000z-proof
858
+ bundlePath: .humanish/runs/synthetic-run-bundle-2026-06-02t10-00-00-000z-proof/run.json
859
+ checks:
860
+ - name: run.json exists
861
+ ok: true
862
+ message: run.json present
863
+ - name: redaction passed
864
+ ok: true
865
+ message: redaction status must be passed
866
+ shareSafety:
867
+ status: share_ready
868
+ reasons: []
869
+ ```
870
+
871
+ ## Policy
872
+
873
+ Policy names boundaries before an actor runs or feedback is promoted.
874
+ `humanish.policy.v1` exists today only as an adapter fixture shape
875
+ (`adapters/fixtures/`); the engine does not validate it. The committed policy
876
+ source files scaffolded by `humanish init` use `humanish.redaction-policy.v1`,
877
+ `humanish.network-policy.v1`, and `humanish.credentials-policy.v1`.
878
+
879
+ Core-owned fields:
880
+
881
+ - `schema`
882
+ - policy kind
883
+ - default action
884
+ - validation outcome
885
+ - redaction status
886
+ - no-secret-value persistence rules
887
+
888
+ Adapter-owned fields:
889
+
890
+ - allowed env var names
891
+ - allowed public hosts
892
+ - app-specific credential manifest
893
+ - network allowlist
894
+ - scenario-specific authority
895
+
896
+ Synthetic fixture:
897
+
898
+ ```yaml
899
+ schema: humanish.policy.v1
900
+ kind: public-safety
901
+ default: deny_sensitive_material
902
+ deny:
903
+ - pii
904
+ - phi
905
+ - secrets
906
+ - tokens
907
+ - raw_private_transcripts
908
+ - private_screenshots
909
+ allow:
910
+ - synthetic_personas
911
+ - synthetic_fixtures
912
+ - env_var_names
913
+ credentialManifest:
914
+ - envName: OPENAI_API_KEY
915
+ valuePersisted: false
916
+ network:
917
+ default: local_only
918
+ allowedHosts:
919
+ - localhost
920
+ ```
921
+
922
+ ## Feedback
923
+
924
+ Feedback turns verified evidence into public-safe issue draft material. The
925
+ default public CLI prints issue text or a prefilled URL; it does not mutate
926
+ GitHub.
927
+
928
+ Core-owned fields:
929
+
930
+ - `schema`
931
+ - run/source/evidence pointers
932
+ - redaction status
933
+ - idempotency key
934
+ - proposed next state
935
+ - failure owner enum
936
+ - public issue eligibility gates
937
+
938
+ Adapter-owned fields:
939
+
940
+ - adapter id
941
+ - scenario id
942
+ - persona id
943
+ - expected/actual language
944
+ - target-specific reproduction steps
945
+ - acceptance proof commands
946
+
947
+ Synthetic fixture:
948
+
949
+ ```yaml
950
+ schema: humanish.feedback.v1
951
+ run_id: synthetic-run-bundle-2026-06-02t10-00-00-000z-proof
952
+ adapter_id: synthetic-cli-adapter
953
+ scenario_id: first-run-smoke
954
+ persona_id: synthetic-maintainer
955
+ actor: synthetic-dry-run
956
+ substrate: local-filesystem
957
+ failure_owner: harness
958
+ summary: Synthetic user needed clearer verification instructions.
959
+ expected: Verification command is visible and public-safe.
960
+ actual: Dry-run review noted missing live behavior proof.
961
+ source_bundle: .humanish/runs/synthetic-run-bundle-2026-06-02t10-00-00-000z-proof/run.json
962
+ evidence:
963
+ - path: .humanish/runs/synthetic-run-bundle-2026-06-02t10-00-00-000z-proof/review.md
964
+ kind: review
965
+ note: Public-safe synthetic review.
966
+ redaction:
967
+ status: passed
968
+ notes: Synthetic fixture only.
969
+ idempotency_key: synthetic-cli-adapter:first-run-smoke:verification-instructions
970
+ proposed_next_state: watch
971
+ acceptance_proof:
972
+ - pnpm humanish -- verify --run latest --json
973
+ ```
974
+
975
+ ## Contract Stop Conditions
976
+
977
+ Do not promote a contract fixture when:
978
+
979
+ - it needs private artifact data to make sense;
980
+ - it contains credential values instead of env var names;
981
+ - it embeds raw hosted stream URLs or auth-bearing links;
982
+ - it uses product-specific private nouns in a core-owned schema;
983
+ - it implies GitHub mutation without explicit maintainer authority;
984
+ - it cannot be proven with `git diff --check` and public-surface scanning.