human(ish)

Observer and terminal

Browse studies as a person, read results through JSON as an agent, or serve the run library.

Browse in a human terminal

humanish tui is the one command built for a person rather than an agent:

npx humanish tui

Arrow keys move, Enter opens, Esc goes back, q quits. There are seven screens. Three are the main path: all studies, one study, one run. The row below the last study opens all runs, every participant working now across every study. ? shows the keyboard shortcuts, and c opens keys and accounts: each provider key humanish keys lists, with where it comes from or the command that adds it, and the email connection. Enter on a key asks for its value with hidden input and stores it as humanish keys set does. A run you are watching updates in place, from running to its verdict, without the screen jumping.

  • studies: every study in the project, with the ones running now listed first. Each row shows what a live run of it usually takes. A study that has never run says never run, and one with only dry runs says <n> runs, none live; dry runs spend nothing and take no time, so they are not averaged.
  • study: that study's description, history and two ways to start it. A dry run starts on one keypress because it costs nothing. A live run takes two, with the cost shown between them: what its live runs have cost, its participant caps, and the analysis line humanish run prints. A study whose file sets mode: dry-run cannot start live from here, because humanish run runs a file in the mode it sets; set mode: live in the file first. The keys line is checked against a live run of the study either way.
  • run: who is in the run, what each participant is doing now, and how far along it is, with time and money underneath. A terminal cannot show screenshots, so when the run has an Observer file, the run screen's Open in Observer action opens it in your browser. Run again on a live run asks twice, with the same cost shown, as a live start does.

Five actions take two Enters: a live start, Run again on a live run, Stop this run, Cancel analysis, and Set up humanish here in a directory that is not a project yet. The second Enter counts only on the same action within 30 seconds of the first. An Enter less than 400 ms after the one before it is ignored and restarts that wait. Any other key cancels the first Enter, and Esc cancels it without going back.

A run started from the TUI is detached: it keeps going if you quit, and it survives a dropped SSH session. The TUI follows it by reading .humanish/runs/<id>/status.json and holds no handle on it, so you can quit mid-run, reopen, and find it still there.

For coding agents and scripts

tui requires interactive stdin and stdout. It also refuses detected coding-agent sessions, even when the runner allocates a TTY.

SituationJSON error.codeExit code
A recognized coding-agent sessionHUMANISH_TUI_AGENT_SESSION2
Non-interactive stdin or stdout, with no agent markerHUMANISH_TUI_REQUIRES_TTY2
Interactive human terminal on Node older than 22HUMANISH_TUI_UNSUPPORTED_NODE2

--json returns the refusal envelope; it does not turn the TUI into a JSON browser. Agent detection uses runner environment markers; absence of a marker does not prove a human is present. --force bypasses only the agent-session guard and is for a person actually at the keyboard. The TTY and Node requirements still apply.

Use these read-only commands to answer the same browsing questions:

npx humanish study list --json
npx humanish study show first-run --json
npx humanish runs --json
npx humanish review --run latest --json
npx humanish stats --json

study list shows available studies, study show shows a study's source, runs lists run history, and review reads an existing run. If there are no runs yet, do the keyless dry run before asking for latest.

Starting a run is a separate action. npx humanish run first-run --json --no-open creates a keyless preview; replacing first-run with a live study can launch providers and incur spend. See the CLI reference for all flags.

Watch and review in the Observer

Observer shows a study's participants, their recorded screens, and the events that explain those screens. The grid, player and exported HTML use the same current renderer. Screens fit their actual aspect ratio; a portrait capture is never cropped to fill a wide tile.

Choose an entry point

humanish watch <study> runs the study and keeps its viewer attached. The process that owns a hosted desktop can offer its live stream while it runs. --no-open keeps that attachment without launching a browser. --json is the machine-result path and does not keep an interactive stream server attached. Without an interactive terminal, as in a coding agent's shell or a pipe, watch and observe print the result and the Observer path and exit; --serve keeps the server attached there.

humanish observe --run <id> opens an existing study without starting another. The TUI's Open in Observer action does the same through its session-owned loopback library. These entry points follow saved captures; they cannot recover live desktop credentials owned by another process. Keep the owning CLI or TUI open while using its viewer. Exported HTML is an independent offline recording.

Read the status band

The top band separates the run's current status from evidence freshness. A finished study never uses its execution mode (live, as opposed to dry-run) as a claim that it is still running. When updates fail, the last usable evidence stays visible, the band reports the failure, and Retry requests another snapshot. A stale process heartbeat does not prove that the process died: the band reads Run status unconfirmed until a record says the run ended.

A player's Live desktop label identifies what it displays. An iframe loading is not a connection-health signal: the provider owns that connection. Reload stream reloads the desktop viewer without restarting a participant. Latest capture follows the latest saved screenshot without a desktop stream. Replay means the researcher has chosen a recorded moment. Offline exports say so explicitly.

Freshness uses recorded activity and capture timestamps, not the time the server reserialized the same data. Browser tabs poll less frequently while hidden and refresh when visible again. Requests are serialized, bounded by a timeout and cancelled when the viewer closes.

Work through a study

Previews share a height and wrap into rows; desktop cards are wider and portrait cards narrower. Complete screens remain contained when the available width is smaller than a preview. Sparse rows keep their chosen size. Open View and filter participants in the top right to search, filter by status, kind or persona, change Preview size, or enter Monitor for a larger evidence area. Exit monitor stays beside the run status. When participants ended more than one way, a row under the tally counts them by status; press a count to show only those participants, and press it again to show everyone. Open a card's Participant details to Pin it at the beginning. Ordering changes only when the researcher changes it. A study of more than 36 participants shows them in pages of 36, and turning a page opens it at its first participant. At most four desktop previews attach in the grid; allocation prioritizes screens that are actually visible, with the hovered or keyboard-focused participant taking priority. Other cards show their latest saved capture.

Each card follows its screen's aspect ratio without a minimum width that adds portrait gutters. Its 44px caption holds the participant name, a short preview source (Live, Capture, or Snapshot) or finished outcome, and a stable Participant details button. Labels and controls never cover captured pixels. Details contains labeled Pin and Compare toggles, the full source and freshness, final messages, recorded notices, duration and dimensions. Historical notices do not turn the details control into a warning; exceptional outcomes remain in the caption. Comparison permits three participants and explains the limit while keeping selected participants removable.

Icon buttons use Lucide glyphs, accessible names, hover/focus hints and visible keyboard focus. Touch targets expand to 44px. Popovers have explicit close buttons; Escape dismisses a hint without moving the player. These conventions follow Carbon icon usage, ARIA toggle-button semantics, and WCAG target sizing. The bookmark icon in the top bar opens Saved moments; recalling an available moment closes the popover, while an unavailable moment leaves the error visible.

Opening a participant provides elapsed-time seeking, the original frame number, playback speed, Fit, Actual size, zoom/pan and fullscreen. The inspector can be hidden or resized. Click markers use the recorded desktop coordinate system and the image's rendered rectangle, including at zoomed sizes.

Arrow keys, mouse controls, same-participant links, browser navigation and reload agree about the selected moment. Seeking leaves live mode. Pausing on the newest frame stays paused when new evidence arrives; Go live explicitly resumes following. A missing addressed moment is reported instead of silently replaced.

Screenshot playback is sparse evidence, not a video recording. Capture gaps stay visible. Skip waits compresses intervals associated with recorded waits and reports the skipped duration. Next action, Next flagged frame, activity filters and grouped waits make long traces easier to inspect without deleting events. Older unstamped recordings use labeled estimated pacing. Long feeds, filmstrips, terminal output and event lists stay bounded while earlier/later content remains reachable.

Copy moment link creates a frame address; denied clipboard access exposes a selectable link. Original frame opens/downloads the captured raster. Saved moments keeps bounded run/participant/frame identifiers in this browser. It does not save credentials, publish annotations or change the evidence bundle. Storage failure leaves the controls usable for the current visit.

Move between review views

The library button opens a drawer while reviewing a participant or comparison, including on desktop. Comparison panels use the same participant names as the grid. Open frame opens the displayed capture in the player, where it can be saved; Back to comparison restores that comparison's selection, alignment and cursor. Browser Back also returns to the addressed comparison. Comparing another run needs a free slot within the three-participant limit.

Reported thinking is an explicit filter; the Thinking checkbox controls only All evidence. Warnings & findings includes a separate Run and setup notices section. These recorded operational notices retain their source timestamps and are not assigned invented screenshot positions or counted as participant findings.

Compare recorded evidence

Select participants and open Compare selected. Capture-time alignment chooses the latest frame at or before the cursor, never a future frame. Before/after coverage and each frame's age are explicit. Clock differences between desktops remain an uncertainty; matching timestamps alone do not establish causality.

Elapsed-time alignment starts each participant at its first capture and compares progress rather than simultaneous events. Missing or descending timestamps cannot be used as a shared clock. Another run can be loaded from the available library as recorded evidence. The address retains the chosen runs, participants, clock and cursor so reopening it restores the comparison.

Add reviewer notes

Pause the study timeline on a moment and choose Add a note at 02:31 in the bottom dock to write what you saw there. A note added while a participant is open belongs to that participant; one added from the grid belongs to the whole study. Each note is a mark on the study timeline. Findings lists the notes under Reviewer notes, each marked Human, and a note's time opens that moment.

Each note is saved in the run directory as its own file, notes/<id>.json, and is never rewritten, so notes added at the same time from the Observer and the CLI are all kept. A note's time counts from the study timeline's 00:00: the first timed capture or desktop video of any participant. From a terminal or an agent, humanish notes <run> lists the notes and humanish notes <run> --add --at 02:31 --participant <id> "text" adds one. --participant takes the participant's id from the study, such as support-lead, or its stream id; leave it out for a note on the whole study. Listings name each participant by its caption, as the Observer does.

Only an Observer that humanish observe, watch or run --open serves on 127.0.0.1 takes notes. The server accepts a note from the page it rendered: the page carries a token that changes each time the server starts, and the request must come from that page's own address. An Observer shared with --expose, a saved observer/index.html opened from disk and an exported page show the notes and offer no way to add one.

Text that looks like a secret or a local path is replaced before a note is saved. humanish verify reads the note files like the rest of the run, so a secret-shaped value in a note keeps the run from share_ready. Feedback drafts include the notes under Reviewer notes, apart from what participants said. A draft and an HTML export read the notes once, check that set as verify would, and include that set only, so a note added or changed after verify ran cannot go out unchecked. A file in notes/ that is not a readable note is skipped, and humanish notes names it.

What the viewer keeps out

Only the attached process can grant a desktop iframe its own origin for provider module loading. Stored grants are removed, exports contain no runtime grants, and every local viewer response refuses to be framed. Generic imported embeds retain an opaque sandbox. Artifact and history links cannot introduce arbitrary URL schemes or traverse outside their evidence roots.

Review study findings

Participants and Findings share one Observer workspace. The participant grid, recordings and original feedback remain available while you review ranked observations and follow their evidence links.

Supported live studies request analysis by default when the run ends; review.analysis: false in the study turns it off. Analysis usually takes 1 to 4 minutes after the last participant ends. Until it finishes, the served Observer's status band reads Writing findings and the Findings view shows the analysis as pending, with how long ago it started; participant recordings stay open to review meanwhile. Automatic analysis has the analyst a study gets (the Codex account for a Codex participant, otherwise OpenAI with a $10 admission limit) and the failure behavior. For an existing recording, inspect the admission estimate, then generate:

npx humanish analyze --run latest --max-cost 10 --dry-run --json
npx humanish analyze --run latest --max-cost 10 --json
npx humanish observe --run latest

Generation sends selected retained text and captures to OpenAI and requires OPENAI_API_KEY; opening Observer never starts it. The review covers intent, outcome, friction, recovery and participant feedback; each finding links to original evidence. Coverage limits and missing evidence remain visible.

Each finding leads with a plain headline and a short account of what the person tried and what got in their way. Its title, summary and observations sit under an Evidence disclosure. When a reviewer amends a finding, the amended claim replaces the headline and the finding says it was corrected.

Design findings lists problems a designer would notice in the captures, such as a button cut off at the bottom of the window, whether or not a participant mentioned them. They are grouped by severity, and each one names the screen, what a designer notices, why it matters and a suggestion. Each cites at least one capture, shown as a thumbnail that opens that moment in the recording. Analyses from before this feature have neither headlines nor design findings and show their findings by title.

Concerns considered shows material concerns reviewed in the analysis, including reasons for leaving some out of the ranked findings. Follow those evidence links to inspect a reported concern or recovered mistake. Reported confusion may be useful even when a link is correct or the study setup explains it; the finding should qualify its cause. These decisions are model interpretations and still need review.

analyze list --json includes version history and execution receipts. analyze show prints the findings of the latest usable version, and analyze show --json prints its validated record. Use --rerun to request a new version, or --question for an additional reviewer question.

npx humanish analyze correct --run latest --analysis <id> --finding F1 --status confirmed --reason "The cited captures reproduce the issue."
npx humanish feedback issue --run latest --analysis <id> --finding F1 --repo owner/repo

Corrections are bound to the exact version reviewed. Feedback still requires share-ready evidence. Redacted exports omit analysis that refers to original source hashes; analyze the derived run again.

Replay for an audience. Open the Observer artifact with ?autoplay=8&loop=1&sidebar=closed before the # and it presses play on the study transport at 8x as soon as the recording loads, restarts two seconds after the last capture, and starts with the library collapsed. autoplay alone means 8x; any value from 1 to 64 works; sidebar=closed also works on its own. Every control still works, and a viewer who pauses stays paused. This is how humanish.dev embeds a saved run.

Serve the run library

humanish watch follows one attached run; humanish observe --all serves the whole local run library under .humanish/runs/, a library index plus every run's Observer page:

npx humanish observe --all
npx humanish observe --all --expose --tunnel ngrok --oauth google --allow-email you@example.com
npx humanish observe --all --safe --expose --tunnel ngrok
npx humanish observe --all --expose --public-url https://observer.example.com

The first serves the library on loopback only. The second is the phone path: ngrok's edge authenticates viewers with Google OAuth (restricted to your --allow-email/--allow-domain allow rules) before any request reaches the loopback server; humanish carries no in-process auth. The third is a secretless safe observer: no login, but only runs whose humanish verify shareSafety is share_ready exist at all; everything else is absent and 404s. In every mode a run's sandbox-receipts.ndjson and status.json 404 as a missing file does: they hold the raw sandbox ids and the recording process id. A run is verified again before it is served whenever any of its files changes, so a file added or rewritten after verification is not served until the run grades share_ready again. Verify before sharing lists what keeps a run from share_ready. The fourth trusts an edge you already secure (Cloudflare Access, Tailscale, a proxy you own) and just binds loopback behind it.

In every mode the server binds 127.0.0.1. --expose is refused before binding unless it has a reachable origin and either edge auth or --safe; the exposure matrix lists each combination and its error code.

Watch a live run from your phone

humanish watch <study> --expose --tunnel ngrok --oauth google --allow-email you@example.com streams the live desktop of a computer-use run to an edge-authenticated remote viewer while it plays. The attached server comes up during the run and survives a timed-out/failed run, so you can inspect a failed run's evidence too. A live run is never share_ready, so watch --expose always requires edge auth; --safe is an observe --all library filter and is rejected on watch (HUMANISH_WATCH_SAFE_NOT_APPLICABLE). An exposed watch serves only the attached run (its history lists just that run and every other run id 404s), so a remote viewer can never reach your other runs' raw evidence.

Live E2B desktop stream URLs reach a remote viewer only on watch --expose, and only behind edge auth; observe --all never injects them (remote viewers of the library see only persisted evidence: screenshots, events, terminal tails). See Serve architecture.

Edit this page on GitHub