The Console is the local page served by the binary, separate from the public site. Build it, then run Playwright against fixture Command records:
cargo build --locked
cd console
npm ci
npx playwright install --with-deps chromium
npm testTHIRDSHIFT_BINARY can name another built binary. Each run uses an isolated
home folder and a free localhost port, and closes its own server and browser.
Node and npm are test tools only; the release build embeds its assets.
The README and public site's Console section share the PNGs in
site/screenshots/. After the build and browser setup
above, refresh both from the repository root:
node console/screenshots.cjsThe capture needs Python 3 for its fake gh. It opens the real Console in
Chromium at 1440 × 1200, using the Focus light Theme, and captures the full page
below the machine header. It creates sample Commands and Session spans in a temporary home,
answers GitHub reads with a local fake gh, and closes its browser and server
before removing that home. It reads no personal logs or GitHub data. Inspect
both images before committing them; the Console layout can change. The images
show Inbox with Command details and Waterfall with every call expanded.
Inbox rows without an issue or pull request show the repository above the Command name, including Security runs, Triage runs and Custom processes. Long repository names wrap on desktop and phone; the full Command subject remains available on hover and in Command details.
Live Fix runs and Review runs also show a clickable PR #number in the compact Working row, including Overview, for explicit and Pass-selected targets, locally and across the Fleet. Base branch CI repairs show their recorded Base branch before PR publication and retain it alongside the PR link afterwards. Older records without a target keep their repository and Command label.
For Console CSS and JavaScript development, serve the assets from this checkout:
THIRDSHIFT_CONSOLE_ASSETS=src/console/assets cargo run -- console --no-sign-inIn a debug build, /assets/* reads that directory with no-store; missing files
return 404. Open Console pages use their existing /events connection to swap
changed stylesheets in place after loading them, and reload for changed scripts,
usually within a second. No rebuild or manual reload is needed for asset edits.
Paths outside the directory and symlinks are refused. Custom Theme stylesheets
and fonts retain their usual handling.
Without the variable, and in release builds even when it is set, assets come from the binary and disk edits have no effect. Askama templates and Rust code stay compiled in and still require a build.
Tests load Playwright through tests/playwright.cjs, which gives every page a
5 s default timeout (#1391): a broken locator fails fast instead of waiting
30 s. A step that needs longer passes its own timeout.
The Resources page (9, or g e) shows a host snapshot and the configured
session admission decision, also available with thirdshift resources --json.
Reload to refresh it. Set [resources] min_available_memory_mb and
max_load_per_cpu_percent in User config; zero disables each limit.
Below the notification control on every reader page, a host indicator shows
whether another session can start under those limits. It refreshes every ten
seconds while the page is visible and links to Resources for measurements and
blocking reasons. With both limits disabled it says Host limits off; a
failed read says Host resources unavailable. Without JavaScript, reload
the page to refresh the indicator. This is a snapshot, not reserved capacity.
The Themes picker offers Make a Theme from this, starting from any built-in or custom Theme. Edit its JSON name and tokens, then Save / import JSON. Choose the saved name in the picker to apply it. Export JSON gives a document to copy into a file or paste into another Console; there is no file upload control. Both CLI commands use exactly the same format and validation:
thirdshift console theme export garden-dark > garden.json
thirdshift console theme import my-garden.json
thirdshift console theme list --json
thirdshift console theme my-gardenCustom Themes are ~/.thirdshift/console/themes/<name>.json. A minimal document:
{"name":"my-garden","base":"garden-dark","tokens":{"bg":"#142018","font-body":"mine.woff2, sans-serif"}}base is a built-in name with auto/light/dark mode (default focus-auto). Missing
tokens get that base's defaults, including tokens added in later releases. Export
includes every token. Names use ASCII letters, digits, hyphens and underscores,
up to 100 characters; list, import, export and altered built-in names are
reserved. Unchanged built-in exports import successfully without replacing them.
Invalid JSON, unknown fields/tokens and invalid values are refused with a reason.
Malformed files placed in the folder by hand are omitted from the picker/list.
Saving records a theme-saved Console log event with its actor; selecting records
theme-changed. Saving does not select the Theme.
Token values are strings. Each token accepts its values from any built-in Theme.
Colour tokens additionally accept hex colours (3/4/6/8 digits), transparent,
currentColor, or light-dark(<hex>, <hex>). Body size accepts 10–32px, radius
0–32px, heading weight 100–900, heading case none/uppercase/lowercase/capitalize,
and activity speed 0.1–60s. Effects and activity motion use the built-in values,
keeping motion tied to activity. Arbitrary CSS and URLs are refused. User Themes
are their owner's responsibility to check for contrast.
Font tokens (font-head, font-body, font-mono) accept a comma-separated list
of built-in families, system stacks (system-ui, sans-serif, serif,
monospace, ui-sans-serif, ui-serif, ui-monospace) and font filenames.
Copy .woff2, .woff, .otf or .ttf files by hand into the themes folder's
fonts/ directory, then use e.g. mine.woff2, serif. Filenames use ASCII letters,
digits, dots, hyphens and underscores, with no leading dot, up to 100 characters.
Fonts come from the Console's own address; other extensions, directories and
symlinks (including a symlinked fonts/ folder) are refused. Unknown or unavailable
families fall back to the supplied stack, then sans-serif (monospace for code).
To open your machine's Inbox locally without accounts, run
thirdshift console --no-sign-in and visit the localhost address printed on
stdout. To require sign-in, set the allowlist in ~/.thirdshift/config.toml:
[console]
address = "https://console.example.com" # used by console install
port = 8711
allowed_emails = ["you@example.com"]
fleet = [{ name = "other", address = "https://console.example.test" }]
other_accounts = "none" # or "readable", or ["conductor", "operator"]
theme = "focus-auto" # also focus-light or focus-darkFor a persistent service behind Caddy, Apache or nginx, see the manual's server installation instructions.
Fleet entries without a saved token are header links. Pair a machine with
thirdshift console fleet add <name> <address>, supplying its token on stdin;
console fleet list --json shows saved identity, scopes and last contact.
console fleet remove <name> deletes the entry and its Credentials token.
See Fleet for pairing and legacy link migration.
Each allowed address must contain exactly one @, a nonempty local part,
and a domain containing a dot, with no leading or trailing dot. Whitespace
is not allowed. Setup trims each comma-separated entry, but rejects empty
items, including trailing commas, and asks again with the invalid entry named.
Loading a User config with an invalid entry also fails and names the entry.
Run thirdshift console password --email you@example.com in a terminal to set
the password, then thirdshift console. Password entry is hidden and asks
twice. With exactly one allowed address, --email can be omitted. An empty
allowlist refuses to serve unless --no-sign-in is explicit. Both modes bind
only to 127.0.0.1. Use HTTPS at the machine's reverse proxy for sign-in: cookies
are always Secure, HttpOnly and SameSite=Strict (localhost is suitable for tests).
Accounts live in ~/.thirdshift/console-accounts.json, beside the Credentials,
with owner-only permissions. Passwords use argon2id; only hashes of session
tokens are stored. Changes use a file lock and atomic replace, including across
CLI and server processes. Sessions survive restarts and expire 30 days after
last use. Every action, including sign-in and sign-out, requires a CSRF token.
After five wrong passwords, an address gets one attempt per minute until a
successful sign-in or a new password resets the slowdown.
The Account page signs out the current browser. thirdshift console sign-out [--email <address>] [--json] ends every session for that address; setting a new password
also ends all its sessions. Removing an address from allowed_emails blocks
it and revokes its sessions, while retaining the password for later re-adding.
The server checks current settings on requests and while streams remain open.
thirdshift console log [--json] and the Console log page show the same
account events from ~/.thirdshift/console.log. They name the account but
never contain passwords, codes, session tokens or page views.
With a Resend key in RESEND_API_KEY or the Credentials, Set your password
on the sign-in page creates an account or resets its password by email code.
Only allowed addresses receive mail, from email.from, but every address gets
the same answer, without waiting for address-dependent delivery. Codes are
stored as argon2id hashes, last 15 minutes and allow
5 tries. An address may receive one code a minute and 10 in a rolling 24 hours;
these limits survive restarts and password changes. A correct code verifies
the address, sets its password, ends its old sessions and signs it in.
The Console log records codes sent and passwords set. Without a key the flow
is off; ask the owner to use thirdshift console password instead.
The header names this machine and links to those addresses, without fetching
them. The Inbox and its /events stream pair with thirdshift status. One
server observer checks records and log lengths every second; the page morphs
after a change notice, preserving the open section, selected row and scroll. If the
stream drops or is unavailable, the page polls every five seconds until it
reconnects. The heartbeat only beats for observed log growth, stays flat while
Silent and respects reduced motion. The tab counts Needs you and its favicon
marks urgent items (Slow alone is not urgent). Each row shows the same state
word as thirdshift status: Landing and Ready rows take their Theme's Landing
and Ready tag colours, Landing keeps Working's live-activity marker, and a live
row given a Stop shows Stopping with a drifting stripe until it ends. Pause
motion and reduced motion hold the stripe still.
The Inbox opens on Overview: Needs you, Ready for review, Landing and Working
show together, each under its heading and count, including empty groups.
Every group starts open. Click its heading, or focus it and press Enter or Space,
to fold its rows. The heading and count stay visible; counts and active runs
still include folded rows. Each browser keeps its group choices through live
updates, folder changes and reloads when storage is available. Without storage,
folding still works through live updates. Keyboard row navigation skips folded
rows, and the same controls work in every Theme and on phones. Without
JavaScript, groups start open and their native disclosures still work.
The folders on the left can filter to one section, including Done and Resolved.
Rows show state, subject, title, gist and age. A Spec with recorded Tickets also
shows 2 / 5 Tickets complete, including on phones and on the visible row of a
Pass that dispatched it. This counts recorded merged Tickets, including Tickets
closed before the run, and updates with the Command record. Fleet uses the owning
machine's records. The last count remains after the run ends; absent or empty
Ticket accounting shows no counter. Completing all Tickets still leaves Spec
review and Delivery to finish; the row's state and stage show where it stands.
Ready for review
subjects link to the recorded pull request and show its title from the batched
GitHub refresh, including Review runs without an issue. Until a title is known,
the PR link stays usable with “(title unavailable)”. Without a PR, rows keep
the issue subject and title, or the Command subject when neither exists.
Clicking the subject link opens GitHub directly; clicking elsewhere opens Details.
On a desktop, issue and pull request
numbers stay visible as long repository or Process subjects shrink; hover
the subject for its full text. The gist is where the
Command stands, such as review failed; a Pass without an issue adds the
issue it dispatched, its first security finding or its cause. Hover an ended
row's age for its finish time. The list never scrolls inside itself; the page does. The
folder is in the URL, such as /?section=done, so it survives a reload and
works without JavaScript; with none named, Overview opens. Hidden sections stay
hidden in Overview too.
The page opens with nothing selected and Command details closed. Click a row,
or select it and press Space, to open its details beside the list; they stay
open for the selected row as you move with j / k or switch folders. On a
phone the folders are a row of tabs and Command details open as a bottom sheet. Without
JavaScript, rows show in full, with their own Dismiss and logs links.
Command details open with the Command's issue subject and full issue title, before
Stop and Dismiss. The subject links to the issue, and long titles wrap. A
Command without an issue shows its Command subject alone; an unknown issue title
says “(title unavailable)”. The same recorded title is in
thirdshift show <command> --json. An ended, unsuccessful Command shows its full
failure reason immediately below the heading, before Stop and Dismiss, with URLs
as links. When its own cause is absent, it uses the first cause from failed
dispatched work. Successful or live Commands, and those with no cause, show no reason.
The cause also stays in the facts list below, one line per recorded fact, label
then value. A long value, or a fact with its own facts, such as stages,
the dispatched Run or run metrics, starts folded behind a one-line preview (a
title, a name or a count); open it to drill down, as deep as the record goes.
Session traces fold into one count. Folds stay as you left them while live
updates refresh the same Command. A file path shows on one line with its end
kept and a copy icon that copies the whole path.
Above Full Command log, Command details shows the last five lines of recent
Command output, reading at most 4 KiB. It updates with the page's existing live
connection, including while a Slow run keeps logging. Empty or unavailable logs
say so without hiding the details; the full-log link stays available.
Remote previews request that bounded tail from the owning machine through
/api/v1/log?tail=true, the twin of logs <command> --tail --json, and keep
only the current request's answer. The full Command log is fetched only when
its reader opens.
Each Needs you row has a Copy button at the end of its line for sharing its
problem with an agent. It copies the machine, Command selector, state, cause,
issue title, Harness, Tickets and any findings, with absolute issue, pull
request and Console log links, plus the thirdshift show '<command>' --json
command, without opening Command details. If clipboard access is unavailable,
a dialog selects the same text for copying by keyboard. The dialog keeps that
snapshot through live updates.
Dismiss is always visible on the row in Needs you and Ready for review,
beside Copy where available. d dismisses the selected row and selects the next
one, without opening Command details. An inline dismissal shows a temporary
Dismissed <subject> · Undo notice; Undo restores that Command's row. Notices
stay while hovered, focused, saving or showing an error. Command details also
keeps Dismiss and Undo. Both immediately
show “Dismissing…” or
“Taking back dismissal…” while the request is pending. The button stays disabled
through live updates to prevent duplicate submissions. A failed request shows
the error and enables the button for another try.
Ended rows show their recorded finish in this machine's time zone, to the minute, followed by an explicit relative age, such as “Ended 2026-10-23 19:03 EDT · 52 min ago”. Every date and time the Console shows reads the same way, including ones inside causes and Command details; records, CLI output and Session traces keep their full RFC 3339 instants. That age is time since finish, not run duration. Each nested Ticket run uses its own finish time, including in History. Refreshing recomputes the relative age and keeps the recorded instant. The information is rendered by the server and remains visible without JavaScript. Missing or invalid finish times say “End time unknown”; file modification times are not presented as recorded finishes.
Above its sections, the Inbox shows how many runs are active, such as
2 active runs, the same active_runs total as thirdshift status --json.
Each Command with live work anywhere in it counts once, whatever its group or
whether that section is open or hidden: a Spec run's Tickets, and the Spec run
or Ticket run a Pass dispatched, are part of that one. The live update refreshes
it as Commands start and end.
When no Commands are live on this machine, the Inbox explains that above its sections. It points to pull requests in Ready for review, and offers the last recorded skipped Pass reasons from Activity logs under Last recorded skipped Passes, and when each Pass last fired under Last fired Passes, both folded closed on each page load. Open either to read it; live updates keep it open while you read. Each bullet starts with its recorded date and time: the Activity log time for a skipped reason, or the saved Pass start for a last-fired entry. Repeated skips keep the reason's original recorded time while updating the last-fired time. These reasons are retained observations, not a check that the scheduler still runs. With none recorded, it says so. A live Command in Needs you, or in a hidden section, still counts as running.
Choose Turn notifications on in the Console header and allow the browser's permission prompt. This browser remembers the choice for this Console address; the same button turns it off across its open tabs. Keep a Console tab open (a pinned tab works): a rise in Needs you shows one browser notification with the count, machine and newest arriving item. The item's state comes first, then what it is about and its title, so a toast cut short still says what happened. Clicking it focuses the tab and opens the Inbox. Opening or reloading a tab never notifies for existing items, and several tabs share one notification per rise. Notifications work from the other Console pages too.
Notifications require HTTPS (or localhost), browser notification support, permission, browser storage and Web Locks for coordination between tabs. The control explains when these are unavailable; if permission is blocked, change it in the browser's site settings. There are no notifications with all Console tabs closed, and no service worker, Web Push or subscription to set up.
History and Security folds and the last-look time stay in this browser. Commands started since the previous visit are marked new, including those arriving while the page is open. Missing or malformed storage hides nothing. Replacing the installed binary closes open streams and re-executes the Console with its original arguments.
The available pages are Inbox (1), Lanes (2), Stages (3), Waterfall (4),
History (5), Logs (6), Security (7) and Cron jobs (8). Inbox through Security links
keep the page in ?page=. History includes every ended Command, including old
logs, and filters by repository and kind of outcome (merged, ready for review,
failed, plan and so on), not by each PR or error. Security shows retained findings
and Session traces. Older records without finding metadata still show their logs.
Pages give the high-level view first and open on demand. History and Security list one line per Command, like the Inbox; a click opens its Command details beside the list, and without JavaScript rows show in full. The Experiments list shows each Experiment's headline and status line, and a click opens its page. An Experiment page folds each suite's Verdict to its intervals, each suite's Trial grid to its task count, and What was compared and the Bar to their headings. Stages by repository folds each repository to its run count, and Logs folds each repository to its log count when it lists more than one. Folds stay as left through live updates.
Cron jobs (/cronjobs, or g then j) shows schedules and commands from the
Unix account running the Console's crontab -l. The same inventory is available
with thirdshift cronjobs [--json]. It selects active entries containing a
literal thirdshift word or an executable path ending in /thirdshift, including
commands after cd and wrappers. Shell scripts and variable-built executable
names are not inspected or evaluated. Comments and environment assignments
are omitted, and input after cron's first unescaped % is not searched.
Other users, system crontabs and other schedulers are outside this view.
Schedules retain cron's expressions and time zone; reload to see changes.
The page reads like a terminal crontab: one monospace line per entry, schedule
then command, with schedules highlighted in the current Theme. Long lines scroll
horizontally within the listing; focus it to scroll with the arrow keys.
No matching entries or no crontab shows an empty view; an unavailable crontab
command or a read failure reports an error. This view never changes the crontab
or starts a Command.
Scores (/scores, or g q) lists the sample by week and each criterion's
calibration, from the same retained records as thirdshift scores --json.
Enter five pass/fail Scores and reasons in a sample's form. The model's Scores
are withheld from the response until all five of yours are saved, using the
same lock and atomic write as thirdshift scores <pull request> --mine --json.
Score again opens another blind entry. Reload to see changes from other clients.
Bug origins (/bug-origins, or g u) lists proposed, confirmed and rejected
origins with their evidence, pull requests, Ticket-run attribution and settled
Eval task. Confirm, Correct (a pull request URL or number) and Reject use exactly
the operations behind thirdshift bug-origins <issue> --confirm,
--correct <pull request> and --none, including their refusal of concurrent
Scoring writers. Every form requires CSRF; both pages require sign-in unless
the Console runs with --no-sign-in. Confirmation proposes an Eval task and
starts no work. node --test console/tests/scoring.test.cjs checks navigation,
all four actions, escaping and 390 px after a build.
Lanes shows each Command and its child Runs on a shared time axis, over the last
3.5, 12 or 24 hours. The window is kept in hours=; bars clip to that window,
while their text retains the whole stage's time. Older Commands are on History.
Stages defaults to All runs, one grid across repositories. By repository
shows one grid per repository, each folded to its name and run count; the
browser remembers an explicit view choice.
Each Spec run stays with its exact Ticket runs, including repeated attempts on
the same issue. Groups are newest-started first by their owning run, and Ticket
runs are newest-started first within each Spec group. Live state and finish
times do not change that order. Dispatch ancestry stays visible.
Rows follow the Command record's stage order: implement through cleanup, then
the stages of other Command kinds. Unrecognised stage names follow all known
stages in alphabetical order.
Stages fills the window's width, beside the peek when one is open. More runs
extend the grid to the right on desktop and phones, with stage names fixed at
the left. Each cell's Usual range belongs to that run's repository.
Live updates keep the visible run, selection and focus; Newest runs returns
the relevant grid to its left edge. Historical Runs without stage metadata show
an unknown time, distinct from a stage that was not reached. Choose
a column's Run link to open its exact peek. Repeated stages retain every visit;
missing end timestamps say “time unknown” rather than inventing a duration.
Both pages use the observations from status --all --json. The Rust parity
test checks their stage times and Usual ranges against the CLI; the Playwright
time tests check scale in all windows, grouping, both views, preferences, live
insertions, keyboard access, Themes, 390 px and Stages at 1920 px.
Waterfall chooses one retained Command with live, died and ended chips, keeping
its exact selection in command=. The chips fold under a Runs line that names
the Command shown; open it to choose another. Each chip and the selected Runs
line include the retained issue title when available, shortened with an ellipsis
to keep the choice brief; hover the title to read it in full. Older records
without a title and Commands without an issue keep their existing labels.
A span tree replaces the old Run sections:
Command → Runs → stages → Sessions → model calls, turns, tools and sub-agents.
Child Runs sit under the stage that started them; reused Sessions appear only
under their first stage. Commands, Runs and stages start expanded, Sessions
folded. Calls enter the DOM only after their Session is expanded.
The same tree and selected span details are available from the CLI:
thirdshift show widgets#7 --spans --json
thirdshift show widgets#7 --span <span-id> --jsonThe tree uses the recorder's stable Command, Run and stage span IDs. Each row
has its kind, status, duration and bar on a shared axis. Errors carry their
message, running calls extend to now with a hatched bar, and estimated timings
are dotted and marked *. Converted Session tools keep the old scoped
Command-log estimates, including unknown times. Commands without traces still
show their Runs and stages.
The open Slowest calls list ranks ten tool and model calls in the timeline's
window; turns and sub-agents are excluded. Selecting a call reveals its row
and summary Peek. Ancestors in the Peek select their own span. The card shows
applicable timing, usage, cost, call and error facts, relative-position bars,
input/output and attributes, plus a conversation link and Copy CLI. Conversation
links open the Session because that reader does not yet anchor individual
records. Command details opens the existing Peek with Stop, logs and the PR.
Its Run metrics come from the same details as thirdshift show <command> --json,
including nested Ticket runs. Recorded zeroes stay zero; unavailable signals say
Missing, including older Ticket runs without retained metrics.
That Peek's Copy CLI copies its thirdshift show '<command>' --json line; if
the browser refuses the clipboard, the line is selected to copy by keyboard,
and stays selected through live refresh. The Dismiss line works the same way:
[Dismiss] thirdshift dismiss '<command>' [Copy CLI]. Each CLI line stays on one
line, cut off at the pane's edge, with the whole command on hover and in the
copy. Session trace links also stay on one line, trimmed from the start so the
end of each path, which tells them apart, still shows.
After a build on Linux, python3 console/reproductions/peek-cache-contention.py
checks that parsing another Command's cold Session trace does not delay cached
Command details.
Historical Session signals are cached in memory and in disposable
*.signals-cache.json files beside their Session traces, with per-trace
*.signals-cache.lock files coordinating CLI processes. Appends, replacements
and binary changes invalidate saved signals. A read-only logs directory still
works by parsing without saving a cache. Command records are never rewritten.
Zoom fits a parent span or a padded window around a call. The sticky axis keeps the zoom notice and Zoom out control visible; the slowest calls re-rank within the window. Folds, selection, zoom and Peek disclosures survive live refresh. On a phone, labels fill one row, bars and durations the next; revealed rows stay between the sticky axis and the Peek bottom sheet.
Within the tree, arrows and j / k move focus, Right expands or enters a
child, Left folds or returns to the parent, Home/End reach either end, and
Enter/Space opens the Peek. Outside the tree, the Console's usual keys apply.
Rust route/CLI parity and timing tests live in tests/console/spans.rs and
tests/console/waterfall.rs. The Playwright span and Waterfall tests cover
folding at scale, keyboard navigation, Peek, zoom, live refresh, bar geometry,
escaping and 390 px. Retained timing checks also run with
python3 console/reproductions/waterfall-fractional.py,
python3 console/reproductions/waterfall-stage.py, and
python3 console/reproductions/waterfall-trace-axis.py after a build.
Experiments (/experiments, or g then x) uses Running, Needs you and Done
groups. Its Experiment page opens with What it tests: the Candidate's changes
from the Baseline (build with its commit title, Harness, Model, Effort or
Harness version), the suites it is judged on and the Bar's margin, or, when the
arms match, that it tests no change and shows run-to-run variation. It then
shows the live Trial grid, per-suite Verdict
intervals, pending rechecks, Arm comparison and folded Bar. A Trial or Eval task
opens the Peek; each retained attempt links to its Session log or Session trace.
Both pages use the same view as thirdshift experiments [<id>] --json, including
the saved refs/title, end time, resume command, active Trials and session IDs.
thirdshift experiments <id> --session <session-id> [--json] reads a listed
session; JSON returns its id, trace flag and contents.
These pages poll every two seconds independently of Command-record events, and
retain folds, filters and the open Peek. Rechecks stay in the CLI; Copy command
copies the current Trial's command. node --test console/tests/experiments.test.cjs
checks live updates and interaction at 390 px after a build.
The Experiments list also shows proposed Eval tasks; each Experiment page shows
proposals for its suites. Approve one there or with
thirdshift evals approve <suite> <task> [--json]. Both change only the manifest's
state and start no work. Approval affects future Experiments; an existing
Experiment keeps its fixed Eval tasks on resume.
Process Trial Session traces are checked for the real fix's identifiers. Pending
hits appear on the Experiment page with a Session evidence link and hold the
Verdict. Dismiss a false positive or confirm a leak with a reason there, or use
thirdshift experiments <id> --leak <hit-id> confirm|dismiss --reason '…'.
Both retain the reviewer and time; dismissals stay auditable, and confirmations
make the Trial Void. Decisions require a stopped Experiment and act scope;
the API twin is POST /api/v1/experiments/{id}/leaks/{hit} with form fields
decision and reason.
j / k select headings and visible rows, wrapping at either end; on the
Inbox, open Command details follow the selection. Space toggles a row's
peek, or folds a heading; Enter opens that row's full Command log. d dismisses
a selected dismissible row and selects the next row. z folds
the current History or Security section. Escape closes help, jumps or the
peek, one layer at a time, then clears selection. ? opens the key list. g
opens the jump list: i Inbox, b Lanes, t Stages, f Waterfall, h History, o Logs, s Security, j Cron jobs,
c Console log, a Account, or an Inbox section's displayed letter, which
opens its folder. Text fields keep their normal keys. The peek starts closed
and sits beside the list on a wide screen or at the bottom of a phone.
For an exact older Command, show and logs also accept the absolute record
or Command-log path. The Console adds ::name to a record path to distinguish
a dispatched Run from its enclosing Command. A retained Session-trace path can
select a historical Command without a record; logs --session accepts an exact
path from that Command's Session traces as well as a session kind.
Page 6, /logs, lists every Command log and Session trace in the current
commands/ layout, including Security logs, and marks the live ones. Every
Command row links to its Command logs and Session traces, including child Ticket Runs.
Older sessions/ folders are not linked. The same inventory is available with
thirdshift logs or thirdshift logs --json; thirdshift logs --file <id>
reads an exact listed file and accepts --follow.
thirdshift logs --command <id> --json selects the same Command and child Run
logs as its Console link; the inventory's owners field lists those exact IDs.
Command logs are plain text. Session traces from all six Harnesses render their normalized messages, tool calls and results as conversations with folded tool groups and Bash, Edit, Read and Grep views. Unfamiliar events stay visible as plain raw lines. Older Claude and Codex Session traces retain their conversation rendering and collapsed thinking. A live reader uses its own SSE stream to append escaped HTML from a byte offset. Reconnecting resumes from the last event's byte offset; incomplete live lines wait until their newline arrives. The Follow newest line checkbox lets you read back while the Session grows. The Playwright log test checks appends, retained DOM identity, folding across batches, incomplete lines, reconnection and following on a phone viewport. When streaming is unavailable, the reader polls finite appended-line batches from its last byte offset without replacing the rendered conversation.
Ready Runs dispatched by a Pass have their own Ready for review row in both
status and the Console. The complete Pass tree remains available in History
and show. Run python3 console/reproductions/ready-dispatch.py after a build
to check both Inbox views.
A live Command's Details offers Stop beside its equivalent CLI command.
Like Dismiss and Show, the CLI stays on one line, with long commands cut off
and a Copy CLI button that copies the full command. Hover for the full text.
The confirmation page explains needs-triage, offers Requeue instead
(thirdshift stop --requeue), and requires the current account's CSRF token.
Both actions use the CLI's Stop, including its process identity checks and
refusal to stop a single Ticket's Run. The Activity log names the Console
and the signed-in email; localhost mode records localhost (no sign-in).
The page shows “still ending” and a stopping bar until cleanup records the
outcome, then lists the branches and pull requests left on origin.
It updates through /events, with polling if the stream disconnects.
The stopping bar respects reduced motion. HTTP tests use real Commands
with a waiting fake Harness and fake GitHub; Playwright checks confirmation,
cancellation, live controls and pushed completion on a phone.
thirdshift stop <command> [--requeue] --json waits quietly and returns the
outcome, issue_branches and pull_requests as one JSON object.
On Linux after a build, python3 console/reproductions/session-stop.py checks that
a live Session reader's Details link reaches its exact Command's Stop.
Claude Edit events include old/new text and show unified diff excerpts. For Codex,
thirdshift reads the thread's native session file after exit, under
$CODEX_HOME/sessions (default ~/.codex/sessions). When the current turn's edits
match the stream in order and file path, the Session trace keeps each patch as
Codex's record of the edit. The reader replaces its placeholder with that diff,
including in an open live reader. Shell edits and missing, unreadable, changed-format
or mismatched session files keep the fallback. The format is unpublished, so this
evidence is optional and never changes a Run's outcome.
Run cargo test --test console codex_patches for the fake-Codex regression and
node --test console/tests/logs.test.cjs for the live-reader checks after building.