Skip to content

What's New

0.83 — paddock doctor, and an install that checks first

Section titled “0.83 — paddock doctor, and an install that checks first”
  • paddock doctor checks that Paddock can run here, and says how to fix what can’t. One line per check: your Node, how Paddock was installed, the service, the running version, the port, linger on Linux, the data dir, the config, the projects folder, your Claude login and the claude CLI. Each starts ✓, !, ✗ or - (doesn’t apply here), and every warning and failure has a fix: line under it.

    ✓ Node 22.23.4 (Paddock needs >=22)
    ! the service is installed (systemd) but not running
    fix: paddock service start (and `paddock service logs` if it doesn't stay up)
    ! linger is off for ed: the service stops when you log out, and starts only when you log in
    fix: sudo loginctl enable-linger ed (Paddock never runs sudo itself)
    ✓ Paddock shares this machine's Claude Code login (~/.claude, claude.credentials: host)

    It exits 1 if a check failed; warnings don’t count. --json gives a script the same checks as one document, and --offline skips the only check that leaves the machine, the one that asks npm for a newer version. It never creates an instance. More on Updating.

  • paddock service install refuses a service that could only crash-loop. If something other than Paddock holds the port, it exits 1 instead of writing a unit that will never bind. A Paddock on the port is fine: that’s a reinstall. Installing from a git checkout or an npm link is refused too, unless you add --from-checkout, because a unit that runs a working tree follows every rebuild, branch switch and move of it.

  • Running service install again keeps your settings. A plain reinstall over a service on --port 9000 used to put it back on 7233. It now keeps the installed unit’s port, host, data dir and verbosity unless a flag says otherwise, which in 0.82 only --refresh did. A first install that systemd or launchd refuses no longer leaves its unit behind.

  • service install --dry-run prints the unit and writes nothing, not even a folder. The unit goes to stdout and where it would go to stderr, so paddock service install --dry-run > paddock.service captures the unit alone.

  • For scripts: --json on every service action, and serve --quiet. install, uninstall, start, stop and restart take --json and print one document, { ok, action, error?, … }, with the same fields as status --json as they are afterwards, and all prose on stderr. The exit codes don’t change. paddock serve --quiet (-q) prints only Paddock <version>: <url>. A flag a command can’t act on, such as --json on serve, service logs or config eject, now exits 2 rather than being accepted and ignored.

Every command and flag is now on one page: the CLI reference.

  • paddock update installs a newer Paddock, and puts the old one back if the new one doesn’t start. Updating a service used to be npm i -g, then remembering to restart it. Now it’s one command. It checks Paddock isn’t busy before stopping anything, asks you, downloads the new version while the old one is still serving, stops the service, installs, and waits until /api/health reports the new version. If it never does, update reinstalls the old version and exits 6. If the old one doesn’t come back either, it exits 7 and prints the commands to run by hand.

    paddock update update to the latest release, asking first
    paddock update --check [--json] what a new version would be; changes nothing
    paddock update --to 0.79.1 a version — the only way to go BACK
    paddock update --to next a dist-tag
    paddock update --yes do not ask (required with no terminal)
    paddock update --force skip the busy check, and nothing else

    The whole sequence is on Updating. Run it from a terminal: a Paddock chat that runs it is itself one of the turns the busy check counts.

  • An install that isn’t Paddock’s to replace says what would work. A Docker container, npx, a git checkout, a dependency of another project, a yarn classic global or a global folder you can’t write to exits 5 and prints the right command. That includes a sudo line, which Paddock never runs itself. Set PADDOCK_UPDATE_HINT for an install something else manages, and update prints that instead. A global npm, pnpm, bun or volta install updates in place.

  • The image puts paddock on PATH. docker exec <container> paddock update --check names the image to pull, and paddock busy there is what lets Watchtower’s pre-update hook wait for a quiet moment.

  • Nothing checks for updates on its own: not the server, not at startup, not in the background. The registry is asked only when you run update. service status repeats what the last update --check found.

  • paddock service install --refresh re-writes the unit from the settings the installed one records (its port, host, data dir and verbosity) rather than from the shell you happen to run it in. With nothing installed it exits 4. On Linux, a reinstall now restarts the service onto the new unit, where before it left the old process serving.

  • update with no terminal exits 2 unless you give --yes, rather than guessing. --force, --check, --to, --yes and --refresh on a command that can’t act on them exit 2 too.

  • 0.82.1: steadier update and service, now running on the shared code from the CLI contract. The “is available” notice goes to stderr, so a script should read updateAvailable from update --check --json. update waits for the old version to stop answering before it installs, and service stop on macOS waits for launchd to finish, so a start straight after it doesn’t race it. service start and restart clear systemd’s failed state first, so a unit that hit the start limit comes back. Exit codes are unchanged.

0.81 — A stop that won’t cut a turn off

Section titled “0.81 — A stop that won’t cut a turn off”
  • paddock service stop and restart refuse while Paddock is busy. Busy means a chat turn running (yours, a trigger’s or a cron fire’s), a background task a live session still holds, or a scheduled job. A restart kills a turn mid-sentence, and the turn can’t pick up where it was. So they exit 75 and say what’s running. --force goes ahead, printing the same message prefixed Overriding:. A script that stops Paddock unconditionally needs --force, or needs to handle 75. start and uninstall don’t ask.

    paddock: Paddock is busy: 1 chat turn is running. Run it from a terminal, not from inside
    a Paddock chat.

    That last sentence is there because a chat that runs the command counts as one of the turns, and --force from there would stop the service running the chat.

  • paddock busy asks the same question on its own: exit 0 not busy, 75 busy, 3 nothing running, and --json for the counts. 75 is also the code Watchtower’s pre-update hook reads as “skip this one”, so labelling a container com.centurylinklabs.watchtower.lifecycle.pre-update: paddock busy makes an image update wait for a quiet moment. Over HTTP it’s GET /api/busy, which needs no authentication because it carries counts and nothing else: no chat title, session or project name.

  • Stopping Paddock takes 15 seconds at most, inside launchd’s 20-second kill budget. An open WebSocket could make it overrun before. A second Ctrl-C or SIGTERM no longer starts a second shutdown, and the log says how many chat turns were cut short.

  • Fixed: on macOS, service install and restart could stop Paddock and not start it again, exiting 1 with Bootstrap failed: 5: Input/output error. They raced launchd’s teardown of the old job. They now wait for it to go, and retry launchd’s two “still busy” errors.

0.80 — A command line scripts can rely on

Section titled “0.80 — A command line scripts can rely on”
  • paddock serve runs Paddock in the foreground. paddock start stays as an alias for good, since every installed unit names it, and a bare paddock still does the same. The first line on stdout is now always Paddock <version>: <url>. The server’s logs and the first-run welcome moved to stderr, so a machine with no Claude login no longer prints a warning ahead of it. A log shipper that collects stdout alone is the one thing that changes.

  • Exit codes say what happened. A typo (an unknown command or flag, or a bad value) now exits 2, not 1, and prints the usage after the message, so 1 means only that something failed. service status exits 0 installed, running and answering, 3 installed but not running or not answering, and 4 not installed: the codes systemctl status uses. An install, start or restart that never answers exits 1, where it used to print a caveat and exit 0. All of them are in the CLI reference.

  • paddock service status --json reports installed, running and answering, the installed and running versions, the port, URL, pid, unit, log and any warnings. status also warns when the Node a unit runs has gone, and when the running version isn’t the installed one.

  • paddock service logs [-f] [-n <lines>], and on Linux the log is a file. Both platforms now write both streams to <data-dir>/logs/service.log, which Paddock rotates at 8 MB, keeping three older ones. journalctl --user -u paddock.service has nothing in it any more. An existing unit keeps the path it names until you run paddock service install again.

  • Service units survive your Node version manager. The unit names Node by its real path (an fnm path died with the terminal that installed it), leaves per-session entries out of the PATH it records, and quotes every ExecStart word, so a data dir with a space in it works. On Linux, install and status check whether linger is on rather than telling everyone to turn it on.

  • GET /api/health reports the version, { ok, version }, with no authentication.

  • Stop followed by a new message now starts straight away. Stopping a turn worked, but the message you sent next sat there for five minutes with nothing on screen before the agent picked it up, and refreshing didn’t help. Claude Code doesn’t announce the end of a turn that was interrupted, so the stopped session was never closed and the new message waited for it. Paddock’s engine now spots an interrupted turn and closes the session. The follow-up starts at once in a new chat, and within about fifteen seconds in one that already had earlier turns. It also stops each stopped chat leaving an idle claude process behind.

    One case still waits: a message sent while a background shell, dev server or Monitor is still running. Stop that task first from the running-work bar and the message goes through within about fifteen seconds.

  • Sidebar group headings collapse. Click Homelab, Side Projects or any other group heading to fold it. A folded group stays folded across reloads and follows the matching section on the Projects page, in this tab and others. It shows the running and unread counts of the projects it hides, and the project you’re in stays visible even when its group is folded.

0.78.2 — Agents can save memories under transcripts: host

Section titled “0.78.2 — Agents can save memories under transcripts: host”
  • Memory writes work again when chats are shared with your terminal. With claude.transcripts: host, every attempt by an agent to save a memory was refused as “a sensitive file”, and with nobody there to approve it the memory was simply lost. Paddock now hands each agent the real path of its memory folder, which Claude Code accepts. It is the same folder as before, still shared with your terminal sessions, so no existing memories move. Instances on the default own setting were not affected.

0.78 — A real GPU for the devbox browser

Section titled “0.78 — A real GPU for the devbox browser”
  • Agents on the devbox image can get hardware WebGL in their browser. The browser MCP’s Chromium drew WebGL with SwiftShader, a software renderer that pegged eight cores to manage 14 fps on a simple shader. Map the host’s render node into the container (--device /dev/dri/renderD128) and Paddock hands Chromium a real GPU instead. On an Intel UHD 630 that measured 12.7× faster on 16× less CPU. Setup and a one-line check are in the devbox guide.

    Nothing changes on a host without a GPU. The flags are passed only when the device actually opens, because on this image passing them without one removes WebGL entirely rather than falling back. The boot log names which renderer agents got either way. docker: true projects keep the defaults.

0.77 — Tabs you can tell apart, and chats that wait for you

Section titled “0.77 — Tabs you can tell apart, and chats that wait for you”
  • Browser tabs say what they’re showing. Every tab used to read just the instance name, so three open chats were three identical tabs. A chat’s tab now reads Fix the leaking tap · hushpod — Paddock, and Files, Discover, Config and tag pages are named the same way. The root workspace’s pages leave its name out, since the instance name already says where you are. The title follows a rename.

    A mock vertical tab strip of eight Paddock tabs, which before this release all read just "Paddock". Now they read "Render the Cairngorms bundle at 256 co…", "Why is the elevation overlay off by one…", "Beacon — Paddock", "OVERVIEW.md · Files · Beacon — Paddock", "Discover — Paddock" and, last, the root page, still just "Paddock"

    A mock tab strip: Playwright can’t photograph Chrome’s own. Every title and icon in it was read from the running build’s page.

  • The tab icon is the instance’s brand chip, and it shows live status. The favicon is your brand.logo on your accent colour. A renamed instance that kept the default accent gets a colour picked from its name, so two instances’ tabs stop looking alike. While a turn runs, the title gains a ● and the icon an orange dot. When a reply lands in a background tab, a chat’s tab gets ✓ and a project or Home tab gets a count like (2) , with a green dot. The count is only what’s new since you last looked at that tab, not the whole unread backlog.

    The same three tabs (a chat, its project's Home, and the root Home) shown at five moments on an instance called House, with a house emoji on green as the tab icon. Idle: plain titles. A turn running: each title starts with "●" and each icon has an orange dot. Turn finished while every tab was in the background: "✓" on the chat, "(1)" on the other two, with green dots. Chat tab shown again: its prefix and dot clear. Root Home shown: its count resets too

    Also a mock tab strip, with background tabs simulated by overriding document.visibilityState. The titles and icons are live values from the build.

    A chat open in a background tab is no longer marked read until you actually look at it. That means “finished while you were away” now shows in the sidebar and on the fleet strip too.

  • Sidebar projects get a coloured initial instead of a folder icon, so you can find one at a glance. The colour comes from the project’s slug, out of the same twelve-colour palette as the tab icon.

    The sidebar in dark and light themes. Each project row starts with a small rounded chip holding its initial: a teal B for Beacon, a pink S for Switchboard, a green T for Tapedeck, purple L and indigo L for Ledger Lite and Lumen CLI, and a brown T for Trail Atlas

  • Finished chats stay on the fleet strip until you read them. A turn that landed while you were elsewhere used to drop off the strip the moment it stopped running. Now its channel stays to the right of the running ones, with a dashed outline and “4m ago” in place of the clock, until you open the chat. The row scrolls sideways (wheel or finger) instead of collapsing to +N.

    Home’s separate Running and Unread tables are now one Running & Recent list: live turns first, in the strip’s order, then every other chat newest first, with unread ones marked. The old Unread table wasn’t sorted by time, so a three-day-old reply could sit above one from this morning.

A turn starts and finishes, recorded on a credential-free demo instance. It moves from the running group to the top of the recent group, and its channel stays on the strip as 'just now'.

On a phone, the strip hides the “Merge chats” chip, which is still on the Config screen. That widens the channels from 44px to 169px on a 390px screen.

  • A sent file’s header has Copy and Download buttons. Copy puts a text file’s source on your clipboard: the markdown, not the rendered page. It works while the file is collapsed. Download saves any text file or video under the name the agent gave it. Images and PDFs keep the Download they already had.

    The header of a sent markdown file named pipeline.md, showing the MARKDOWN kind label followed by three icons: copy, download and maximize. Below it the rendered document begins "How a seed colour becomes a theme"

  • Any file the agent sent can open full screen, and ←/→ step through the chat’s other sent files. A new ⛶ button sits in every sent file’s header, and clicking an image does the same. As you step, the chat scrolls to each file’s message behind the viewer, so closing it leaves you where that file was. If the render cap is hiding older messages, stepping back past the oldest loaded file loads them first, so no file is skipped.
Stepping through a chat's eight sent files with the arrow keys: images, a PDF, code, a diagram and HTML.

0.74.1–0.74.4 — Fixes, a slimmer header, and a batch-mode security fix

Section titled “0.74.1–0.74.4 — Fixes, a slimmer header, and a batch-mode security fix”
  • 0.74.1: fixed chats failing on the new default model. 0.74 made Claude Opus 5.5 the default, but the claude binary bundled with Paddock was too old to run it. Every message on a project without a pinned model failed with API Error: 400 … version 2.1.280 or newer is required. The bundled binary is now 2.1.283. Projects that pinned an older model were never affected.

  • 0.74.2: finished sub-agents leave the running-work bar right away. An agent that fanned out, waited, and fanned out again used to pile up stale rows: five finished deep-reads beside three live edits under “8 sub-agents running”, until the whole turn ended. Each row now goes the moment its task does.

    A chat where five finished general-purpose sub-agents ("Deep-read 2020 return" through 2024) show their durations, while three still carry RUNNING. Above the composer, the running-work bar reads "3 sub-agents running" and lists only those three Write tasks, each with a step count and a ✕

  • 0.74.3: the project header is one row. The project’s name, then its tabs. The status pill, domain tags, Overview / Repo / Linked badges, the “updated” stamp and the ⋯ menu are gone, and the tab strip moves up into the space they left. That gives every tab back a whole band of height: 79px at 1440×900. The menu’s “Edit details” only opened Settings, which is in the strip, and delete now lives in Settings too. The project summary moved to the top of its Home tab. On phones the tabs keep their own row.

    Two project headers stacked. Top, before: the name Switchboard on its own row with badges, a summary line and a ⋯ menu, and the Home, Chat, Files, History, Settings and Triggers tabs on a second row. Bottom, after: one row with the name followed directly by the tabs

  • 0.74.4: a security fix for driveMode: batch. On batch, the tools Paddock injects into a turn (send_file, and paddock_manage where enabled) were served over an HTTP bridge. That bridge listened on every network interface with no authentication while a turn ran. It now binds to 127.0.0.1 and needs its own bearer token. Batch turns also stop putting MCP server credentials on claude’s command line, where ps could read them. A docker: true project on batch is the one exception, and the startup warning names it. The default driveMode: session never used the bridge. No configuration changes are needed.

0.74 — Claude Opus 5.5, and delete from Settings

Section titled “0.74 — Claude Opus 5.5, and delete from Settings”
  • Claude Opus 5.5 is in the model picker and is the instance default. It is newer and cheaper than Opus 5: $4/$20 per million tokens against $5/$25, with cheaper cache reads too. A project that hasn’t pinned a model gets the better model for less. Every older model stays in the catalog, so existing pins keep working. If your models: allow-list names models explicitly, nothing moves until you add claude-opus-5-5.

    Sonnet 5’s estimated cost was 50% too high. Its scheduled rise to $3/$15 was cancelled and $2/$10 became the standard price, but Paddock had encoded the rise.

  • Delete a project from its Settings tab. Settings now ends in a Danger zone. The confirmation also stops lying about linked projects. It used to say the project “and all its chats and files will be permanently removed”, but deleting a linked project only unlinks it, and your directory is untouched. The dialog now says so and names the directory. The root workspace has no Danger zone, because it can’t be deleted.

    The Delete project dialog for a linked project called Switchboard: "Switchboard will be unlinked. Paddock's notes, settings and chats for it are permanently removed, and this cannot be undone — but the directory it works in is left alone:" followed by the path /home/ed/code/switchboard and "Nothing in there is deleted.", with Cancel and a red Delete project button

  • Discover has a button beside the + in the side nav’s Projects header. It used to be only in the footer, grouped with Config and the theme toggle, where an early user couldn’t find it. Both doors lead to the same screen.

  • The root workspace is titled “Home”, the word the side nav already used. Before, it was titled after the projects directory, which on a fresh install meant lowercase projects. A name you set yourself is kept. If you saved root settings on an older build, your root project.yaml may hold a derived name: line; delete it by hand to get “Home”.

  • A chat renders only its most recent 500 messages, so opening one that has run for days is quick. Mounting a few thousand messages was what made switching into it slow. Nothing is deleted: the transcript says how many earlier messages it isn’t showing, and a deep link to one of them still works. Change the cap with ui.transcriptRenderLimit in Settings → Interface or the config file (0 means no limit).

    The top of a long chat, with a small centred line reading "700 earlier messages are not shown. They are still saved — reopening this chat always shows the most recent messages first." above the conversation, which starts at checkpoint 351

  • PDFs open in the Files and Changes tabs. Before, a .pdf there showed pages of garbled text. It now gets the same built-in viewer as a PDF the agent sends.

    The Files tab showing release-notes.pdf in the browser's PDF viewer, with page thumbnails on the left and the first page, "Lumen CLI Release Notes", readable on the right

  • Claude Fable 5.1 is in the model picker. Fable 5 stays for existing pins. Paddock also learned Fable 5.1’s much cheaper cache reads. Without that, a Fable 5.1 chat’s estimated cost would have read roughly four times too high.

  • A big batch of attachments no longer pushes the message box off screen. The tray above the composer stops growing at a fixed height and scrolls instead. Two or more files get a Clear all button.

  • The dialog that merges chats into ~/.claude says the same thing in fewer words: 39% fewer, and 29% less height on the same plan. Per-row explanations became tooltips, and the destination is shown relative to your home directory.
  • profile: paranoid | balanced | yolo sets an instance’s security posture in one line, instead of a dozen PADDOCK_* variables. It supplies the defaults for the Claude sharing modes, spawn depth and the capability toggles, and nothing else — it cannot change your port, bind address or auth. The default is balanced: on upgrade, an instance with no config file starts inheriting the host’s instructions and mcpServers, and profile: paranoid restores the old behaviour exactly.

    paddock config show --resolved is the answer to “so what am I actually running?” — every effective value, and the layer it came from:

    Profile balanced (built-in default)
    Capabilities
    driveMode session default
    selfMcpEnabled true profile (balanced)
    selfMcpWriteEnabled false profile (balanced)
    maxSpawnDepth 1 profile (balanced)
    …
    Advanced (read-only)
    claude.transcripts own profile (balanced)
    claude.credentials host profile (balanced)
    claude.instructions host profile (balanced)
    claude.hooks own profile (balanced)
    claude.mcpServers host profile (balanced)

    Every lever, key by key, is on Config profiles. paddock config eject writes the whole resolution into paddock.config.yaml for anyone who would rather pin it in git than inherit it; it previews by default and applies with --write.

  • Your chats can move into ~/.claude, so claude --resume in a terminal can see them. Paddock keeps a project’s chats in its own .chats/ by default, which is right for trying it and wrong for keeping it. The fleet readout offers the move when there is something to move, and nothing is deleted: a chat you leave unticked is set aside in .chats-pre-migration/ beside its project.

    The migration dialog, headed "Merge chats into ~/.claude": a line reading "14 of 14 chats will move into ~/.claude. The rest are set aside in .chats-pre-migration/, not deleted", filter chips for All and New, a Select all box, then the chats grouped under their project — projects, Harbour Notes and Lanternfish — each row ticked and tagged New, with Cancel and a "Merge 14 chats" button

  • Fixed: flipping claude.transcripts from host back to own left the instance writing into your real ~/.claude. host plants a symlink at <project>/.chats pointing out at your Claude home and nothing ever removed it, so own stopped isolating and deleting a chat unlinked a transcript Paddock had never owned. The stale link is now unplanted on every boot, and the delete refuses to run while a store is still a planted symlink.

  • Forking from a message asks for a name first, like the sidebar’s fork button already did, rather than firing on click and titling the result Fork of <chat>. The message you picked is still the branch point. (Versioned as 0.71.3, which was never released; it reached users in 0.72.)

  • An installed Paddock service survives a graceful stop. Both unit files relaunched Paddock only on a non-zero exit, while the server handles SIGTERM by exiting 0 — so sleep, logout or a stray kill all read as an intended stop and Paddock stayed down until the next login. A crash was the survivable case. Upgrading does not rewrite a unit file, so paddock service status now says when yours has the old shape and names the fix.

  • paddock service start | stop | restart. Bouncing an installed service previously meant uninstalling it or dropping to the raw supervisor. start and restart wait for the URL to answer before reporting success, so it can no longer claim to be running over a service that is crash-looping on a port clash.

  • Home dropped its Files section.

  • The root workspace’s Home is the instance’s onboarding surface. A brand-new install used to dead-end on its own front door: / rendered Discovery instead of the workspace whenever the instance was empty, and on a machine with no Claude Code history both of Discovery’s exits were gated on something that could never happen — a front door with no button anywhere on it. / is now always the root workspace, carrying Discovery inline while the instance is empty. Two cards sit on it permanently, each showing one entry at a time:

    Two cards side by side on the root workspace's Home. The left card, headed What's New, shows v0.72 "One key for the whole posture" with a Read more link and a 1/12 pager; the right card, headed Tips, shows "Paste a screenshot straight into the composer" with its own 1/32 pager

  • A chat’s “updated X ago” comes from its last message, not the file’s mtime. Paddock touches transcripts for reasons that are not conversation — discovery re-stats, attribution, a resume appending a mode record — so idle chats were restamped as “updated a few minutes ago” and floated to the top of the list in batches, phase-locked to a periodic task.

  • paddock service install names each stage as it starts it, rather than sitting silent for several seconds and then dumping the whole success block.

  • You can stop a single piece of background work. Each row in the running-work bar gets a ✕, and a Stop all appears once more than one thing is running — so a session with fifteen stray shells no longer has to be reaped whole. A shell stops in one click; a sub-agent asks first, because the kill cascades to everything that sub-agent started. A stop the runtime refuses says can't stop and stays retryable rather than hanging at stopping….

    Each shell now shows the command it is running beside the description it was launched with, which is the difference between three plausible-looking waits and three copies of one loop polling a file that does not exist:

    The running-work bar headed "4 things running" with a Stop all button. Three shell rows each pair a description — "wait for scan completion #1", "#2", "#3" — with the command actually running, "until grep -q SCANDONE /tmp/paddock-fake/scan-1.log 2>/dev/nu…", and carry a ✕ at the right. A fourth row, a monitor watching "errors in deploy.log", has no ✕, because the runtime cannot stop that kind

  • Deep-link to a single message. The per-message hover rail’s time/context pill is a real anchor: click it to copy the URL, and opening that URL loads the chat scrolled to the message. A link whose target has been reverted away says so instead of doing nothing.

  • A measured zero draws as an empty gauge. The fleet readout lit one of six segments for a chat measuring 0% context while its own tooltip read “Context 0% full”. A barely-started chat still lights one segment, so it cannot be mistaken for one that was never measured.

0.70.1 — Linked directories, all the way through

Section titled “0.70.1 — Linked directories, all the way through”
  • The Changes tab, the file browser and Push act on the project’s own directory. For a project whose working directory isn’t its metadata directory — a linked worktree, a linked checkout, a repo-backed clone — three surfaces had been left behind, each failing silently: an untracked file rendered “File not found”, the Push button pushed the backing store rather than the project’s own repository, and a managed project with a path: had an empty Files tab.

  • The running-work bar collapses above four rows. A real session hit fifteen rows, taller than the composer the bar docks above, pushing the conversation off screen at the moment you most want to read it. It now opens as one line naming the mix and the age of the oldest task:

    The collapsed running-work bar: a header reading "15 things running" with an expand chevron and a Stop all button, above one summary line reading "14 shells · 1 monitor" with "oldest 0:45" at the right

  • The Triggers tab stopped re-parsing the whole jobs directory every ten seconds. Measured against 2,016 job records, a warm call went from ~1,050 ms to ~75 ms.

Mostly documentation and capture-rig work. Three things a user sees:

  • Inline code has its background back, and blockquotes their colour. The code chip borrowed a surface token, and when 0.68’s colour restoration raised the prose card underneath it the gap between the two closed to 1.04:1 — invisible. Both roles have tokens of their own now.

    An assistant reply rendered in a chat, with the words "readings" and "readings/time" set as inline code chips on a visibly darker fill than the message card they sit on

  • recovery.limboTimeoutMs is gone — parsed, defaulted, editable in Settings, and read by nothing since the day it was introduced. A limboTimeoutMs: left in a config file is ignored exactly as it was before, and PADDOCK_RECOVERY_LIMBO_MS can be dropped from your environment.

  • The root workspace’s Settings tab shows (root) instead of a blank slug, and PADDOCK_SELF_MCP_PROJECTS now says that as well as creating projects it gates promote_project, which git clones a URL the agent supplies.

  • Anything still running is pinned above the composer, not just sub-agents. A background Bash, a Monitor or a workflow could run for minutes behind a card scrolled far up the transcript, and the only hint was a static running chip — which meant “no completion notification was found in the transcript”, not “we checked”. A killed task kept that chip forever. The bar now names each running thing, what it is doing right now, how long it has been going and how many steps it has taken, and it clears when the work actually ends. It renders nothing when nothing is running.

    Two limits are worth knowing. The signal is per-process, so after a server restart the bar is empty until something new starts — which is right rather than a gap, because Paddock stops the fleet without waiting for jobs and those tasks really are dead, unlike the old chip that went on claiming a killed one was alive. And it is driveMode: session only: the CLI runtime reads the transcript file, and these are stream-only control messages that never reach it.

  • A chat that is still working no longer reports itself idle. This is the fix proper. Background work outlives the turn that launched it, so the moment the reply landed the sidebar’s streaming dot, Home’s in-flight badge, the running-only filter and the fleet strip were all told the session had stopped while minutes of work carried on. They now read the truth. The composer is deliberately not locked while only background work runs — an hour-long Monitor should not make the chat unusable — so “this chat is busy” and “a model turn is in flight” are now two different questions with two different answers.

    Still open: sending into a chat that has live background work is not solved yet (#806). The composer is unlocked, but a message sent while a background task is still running can sit waiting on the runtime’s collision guard for up to five minutes before the new turn starts, and Stop still means end the session, which kills the work you were watching rather than interrupting the turn.

  • Fixed from 0.68: an imported directory appears in the sidebar straight away. Discover’s success screen said “they are in the sidebar now” while the sidebar still said “No projects yet” until you reloaded the browser. The list refreshes the moment the run finishes, and the results screen — including the rows that failed and have something to say about it — now stays up until you leave it rather than being unmounted by its own refresh. (Shipped to npm as 0.68.1, which has no git tag or release page of its own.)

  • A new instance opens on Discover instead of an empty project list. It reads your Claude Code history, works out which directories on this machine you have actually been using claude in, and offers them as projects — with conversation counts, last-used dates and git remotes, so you can tell them apart. Tick the ones you want, press Import N projects, and each becomes a project pointing at that directory with its conversations brought across as resumable chats. It stays in the sidebar afterwards; it is not only a first-run screen.
Discover offers the directories you have actually been using Claude Code in. Importing links the directory and copies its conversations across — your own history is never moved or deleted.

The heuristic is most of the feature. A naive scan of a real developer machine surfaces around 166 transcript folders, roughly 150 of them throwaway temp-dir sessions, plus /, ~/Downloads and /tmp. Discover drops those, along with system paths, Paddock’s own directories, your home directory itself, and anything already a project. Two rules are soft — no git repository, and outside your home — and appear as toggles below the list, but only when relaxing one would actually reveal something. A line above them says how many went each way, so “why 5 and not 12?” has an answer on screen rather than looking like a bug.

Rows expand lazily, fetching a directory’s sessions only when you open one, and their tickboxes go three-state so you can take some conversations and not others. A directory whose transcripts record a different spelling of its path — a symlinked home, /var against /private/var — is warned about before you import, that being how an import otherwise comes back mysteriously empty. Rows fail independently and each says why: a project that was created but whose chats did not come across is called out in amber rather than green, because it leaves a real empty project behind.

  • --here is removed, and nothing replaces it — because nothing needs to. It opened the directory you were standing in as the workspace. Discover covers the job it was built for, from one instance with as many linked directories as you like, which is also the only shape that can run as a background service (#796): a launchd agent hosts one instance, so three directories opened with --here were three instances of which it could run at most one.

    There is nothing to migrate — the flag is now rejected like any other unknown option, and a run that used to resume a directory now starts the ordinary ~/.paddock instance. If you did open a directory with it, its state is the .paddock/ and .chats/ folders inside it: add the directory through Discover to bring its conversations across, then delete those two folders and the two lines --here added to your .gitignore.

  • Where you run paddock from no longer affects anything. --data-dir (or PADDOCK_DATA_DIR) is the only thing that picks which instance you get. The startup line that used to name a workspace now names the data directory.

  • Importing a directory writes nothing into it. No .paddock/, no .chats/, no .gitignore edit, no CLAUDE.md — the project record and its transcripts both live in the data dir, and the project simply points at the path. Under the default claude.transcripts: own, your ~/.claude transcripts are copied with their timestamps preserved, never moved or deleted, so your terminal claude is unaffected. Under claude.transcripts: host nothing is copied at all — the project reads your ~/.claude folder directly, and adopting only registers the sessions that are already there. Working in chats sets out the difference.

  • Paddock can keep itself running. paddock service install registers the instance as a per-user launchd agent on macOS or a systemd --user unit on Linux, so it starts when you log in rather than when you remember — with uninstall and status alongside it. Nothing else needs installing: the unit runs the Node binary you already have, by absolute path, and restarts on a crash but not on a clean exit. Keeping Paddock running on your laptop covers it.

0.67 — A design system, four themes, and the fleet readout

Section titled “0.67 — A design system, four themes, and the fleet readout”

The UI had no design document and no token layer. Colour was addressed by palette step in 1017 places, with 722 hand-written dark: pairs, and a single ramp tuned against a dark canvas had been reused unchanged against a light one — which is why light mode failed WCAG AA at its most-used tokens. 0.67 replaces the whole colour layer, then builds three more themes on top of it.

  • Light mode now passes AA. Help text and field labels went 3.75:1 → 6.71:1, muted text and placeholders 2.81:1 → 4.90:1, and the primary button’s white label 4.17:1 → 5.53:1. Light and dark ramps are derived separately in OKLCH now rather than one being reused for both, and the mid-steps lose the high-chroma tan cast that made light mode read muddy.

    Contrast is enforced rather than asserted: a test parses the real stylesheet and fails the build if any text-on-surface pair drops below 4.5:1 (3:1 for control boundaries) in either mode, or if a colour falls outside the sRGB gamut. Along the way dialogs started trapping and restoring focus, menus gained arrow-key navigation, prefers-reduced-motion is honoured throughout, and chat messages no longer animate in — a 250ms fade-with-translate on something that happens a hundred times a day.

  • Four themes, in Config → Appearance, applied instantly. Foundation is the neutral base — warm ground, terracotta accent. Parchment is a 90s RPG menu: wine chrome, brass fittings, corner brackets, an old-style serif. Terminal is green phosphor and ANSI in the dark, greenbar and ribbon ink in the light. Sci-Fi is a deep-space ground and luminous cyan. Both light and dark are designed for each theme, not inverted from one another, and every theme is contrast-guarded in both modes by the same build-time check — which now also fails both ways, on a theme registered with no stylesheet and on a stylesheet no one registered.

    It is a per-browser choice: no save, no restart, nothing an operator sets for everyone, and it survives a reload without a flash.

The same screen — a project’s Settings tab — in each of the four, all in dark mode:

Foundation: a warm near-black ground with a plain sans-serif, terracotta on the new-chat button and the Save changes button, and the project header sitting flush on the page ground. Unread chats carry terracotta dots and each project shows an unread badge in the sidebar

Parchment: the project header becomes a wine-red band, Save changes is a brass pill, the settings card is drawn with corner brackets, and every label is an old-style serif in small caps — the section heading "Identity & Metadata" carrying an italic ampersand. Unread chats are marked in amber against the brown ground

Terminal: green phosphor on near-black, with the sidebar, labels and field text all in the same green, and the settings card outlined in a thin phosphor rule. Unread dots and the sidebar's project badges glow the same green

Sci-Fi: a deep blue-black ground with luminous cyan on the new-chat button, the Save changes button and the active tab underline, and the project's tag chips set in letter-spaced uppercase. Unread dots and sidebar badges pick up the same cyan

  • Pick any colour for the accent and it stays readable. The picker takes a colour and nothing else. The theme supplies its own saturation and its own target contrast, and the lightness is solved to clear that floor — so the colour you pick is re-solved against whichever theme and mode you are in, rather than used at whatever lightness it happened to arrive with. Flip to dark and it is solved again. There is a spectrum strip, ten named hues, and a Theme’s own button to put it back.

    Optionally the same colour tints the page ground — None, A little, More — which is the fast way to tell two Paddock instances apart at a glance. No colour theory is exposed anywhere in the UI.

    PADDOCK_BRAND_ACCENT still composes: with no colour picked, the solver reads the hue your brand colour produced and re-solves it against the active theme, so it is now a hue seed rather than a literal colour. A colour you pick yourself overrides it.

The Appearance section of the Config screen: four theme cards each previewing their own chrome, a full-spectrum strip to drag along, ten named hues from Ember to Rose with a "Theme's own" reset, a None / A little / More control for tinting the background, and a live preview row showing buttons, a link, an accent chip and the status hues the theme keeps for itself

Change the accent and the whole UI follows — and a theme changes ground, type and chrome together. Applies immediately, per browser, with no save step.

Also fixed here: in dark mode the primary button’s fill lightened on hover, taking its white label from 5.53:1 to 4.17:1 — below AA, on hover, on the most-clicked control in the app. Hover raises contrast in both modes now.

  • A live strip above every screen says what the herd is doing. How many turns are in flight fleet-wide, how many chats are holding a reply you have not read, and a channel per running turn carrying its project, a live elapsed clock and a segmented context gauge. Longest-running first, up to three channels depending on the width of your window and an honest +N for the rest — the counts themselves stay exact. Clicking a channel opens that chat.

    Two of those did not exist anywhere in the UI before. A turn that had been going forty minutes and one that started eight seconds ago looked identical, and context pressure was visible only inside the chat it belonged to — by which point you had already opened it. An idle fleet costs nothing: no timers, no requests. While something is running it refreshes the chat names and context fills every thirty seconds, and the only thing that animates is the clocks, because a persistent readout is on screen 100% of the time and anything decorative in it is decorative forever.

    (Since 0.69 the strip also counts chats that are only running background work, which have no turn to time — those channels show —:— where the clock would be.)

  • Home’s empty states are invitations, and the Config screen is readable. A quiet workspace used to render five near-identical rounded boxes down one viewport, four of them dead ends, with the first two saying the same thing twice. The two attention feeds now collapse into a single “All caught up” panel when both are empty — one state, not two — and it is the only thing on the screen carrying a primary action. It is deliberately not shown while the feed is loading or after it errored, because claiming all is caught up before the answer arrives is a lie the reader acts on. The remaining empty states say who fills them in and when: OVERVIEW.md and CHANGELOG.md are written by the post-turn sweeper, not by hand.

    On Config, one measure replaces four unrelated left edges, fields became rows instead of a ragged two-column grid, everything routes through the same card and control primitives as the other settings screen, and the dirty marker no longer shoves an edited field out of its own track. The section rail used to vanish below 1024px, handing you back the 5,500px scroll its whole flat shape was justified by; it runs horizontally under the filter at those widths now. The twenty amber env chips are quiet, because being set from the environment is a fact about a field rather than a warning about it. The restart banner stays loud. It earned it.

  • A file staged while a turn was running no longer rides the next message. Attachments were consumed by sending and never by queueing, so a file staged mid-turn sat in the tray and went out silently with whatever you sent next. Attachments now travel with the queued message, every window sees them, and Stop hands them back.
  • Reloading mid-turn no longer eats the reply. A remount fetched the transcript and applied it wholesale, throwing away every frame that arrived while the fetch was in flight — losing the assistant’s entire reply and leaving a sub-agent card spinning on “running” until another reload.
  • The sidebar unread badge can always be cleared. It counted deleted chats, so it could read 3 with one chat left and no way to reach zero. Deleting a chat now takes its bookkeeping with it, and an instance already stuck heals itself on the next load.
  • A new project with an old project’s name starts empty. Re-creating “Foo” used to inherit the deleted Foo’s run history and a phantom unread badge.
  • Archiving a chat silences it everywhere. It used to count toward the sidebar badge while being excluded from Home’s Unread feed.
  • Three ways a queued message could be silently lost are fixed. The queue — the chip holding what you type while a turn runs — deduped on a timestamp from your browser, so one fast clock destroyed every later queued message on that chat. It also drained from only one of the eight places a turn can end, so anything queued behind a /compact, a trigger or a background sub-agent sat stranded until a later message flushed it.
  • A second tab merges instead of overwriting. The queue is one shared slot per chat. Previously a second window replaced the first one’s message, and that client then watched someone else’s text appear as though they had typed it.
  • Stop returns your queued message to the composer rather than sending it.

Everything from 0.66.0 back to 0.29 lives on What’s New — earlier releases: the Config screen redesign and the move to port 7233, promote_project over MCP, managed and unmanaged linked directories, host plugins, the claude: inheritance block, Paddock’s own Claude home, the npx install, adopting Claude Code CLI chats, the npm package, the environment system prompt, Home’s attention feeds, subtree actions and the one-front-door sidebar, the root becoming a workspace, scratch being retired, driving Paddock from outside over MCP, per-message fork and revert, attachments, streaming, unified triggers, and the rest.


Maintaining this page: add a short, user-facing entry here whenever you cut a release (see RELEASING.md). When this page gets unwieldy, move the oldest entries to the archive page verbatim — the archive is append-only and its entries are never rewritten.