Skip to content

Spec: the Console, a web page for each machine's Commands #776

Open
16 of 16 issues completed
Open

Description

@JacobStephens2

From the #711 grilling. The decision record is .scratch/grill-with-docs/711-live-runs.md; see its "Settled" list, Rounds 6 and 7 included. This Spec builds on #711, the CLI half, which has shipped: the Command record and status, show, logs and stop [--requeue]. The look (Q27) was decided from the prototype on the branch prototype/711-console; see Further Notes.

Problem Statement

#711 gives a terminal everything it needs to see and Stop a machine's Commands. Away from a terminal, especially on a phone, you still can't:

  • see what needs you and what's fine;
  • tell a Run that's slow from one that's stuck;
  • read a Session log as it grows;
  • stop a Run that started too early.

thirdshift status lists Commands, but it doesn't say which of them need you.

Two machines run thirdshift, thirdshift and stephens. Setting up a page on a new one shouldn't take more than a command and a paste, and someone else's machine shouldn't need anything of yours. A page you look at every day should also look the way you like.

Solution

Each machine serves its own Console: a web page at an address of its own, here console.thirdshift.app and thirdshift.stephens.page. It opens on the Inbox, which groups the machine's Commands by what each asks of you:

  • Needs you: Silent and Slow Runs first, then Commands that died or failed, Stops left for triage, and Security findings waiting on you;
  • Ready for review;
  • Landing;
  • Working;
  • Done;
  • Resolved.

A stall is silence, not a long stage. A Run is Silent when its logs stop growing, and Slow when it has gone past its stage's Usual range while still logging.

Seven pages sit a level above the Inbox's sections: 1 Inbox, 2 Lanes, 3 Stages, 4 Waterfall, 5 History, 6 Logs, 7 Security. A live Command can be stopped from any of them, with a confirm step.

The page has one layout and many Themes. Focus, the default, follows the system's light or dark setting, and Plain, Press, Signal, Garden and Factory ship beside it. You can make your own Themes, with your own fonts, and the chosen Theme is saved in the User config, so it's the same in every browser.

The page sits behind a sign-in by email and password. Only addresses allowed in the User config may sign in, and an address proves itself with a code sent by email. A Console log records sign-ins and account changes, and the Activity log records who stopped what.

The page does nothing the CLI can't: every view and action is also a command. thirdshift status shows the Inbox's groups and order too. thirdshift console serves the page from 127.0.0.1, with Caddy or Apache in front. thirdshift console install prints one script for sudo that sets everything up, and Setup offers it.

User Stories

The Inbox:

  1. As the Day shift, I want to open my machine's Console on my phone, so that I can see what the factory is doing without a terminal.
  2. As the Day shift, I want the Console to open on an Inbox that groups Commands by what they ask of me (Needs you, Ready for review, Landing, Working, Done, Resolved), so that what needs me is never below what's fine.
  3. As the Day shift, I want Needs you open on load and every other section foldable, so that the first screen is what needs me.
  4. As the Day shift, I want live problems first in Needs you (Silent, then Slow), then Commands that died, failed, were Stopped to triage, or have a Security finding waiting, each newest first, so that a Run I can still save comes before one that has already ended.
  5. As the Day shift, I want an ended problem to leave Needs you when a later Command on the same issue starts, so that I don't see what someone has already acted on.
  6. As the Day shift, I want an ended problem to leave Needs you after inbox.days, 7 by default, so that a long weekend doesn't bury the Inbox.
  7. As the Day shift, I want Done to show the last day and Resolved to reach back inbox.days, so that I can find what a Run notification was about.
  8. As the Day shift, I want each row to show a state word, never colour alone, plus the issue and title, a one-line now-line and the age, so that I can scan it on a phone and in any Theme.
  9. As the Day shift, I want Commands failing with the same cause shown as one item with a count, so that one broken base doesn't fill the Inbox.
  10. As the Day shift, I want each Run's Harness, Model and Effort, and its stage with its age, such as "CI watch · 52 min", so that I see what's running at a glance.
  11. As the Day shift, I want child Runs nested under their Command (a Pickup run, its Spec run, then its Tickets), so that I see the whole tree.
  12. As the Day shift, I want a Spec run's Tickets shown as a plan checklist: running, merged, failed, waiting on named blockers or not started, so that I see why it's slow.
  13. As the Day shift, I want a Command whose process is gone with no outcome shown as "died at ", so that a crash or reboot is visible.
  14. As the Day shift, I want each repository's idle Passes shown with their latest skip reason, so that I see why Weeding or Fencing waits.
  15. As the Day shift, I want the Inbox to update itself as Commands start, change stage and end, without a reload, so that I can leave it open.
  16. As the Day shift, I want the page to keep working if the live connection drops, by checking again at intervals, so that a flaky network on my phone doesn't freeze it.
  17. As the Day shift, I want the tab title to say "‹n› need you", with a dot on the favicon when something is urgent, so that I notice from another tab.
  18. As the Day shift, I want rows that are new since I last looked marked as such, so that I see what changed overnight.

Silent and Slow:

  1. As the Day shift, I want a Run whose Session log and Command log have both stopped growing for inbox.silent_minutes (10 by default) marked Silent, so that a stuck Run stands out.
  2. As the Day shift, I want a Silent Run to escalate in words at three times the silent time ("silent 45 min · past 30"), so that I can tell how long it has been stuck.
  3. As the Day shift, I want a Run past the top of its stage's Usual range, but still logging, marked Slow ("slower than usual (12–17 min), still working"), so that I don't mistake slow for stuck.
  4. As the Day shift, I want a Slow Run to escalate at twice its usual time ("3× usual"), so that a badly slow one rises.
  5. As the Day shift, I want the Usual range worked out per repository, from the middle half of that stage's last 30 times, and shown only once there are 3, so that chart35's long checks don't make thirdshift's look fast.
  6. As the Day shift, I want a stage that waits, such as CI watch or Self-merge, to write a line every logs.waiting_line_minutes (5 by default), so that waiting on CI never reads as Silent.
  7. As the machine's owner, I want status, the page and Setup to warn when the waiting line isn't more often than the silent time, with thirdshift using half the silent time instead, so that a typo doesn't turn every CI watch red.

The pages:

  1. As the Day shift, I want seven pages (Inbox, Lanes, Stages, Waterfall, History, Logs, Security) switched with keys 1 to 7 and kept in the URL, so that I can link to one and get back to it.
  2. As the Day shift, I want a Lanes page showing each Command as a lane of its stages over the last 3.5, 12 or 24 hours, so that I see how long each step took.
  3. As the Day shift, I want a Stages page that sets each stage's time beside its Usual range, Run by Run, so that I can tell whether today is normal.
  4. As the Day shift, I want a Waterfall page showing one Command in depth, from Run down to tool call, so that I see where its time went.
  5. As the Day shift, I want a History page of every ended Command, newest first, filtered by outcome and repository, back to the oldest logs, so that I can look back as far as the logs go.
  6. As the Day shift, I want a Logs page listing Session logs and Command logs, live ones marked, so that I can open any log from one place.
  7. As the Day shift, I want a Security page of Security runs, their findings and their sessions, behind the sign-in, so that I can follow Security runs too.
  8. As the Day shift, I want a peek with a row's details beside the list, or as a bottom sheet on a phone, closed on load, so that I can look without leaving the Inbox.
  9. As the Day shift, I want one Command's details to show everything thirdshift show does, so that I can see it all in one place.

Session logs:

  1. As the Day shift, I want to read a Command log on the page, so that I can see what it printed.
  2. As the Day shift, I want to read a Session log rendered as a conversation, with the agent's messages, tool calls and results, so that I can follow what the agent did.
  3. As the Day shift, I want consecutive tool calls folded into one group I can open, so that a long session stays readable.
  4. As the Day shift, I want thinking collapsed by default, so that the reasoning doesn't drown out the actions.
  5. As the Day shift, I want Bash commands, file edits (shown as diffs), file reads and searches each rendered in their own way, so that each tool is easy to read.
  6. As the Day shift, I want anything the page doesn't know how to render shown plainly, never hidden, so that nothing in a log is lost.
  7. As the Day shift, I want a live Session log to add new lines at the bottom as they arrive, without redrawing what's there, so that a long log stays fast.
  8. As the Day shift, I want to choose whether the view follows the newest line, so that I can read back while it grows.
  9. As the Day shift, I want both Claude and Codex Session logs rendered, so that it doesn't matter which Harness ran.
  10. As the Day shift, I want a Ticket's Run and its Session logs reachable from its Spec run, so that I can follow one Ticket.

Stop:

  1. As the Day shift, I want a Stop button on each live Command, and only while it's alive, so that I can stop a Run that started too early.
  2. As the Day shift, I want Stop to ask me to confirm, saying the issue will be labelled needs-triage, with a "requeue instead" choice, so that a mis-tap on my phone doesn't cost an hour of work.
  3. As the Day shift, I want the page to show the Command still ending, then its outcome and anything left on origin, so that I know the Stop worked.
  4. As the Day shift, I want the Activity log to say a Stop came from the Console and from which account, so that the repository's history says who stopped it.

Moving around:

  1. As the Day shift, I want j and k to walk rows and section headings and wrap, space to fold a heading or open the peek, enter to open the full log, and esc to close one layer at a time, so that I can work the Inbox from the keyboard.
  2. As the Day shift, I want g plus a letter to jump to a section or page, z to fold the current section, ? to list every key and Shift+T to open Themes, so that nothing needs the mouse.
  3. As the Day shift, I want to hide the sections I don't use, so that the Inbox shows only what I care about.
  4. As the Day shift, I want the header to name the machine and link to my other machines' Consoles, so that I can check the other one in one tap.
  5. As the Day shift, I want each page to show the CLI command that does the same thing, such as thirdshift stop chart35#239, so that I can do it over SSH or hand it to an agent.

Themes:

  1. As the Day shift, I want the Focus Theme by default, following my system's light or dark setting, so that what needs me carries the most weight.
  2. As the Day shift, I want to choose among Focus, Plain, Press, Signal, Garden and Factory, each as auto, light or dark, so that the page suits me and the time of day.
  3. As the Day shift, I want my chosen Theme and hidden sections saved in the User config, so that every browser I use on this machine looks the same.
  4. As the Day shift, I want to make a Theme in an editor on the page and have it saved to my themes folder, so that I can make the Console my own.
  5. As the Day shift, I want to export a Theme as JSON and import one, on the page and with thirdshift console theme import and export, so that I can move it to my other machine.
  6. As the machine's owner, I want to copy font files into ~/.thirdshift/console/themes/fonts/ and use them in a Theme, served from the Console's own address, so that I'm not limited to the built-in fonts.
  7. As the Day shift, I want motion only where thirdshift is doing something (the Working spinner, a heartbeat dot when a log moves, the live log cursor, the stopping bar, the running stage), so that anything moving means activity.
  8. As the Day shift, I want a Pause motion control, so that I can stop every animation.
  9. As the Day shift, I want every built-in Theme to pass AA text contrast, so that every Theme is readable.
  10. As the machine's owner, I want thirdshift console theme <name> and thirdshift console sections to set what the page sets, so that the page does nothing the CLI can't.

Accounts and sign-in:

  1. As the Day shift, I want to sign in with my email and a password, so that nobody else can see my private repositories or unfixed vulnerabilities.
  2. As the machine's owner, I want only the addresses listed in the User config able to sign in, so that I decide who may.
  3. As someone on the allowlist, I want to create my account by entering my email, getting a code and setting a password, so that I don't need the machine's owner to set it for me.
  4. As someone on the allowlist, I want to reset a forgotten password with the same flow, so that I'm never locked out.
  5. As the machine's owner, I want the page's answer to an email to be the same whether or not the address is allowed, so that it never reveals who may sign in.
  6. As the machine's owner, I want codes to last 15 minutes, allow 5 tries, and be sent at most once a minute and 10 times a day per address, so that the flow can't be abused.
  7. As the machine's owner, I want sign-in to slow to one try a minute after five wrong passwords, so that guessing is too slow to work.
  8. As the Day shift, I want to stay signed in for 30 days from my last visit, so that my phone doesn't ask every time.
  9. As the Day shift, I want to sign out, and setting a new password to end my other sessions, so that a lost phone can be cut off.
  10. As the machine's owner, I want taking an address off the allowlist to end its sessions and block it at once, while its account is kept, so that the list is the one switch and a mistaken edit costs nothing.
  11. As the machine's owner, I want thirdshift console password --email <address> to set any allowed account's password, so that accounts work without email.
  12. As the machine's owner, I want thirdshift console sign-out --email <address> to end that account's sessions, so that I can act from a terminal.
  13. As the owner of a machine with no Resend key, I want the email flows turned off, with the page saying to ask the owner to run the password command, so that sign-in still works.
  14. As the machine's owner, I want a Console log of sign-ins, failed sign-ins, codes sent, passwords set, sign-outs and look changes, shown on the page and by thirdshift console log, so that I can see who has been in.
  15. As the machine's owner, I want page views not recorded, so that the log guards the machine without watching its readers.
  16. As the machine's owner, I want thirdshift console to refuse to start with nobody on the allowlist, unless I pass --no-sign-in, so that it can't be served open by mistake.
  17. As someone on a laptop or using an SSH tunnel, I want --no-sign-in to serve the page to localhost only, with no accounts, so that I can use it without setting up sign-in.

Setting up:

  1. As the machine's owner, I want thirdshift console install to see whether Caddy or Apache is running and print one script for sudo, so that setting up a new server is one command and one paste.
  2. As the machine's owner, I want that script to write a system service running as me, the proxy's site file and the HTTPS step, then start everything, so that the Console survives reboots and log-outs.
  3. As the owner of an Apache machine, I want the site file to stream live updates through Apache and get its certificate from certbot --apache, as my other sites do, so that it fits how that machine already works.
  4. As the owner of a machine with neither Caddy nor Apache, I want instructions for an SSH tunnel, and a README section for nginx, so that I can still reach it.
  5. As someone setting up a machine, I want Setup to ask whether to run the Console, its whole address and the allowed emails (defaulting to my Run notification address), then offer console install, so that the Console is part of setting up thirdshift.
  6. As the machine's owner, I want the Console to keep running across thirdshift update, restarting itself on the new binary, so that it never serves from a stale one.

The CLI and agents:

  1. As the Day shift in a terminal, I want thirdshift status to show the Inbox's groups in the Inbox's order, with the Silent and Slow words, so that the terminal and the page never disagree about what needs me.
  2. As an agent, I want status --json to give each Command its group and health, and show --json each stage's Usual range, so that I can read what the page shows.
  3. As an agent, I want nothing on the page that the CLI can't do, enforced by a test, so that I never need the page.
  4. As the machine's owner, I want the page to load nothing from other sites, fonts included, so that it works offline behind the proxy and leaks nothing.

Implementation Decisions

  • The process. thirdshift console serves the page on 127.0.0.1, at a port from the User config, behind the machine's proxy (ADR 0016).

    • It runs on axum, and its async runtime stays inside the Console; the rest of thirdshift stays blocking.
    • It re-executes itself when thirdshift update replaces the binary.
    • It reads the Command records, Activity logs, Command logs and Session logs through the same code status, show and logs use.
    • It runs as the machine's owner, so it reads the logs where they are and nothing is opened to the proxy's user.
  • The User config.

    • A [console] section holds:
      • the Console's address and port;
      • the allowlist of emails that may sign in;
      • the addresses of the owner's other Consoles, which the header links to (nothing is fetched from them);
      • the chosen Theme;
      • the hidden sections.
    • inbox.days = 7 sets how long an ended problem stays in Needs you and how far back Resolved reaches. Done is one day.
    • inbox.silent_minutes = 10 sets when a Run is Silent. It escalates at three times that.
    • logs.waiting_line_minutes = 5 sets how often a waiting stage writes a line. When it isn't less than the silent time, status, the page and Setup warn, and half the silent time is used.

    The inbox.* and logs.* settings are read by the CLI as well as the page, so they sit outside [console].

  • The Inbox's model lives in the shared blocking code that status uses, so the page and the CLI compute the same thing. It is a grouping of Commands:

    • Needs you:

      • live problems first: Silent, then Slow;
      • then, newest first:
        • Died: the process is gone with no outcome;
        • Failed;
        • Triage: a Stop that left the issue needs-triage;
        • Finding: a Security finding waiting on a decision.

      An ended item leaves when a later Command on the same issue starts on this machine, or once it's older than inbox.days. Nothing is read from GitHub, and there's no acknowledge, shelve or steer. Failures with the same cause become one item with a count.

    • Ready for review: ended ready, with its pull request; a Pass's dispatched ready Run shows as its own row.

    • Landing: live Runs at CI watch or Self-merge. One already in Needs you points there instead of appearing twice.

    • Working: every other live Command, with a Spec run's Tickets as a plan beneath it.

    • Done: Commands that ended in the last day and aren't in Needs you.

    • Resolved: Commands that ended from one day to inbox.days ago, with nothing waiting.

  • Health.

    • Silent: a Run in a stage whose Session log and Command log have both stopped growing for the silent time.
    • Slow: a Run whose stage has gone past the top of its Usual range.
    • Usual range: the 25th to 75th percentile of that stage's times over the last 30 Command records in the same repository on this machine, and none with fewer than 3.

    Escalation is in words: "silent 45 min · past 30", "past usual 12–17 min", "3× usual".

  • Waiting lines. CI watch and Self-merge write a Command log line at least every logs.waiting_line_minutes while they wait, such as "CI on f7c3940: 3 still running out of 17 checks", even when the count hasn't changed. Today CI watch writes only when the count changes.

  • The CLI's side.

    • thirdshift status:
      • shows the Inbox's groups in the Inbox's order, with state words and the Silent and Slow wording;
      • --json gives each Command its group and its health;
      • --all still adds the history.
    • show --json gains each stage's Usual range.
    • New commands:
      • console theme <name>, console theme list, console theme import <file> and console theme export <name>;
      • console sections [--hide <section>…] [--show <section>…].
    • Unchanged from earlier decisions:
      • thirdshift console, with --no-sign-in;
      • console password [--email <address>], which prompts twice and doesn't echo;
      • console sign-out [--email <address>];
      • console log [--json];
      • console install.
  • Parity with the CLI. Each route is paired with the CLI command whose output holds its data:

    • Inbox: status.
    • Lanes, Stages and History: status --all --json.
    • Waterfall: show --json and logs.
    • Logs and the log views: logs.
    • Security: status --all --json and show --json.
    • Stop: stop.
    • The Console log: console log.
    • Setting the Theme: console theme.
    • Hiding sections: console sections.
    • Theme import and export: console theme import and export.

    A test walks the routes and fails on any route without a pair. Each page shows its CLI equivalent. The HTTP routes are for the page only and promised to nobody.

  • What the page writes. It writes only three things:

    • a Stop;
    • its own look under [console]: the chosen Theme and the hidden sections;
    • Theme files in the themes folder.

    It never writes the allowlist, the addresses, the inbox.* or logs.* settings, or any other section. Config writes keep the file's comments and layout, as Setup's do. Each write goes in the Console log, naming the account. The look is per machine, the same for every account. Per browser, in local storage, are "new since you last looked", Pause motion, what's folded and the peek. All of those are conveniences, and losing them hides nothing. ADR 0016 is amended to match.

  • Pages. There are seven, a level above sections:

    1. Inbox: the groups above.
    2. Lanes: each Command a lane of its stages, over a 3.5, 12 or 24 hour window. Older Commands are on History.
    3. Stages: the stage grid. Rows are stages and columns are Runs, live ones pinned first. Each cell gives a state and its time beside the row's Usual range.
    4. Waterfall: one Command from Run to stage to tool call. Session log lines carry no time, so each tool call's time comes from matching it to its HH:MM:SS line in the Command log.
    5. History: every ended Command, newest first, filtered by outcome and repository.
    6. Logs: every Session log and Command log, live ones marked, with a reader.
    7. Security: Security runs, their findings and their sessions.

    Keys 1 to 7 switch pages, and the page is in the URL as ?page=. The peek is the details beside the list on a wide screen, or a bottom sheet on a phone, and it's closed on load.

  • The keyboard model comes from the prototype:

    • j and k walk rows and section headings, and wrap.
    • space folds a heading, or opens or closes the peek on a row.
    • enter opens the full log.
    • esc closes one layer at a time, then hides the selection mark.
    • g plus a letter jumps to a section or page; a tooltip lists them.
    • z folds the current section.
    • ? lists every key.
    • Shift+T opens Themes.
    • Moving the selection never re-renders the list.
  • Themes (ADR 0018). There is one layout, and a Theme is only tokens, applied as CSS custom properties. It can't change what the page shows, its order, or where things are. This shape comes from the prototype, trimmed:

    {
      id: "press", name: "Press",
      colors: { bg, panel, panel2, selBg, text, head, dim, rule, link,
                urgent, urgentText, look, lookText, working, workingText, landing, ready, readyText, done,
                proof, ramp, primary, term, termText, termDim, sheet, sheetText,
                glowWorking, glowUrgent, glowLook, ink1, ink2, ink3, ink4 },
      fonts: { head: "Big Shoulders Display", body: "JetBrains Mono", mono: "JetBrains Mono" },
      type: { headStyle: "display", headCase: "none", labelCase: "uppercase", bodySize: 13 },
      shape: { radius: 0, density: "compact" },
      tags: "filled",
      effects: { scanlines: true, misreg: true, halftone: true, glow: true, colorbar: true, crops: true, tilt: true },
      motion: "full",
    }
    • Text on a solid fill is black or white, whichever reads better, worked out from the fill.
    • Built-ins are compiled in: Focus (the default), Plain, Press, Signal, Garden and Factory, each as auto, light and dark.
    • Focus is "weight follows need". Plain is the quiet inbox look. Press is thirdshift.app's press look in plain words. Signal uses colour only for state, in Okabe–Ito colours. Garden and Factory are themed; Factory takes after Factorio.
    • Your own Themes are <name>.json files in ~/.thirdshift/console/themes/. The page's editor saves them there.
    • Importing checks every token and gives defaults for any added later, so older exports keep importing.
  • Fonts.

    • Built-in Themes' fonts are embedded as Latin-subset woff2 files at the weights they use, all under the SIL Open Font License: Big Shoulders Display, JetBrains Mono, Inter, Caprasimo, Nunito, Barlow Condensed, Titillium Web, Atkinson Hyperlegible, IBM Plex Sans and IBM Plex Mono.
    • Your own fonts are woff2, woff, otf or ttf files in the themes folder's fonts/. The Console serves them from its own address. It serves only those file types from that folder, and never follows a link out of it.
    • A Theme names a font as a built-in family, a system stack or a file in fonts/. Any other name falls back to its stack.
    • Fonts are copied in by hand. The page never uploads files.
  • Motion belongs to the layout, not the Theme, and is only for thirdshift's activity.

    • Kept:

      • the Working spinner;
      • a heartbeat dot that beats when a log moves;
      • the latest line flashing as it arrives;
      • the live log cursor;
      • the stopping bar;
      • stripes on the running stage;
      • the Silent alarm, which pulses three times and then holds.

      Factory's conveyor and Garden's bed move only while a Command is live.

    • Pause motion stops all of it, to meet WCAG 2.2.2, and prefers-reduced-motion is respected.

  • Visual calm. There are no coloured bars between sections; the title's bar is the only one. With Needs you open, every section heading fits above the fold on a 14" laptop. It works at 390 px with no sideways scroll.

  • HTML is rendered on the server, with templates checked at compile time.

    • Libraries: htmx 2.0.7, its server-push extension 2.2.3 and idiomorph 0.7.4 (all 0BSD), plus a small hand-written ES module. The module does the keyboard, the peek, following the tail, the Theme editor, Pause motion and copying.
    • Packaging: these and the fonts are vendored in the source and compiled in with include_dir, because the crate publishes only its source, Factory skills, README and licence.
    • Nothing from outside: no npm in the release build, no CDN, and nothing loaded from another site.
  • Live updates.

    • One server-push stream tells the page the Commands have changed, and the page re-renders with a morph rather than replacing anything.
    • Each open Session log has its own stream, which sends rendered HTML for lines appended since a byte offset.
    • When a stream drops, the page falls back to polling.
    • The heartbeat comes from log growth that the server sees.
  • Rendering Session logs.

    • The page reads Claude's stream-json output and the codex exec --json output that thirdshift keeps, and turns Codex items into the same shape.
    • Renderers are registered per tool name over a plain fallback. Bash, Edit with a unified diff, Read and Grep come first.
    • Consecutive tool calls fold into one group, and thinking is collapsed.
    • Unknown event types are shown plainly.
    • Only appended lines are rendered; a whole log is never redrawn.
    • The Claude types may be based on Vibe Kanban's (Apache-2.0, with attribution) or written from the shapes in real logs. Either way, they're tested against real logs.
  • What's shown.

  • Console accounts live in a file of their own beside the Credentials, readable only by the owner, written by atomic replace. Each account holds:

    • an email;
    • an argon2id password hash;
    • whether the address is verified;
    • hashed session tokens with their last use.
  • Sign-in.

    • Email and password. The session cookie is HttpOnly and Secure, and lasts 30 days from last use.
    • CSRF: every action needs a token.
    • Slowdown: after five wrong passwords for an address, sign-in for that address slows to one try a minute.
    • Ending sessions: a new password, or console sign-out, ends that account's sessions.
    • The allowlist is the switch: taking an address off it ends its sessions at once and blocks it, and the account is kept.
    • An empty allowlist: thirdshift console refuses to start, unless --no-sign-in is given. Without sign-in, it serves only to localhost, with no accounts.
  • "Set your password". One flow both creates an account and resets a password:

    1. You enter an email.
    2. The page answers the same way whatever the address.
    3. An allowed address is sent a 6-digit code: valid for 15 minutes, with 5 tries. Codes go out at most once a minute and 10 times a day per address, and are stored only as hashes.
    4. You enter the code and a new password. That sets the password, marks the address verified, and signs you in.

    The email comes from email.from, using the Resend key from the environment or the Credentials. With no key, the flow is off and the page says how to get a password from the owner.

  • The Console log is one file per machine beside the accounts, one line per event, each naming the account. The events are:

    • sign-in and failed sign-in;
    • code sent;
    • password set;
    • sign-out;
    • an address blocked by the allowlist;
    • a Theme or sections change.

    Page views are never recorded.

  • Stop from the page.

    • The button shows only while the Command is live.
    • A confirm step offers Stop or "requeue instead", which is stop --requeue.
    • The action calls the same Stop as thirdshift stop, with "from the Console" and the account's email as who asked. The Activity log reads <subject> stopped from the Console by <email>.
    • The page then shows "still ending" until the outcome arrives.
  • console install sees which proxy is running and prints one script to run with sudo. The script:

    1. writes a system service that runs thirdshift console as the owner, restarting it if it stops;
    2. writes the proxy's site file for the configured address:
      • Caddy: a file in its sites folder that reverse-proxies with flushing turned off;
      • Apache: a port-80 virtual host that proxies with flushpackets=on and gzip off on the stream paths, preserving the host and setting the forwarded protocol;
    3. for Apache, runs certbot --apache -d <address>;
    4. enables the service, then restarts the proxy. For Caddy that's a restart, not a reload, which would break HTTP/3 on Android.

    With neither proxy, it prints the service and how to reach the Console over an SSH tunnel. The README covers nginx. thirdshift itself never runs as root.

  • Setup asks whether to run the Console. If yes, it asks for the Console's whole address, showing thirdshift.example.com as an example with nothing detected, and the allowed emails, defaulting to email.to. Then it offers console install. It also warns about the waiting line and silent time, as above.

  • Portability. Nothing outside console install assumes Linux or systemd, so thirdshift console --no-sign-in runs on a Mac.

  • The glossary defines Console, Console account, Console log, Command record, Stop, Inbox, Needs you, Silent, Slow, Usual range and Theme.

Testing Decisions

  • A good test checks what a user or an agent sees:

    • the HTML and its server-push events;
    • status output and its JSON;
    • responses to sign-in steps;
    • labels on the fake GitHub;
    • Activity log and Console log lines;
    • User config contents;
    • emails at the Resend stand-in.

    It never checks templates' internals, the accounts file's layout or CSS.

  • The CLI binary, as now, covers:

    • status's groups and order: Needs you's order, an ended item leaving when a later Command on the same issue starts or after inbox.days, Silent from fixture logs with old modification times, Slow and its Usual range from fixture Command records, and the escalation words;
    • CI watch's waiting line, against the fake GitHub with checks that stay running;
    • the settings' clamping and warnings;
    • the Console subcommands:
      • console theme, console sections, console theme import and export, which change the User config and keep its comments;
      • console password, console sign-out and console log;
    • console install, with a fake caddy or apache2ctl on PATH deciding which script is printed, and none for the tunnel case;
    • Setup's new questions, using the Setup tests' harness.

    Prior art: the tests Spec: see, follow and Stop a machine's Commands from the CLI #711 added for status and stop, and the Setup and User config tests.

  • The main seam for the page is thirdshift console running on a free localhost port, driven over HTTP, with fixture Command records and logs and the existing fakes. It covers:

    • every page and its HTML:
      • the Inbox's groups, matching status --json for the same fixtures;
      • Spec run plans, history and idle Passes;
      • Lanes, Stages and Waterfall data;
    • the Session log view: folding, the renderer for each tool, the fallback, and appended lines from a byte offset;
    • sign-in: the slowdown, session lifetime and sign-out;
    • CSRF on every action;
    • allowlist removal;
    • the "set your password" flow through the Resend stand-in, including the identical answers, code expiry, tries and send limits;
    • no Resend key;
    • refusing to start with an empty allowlist, and --no-sign-in;
    • Stop from the page against a real Command with a waiting fake Harness, checking labels and the Activity log line;
    • writing the Theme and sections: the User config changes, nothing outside [console] can be written, and a Console log line is added;
    • saving a Theme into the folder;
    • serving a user font, while refusing other file types and links out of the folder;
    • the parity test over every route.

    Prior art: the Run notification tests and the email-test tests for the Resend stand-in.

  • A contrast test over every built-in Theme checks AA contrast for each text-on-background pair the tokens define.

  • A few browser tests with Playwright, as site/ already runs them, for what HTTP alone can't see:

    • a live Session log gaining lines without a redraw;
    • the Inbox morphing after a push, without moving the selection;
    • the keyboard model;
    • Stop's confirm step;
    • Pause motion;
    • switching Themes;
    • every page at 390 px with no sideways scroll.

    They run in CI beside the site's browser tests. The release build never needs Node.

Out of Scope

  • Starting Commands from the page. It's wanted later and will follow the same CLI-first rule.
  • Charts of totals over time: Charts on the Console: what to chart over time #813.
  • Things the CLI can't do: acknowledging, shelving or steering a Run.
  • Reading other machines' Commands, or GitHub for the Inbox, or any HTTP API for agents or other machines. The CLI's --json is the API.
  • A look per Console account. The look is per machine until a machine has more than one person.
  • Uploading files through the page, fonts included.
  • Separate issues: launchd on macOS, and Stopping a single Ticket's Run.
  • Accounts: recording page views, roles or permissions beyond the allowlist, and passkeys.
  • Old data: logs in the flat layout from before ADR 0011, Session logs in the sessions/ folder from before Move session logs from .thirdshift/logs/<user>/<repo>/sessions to .thirdshift/logs/<user>/<repo>/commands/<command> #599, and an SQLite index.

Further Notes

Activity

  1. added
    architect-ideaAn Architect idea: the issue an Architecture review with no Strong candidate ended on
    on Oct 19, 2026
  2. JacobStephens2 commented on Oct 19, 2026

    @JacobStephens2
    OwnerAuthor

    Q27, the Console's look, is decided. The choice is the console prototype, opening on its Inbox page.

    • Prototype: throwaway branch prototype/711-console, commit 05b1b0a. Run python3 .scratch/prototypes/711-console/serve.py, then open http://localhost:8711/?variant=console&page=inbox. All of its data is made up.
    • Verdict: in the grilling file's Settled list (the Q27 bullets), on branch web-ui.
      • One layout, with Themes made of tokens. The default is Focus, following the system's light or dark setting.
      • An attention-grouped Inbox: Needs you, Ready for review, Landing, Working, Done, Resolved.
      • Seven pages.
      • Silence, not stage length, is the stall rule.
      • Motion is only for thirdshift's activity.
      • A keyboard model.
    • Glossary and ADR: CONTEXT.md gains Inbox, Needs you, Silent, Slow, Usual range and Theme. ADR 0018 records one layout with Themes made of tokens.
    • Research behind it: docs/research/console-run-monitoring-ui.md, software-factory-console-trends.md and focus-theme-attention.md.

    This Spec's body will be rewritten to match once the grilling's Round 7 is answered: the settings, what the page may write, user Themes and fonts, and how the Specs are cut. Its Tickets come after that.

  3. added a commit that references this issue on Oct 19, 2026
  4. added
    ready-for-agentFully specified, ready for an AFK agent
    in-progressA Claim: thirdshift has taken this issue
    and removed
    needs-triageMaintainer needs to evaluate this issue
    ready-for-agentFully specified, ready for an AFK agent
    on Oct 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    architect-ideaAn Architect idea: the issue an Architecture review with no Strong candidate ended onenhancementNew feature or requestin-progressA Claim: thirdshift has taken this issue

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions