AgentCanvas / Pages / Developer Guide / Design Docs / Monitors / Human Performance Tab
2026-07-20

The Human tab puts a person in the exact seat the coding agents sit in: same env_habitat episodes, same discrete action space (0 = STOP, 1 = forward 0.25 m, 2/3 = turn 15°), same 512 px default render, and — the hard promise — the same ruler: every number on the tab comes from habitat's own measures via env_habitat__evaluate, computed by the same auto_host surface the agent drivers call, never browser-side. Human SR/SPL therefore sits directly against the standard board. The agent counterpart of this tab is the Coding-Agent Monitor; the experiment itself is described in the capability page.

The browser owns the control loop. One keypress becomes one POST /api/human/step; the runner translates it into the same auto_host HTTP calls the agent drivers make and hands the new egocentric frame back inline (base64 PNG) in the same response.

HumanPage (Human tab) D-pad · ↑ ← → · Enter = STOP /api/human 1 POST per action HumanRunner lock · one session env_habitat auto_host BaseServer · free port · PDEATHSIG step_discrete · observe_egocentric · reset · evaluate outputs/human/{split}/ episode_i.jsonl · summary.json persist frame (base64 PNG) + pose, inline in the response Per-episode protocol (HumanRunner): load_episode(i) → panel field episode_index + play → env_habitat__reset(rgb_resolution) → observe step(a∈{1,2,3}) → env_habitat__step_discrete → observe · stop() → step(0) if live → env_habitat__evaluate

1. What it does

The Human tab mid-session: header shows the key bindings (up = forward, left/right = turn, Enter = STOP), split rand100, End Session button and env: ready pill; episode 0 loaded live at 8 steps with the instruction 'Go straight past the pool. Walk between the bar and chairs. Stop when you get to the corner of the bar.'; the egocentric frame shows the blue pool room; a D-pad with a STOP button sits beside the frame and the 100-episode grid on the right reads 0/100 tested
A live session on episode 0 (rand100), 8 steps in, after turning left toward the pool the instruction names. Left: the egocentric 512 px frame. Middle: the D-pad + STOP (Enter). Right: the tested/untested grid over all 100 episodes.

It measures human performance on the benchmark the agents run. You pick an episode from the 100-cell grid (rand100 split), read the same instruction the agents get, walk the scene one discrete action at a time from the egocentric view, and press STOP when you believe you are at the goal. The env then scores you exactly as it scores an agent — SR, oracle SR, distance-to-goal, nDTW, SPL — and the tab keeps per-episode records plus a running aggregate. The whole session surface is four HTTP calls:

# 1. GPU up (blocks-then-polls; ~seconds for a warm scene cache)
curl -X POST :8000/api/human/start-server -d '{"split": "rand100"}'
# 2. place episode 7, get instruction + first frame (base64 PNG inline)
curl -X POST :8000/api/human/episode/7/load -d '{"rgb_resolution": 512}'
# 3. one keypress = one discrete action (1 fwd / 2 left / 3 right)
curl -X POST :8000/api/human/step -d '{"action": 1}'
# 4. STOP + evaluate + persist — habitat computes SR/OSR/NE/nDTW/SPL
curl -X POST :8000/api/human/stop

2. Session protocol — one lock, one session, driver-side everything

HumanRunner is a lifespan singleton owning at most one auto_host and one live episode; a threading.Lock serializes every env HTTP call with every session mutation (human_runner.py · HumanRunner).

3. Persistence & aggregate

Every action and trajectory coordinate is persisted; the aggregate is a plain mean over tested episodes. Under outputs/human/{split}/: episode_{i}.jsonl carries episode_meta (instruction, scene, start position, render resolution) plus one step/stop line per action (action name, position, orientation, terminated/truncated) and a final metrics line; summary.json holds one record per tested episode (metrics, step counts, called_stop, end_reason, wall time) with re-tests replacing their index in place, plus an aggregate that averages every numeric metric and num_steps over the tested set (human_runner.py · _persist_record / _aggregate).

4. The UI surface

The page is a thin, keyboard-first shell over the four endpoints (HumanPage.tsx): a split selector, a 100-cell episode grid with tested/success badges fed by GET /status, the egocentric frame with instruction overlay, a D-pad, and a metrics panel after STOP.

KeyAction
step(1) — forward 0.25 m
/ step(2) / step(3) — turn 15°
Enteropens the STOP confirm (STOP is permanent, so it is never one keypress)

Key events are ignored while a form field has focus, and movement keys are dead while a request is in flight (busy) or the episode is done — the browser enforces the one-action-at-a-time loop the runner's lock assumes.

5. Where it deviates from the agent protocol

No turn cap, no wall clock. Agent cells run under a hard max-turns and a 2400 s episode timeout; the human session has neither — only the env's own step budget binds (surfaced as end_reason="budget"). A human can also stare at a frame indefinitely. Human numbers are comparable on the metric ruler, not on the resource envelope.
The aggregate covers the tested subset, not the split. aggregate averages whatever episodes have been tested (tested: N is printed beside it). Until all 100 are tested, comparing it against a full-board cell aggregate carries selection bias — you may have tested the easy ones first.
One session globally. One habitat env = one GPU, so the runner owns at most one server and one live session; a second browser loading an episode replaces the current session mid-flight. There is no per-user isolation.
Backend auto-reload kills the env. uvicorn --reload restarts the worker on any .py change and the PDEATHSIG-armed habitat subprocess dies with it. The tab surfaces this as a recoverable error with a Start-Session retry; the durable fix is running the backend without --reload (HumanRunner._DEAD_MSG).

6. Key files

PieceWhere
Human page (tab)agentcanvas/frontend/src/pages/human/HumanPage.tsx
REST routeragentcanvas/backend/app/api/execution/human.py
Runner serviceagentcanvas/backend/app/services/human_runner.py
Artifactsoutputs/human/{split}/episode_{i}.jsonl + summary.json
AgentCanvas docs