humanish 0.114.0 → 0.116.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 (204) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/CONTEXT.md +1 -1
  3. package/README.md +7 -6
  4. package/TELEMETRY.md +6 -0
  5. package/dist/actors/closing-report-limits.d.ts +11 -0
  6. package/dist/actors/closing-report-limits.js +13 -0
  7. package/dist/actors/closing-report.d.ts +68 -0
  8. package/dist/actors/closing-report.js +55 -0
  9. package/dist/actors/codex/app-server-ui.js +11 -4
  10. package/dist/actors/codex/restricted-participant-policy.d.ts +4 -47
  11. package/dist/actors/codex/restricted-participant-policy.js +12 -40
  12. package/dist/actors/computer-use/loop/debrief.d.ts +1 -10
  13. package/dist/actors/computer-use/loop/debrief.js +68 -70
  14. package/dist/actors/computer-use/loop/impressions.d.ts +2 -2
  15. package/dist/actors/computer-use/loop/impressions.js +6 -1
  16. package/dist/actors/computer-use/loop.d.ts +0 -1
  17. package/dist/actors/computer-use/loop.js +3 -3
  18. package/dist/actors/computer-use/openai-conversation.d.ts +1 -1
  19. package/dist/actors/computer-use/openai-conversation.js +3 -29
  20. package/dist/actors/computer-use/openai-provider.js +11 -5
  21. package/dist/actors/contract.d.ts +2 -20
  22. package/dist/actors/contract.js +0 -13
  23. package/dist/actors/scripted-browser/steps.js +6 -5
  24. package/dist/analysis/admission.d.ts +14 -4
  25. package/dist/analysis/admission.js +14 -9
  26. package/dist/analysis/analysis-limits.d.ts +35 -0
  27. package/dist/analysis/analysis-limits.js +41 -0
  28. package/dist/analysis/execute.d.ts +5 -0
  29. package/dist/analysis/execute.js +3 -0
  30. package/dist/analysis/finding-lead.d.ts +27 -0
  31. package/dist/analysis/finding-lead.js +23 -0
  32. package/dist/analysis/service.js +5 -3
  33. package/dist/analysis/validation.js +14 -21
  34. package/dist/cli/commands/analyze.js +2 -1
  35. package/dist/cli/commands/notes.js +5 -3
  36. package/dist/cli/commands/run-signals.js +5 -1
  37. package/dist/cli/commands/runs.js +2 -1
  38. package/dist/cli/commands/setup.js +3 -3
  39. package/dist/cli/commands/study-format.d.ts +5 -2
  40. package/dist/cli/commands/study-format.js +29 -9
  41. package/dist/cli/commands/study-route-computer-use.js +2 -2
  42. package/dist/cli/commands/study-route-scripted.js +1 -1
  43. package/dist/cli/commands/tui.js +59 -17
  44. package/dist/cli/doctor.js +9 -2
  45. package/dist/cli/findings-text.d.ts +5 -0
  46. package/dist/cli/findings-text.js +169 -0
  47. package/dist/cli/findings.d.ts +0 -4
  48. package/dist/cli/findings.js +11 -195
  49. package/dist/cli/io.js +10 -8
  50. package/dist/cli/telemetry.js +10 -22
  51. package/dist/cli/update-check.js +10 -19
  52. package/dist/cli/user-config.d.ts +11 -0
  53. package/dist/cli/user-config.js +25 -0
  54. package/dist/comms/connections.js +4 -3
  55. package/dist/comms/email-catch.js +7 -31
  56. package/dist/comms/setup.js +18 -12
  57. package/dist/evidence/encoded-text.d.ts +12 -11
  58. package/dist/evidence/encoded-text.js +81 -70
  59. package/dist/evidence/redaction.d.ts +11 -0
  60. package/dist/evidence/redaction.js +30 -11
  61. package/dist/evidence/secret-scrub.d.ts +26 -3
  62. package/dist/evidence/secret-scrub.js +172 -38
  63. package/dist/feedback/draft.d.ts +1 -1
  64. package/dist/feedback/draft.js +13 -16
  65. package/dist/feedback/export-bundle.js +21 -17
  66. package/dist/feedback/export.js +32 -46
  67. package/dist/feedback/feedback.js +31 -26
  68. package/dist/guest/desktop-keys.js +59 -5
  69. package/dist/keys/key-resolution.d.ts +9 -0
  70. package/dist/keys/key-resolution.js +62 -55
  71. package/dist/keys/key-status.d.ts +2 -0
  72. package/dist/keys/key-status.js +9 -16
  73. package/dist/observer/artifact.d.ts +1 -1
  74. package/dist/observer/http.d.ts +7 -0
  75. package/dist/observer/http.js +19 -1
  76. package/dist/observer/notes-route.d.ts +0 -1
  77. package/dist/observer/notes-route.js +24 -44
  78. package/dist/observer/pinned-files.js +4 -19
  79. package/dist/observer/render.js +6 -4
  80. package/dist/observer/run-routes.js +13 -28
  81. package/dist/observer/serve.js +2 -1
  82. package/dist/observer-app.html +7 -7
  83. package/dist/routes/computer-use/bundle-parts.d.ts +4 -4
  84. package/dist/routes/computer-use/bundle-parts.js +2 -2
  85. package/dist/routes/computer-use/bundle.d.ts +4 -5
  86. package/dist/routes/computer-use/bundle.js +1 -3
  87. package/dist/routes/computer-use/fanout-records.js +10 -11
  88. package/dist/routes/computer-use/live-phase.d.ts +1 -1
  89. package/dist/routes/computer-use/live-phase.js +0 -1
  90. package/dist/routes/computer-use/participant-execution.d.ts +3 -2
  91. package/dist/routes/computer-use/participant-runs.d.ts +6 -2
  92. package/dist/routes/computer-use/participant-runs.js +9 -5
  93. package/dist/routes/computer-use/plan.d.ts +3 -0
  94. package/dist/routes/computer-use/plan.js +14 -11
  95. package/dist/routes/computer-use/rerun-selection.js +2 -2
  96. package/dist/routes/computer-use/result.js +3 -6
  97. package/dist/routes/computer-use/setup.d.ts +4 -6
  98. package/dist/routes/computer-use/setup.js +2 -6
  99. package/dist/routes/computer-use/single-bundle.d.ts +4 -5
  100. package/dist/routes/computer-use/single-bundle.js +22 -25
  101. package/dist/routes/computer-use/subject-projection.d.ts +4 -8
  102. package/dist/routes/computer-use/subject-projection.js +9 -31
  103. package/dist/routes/computer-use/types.d.ts +4 -41
  104. package/dist/routes/scripted/scenario.js +28 -12
  105. package/dist/routes/scripted/session-result.js +6 -3
  106. package/dist/routes/shared-world/bundle-records.js +2 -8
  107. package/dist/routes/shared-world/participant-specs.d.ts +0 -2
  108. package/dist/routes/shared-world/participant-specs.js +1 -20
  109. package/dist/routes/shared-world/plan.d.ts +3 -0
  110. package/dist/routes/shared-world/plan.js +23 -3
  111. package/dist/routes/shared-world/setup.js +2 -2
  112. package/dist/routes/terminal/ledger.d.ts +9 -9
  113. package/dist/routes/terminal/ledger.js +63 -40
  114. package/dist/routes/terminal/live-finish.js +7 -10
  115. package/dist/routes/terminal/plan.d.ts +3 -0
  116. package/dist/routes/terminal/plan.js +8 -5
  117. package/dist/routes/terminal/token-usage.d.ts +1 -2
  118. package/dist/routes/terminal/token-usage.js +4 -6
  119. package/dist/routes/terminal/types.d.ts +9 -1
  120. package/dist/run/bundle-shape.js +6 -1
  121. package/dist/run/bundle.d.ts +5 -3
  122. package/dist/run/contained-output.d.ts +59 -10
  123. package/dist/run/contained-output.js +160 -68
  124. package/dist/run/detail.d.ts +2 -1
  125. package/dist/run/detail.js +12 -7
  126. package/dist/run/escape-sequences.d.ts +11 -0
  127. package/dist/run/escape-sequences.js +93 -0
  128. package/dist/run/evidence-files.d.ts +8 -1
  129. package/dist/run/evidence-files.js +24 -18
  130. package/dist/run/locate.d.ts +32 -3
  131. package/dist/run/locate.js +45 -40
  132. package/dist/run/note-files.d.ts +26 -45
  133. package/dist/run/note-files.js +86 -58
  134. package/dist/run/note-shape.d.ts +35 -0
  135. package/dist/run/note-shape.js +20 -0
  136. package/dist/run/notes.d.ts +3 -14
  137. package/dist/run/notes.js +23 -75
  138. package/dist/run/outcomes.d.ts +24 -0
  139. package/dist/run/outcomes.js +19 -0
  140. package/dist/run/participant-records.d.ts +2 -0
  141. package/dist/run/participant-records.js +17 -2
  142. package/dist/run/paths.d.ts +2 -0
  143. package/dist/run/paths.js +5 -1
  144. package/dist/run/preflight-receipts.js +13 -8
  145. package/dist/run/project.js +7 -4
  146. package/dist/run/reclaim-outcomes.d.ts +8 -6
  147. package/dist/run/reclaim-outcomes.js +10 -6
  148. package/dist/run/reclaim.d.ts +5 -0
  149. package/dist/run/reclaim.js +43 -16
  150. package/dist/run/run-clock.d.ts +50 -0
  151. package/dist/run/run-clock.js +66 -0
  152. package/dist/run/run.js +5 -2
  153. package/dist/run/sandbox-ids.d.ts +7 -2
  154. package/dist/run/sandbox-ids.js +27 -11
  155. package/dist/run/secrets.d.ts +2 -1
  156. package/dist/run/secrets.js +18 -22
  157. package/dist/run/stored-runs.js +14 -7
  158. package/dist/run/transient-comms-secrets.d.ts +6 -5
  159. package/dist/run/transient-comms-secrets.js +23 -39
  160. package/dist/run-study.js +22 -13
  161. package/dist/study/adapter-scorer-loader.js +6 -4
  162. package/dist/study/discover.js +9 -6
  163. package/dist/study/init-paths.js +7 -5
  164. package/dist/study/persona-resolve.d.ts +2 -2
  165. package/dist/study/persona-resolve.js +9 -5
  166. package/dist/study/plan-types.d.ts +2 -2
  167. package/dist/study/plan.d.ts +5 -3
  168. package/dist/study/plan.js +18 -9
  169. package/dist/study/preflight-probes.js +11 -3
  170. package/dist/study/preflight.js +9 -5
  171. package/dist/study/summary.d.ts +12 -7
  172. package/dist/study/summary.js +24 -24
  173. package/dist/study/warnings.d.ts +19 -12
  174. package/dist/study/warnings.js +39 -17
  175. package/dist/substrates/e2b/connection.d.ts +12 -0
  176. package/dist/substrates/e2b/connection.js +35 -0
  177. package/dist/substrates/e2b/desktop-executor.d.ts +1 -3
  178. package/dist/substrates/e2b/desktop-executor.js +42 -9
  179. package/dist/substrates/e2b/lifetime.d.ts +24 -4
  180. package/dist/substrates/e2b/lifetime.js +41 -4
  181. package/dist/substrates/e2b/sandbox.d.ts +4 -3
  182. package/dist/substrates/e2b/sandbox.js +13 -18
  183. package/dist/substrates/e2b/sdk.d.ts +20 -9
  184. package/dist/substrates/local/capacity.d.ts +18 -2
  185. package/dist/substrates/local/capacity.js +21 -2
  186. package/dist/substrates/local/runtime-host.js +21 -14
  187. package/dist/substrates/local/runtime.js +3 -11
  188. package/dist/tui/contract.d.ts +30 -5
  189. package/dist/tui/contract.js +4 -1
  190. package/dist/tui-app.js +149 -147
  191. package/dist/verify/artifacts.d.ts +9 -2
  192. package/dist/verify/artifacts.js +37 -13
  193. package/dist/verify/liveness.js +19 -5
  194. package/dist/verify/verify.js +54 -27
  195. package/docs/architecture/actor-contract.md +3 -3
  196. package/docs/architecture/guest-desktop.md +7 -2
  197. package/docs/architecture/restricted-codex-analysis.md +1 -1
  198. package/docs/architecture/state-driven-executor.md +5 -3
  199. package/docs/contracts/run-bundle.md +25 -13
  200. package/docs/contracts/schemas.md +38 -29
  201. package/docs/contracts/study-analysis.md +22 -5
  202. package/docs/product/automatic-analysis.md +7 -1
  203. package/package.json +8 -7
  204. package/skills/humanish/SKILL.md +1 -2
@@ -169,7 +169,7 @@ The rest of this section names keys by their v3 spelling:
169
169
  - `subject.state` (clone or local-tree subjects, computer-use route): the
170
170
  subject's state story. `state.seed[]` declares ordered, bounded
171
171
  seed/migration/fixture steps (`{ name, command, when: before-build |
172
- before-start | after-ready, timeoutMs }`) executed in-sandbox around the
172
+ before-start | after-ready, timeoutMs }`) executed in-sandbox around the
173
173
  serve sequence; `state.external[]` declares env var names (each must also
174
174
  appear in `subject.env`) pointing at state the study does not control,
175
175
  recorded as unpinned in provenance. Commands persist in evidence as
@@ -189,7 +189,7 @@ before-start | after-ready, timeoutMs }`) executed in-sandbox around the
189
189
  single participant (no E2B to fan out);
190
190
  - a `participants` list (computer-use E2B route): a differentiated fan-out,
191
191
  each entry `{ id?, actorType?, surface?, caseGroup?, persona?, device?,
192
- instruction?, target?, entry?, host?, reasoningEffort?, stopWhen?, dwell? }` becoming one independent E2B desktop (or, on the
192
+ instruction?, target?, entry?, host?, reasoningEffort?, stopWhen?, dwell? }` becoming one independent E2B desktop (or, on the
193
193
  shared-world routes, one participant against the shared plane). `actorType`,
194
194
  `surface`, and `caseGroup` are adapter-owned public-safe labels for grouping
195
195
  simulated users; they are not core enums, and `actorType` is deliberately
@@ -317,9 +317,9 @@ instruction?, target?, entry?, host?, reasoningEffort?, stopWhen?, dwell? }` bec
317
317
  fail-closed cap in force. `maxMinutes` is the
318
318
  wall-clock kill; `maxUsd`/`maxJobs` are enforced fail-closed against the cost
319
319
  ledger after the session (a run whose known spend exceeds the cap fails closed,
320
- `HUMANISH_TERMINAL_CAPS_EXCEEDED`). Core records Codex provider spend as
321
- unpriced tokens and measures no product, media or payment spend, so no line can trip a
322
- positive `maxUsd`: a live run refuses `maxUsd > 0` before creating a sandbox
320
+ `HUMANISH_TERMINAL_CAPS_EXCEEDED`). Core prices the Codex participant's tokens as an
321
+ estimate that the cap does not count, and measures no product, media or payment spend, so no
322
+ line can trip a positive `maxUsd`: a live run refuses `maxUsd > 0` before creating a sandbox
323
323
  (`HUMANISH_TERMINAL_UNPRICED_CAP`). A terminal `maxUsd` cap has no adopter cost source
324
324
  until [issue 347](https://github.com/danielgwilson/humanish/issues/347) lands;
325
325
  `StudyDeps.costProbe` is a test seam. The sandbox's server-side timeout is the steps before the
@@ -529,20 +529,23 @@ Core-owned fields:
529
529
  an older run, else `inferLegacyStudyId`, which reads the `persona.source` or `scenario.source`
530
530
  convention `study:<id>`, or `lab:<id>` from runs written before 0.108, and nothing else.
531
531
  - `subject` (optional, additive): structured subject provenance,
532
- `{ source: clone | app-url | local-tree, repo?, commit?, archiveSha256?,
533
- dirty?, envNames?, state }` where `state` is `{ provenance: seeded |
534
- unpinned | declared-not-run | undeclared | external-public, seed?: [{ name, when,
535
- commandDigest, ok?, exitCode?, timedOut?, durationMs? }], externalEnvNames?
536
- }`. Emitted by the computer-use and shared-world routes and the clone
532
+ `{ source: clone | app-url | local-tree | desktop-cli, repo?, product?, commit?,
533
+ archiveSha256?, dirty?, envNames?, state }` where `state` is `{ provenance: seeded |
534
+ unpinned | declared-not-run | undeclared | external-public, seed?: [{ name, when,
535
+ commandDigest, ok?, exitCode?, timedOut?, durationMs? }], externalEnvNames?
536
+ }`. Emitted by the computer-use and shared-world routes and the clone
537
537
  scripted-browser route; absent on pre-existing and other bundles. `repo`/`commit` are clone-route fields; `archiveSha256`
538
538
  (64-hex sha256, the local-tree provenance pin) and `dirty` (host git
539
539
  porcelain status at pack time) are local-tree-route fields, additive under
540
540
  `humanish.run-bundle.v1`: a dirty working tree cannot be commit-pinned, so
541
- the archive content digest stands in for it. `commandDigest` is the
541
+ the archive content digest stands in for it. `product` is the desktop-cli field: the
542
+ `subject.product.name` the study declares, with state `undeclared`. A computer-use
543
+ run of a desktop-cli study saved before this field existed records `app-url` with no
544
+ `product`. `commandDigest` is the
542
545
  sha256-16 of the exact seed command; command text and env values never
543
546
  appear. `humanish verify` fails closed when a live `local-tree` bundle carries
544
547
  no well-formed `archiveSha256`, in addition to the existing `subject state
545
- provenance` check.
548
+ provenance` check.
546
549
  - `desktopTemplate` (optional, additive): the custom E2B desktop template (image)
547
550
  the run's sandbox(es) launched on, from `execution.desktop.template`, so the
548
551
  evidence shows which image ran. Present only when a template was configured;
@@ -665,7 +668,7 @@ A shared-world bundle adds two additive, optional fields to `humanish.run-bundle
665
668
  0.106.0):
666
669
  - `sequence: [roleId, …]`: the role ids that actually took a turn, in declared order.
667
670
  - `skippedTail` (optional, live sequential only): `{ afterRoleId, roles,
668
- cause, maxTotalUsd?, estimatedTotalUsd? }`. Each ordered `roles` entry names
671
+ cause, maxTotalUsd?, estimatedTotalUsd? }`. Each ordered `roles` entry names
669
672
  `{ roleId, simId, streamId }` for an unstarted participant. Together with the
670
673
  executed prefix it must account for the full declared denominator. The
671
674
  predecessor must have a matching `harness_error`, explicit `session_error`,
@@ -700,7 +703,7 @@ cause, maxTotalUsd?, estimatedTotalUsd? }`. Each ordered `roles` entry names
700
703
  the internet-reachable getHost URL is synthetic seeded data (author-trust + a
701
704
  provenance gate, not a no-real-data guarantee).
702
705
  - `laneWindows: [{ roleId, simId, streamId, startedAt, endedAt, verdict, routeHostDigest,
703
- commit?, seedDigest }]`: one harness-clocked window per actor; overlapping windows
706
+ commit?, seedDigest }]`: one harness-clocked window per actor; overlapping windows
704
707
  prove ≥2 personas were active simultaneously. `routeHostDigest` == `plane.hostDigest`
705
708
  (every actor drove exactly the harness-minted host).
706
709
  - `stateSeries: [{ timestamp, digest }]`: cadence digests of the shared world under
@@ -1029,7 +1032,7 @@ Core-owned fields:
1029
1032
  (the Codex participant), or, when the participant ended the session itself
1030
1033
  without them, one impressions-only request (OpenAI computer-use, threaded or
1031
1034
  `explicit_context`). That request is recorded as `debrief` with `trigger:
1032
- participant_end` and never has a `report` or `messageId`; it leaves the
1035
+ participant_end` and never has a `report` or `messageId`; it leaves the
1033
1036
  participant's `reason`, outcome and friction as they were. An empty `items`
1034
1037
  list means the participant named none. `status: not_collected` carries a
1035
1038
  `reason`: the closing report or impressions request was skipped or failed
@@ -1142,15 +1145,20 @@ discipline** that distinguishes three states and never conflates them:
1142
1145
 
1143
1146
  `knownTotalUsd` sums only the non-null lines (a `null` line contributes nothing
1144
1147
  and is never coerced to `0`); `fullyMeasured` is true only when no line is null.
1145
- Core meters only the `provider` line, populated from the actor trace's
1146
- `tokenUsage.costUsd` when present (else `null`); `product`/`media`/`payment`
1148
+ Core meters only the `provider` line, from the actor trace's `tokenUsage.costUsd` when present
1149
+ (the Codex usage records the terminal route reads carry none today); `product`/`media`/`payment`
1147
1150
  remain `null`, because no adopter can supply those signals until
1148
- [issue 347](https://github.com/danielgwilson/humanish/issues/347) lands. The terminal route records the model as `codex` and does not pin Codex's
1149
- model, so a measured token count stays `usd: null` with `source: unpriced-token-usage`, and
1150
- `knownTotalUsd: 0` with `fullyMeasured: false` means no line of this ledger was priced. E2B
1151
- time for the terminal sandbox is not a line of this ledger, whose lines are checked against
1152
- `caps.maxUsd`. The run bundle's `cost` summary prices it as a `desktop-minutes`
1153
- line (see Run Cost Summary And Estimated Actor Cost below).
1151
+ [issue 347](https://github.com/danielgwilson/humanish/issues/347) lands. Without a measured
1152
+ charge the `provider` line keeps `usd: null` and counts the participant's tokens. The route
1153
+ passes Codex a model and prices the tokens once from it (the actor trace's `estimatedCost`); the
1154
+ line carries that price as `estimatedUsd` with `source: estimated-token-usage`, the same figure as
1155
+ the run bundle's `model-tokens` cost line. The estimate is not part of `knownTotalUsd`, so
1156
+ `caps.maxUsd` and the no-spend proof do not count it. Tokens of a model with no rate stay
1157
+ `source: unpriced-token-usage` with the model named in the note, and a run whose output had no
1158
+ usage record has `source: unmeasured`. `knownTotalUsd: 0` with `fullyMeasured: false` means no
1159
+ line of this ledger was measured. E2B time for the terminal sandbox is not a line of this ledger,
1160
+ whose lines are checked against `caps.maxUsd`. The run bundle's `cost` summary prices it as a
1161
+ `desktop-minutes` line (see Run Cost Summary And Estimated Actor Cost below).
1154
1162
 
1155
1163
  ```yaml
1156
1164
  schema: humanish.terminal-cost-ledger.v1
@@ -1159,7 +1167,8 @@ lines:
1159
1167
  product: { usd: null, count: null, source: unmeasured, note: "…no signal yet…" }
1160
1168
  media: { usd: null, count: null, source: unmeasured, note: "…no signal yet…" }
1161
1169
  payment: { usd: null, count: null, source: unmeasured, note: "…no signal yet…" }
1162
- provider: { usd: null, source: unmeasured, note: "…no tokenUsage.costUsd this run…" }
1170
+ provider:
1171
+ { usd: null, estimatedUsd: 0.37157, source: estimated-token-usage, note: "…estimated at…" }
1163
1172
  knownTotalUsd: 0
1164
1173
  fullyMeasured: false
1165
1174
  ```
@@ -1171,9 +1180,9 @@ it explicitly cannot vouch for). `satisfied` is true only when every known line
1171
1180
  is within `maxUsd` (for a no-spend run, `maxUsd: 0` ⇒ every known line is `0`).
1172
1181
  Unmeasured lines never break it, so a ledger whose lines are all `null` is
1173
1182
  `satisfied` with nothing proven. The `statement` says which case applies: it
1174
- names the measured lines and their dollars, provider tokens that were counted
1175
- but unpriced (with the counts), and the lines with no signal; when no line was
1176
- measured it says the proof was not established. A proof never claims zero on a
1183
+ names the measured lines and their dollars, the provider tokens that were counted
1184
+ (with the counts, and their estimate or that they are unpriced), and the lines with
1185
+ no signal; when no line was measured it says the proof was not established. A proof never claims zero on a
1177
1186
  `null` line; verification fails closed if it does.
1178
1187
 
1179
1188
  ```yaml
@@ -1374,7 +1383,7 @@ Three new `.v1` schema tags ship, all additive and optional so
1374
1383
  the E2B shell sandbox (its acquired-to-cleanup span and `e2b.getInfo` size) and
1375
1384
  one `model-tokens` line for the Codex participant, priced from the model the route passed
1376
1385
  to Codex (`gpt-5.6-sol` unless the study declares one) with `basis:
1377
- aggregated_turns_base_rate`, or `null` with `no_token_usage`. Older terminal bundles record
1386
+ aggregated_turns_base_rate`, or `null` with `no_token_usage`. Older terminal bundles record
1378
1387
  `no_rate_for_model` (model `codex`). The terminal trace records the same `estimatedCost`. The sandbox line is not part of the
1379
1388
  terminal cost ledger, whose lines are checked against `caps.maxUsd`.
1380
1389
 
@@ -1674,7 +1683,7 @@ record its own:
1674
1683
 
1675
1684
  - **Adapter score** (`humanish.adapter-score.v1`, `RunBundle.adapterScore`).
1676
1685
  A namespaced summary the adapter's `score` hook returns: `{ schema, namespace,
1677
- status, score, summary, data? }`. Core never reads `data`, where the adopter's
1686
+ status, score, summary, data? }`. Core never reads `data`, where the adopter's
1678
1687
  component rubric rides; `namespace` (an adopter slug) scopes the whole
1679
1688
  record so a future inert-field audit never misfires.
1680
1689
  - **Namespaced product-noun block** (`RunFeedbackCandidate.adapter`). The
@@ -39,9 +39,11 @@ high-detail image tokens, and prices input at the model's highest input rate.
39
39
  The expected output is 12,000 tokens plus 1,000 per participant, at most the
40
40
  output allowance; the worst case spends the whole allowance. `admission` in
41
41
  `--json` output has `estimatedCostUsd` (the expected cost), `worstCaseCostUsd`,
42
- `maxCostUsd`, `inputTokenAllowance` (the input tokens priced) and
43
- `outputTokenAllowance`. On 148 billed gpt-6-astra analyses the expected cost was
44
- 1.08 to 7.3 times the bill, and 1.08 to 1.32 times for those billed $1 or more.
42
+ `admittedCostUsd` (the figure compared with the cap: the expected cost plus 10%,
43
+ or the worst case when that is lower), `maxCostUsd`, `inputTokenAllowance` (the
44
+ input tokens priced) and `outputTokenAllowance`. On 148 billed gpt-6-astra
45
+ analyses the expected cost was 1.08 to 7.3 times the bill, and 1.08 to 1.32
46
+ times for those billed $1 or more.
45
47
  A refusal's message gives the expected cost, the worst case, the cap, and
46
48
  `humanish analyze --run <id> --max-cost <n>` with `n` the worst case rounded up.
47
49
 
@@ -203,9 +205,24 @@ analysis, and treats an analysis that breaks them as unavailable.
203
205
 
204
206
  The narrative scrub removes a run's known transient values, such as a received one-time code or
205
207
  link, from every generated text field. It finds each value as written and in its encoded forms:
206
- percent-encoded, JSON-escaped, base64, base64url and hex, and where escapes split it. A field
208
+ percent-encoded, JSON-escaped, base64, base64url and hex, and where escapes split it. Base64 and
209
+ hex forms are searched from 6 characters. A value of 4 bytes, such as the code `7439`, is found
210
+ in base64 written whole (`NzQzOQ` or `NzQzOQ==`) and not inside a longer base64 run, where 4 or 5
211
+ characters of its encoding do not depend on the bytes around it;
212
+ `tests/evidence/secret-scrub.test.ts` records the case. The cost of the short forms is
213
+ over-redaction: an identifier that holds a short value's base64 loses that part, as
214
+ `aMTIzNAz` becomes `a[REDACTED_SECRET]z` while the code `1234` (`MTIzNA`) is registered, since a
215
+ match cannot tell whether the identifier came from the value. In the text of 9 real runs, no 6-
216
+ or 7-character form of any 4-, 5- or 6-digit code occurred. Run evidence is scrubbed for the same
217
+ forms. A field
207
218
  with a value is stored decoded with the value replaced; a field without one keeps its spelling. A
208
- known value in any of these forms in an ID, enum or reference refuses the response.
219
+ value inside a bracketed span, such as `[REDACTED_373433393231]`, is found too: only the markers
220
+ humanish writes (`[REDACTED_SECRET]`, `[REDACTED_LOCAL_PATH]`, `[REDACTED_RUNTIME_PATH]`,
221
+ `[REDACTED_PROMPT_TEXT]`, `[REDACTED_LOBBY_CODE]`) are left as they are. If another reading of
222
+ the stored field still holds a value (decoded once more, with percent escapes as UTF-8, with
223
+ transfer escapes expanded, or with Latin-1 characters read as UTF-8 bytes), as for a value encoded
224
+ twice, the whole field becomes `[REDACTED_SECRET]`. A known value in any of
225
+ these forms in an ID, enum or reference refuses the response.
209
226
 
210
227
  When a reviewer amends a finding, its headline and experience describe the replaced claim.
211
228
  Text output, the Observer and feedback drafts show the reviewer's claim in their place and say the
@@ -102,7 +102,9 @@ accepted. Known usage is retained; missing usage remains unknown.
102
102
 
103
103
  The CLI's JSON keeps `runOk` for the route's own result, `automaticAnalysis`
104
104
  for post-run analysis, and `ok` for the overall request. Failed, cancelled or
105
- unknown analysis produces exit code 2 without discarding the recording. A missing
105
+ unknown analysis produces exit code 2 without discarding the recording. When the
106
+ run's own result is ok, the first line of the human output reads
107
+ `humanish run <study>: live run finished; the analysis did not complete`. A missing
106
108
  `OPENAI_API_KEY` skips default analysis and preserves a successful run exit;
107
109
  `automaticAnalysisTrigger: "default"` distinguishes that case in JSON. So does a
108
110
  default analysis whose expected cost, plus the margin, is over the default $3 cap:
@@ -144,5 +146,9 @@ outcomes and the deterministic review verdict are never rewritten by analysis.
144
146
  | `AUTOMATIC_ANALYSIS_CANCELLATION_UNAVAILABLE` | `unknown` | A cancellation could not be recorded |
145
147
  | `AUTOMATIC_ANALYSIS_OUTCOME_UNKNOWN` | `unknown` | The outcome could not be determined |
146
148
 
149
+ A run refused before it was created (`run: not-created`) prints no `analysis:` line, since there
150
+ is no run to analyze and the refusal says why. Its JSON keeps the record, with
151
+ `AUTOMATIC_ANALYSIS_SOURCE_UNAVAILABLE`, or `AUTOMATIC_ANALYSIS_DRY_RUN` for a dry run.
152
+
147
153
  See the [analysis contract](../contracts/study-analysis.md) for selection limits,
148
154
  evidence validation, actual usage, corrections and share-safe export behavior.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "humanish",
3
- "version": "0.114.0",
3
+ "version": "0.116.0",
4
4
  "description": "Synthetic user research for apps, CLIs, and agent-facing product flows.",
5
5
  "keywords": [
6
6
  "cli",
@@ -91,7 +91,7 @@
91
91
  "comms:agentmail:contract": "node --import tsx scripts/agentmail-contract-check.mjs",
92
92
  "format": "oxfmt",
93
93
  "format:check": "oxfmt --check",
94
- "lint": "node scripts/check-lint-cap.mjs --max-warnings=439",
94
+ "lint": "node scripts/check-lint-cap.mjs --max-warnings=436",
95
95
  "knip": "knip",
96
96
  "prose:check": "node scripts/check-code-prose.mjs",
97
97
  "vocabulary:check": "tsx scripts/check-retired-words.ts",
@@ -112,13 +112,14 @@
112
112
  "@e2b/desktop": "^2.4.0",
113
113
  "@types/node": "catalog:",
114
114
  "@types/pngjs": "^6.0.5",
115
- "axe-core": "4.13.0",
116
- "knip": "6.38.0",
115
+ "axe-core": "4.14.0",
116
+ "fast-check": "catalog:",
117
+ "knip": "6.40.0",
117
118
  "oxc-parser": "catalog:",
118
- "oxfmt": "0.71.0",
119
- "oxlint": "1.86.0",
119
+ "oxfmt": "0.72.0",
120
+ "oxlint": "1.87.0",
120
121
  "oxlint-tsgolint": "7.0.2003",
121
- "skills": "1.7.0",
122
+ "skills": "1.7.1",
122
123
  "tsx": "^4.23.15",
123
124
  "typescript": "catalog:",
124
125
  "vitest": "catalog:"
@@ -342,8 +342,7 @@ The following configuration selects **local capture**:
342
342
  ```yaml
343
343
  comms:
344
344
  email:
345
- injectEnv:
346
- RESEND_API_URL # adopter-named: whatever env var YOUR app reads for its
345
+ injectEnv: RESEND_API_URL # adopter-named: whatever env var YOUR app reads for its
347
346
  # email-API base URL. The harness sets it to the in-sandbox catch — do NOT also
348
347
  # list it in subject.env. VERIFY the app actually reads this variable: a stock
349
348
  # email SDK does not honor a base-URL env unless the app passes it through, and