humanish · diff

git:20261008.d01e171 to git:20261021.1dd77bf

43 added, 25 removed. Audit A to A.

---
name: humanish
description: Synthetic user research for apps, CLIs, and agent-facing product flows. Install and configure the humanish CLI in a JavaScript app. Use when an agent needs to add humanish, run safe first setup, create synthetic personas or scenarios, configure env var names without values, capture the email an app sends so a persona can complete an email-gated flow (e.g. a signup verification link or one-time code), run verification and Observer commands, or draft public-safe feedback issues without GitHub mutation.
---
# humanish CLI
Use this skill to add humanish to a target app without relying on chat memory or
private artifacts. Keep every example synthetic and public-safe.
## Hard Boundary
Never read, copy, commit, summarize, or generate PII, PHI, secrets, keys,
tokens, raw private transcripts, private screenshots, raw customer data, raw
patient data, or private upstream artifacts.
Do not edit `.env` or secret files. Do not paste credential values. Use env var
names only, usually `OPENAI_API_KEY` and `E2B_API_KEY`. For live local runs,
prefer an explicit ignored env file passed with `--dotenv <path>`; do not
assume broad inherited job env is safe. Stop before live provider spend,
hosted execution, deploys, public tunnels, or GitHub mutation unless the user
explicitly approves that exact action.
## Not For You: `humanish tui`
`humanish tui` is a human-only surface. It takes over the terminal and waits for
keystrokes, so it will block you and produce nothing you can read. It refuses a
non-interactive stdin or stdout with `HUMANISH_TUI_REQUIRES_TTY` rather than
rendering escape codes into your transcript — but do not invoke it at all.
Everything it shows has a machine-readable equivalent, which is what you want:
| Instead of the TUI | Use |
| ------------------- | -------------------------------------------------------------------------------------- |
| browsing studies | `npx humanish study list --json` |
| browsing runs | `npx humanish runs --json` |
| starting a run | `npx humanish run <study> --json --no-open` |
| a run's findings | `npx humanish review --run <id> --json` |
| communication setup | `npx humanish comms providers --json` and `npx humanish comms connections list --json` |
If a human asks you to "open the TUI", tell them the command to type; do not run
it on their behalf.
For AgentMail credential setup, a human can open `humanish tui`, press `c`,
and choose **Add API key**. Hidden entry returns to Connections after saving or
cancelling. The key is stored for that OS user, while the connection profile is
project-local. Never ask for the key in chat. The CLI alternative is
`humanish keys set agentmail` (hidden prompt; agents may use `--stdin` from an
authorized credential source), followed by
`humanish comms connections add agentmail --json`. Existing env/file precedence
and `HUMANISH_STRICT_KEYS=1` still apply. Read installed provider capabilities:
use `humanish comms check --online --json` for read-only authentication.
Authentication does not establish mailbox permissions, capacity or delivery.
`humanish comms configure --study <path> --json` previews an ignored study copy;
add `--apply --plan-token <digest>` to save the reviewed version. Launch its
exact returned path, not a basename that could resolve to another manifest.
## Setup Workflow
1. Inspect public target-repo files only: `package.json`, docs, route/app
structure, test scripts, and `.gitignore`.
2. Install humanish with the repo's package manager:
```bash
npm i -D humanish
```
The package is `humanish`; the installed binary is `humanish`. After
installation, `npx humanish ...` resolves the local project binary. For a
one-shot command before installation, use
`npx --package humanish humanish ...` to guarantee the binary comes from
the `humanish` registry package rather than a same-named command already
on the PATH.
3. Preview setup:
```bash
npx humanish init --dry-run --json
```
4. Apply setup after the planned changes are understood:
```bash
npx humanish init --yes --json
```
5. Confirm the layout:
- commit `humanish/` source files;
- ignore `.humanish/` runtime artifacts;
- keep committed studies under `humanish/studies/*.yaml`;
- keep private/local studies under ignored `.humanish/studies/*.yaml` or
`.humanish/local/studies/*.yaml`;
- keep `.env.example` commit-safe and value-free;
- never commit generated run bundles.
## Choosing a findings analyst
- Analysis is separate from the participant. Hosted and manual defaults use the
- OpenAI API with its own admission budget. An explicitly local browser study with
- a Codex participant defaults to a separate Codex account analyst. To select that
- restricted Codex ChatGPT account analyst on other supported studies,
+ Analysis is separate from the participant. A study that declares no analyst
+ gets one from its participant: a Codex `local-agent` participant, local or
+ hosted, defaults to a separate Codex account analyst; every other participant,
+ Claude Code included, defaults to the OpenAI API with a $10 admission budget.
+ Without `OPENAI_API_KEY` that OpenAI analysis is skipped, and the run output
+ names the next command: `analyze --provider codex` when Codex is signed in. To
+ select the restricted Codex ChatGPT account analyst on any supported study,
set `review.analysis.provider: codex` or pass `analyze --provider codex` on a
completed recording. This uses remote inference and the qualified CLI/login,
not local inference or the participant's existing conversation. See
[the analysis contract](../../docs/contracts/study-analysis.md) for the current
CLI/model qualification and setup limits.
When post-run analysis is refused for its cost, the run output and `humanish
review` give its expected cost, its worst case and the command that runs it,
`humanish analyze --run <id> --max-cost <n>`. Show the user those costs and run
the command only once they agree to the spend.
Do not pass numeric `maxCostUsd`/`maxOutputTokens` or their CLI flags to the
account branch. It cannot enforce those ceilings and rejects them. Account dollar
cost remains unknown even when token usage is reported. There is no fallback to
an API key or another provider. `analyze --dry-run --provider codex` validates
local evidence/configuration only; `doctor --study` checks setup without a model
request. Inspect a failed attempt before explicitly retrying `--provider codex
--rerun`. Opening Observer never starts analysis.
## Local browser setup
On Linux x64 with local rootful Docker, KVM and TUN, or a supported M3-or-newer
Mac with native ARM64 Node and Lima 2.2+, an `app-url` study can set `execution.target: local` and `actor.type: local-agent` with
`localAgent: codex`. It uses the supported Codex ChatGPT login, not E2B or an
OpenAI API key. Inference is remote and consumes account quota. Existing hosted
studies stay hosted; never silently change their execution or billing provider.
Use `humanish runtime status --json` and `humanish doctor --study <path> --json`
for read-only setup inspection. `humanish runtime setup` downloads and verifies
the pinned runtime; a live local run also prepares it automatically. The normal
`humanish run <path>` command and TUI use the same study runner and Observer.
See [the complete example and limits](https://humanish.dev/docs/local-browser).
Local browsers currently require a loopback app URL with an explicit port above
1023 and use a 960×720 Chromium desktop. For email-gated local apps, start
`humanish comms catch`, point the app's email sends at it, and declare
`comms.email.external.catchBaseUrl`. Use `doctor --study` to check its recipient
routes. This captures app sends without mailbox-provider credentials; it does
not receive arbitrary internet email. Each participant gets only its assigned
inbox through the local desktop. Real receiving still needs a supported hosted
route. See [captured inbox setup](https://humanish.dev/docs/comms-inbox#local-browser-studies).
Optional camera and spoken conversation require a separate media
runtime; follow [participant media](https://humanish.dev/docs/participant-media)
for setup and provider limits. Speech uses the same continuing Codex participant,
not a second conversation. Do not claim that installing the CLI also installs
Docker or Lima, or qualifies a machine for arbitrary participant counts.
For continuous desktop playback, independent computer-use participants can opt into
`execution.desktop.recording: { audio: true }` (`false` for screen-only video).
Screenshots remain the default. Recording does not enable camera or speech;
the raw media stays local-only. See [desktop recording](https://humanish.dev/docs/desktop-recording)
for runtime requirements and export limits.
## Format Stack
When creating or editing humanish files:
- use `.yaml` for human-authored humanish source: studies, personas and scenarios;
- use `.mjs` for executable adopter scorers named by `review.scorer.ref`;
- use `.json` or `.ndjson` for generated machine artifacts, Observer data, run
bundles, event streams, and synthetic fixtures.
Do not create `.yml` files under `humanish/`; `.yml` is for outside ecosystem
conventions such as GitHub Actions workflows. Do not introduce TOML unless the
target project has a concrete scalar global-config need that YAML, TypeScript,
or JSON does not serve.
## Authoring Personas And Scenarios
Create or edit only synthetic files under `humanish/`.
Personas describe the participant's relevant experience, motivations, habits and
access needs. A short profile is enough; use optional multiline `background` for
richer context (up to 32 KiB of UTF-8 text, never silently truncated). `summary` is
a short description, capped at 280 characters with a warning. Unknown fields such
as `backstory` are ignored with a warning; put that material in `background`.
Use synthetic or de-identified research synthesis, never raw customer interviews
or personal data in committed files.
```yaml
schema: humanish.persona.v1
id: volunteer-organizer
name: Morgan
summary: A volunteer organizer trying an unfamiliar planning app.
background: |
Morgan coordinates twelve volunteers using spreadsheets and email.
They know those tools well but have never used this product.
Reconciling replies takes time. They worry about making the roster
public and want to keep a usable copy if the group changes tools.
```
A profile with `background` receives only explicitly declared trait directives.
Legacy profiles without it retain medium defaults for missing patience and
technical confidence. Avoid contradictory prose and traits; inspect the compiled
brief with `humanish study show <study> --json` before running.
Scripted regression checks are a different use of scenarios. When the requested
work is deterministic verification of a known path, a `browser.steps` manifest
can drive it with explicit selectors and assertions. It does not establish
independent persona behavior or discoverability. Keep app-specific paths and
fixtures in the target repo's `humanish/` files.
```yaml
schema: humanish.scenario.v1
id: product-core-flow
title: Scripted product regression
persona: synthetic-new-user
goal: Verify a known path with synthetic data.
mode: browser
browser:
startPath: /
steps:
- id: open-home
label: Open the app
action: goto
path: /
expect:
text: "Get started"
```
Supported actions are `goto`, `fill`, `click`, `assertText`, `waitForText`,
and `waitForSelector`. Supported expectations are `text`, `selectorVisible`,
`urlIncludes`, and `stateChanged`. Use public-safe selectors and synthetic
values only. Do not write real emails, names, customer data, tickets, logs, or
tokens into scenario files.
## Authoring Studies
For an autonomous participant study, write `mission`, participant `instruction`
and `tasks[].goal` as a believable situation and desired outcome. Supply fixture
facts needed to act. Leave discovery, recovery and stopping to the participant.
Keep researcher criteria in `tasks[].success`.
| Bad: scripts the controls | Good: gives a situation |
| -------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| Click "New event", type a title into the field, then press "Save" and open "Settings". | Saturday's event needs two setup volunteers and one cleanup volunteer. See whether this app helps you organize the event and keep people informed. |
`humanish study check` and `humanish run` warn about step lists, chains of UI
actions, quoted control labels, CSS selectors and URLs beyond the subject's entry
point. They quote up to three matched lines and suggest a situation-style mission.
The checks apply to computer-use and shared-world studies; scripted-browser and
terminal studies and `tasks[].success` are excluded. These warnings never refuse
a run, and the run records them in its bundle.
Give each participant a persona with a short, fictional `background` describing
their relevant experience and situation. A missing persona or background also
warns. Use `humanish study show <study> --json` to see what the participant receives.
Do not supply hidden success rules, expected defects or recovery tricks.
Write reusable studies as `.yaml` study files (`humanish.study.v3`):
```yaml
schema: humanish.study.v3
id: first-run
title: First-run synthetic Observer
route: preview
mode: dry-run
subject:
source: this-repo
actor:
type: synthetic-persona
participants: 4
defaults:
open: true
```
A study declares its `route` (`preview`, `computer-use`, `shared-world`, `terminal` or
`scripted`) and composes `subject`, `actor`, `participants`, `caps`, `execution` and
`policies` for it; a key the route does not read is a parse error that names the route.
Run `npx humanish study show <study>` to see how a study file parses. humanish
no longer runs a `humanish.lab.v2` file from 0.107: it fails with
`HUMANISH_STUDY_V2_UNSUPPORTED`. `npx humanish migrate` rewrites every v2 file as v3
under `studies/`, and `--dry-run` lists them first. A v3 file left in a `labs/` folder
fails with `HUMANISH_STUDY_RETIRED_DIRECTORY`; move it to the matching `studies/` folder.
### Many actors at once (fan-out, shared worlds, concurrency)
- **Every declared participant runs live at once by default.** A 6-entry
`participants` list is 6 simultaneous actors; total sessions and spend are the same either
way, only wall-clock and simultaneity differ. `execution.concurrency` is a
CAP, not a mode: declare it only to bound simultaneous paid desktops, and
expect a parse warning when the cap makes participants run in waves (a green
waved run looks identical to the all-live run you meant, so the harness says
so up front).
- **Separate worlds (`per-lane-worlds`) vs one shared world.** A plain
multi-participant computer-use study gives each actor its OWN app instance
(independent studies in parallel). Declare `route: shared-world` for N
actors in ONE world (a lobby, a shared DB, actors seeing each other's
changes). Shared-world participants run at once; `execution.concurrency` must
be at least 2 there.
- **Watching it:** each live participant is its own Observer tile/stream;
participants beyond a declared cap start when a slot frees, which on a capped
run looks like idle tiles — another reason to leave the cap out unless you
need it.
Use committed `humanish/studies/*.yaml` for public-safe, reproducible studies. Use
ignored `.humanish/studies/*.yaml` or `.humanish/local/studies/*.yaml` for private repo
targets, local-only dogfood, or machine-specific settings. Never commit private
repo names, stream URLs, credential values, screenshots, logs, source snippets,
or operational details.
Useful commands:
```bash
npx humanish study list
npx humanish study show first-run
npx humanish run first-run --json --no-open
```
### Off-app email verification (comms)
When a flow needs an email from the app — a verification link, one-time code or
magic link — configure an inbox so the participant can read and use that message.
humanish supports local capture and fresh hosted receiving, with different setup
and privacy behavior.
Choose the transport to match the app:
- **Local capture** (below): app send configuration can point at a test catch;
no external mail service is needed. It tests the email flow without proving
real delivery.
- **Real AgentMail receiving**: use `comms.email: { connection: agentmail }`.
humanish acquires one fresh hosted inbox per participant before desktops start.
The app sends normally. Requires a configured organization-scoped key and
app-url/clone/local-tree hosted computer-use participants; shared worlds
work, local-agent does not. Do not combine
connection with capture settings or substitute fresh addresses for existing
account identities. `allowedOrigins` can name additional trusted link origins.
Real mail can reach hosted desktops, actor models and analysis models. Screenshots
can contain it. Such runs remain `local_only` even after screenshot blurring;
this is a publication restriction, not local-only processing. Provider charges
and model charges are separate. Use `humanish comms recover --json` after an
interrupted run, then `humanish comms recover --run <id> --apply --json` for its
privately recorded resources. Never derive deletion authority from run artifacts.
SMS, participant sending, borrowed inboxes and other providers remain unavailable.
See [real email receiving](https://humanish.dev/docs/email-receiving) for limits and recovery.
The following configuration selects **local capture**:
```yaml
comms:
email:
injectEnv: RESEND_API_URL # adopter-named: whatever env var YOUR app reads for its
# email-API base URL. The harness sets it to the in-sandbox catch — do NOT also
# list it in subject.env. VERIFY the app actually reads this variable: a stock
# email SDK does not honor a base-URL env unless the app passes it through, and
# an app that ignores it sends real mail (or throws) while the inbox stays empty.
# A run where the catch captured zero sends warns at teardown for exactly this.
```
That is the whole block for the common case. Every participant automatically gets a
deterministic inbox address (`<laneId>@example.test`), and each actor's prompt is
extended with the full handoff: its address ("when the app asks for an email
address, enter exactly that"), the inbox URL to open, and the wait steering
("waiting for an email is normal, not a blocker"). Declare `recipients` only to
customize addresses or limit which participants do email:
```yaml
recipients:
- lane: signup-01 # this study's REAL participant id: a `participants` entry's `id`,
# or the generated lane-01..lane-NN names when you use a count. An unknown id
# is a hard parse error listing the study's actual participant ids (a mismatch
# would silently disable the funnel for that participant, which is how a
# 6-actor field run lost every inbox at once). Participants you leave out get
# no inbox and are never told one exists — the parser warns which.
address: user@example.test # what the actor signs up with; the evidence
# drain matches captured mail against it.
```
The app keeps calling its email API normally (Resend/SendGrid-shaped, or a custom
profile); only the base URL is redirected. Route support: the clone/local-tree
computer-use route (inbox on the sandbox's own loopback) and the CONCURRENT
shared-world route (inbox getHost-exposed from the subject sandbox; the default
since every participant now runs live at once). SMTP capture is supported on
provisioned routes with separate worlds (`per-lane-worlds`); shared-world SMTP
is rejected because it is not wired there.
For app-url/operator-provided subjects, run `humanish comms catch` on a reachable
host, point the app's email sends at that catch, and declare
`comms.email.external.catchBaseUrl` (plus `inboxBaseUrl` if different). A declared
`authTokenEnv` is an environment variable name, never a credential value; its
token must be at least 16 characters.
The in-sandbox catch
needs `python3` (the stock E2B desktop has it).
Evidence is digest-only (`humanish.comms-thread.v1` — counts and digests, never
raw mail); the _readable_ proof a persona saw the email is its screenshots of the
inbox page. See `docs/contracts/schemas.md` for the full `comms:` shape and
`humanish <cmd> --help` for run flags — this skill does not restate them.
## First Proof Run
Run the no-credentials path first. It is a dry run: humanish writes a synthetic
run bundle and tests no target app behavior. Every command in this block
finishes and exits, needs no key and spends nothing; lines marked
`# live only` need keys and spend money, so run them only when the user has
approved a live run. `run` prints the run id; use it in place of `<id>`.
```bash
npx humanish doctor --json
npx humanish run first-run --json --no-open
npx humanish verify --run <id> --json
npx humanish review --run <id> --json
npx humanish observe --run <id>
npx humanish run try-live --json --no-open # live only
npx humanish feedback issue --run <id> --repo example/app --format markdown # live only
```
`observe` prints the path of the run's Observer page for the person you are
working for. Without an interactive terminal, `observe` and `watch` print the
result and exit; at a person's terminal they serve the Observer until Ctrl-C.
`feedback issue` drafts from a live run only and refuses a dry run with
`HUMANISH_FEEDBACK_REQUIRES_LIVE_RUN`. Its Markdown draft is checked for
secret, key, token and local-path shapes; it is not checked for personal data. It
must not call the GitHub API, require a token, update Projects, use provider
credits, or claim product behavior proof from a dry run.
When the target app can run locally, prove a known browser path with a
scripted-browser study after starting the app on loopback. The study replays a
`browser.steps` scenario (see above) on a desktop surface, plus a mobile one
- with `count: 2`. The scripted steps make no model requests. Post-run analysis
- still runs by default when `OPENAI_API_KEY` is set, with a $3 cap; keep
- `review.analysis: false` to skip it. Copy the shape of
+ with `count: 2`. The scripted steps make no model requests; the analysis after
+ the run does, and it is what produces findings. Copy the shape of
[`humanish/studies/scripted-demo.yaml`](https://github.com/danielgwilson/humanish/blob/main/humanish/studies/scripted-demo.yaml)
or the example in the
- [study file reference](https://humanish.dev/docs/study-files#scripted-browser-scenarios):
- set `subject.appUrl` to the loopback URL, `scenario` to the scenario id,
- and `mode: live`. Without `mode: live` the study is a dry run that opens
+ [study file reference](https://humanish.dev/docs/study-files#scripted-browser-scenarios),
+ leaving out the demo's `review.analysis: false`, which keeps that zero-spend
+ demo from requesting analysis: set `subject.appUrl` to the loopback URL,
+ `scenario` to the scenario id, and `mode: live`. Set `review.analysis: false`
+ only when the user has chosen a run with no findings. Without `mode: live` the study is a dry run that opens
no browser.
```bash
# in another terminal, start the target app on 127.0.0.1 or localhost
npx humanish study show <study> --json
npx humanish run <study> --json --no-open
npx humanish verify --run <id> --json
npx humanish observe --run <id> --open # for a person; at their terminal it serves until Ctrl-C
```
Do not use `humanish watch --count ...` as a substitute for a scripted-browser
study. `watch` without a study starts a preview run for the Observer; a live scripted-browser
study captures desktop and mobile browser evidence against a running app.
+ ## After a Live Run, Read and Report Its Findings
+
+ A live run ends with a separate analysis of its evidence, and `run --json`
+ carries what that analysis left. `findings.count` is how many findings it made;
+ `findings.top` holds at most five, each with its `title`, `headline`, `impact`,
+ `confidence` and the ids of the `participants` it affected; `findings.review` is
+ the command that prints every finding with its evidence; `findings.observer` is
+ the Observer page that opens on the findings. When the analysis left no
+ findings, `findings` is absent, `automaticAnalysis.message` says why, and
+ `automaticAnalysis.nextCommand` is the command that gets them (null when no
+ command can). Run that command, or tell the user to.
+
+ Read the full findings before reporting anything else:
+
+ ```bash
+ npx humanish review --run <id> # the verdict, then each analysis finding
+ npx humanish review --run <id> --json # the same; findings are under `analysis`
+ ```
+
+ Report each finding to the user: its title, impact, confidence and recovery, the participants it
+ affected, and the capture files it cites. These come from the post-run analysis of the evidence.
+ Label the participant's own report in the review summary as the participant's account. When
+ `analysis.state` is not `ready`, report `analysis.message` and the `analysis.next` command in
+ place of findings; `analysis.next` is null when no command can produce them. `latest` moves with
+ every run, including dry runs, so use the run id the run printed.
+
## Optional Live E2B Study
Live headed E2B desktop participants are optional. Add the substrate dependency only
when the user explicitly wants live E2B execution:
```bash
npm i -D @e2b/desktop
```
Then confirm env var names are documented without values:
```bash
E2B_API_KEY
OPENAI_API_KEY
```
Do not paste values into files, prompts, run bundles, issue drafts, or logs.
Load local values only at invocation time:
```bash
npx humanish run .humanish/studies/local-live.yaml --dotenv .humanish/local/provider.env --json --no-open
```
When choosing dogfood targets, prefer apps, CLIs, or agent-facing tools with a
real observable user surface and local run path. Do not use libraries,
frameworks, starters, or infrastructure packages as default targets unless the
declared scenario is developer-experience testing. Private repos are allowed
only as explicit maintainer-authorized runs with repo redaction left on; never
publish their names, screenshots, logs, source snippets, or operational details.
## Reporting Back
- After a live run, read its findings before anything else:
-
- ```bash
- npx humanish review --run <id> # the verdict, then each analysis finding
- npx humanish review --run <id> --json # the same; findings are under `analysis`
- ```
-
- Report each finding to the user: its title, impact, confidence and recovery, the participants it
- affected, and the capture files it cites. These come from the post-run analysis of the evidence.
- Label the participant's own report in the review summary as the participant's account. When
- `analysis.state` is not `ready`, report `analysis.message` and the `analysis.next` command in
- place of findings; `analysis.next` is null when no command can produce them. `latest` moves with
- every run, including dry runs, so use the run id the run printed.
-
- Then report:
+ Report a live run's findings first, as
+ [After a Live Run, Read and Report Its Findings](#after-a-live-run-read-and-report-its-findings)
+ describes. Then report:
- files changed in the target repo;
- exact proof commands run;
- generated local artifact paths under `.humanish/`;
- whether redaction passed;
- what remains blocked before live browser, OpenAI, E2B, or GitHub mutation.