Stranded delivery list on the bus agents view. Built by Sage's Haiku
subagent (decision 129), rebased onto 57a2ddc5. Phase B, three runs:
the first failed the new test (node:sqlite null-prototype rows, fixed
by mapping to plain objects), the second stopped at cli on a missing
node_modules link, the third passed: 14 node suites, nine scripts,
15/15 mutants killed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
133 lines
13 KiB
Plaintext
133 lines
13 KiB
Plaintext
✔ explicit request, Seen, ordinary completion and a new request have distinct attention states (3.462512ms)
|
||
✔ attention convention ignores reasoning/quoted examples and permits leading blank lines (0.239783ms)
|
||
✔ completed smoke replies and ordinary questions are idle, not human blockers (0.724908ms)
|
||
✔ only an explicit first-line input request makes a finished reply waiting (0.146107ms)
|
||
✔ tool activity, user text, errors and unfinished turns override attention text (0.121836ms)
|
||
✔ STOP access failure is unknown, not absence, under a non-root identity (25.70639ms)
|
||
✔ connector Task never inherits Discord routing envelopes; ordinary Task still uses user text (2.475764ms)
|
||
✔ connector discovery keeps only safe identity; rejects modes, mismatches, links and traversal (1.163762ms)
|
||
✔ canonical owner identity and STOP are independent; no tmux fallback or forged registration (4.7441ms)
|
||
✔ connector reply refusal precedes forged live tmux registration; ordinary agent still sends (0.509134ms)
|
||
✔ server rescans connector discovery and refuses HTTP reply without transport (29.578281ms)
|
||
✔ connector session links and linked directories are not read (1.046731ms)
|
||
✔ newer live matching launch marks old activity, preserves history/attention/attribution, then clears on new activity (2.450976ms)
|
||
✔ CLI print uses the relaunch notice instead of old current preview (55.593283ms)
|
||
✔ connector owner and fixed task never inherit a native relaunch notice (1.885442ms)
|
||
✔ equality, stale/unknown/offline, mismatched registration and unknown activity do not assert relaunch (0.967395ms)
|
||
✔ loadConfig: missing file throws ConfigError (1.369011ms)
|
||
✔ loadConfig: invalid JSON throws ConfigError (0.276245ms)
|
||
✔ loadConfig: missing dataRoot throws ConfigError (0.20175ms)
|
||
✔ loadConfig: relative dataRoot throws ConfigError (0.19657ms)
|
||
✔ loadConfig: valid config returns dataRoot (0.752192ms)
|
||
✔ findNewestSession: picks the newest by mtime among two files (0.419117ms)
|
||
✔ findNewestSession: finds files in nested subdirectories (0.277914ms)
|
||
✔ findNewestSession: returns null for a missing dir (0.109761ms)
|
||
✔ readSession: extracts fields, collapses/truncates text, counts a truncated final line (0.601617ms)
|
||
✔ readSession: model and provider follow the latest model_change entry or assistant turn; null when the log names neither; scanAgent carries them (1.12779ms)
|
||
✔ readSession: lastError carries the assistant errorMessage only when the last assistant turn errored (0.46219ms)
|
||
✔ findNewestSession/scan: never read sibling auth or secrets next to a sessions dir (0.994066ms)
|
||
✔ deriveState: full state table (0.159092ms)
|
||
✔ rule: newest entry is an assistant message with a tool call, after a question-looking text, is working (0.293103ms)
|
||
✔ rule: newest entry is a tool result with no assistant text after it is working (0.256677ms)
|
||
✔ rule: a finished ordinary turn is idle, even if it says your move (0.238945ms)
|
||
✔ task: the first user message of the session, from text blocks (0.243874ms)
|
||
✔ task: a plain-string user content is accepted, whitespace collapsed and long text capped (0.234898ms)
|
||
✔ task: no user message in the log means null (shown as unknown), never a guess (0.247838ms)
|
||
✔ workspace: the live tmux pane path wins; the session cwd is the fallback; neither means null (0.340358ms)
|
||
✔ activeProject: basename of the nearest .git directory or .git file above the workspace; none means null (0.46937ms)
|
||
✔ scan: the written record carries task, workspace and activeProject (0.424461ms)
|
||
✔ registration: overrides task, project and workspace; every source says registration; registered carries the launch fields; the grouping column is untouched (0.511057ms)
|
||
✔ registration: empty task and null project/workspace leave the derived values in place; registered is still non-null (0.355025ms)
|
||
✔ registration: a record whose pid is gone is stale; derived values win, sources say derived, registered stays with alive false; a pid the probe cannot decide is not stale; pidAlive itself (0.861736ms)
|
||
✔ registration: no registration leaves the Gate A fields exactly as before, and registered is null (0.380305ms)
|
||
✔ loadRegistrations: a missing seatsDir gives empty lists (0.139476ms)
|
||
✔ loadRegistrations: one good record, one malformed JSON, one with an unknown field; a stray file under seatsDir is ignored (0.66136ms)
|
||
✔ matchRegistration: matches by sessionsDir, and by realpath through a symlink; sessionsDir null never matches; same seat name with a different sessionsDir does not match (fleet vs repo darkwing) (0.284284ms)
|
||
✔ scan: writes the registration override to disk; index.json carries registered and registrationErrors (0.611224ms)
|
||
✔ scan: a relative seatsDir throws ConfigError; an omitted seatsDir behaves as before (0.282498ms)
|
||
✔ scanAgent: waitingOnYou is true for waiting/error and false otherwise (0.421769ms)
|
||
✔ scanAgent: ageSeconds is computed from the injected now (0.229199ms)
|
||
✔ scanAgent: sessionFile null and state idle when sessions dir is empty but alive (0.135143ms)
|
||
✔ discoverRepoAgents: finds agents with a sessions dir, skips those without, sorted by name (0.321351ms)
|
||
✔ discoverFleetAgents: finds agents with a sessions dir, sorted by name, fleet tmux fields (0.392299ms)
|
||
✔ scan: writes per-agent files and index.json, rerun overwrites, no leftover tmp files (0.766827ms)
|
||
✔ scan: relative boardDir throws ConfigError (0.076061ms)
|
||
✔ CLI: scan with assume-alive liveness exits 0, prints board summary, writes board files (51.129199ms)
|
||
✔ CLI: missing config exits 2 with a refused: message (47.727488ms)
|
||
✔ CLI: unknown command exits 2 (47.883708ms)
|
||
✔ CLI: unknown --liveness value exits 2 (48.23578ms)
|
||
✔ panesRunPi: true when any trimmed line equals 'pi' (0.2978ms)
|
||
✔ panesRunPi: false for bash-only, claude, empty, or node-pi-style lines (0.104799ms)
|
||
✔ tmuxIsAlive: a pane running pi is alive (0.22103ms)
|
||
✔ tmuxIsAlive: session exists but pi has exited is not alive (0.089566ms)
|
||
✔ tmuxIsAlive: no such tmux session is not alive (0.065728ms)
|
||
✔ tmuxIsAlive: tmux could not be run at all is unknown (null), never assumed alive (0.085151ms)
|
||
✔ tmuxIsAlive: passes -L <socket> only when a socket is given (0.127259ms)
|
||
✔ parsePanes: one pane per line, command and optional tab-separated path (0.115255ms)
|
||
✔ tmuxInspect: reports the path of the pane running pi, not of a shell pane (0.094308ms)
|
||
✔ tmuxInspect: no pi pane, no session, or no tmux gives no workspace and the matching liveness (0.128674ms)
|
||
✔ loadSeen: missing file returns {} (0.186971ms)
|
||
✔ loadSeen: invalid JSON throws ConfigError (0.243003ms)
|
||
✔ loadSeen: a JSON array throws ConfigError (0.207626ms)
|
||
✔ loadSeen: a non-string value throws ConfigError (0.228098ms)
|
||
✔ markSeen: seen true adds the key and writes seen.json mode 0600, no leftover tmp files (0.415303ms)
|
||
✔ markSeen: seen false deletes the key (0.328459ms)
|
||
✔ markSeen: missing, empty, or non-string fields throw ConfigError (0.269807ms)
|
||
✔ markSeen: project containing '/' throws ConfigError (0.145128ms)
|
||
✔ markSeen: non-boolean seen throws ConfigError (0.138934ms)
|
||
✔ scanAgent: a seen mark matching the waiting session's lastTimestamp clears waitingOnYou (0.377236ms)
|
||
✔ scanAgent: a stale mark (agent wrote something newer) is not seen and waitingOnYou is true (0.30635ms)
|
||
✔ scanAgent: a working session with a matching mark is not seen (marks only apply to waiting/error) (0.299076ms)
|
||
✔ scanAgent: an error-state session with a matching mark is seen (0.308967ms)
|
||
✔ scan: index.seen and waitingOnYou reflect seen.json, which scan never rewrites or deletes (0.66311ms)
|
||
✔ scan: a corrupt seen.json makes scan throw ConfigError (fail closed) (0.271272ms)
|
||
✔ taskSetBy: a registered task carries the record's setter; a record without the field (pre-#1511) reads unknown; the value is not copied into registered (0.862758ms)
|
||
✔ taskSetBy: null whenever the task shown is not the registered one: no registration, an empty registered task, a stale registration; the field is always present (0.78407ms)
|
||
✔ taskSetBy: scan() reads the field from disk through the seat package (bounded there), writes it to the per-agent record and index, and an invalid on-disk value is a registrationError, never a row value (1.785439ms)
|
||
✔ isLoopbackHost: recognizes loopback hosts (1.152353ms)
|
||
✔ isLoopbackHost: rejects non-loopback hosts (3.89857ms)
|
||
✔ startServer: refuses a non-loopback host with ConfigError, never opens a socket (2.748706ms)
|
||
✔ startServer: serves page, healthz, and a rescanning /api/board (28.889627ms)
|
||
✔ startServer: a seatsDir registration overrides the row and index.registered reflects it (5.377742ms)
|
||
✔ startServer: /api/board returns 500 JSON with an error field when scan throws (2.098377ms)
|
||
✔ CLI: serve refuses a non-loopback host with exit 2 and a refused: message (51.243259ms)
|
||
✔ CLI: serve rejects a non-numeric --port with exit 2 (47.846871ms)
|
||
✔ CLI: scan still works after the async cli refactor (60.910632ms)
|
||
✔ CLI: live serve prints its URL and answers /healthz (69.704882ms)
|
||
✔ page.html: esc() escapes every HTML-significant character (0.673282ms)
|
||
✔ POST /api/seen marks a row; GET /api/board still shows it seen; seen:false clears it (8.658451ms)
|
||
✔ POST /api/seen without a JSON content-type returns 400 and does not write a mark (3.234982ms)
|
||
✔ POST /api/seen with invalid JSON returns 400 (1.603714ms)
|
||
✔ POST /api/seen with a body over 4096 bytes returns 400 (or resets the connection) and writes no mark (1.975787ms)
|
||
✔ POST /api/seen with a missing agent returns 400 (1.308636ms)
|
||
✔ POST /api/board returns 405; PUT /api/seen returns 405 (1.639763ms)
|
||
✔ CLI: scan --print marks a seen row with 's' and the summary line ends with 'N seen)' (55.580484ms)
|
||
✔ page.html: seenControl() escapes rec.project/agent/lastActivity, and the POST uses a JSON content-type (0.307759ms)
|
||
✔ page.html: has a collapsed Seen section that lists seen rows with the shared row builder (0.255742ms)
|
||
✔ page.html: each project has a Hide seen checkbox (default on) beside Hide offline, with a hidden-count note (0.156363ms)
|
||
✔ page.html: a project header reads "N of N" only while a checkbox hides rows (0.137229ms)
|
||
✔ page.html: every row shows Task and Active project, derived or the word unknown, with the workspace in the detail (0.286411ms)
|
||
✔ page.html: task and active project cells show their source via sourceTag(); the detail has a Registered row via registeredText(); SOURCE_LABEL maps registration to registered; every dynamic value in sourceTag/fromSource/registeredText is escaped (0.553952ms)
|
||
✔ POST /api/reply: runs agent-send.sh with -s from the registration, -S <host>:control-board, -m text plus the fixed trailer, no -L on the default socket, MOSAIC_TMUX_SOCKET stripped; answers delivered with the exit code and both streams (26.402292ms)
|
||
✔ POST /api/reply: a registration with a tmux socket adds -L <socket> (24.068721ms)
|
||
✔ POST /api/reply: a non-zero tool exit is a 200 with delivered false, the exit code and the stderr verbatim (28.658539ms)
|
||
✔ POST /api/reply: refusals before the tool runs: empty or blank or long text 400, unknown row 404, no registration 409, stale registration 409, no tmux session 409, bad JSON 400; the tool is never called (15.183644ms)
|
||
✔ POST /api/reply: a missing agent-send.sh is a 500 with the path in the error, not a crash (4.623164ms)
|
||
✔ replyToRow: DEFAULT_AGENT_SEND is the repository's tools/tmux/agent-send.sh and it is executable (0.167406ms)
|
||
✔ page.html: the reply box appears only where canReply() holds (live registration with a tmux session), the detail has a Reply row, the submit posts JSON to /api/reply, receipts and drafts survive a refresh, and every receipt value is escaped (0.573167ms)
|
||
✔ startServer: /api/board carries taskSetBy from a live registration and null for the derived rows (2.458634ms)
|
||
✔ page.html: the task cell and detail show who set a registered task via setByTag()/setByText(), both escaped, only from rec.taskSetBy; the reply gate does not read it (0.333717ms)
|
||
✔ Host/Origin guard: GET /api/board and POST /api/reply refuse a foreign Host, a wrong port and a cross-origin Origin with 403 JSON, before any scan or send, and never send CORS headers (8.211155ms)
|
||
✔ Host/Origin guard: loopback names on this port are accepted, with or without a same-origin Origin (27.32202ms)
|
||
✔ conversation routes (F16): a foreign Host, a wrong port and a cross-origin Origin get 403 before the reader runs, with no CORS headers (5.678278ms)
|
||
✔ every refusal code the reader can raise has an HTTP status (1.010148ms)
|
||
✔ conversation routes: catalogue, first page, next page and follow over HTTP; refusals map to 4xx with their code; nothing is written (47.746908ms)
|
||
ℹ tests 124
|
||
ℹ suites 0
|
||
ℹ pass 124
|
||
ℹ fail 0
|
||
ℹ cancelled 0
|
||
ℹ skipped 0
|
||
ℹ todo 0
|
||
ℹ duration_ms 628.581015
|