Changelog

What we shipped.

Every release, documented. No marketing fluff — just what changed and why.

v1.10.11

2026-06-11

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.10.11

Fixed

  • A quiet side no longer fails a meeting recording. If you mostly listened (or the other side never spoke), transcription now proceeds with whichever channel has speech instead of failing the whole take — a 21-minute meeting is never discarded because one channel was silent.
  • Microphone auto-recovery during recording. When macOS echo cancellation delivers a dead (silent) mic stream — seen with some always-on voice apps — Push now detects it within seconds and restarts capture without echo cancellation, so your words are kept.
  • No more made-up transcript lines from silence. Silent audio is detected before transcription, so the transcriber can no longer invent short phrases ("you", "I'm not sure.") for a channel nobody spoke on.

Notes

This update is available whenever you choose Check for Updates… from the Push menu.

v1.10.10

2026-06-11

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.10.10

Improved

  • Transcription can no longer hang. Recording transcription requests now time out and retry automatically (with longer allowances for big uploads), so a dead connection can't leave an issue stuck on "Transcribing…". Long meetings are unaffected — full-length transcription was verified end-to-end on 70+ minute recordings.

Experimental

  • Quick-capture bar (off by default, Settings → Experimental): ⌘N can summon a Spotlight-style floating capture bar — type, hit Return, and an auto-routed issue is created without opening the full editor.

Notes

This update is available whenever you choose Check for Updates… from the Push menu.

v1.10.9

2026-06-11

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.10.9

Meeting recording polish from the first days of real use.

Fixes

  • Voice dictation no longer looks like a meeting. Apps that hold the microphone through a helper process (Wispr Flow, Chrome, and other Electron-style apps) are now recognized correctly, so dictation tools stay ignored and browser meetings classify properly.
  • Recording no longer turns the meeting down. Starting a recording used to duck system audio to near-silence; the call now keeps playing at full volume while echo cancellation keeps your mic channel clean.

Improved

  • Stopping is instant. The recording ends the moment you hit Stop — the sidebar capsule and menu-bar icon reset immediately. Transcription continues in the background, shown as a small badge on the issue's card that clears itself when the notes land (and offers a click-to-retry if anything fails).
  • The menu bar keeps the door open. While a meeting is still going, Meeting Detected — Record It stays available in the menu bar — even after you dismiss the prompt or stop an earlier take mid-meeting.

Notes

This update is available whenever you choose Check for Updates… from the Push menu.

v1.10.7

2026-06-10

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.10.7

Workspace memory starts working for you, and meeting detection gets smarter.

Improvements

  • Memory shows up where work starts. Beginning work on an issue now surfaces what the workspace already knows about it — the people, clients, and decisions mentioned in the issue appear right in the terminal, so agents stop re-asking what you've already told them.
  • Agents can harvest their own notes. A new sync recipe lifts the cross-project facts your code agents have already curated (who you work with, your preferences) into workspace memory — and leaves a pointer so any agent knows where to look.
  • Memory backup and restore. push memory export output can now be re-imported with push memory import — idempotent, and it never overwrites content you've edited.
  • Smarter meeting detection. Recording capture now attributes the microphone to the exact app using it, ignores non-meeting audio apps, and stops automatically when the meeting ends.
  • Pick the model for background runs. Settings → Runtimes lets you choose which model each runtime uses for system work (capture enrichment, folder descriptions) — no more hardcoded picks.

Fixes

  • File viewer: clicking anywhere on the title box now opens the session-file dropdown, not just the small arrow.

Notes

This update is available whenever you choose Check for Updates… from the Push menu.

v1.10.6

2026-06-10

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.10.6

A focused follow-up to 1.10.5's meeting recording.

Fixes

  • Both-sides recording now asks for permission like it should. In 1.10.5, macOS's one-time System Audio Recording prompt never appeared, so meeting recordings quietly captured only your microphone. The first recording you start on 1.10.6 shows the prompt; once granted, recordings capture the other side of the call too (Me: / Them: in the transcript).

Notes

This update is available whenever you choose Check for Updates… from the Push menu.

v1.10.5

2026-06-10

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.10.5

Meeting recording grows up: capture both sides of a call, get speaker-labeled notes, and let Push notice meetings for you. (Experimental — turn it on in Settings → Experimental.)

New

  • Record both sides of a meeting. Recordings now capture system audio (what you hear) alongside your microphone, so the transcript shows who said what — your words as Me:, the other side as Them:. Echo cancellation keeps your channel clean even on speakers. The first take asks once for macOS's System Audio Recording permission; if it isn't granted, recordings continue mic-only.
  • Push notices your meetings. When another app starts using the microphone — Zoom, Meet in a browser, a Slack huddle — Push offers to record: one notification, once per meeting, never a silent auto-record. You can turn suggestions off in Settings.
  • The menu bar shows it all. A detected meeting puts an orange dot in the menu bar with a one-click Record It; while recording, the dot pulses red and the menu carries Stop and Discard — visible even when Push isn't the frontmost app.
  • Delete audio after transcription. A new option keeps the transcript and notes while removing the recording bytes once transcription succeeds.
  • push transcribe in the CLI. Transcribe any audio file or URL from the terminal using your own API keys.

Improved

  • Recording transcription uses your own API keys (Together required, OpenRouter optional), entered once in Settings → Experimental and stored encrypted in your workspace.
  • Meeting recordings keep recovering after crashes or quits — both channels now resume uploading on the next launch.

Fixes

  • Recordings no longer fail to upload or transcribe when the app and database disagree about identifier casing (this also fixes audio playback for affected past uploads).

Notes

This update is available whenever you choose Check for Updates… from the Push menu.

v1.10.4

2026-06-10

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.10.4

Fixes meeting-recording uploads from the Mac app.

Fixed

  • Meeting recordings upload reliably. Recordings captured on the Mac could fail to attach to their issue ("Issue does not belong to workspace") and never reach transcription. Uploads now match issues regardless of identifier casing, so recording capture works end to end.

Notes

This is a Manual update: Push surfaces it when you choose Check for Updates.

v1.10.3

2026-06-10

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.10.3

One push skill, everywhere — Push now manages its own skill through the skill registry.

New

  • The push skill manages itself. One bundled copy, linked into every code agent's skills home (Claude, Codex, Hermes, and OpenCode) and kept current automatically with every release — no more per-agent installs drifting out of date. Your existing copies are adopted in place on first launch; /push keeps working exactly as before.
  • Record meetings into issues (experimental). Turn on Recording capture in Settings → Experimental to record straight into a meeting-notes issue with automatic transcription — bring your own transcription key per workspace.
  • Push can open Markdown files. Push registers as a system-level handler for .md, so you can open Markdown straight into the file viewer from Finder.

Improved

  • Session file lists now include images a session touched, alongside the files it wrote.
  • One managed Skills row in Settings. Settings → Server shows a single "push skill" row with one-click Repair, replacing the per-agent install table.

Notes

No action needed — existing per-agent skill copies migrate automatically at launch. This is a Manual update: Push surfaces it when you choose Check for Updates.

v1.10.2

2026-06-09

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.10.2

Workspace memory, smarter capture enrichment, and a setup that heals itself.

New

  • Workspace memory. Push now keeps a shared memory per workspace — durable facts, preferences, and decisions that agents can save and recall across sessions instead of relearning them every run. Browse, search, and edit everything from the new Memory page, or drive it from the CLI.
  • Status-line recommendations. Terminal settings now offer curated status-line presets for your coding agents. Copy or apply them with one click — Push never changes your status line on its own.

Improved

  • Setup heals itself. A fresh install — or a switch into Full mode — now always gets a workspace created automatically, with the push command and the Push skill kept installed, no onboarding required. If any part of setup ever fails, Push shows you a banner instead of failing silently.
  • Capture enrichment is more reliable. Quick captures fill in their title, description, and assignee through a dedicated, permission-gated pathway — and assigning an issue while it's still enriching no longer fails with a conflict.

Security

  • Hardening across the local API and relay: stricter trust boundaries, tighter authorization checks, and new request guards.

Notes

Your data migrates automatically on first launch — no action needed. Existing tabs, issues, and background runs are unchanged.

v1.10.1

2026-06-09

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.10.1

Manage your skills across every agent — including the ones that live inside your project folders.

New

  • Folder skills are now first-class. Push discovers skills inside your project folders (.claude/skills, .agents/skills, .hermes/skills, .opencode/skills) for every folder agent, not just your global agent homes — shown in the Skills list with a folder icon.
  • Make a folder skill global. A new Scope control on a skill's detail toolbar shows whether it's folder-scoped or global. "Make Global" moves a folder skill into its owner agent's global skills so you can use it everywhere — with a confirmation, since it's a one-way move out of the project folder.
  • Agent Info + remove, from the agent detail menu. A new ⋯ menu adds a compact Agent Info popover and a Delete action (it unassigns the agent and releases its issues; your sessions, runs, and files are preserved).

Improved

  • Cleaner Skills sidebar. Normalized icons — a folder for folder skills, a grid for skills synced across all your agents, and the owning agent's logo otherwise.
  • Auto agents read more quietly. A folder agent on Auto now shows a subtle "(auto)" label next to its name instead of a badge on its icon.

Notes

No migrations or settings changes — your existing tabs, issues, and background runs are unchanged. This is a Manual update: Push surfaces it when you choose Check for Updates.

v1.10.0

2026-06-09

This release is notable — security fix, breaking change, data-loss fix, or a headline feature. Most releases stay quiet; this one earned the dialog.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.10.0

Sharper, theme-aware file previews in History — and a Files list that keeps itself tidy.

Improved

  • File thumbnails now match your theme — and they're crisp. History's Files previews for text, Markdown, and code used to come from macOS Quick Look, which renders them as a white, light-only page and looked blurry once scaled into the column. Push now draws those previews itself: theme-aware (dark previews in dark mode), rendered at high resolution and scaled down to the card so they stay sharp at any width, and cached so scrolling and resizing never re-render. Images and PDFs still go through Quick Look. The preview tiles are also taller with a more legible font.

Fixes

  • Stale "File no longer exists" cards are gone. The Files surfaces aggregate every session, but the cleanup pass only swept the tab you were looking at — so deleted or renamed files from other (or closed) sessions lingered as dead cards. Push now sweeps every session when the History board or Files gallery appears, and the column heals itself.

Notes

No migrations or settings changes — your existing tabs, issues, and background runs are unchanged. This is a Notable update, so Push surfaces it directly rather than waiting for you to check.

v1.9.92

2026-06-08

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.92

A calmer, more consistent terminal tab strip — plus an OpenCode fix.

Improved

  • A steadier, more consistent tab strip. Each tab's icon is now a calm, static mark (the agent's logo, or a terminal icon for a plain shell), and activity shows as a separate indicator beside it: a spinner while something is running, a blue dot when there's unread output, and nothing when idle. One uniform language across Claude, Codex, OpenCode, Hermes, and plain terminals — no more brand mark that doubled as the animation.

Fixes

  • OpenCode tabs now show the unread dot. When an OpenCode turn finishes while you're looking at another tab, that tab now gets the blue unread dot — same as the other agents always have.

Notes

This update is available whenever you choose Check for Updates… from the Push menu.

v1.9.91

2026-06-08

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.91

Skills get their own sidebar section, terminal tabs show live activity, and the tab glyphs animate as one.

New

  • Skills now live in the sidebar, like Folders. A dedicated, collapsible Skills section with an item count — loads instantly, one row per skill, each with an icon that shows at a glance whether it's shared to your other agents (the owning runtime's logo when it's not, the Push mark when it is).
  • Plain terminal tabs show a live activity glyph. A terminal icon sits in the same spot as the Code Agent logos — static at the prompt, animating the moment a command or process is running, so you can tell which tabs are busy without switching to them.
  • One consistent tab animation everywhere. Every agent (Claude, Codex, OpenCode, Hermes) and the plain terminal now share a single spinner with the tab's own icon woven in, so the tab strip reads as one coherent set instead of a mix of styles.
  • The Reopen chip shows how many tabs are stacked. Closed a few tabs in a row? The Reopen button now shows the count waiting to come back.
  • File count on the tab's file chip. The tab-strip file chip now shows how many files a session has touched.

Fixes

  • Terminal activity detection now actually works. Plain-shell tabs correctly detect a running process — the activity glyph animates, and closing a busy tab warns you first. It also reacts instantly now instead of after a delay.
  • Images render inline in the file viewer. Raster images fit to the window and SVGs render properly.
  • Cleaner link clicks. Clicking a URL in the terminal no longer grabs a trailing period or bracket.
  • Claude tabs re-render cleanly on inspector toggle. Opening or closing the file inspector on an idle Claude tab refreshes its layout without artifacts — including sessions restored after a relaunch.

Notes

This update is available whenever you choose Check for Updates… from the Push menu.

v1.9.90

2026-06-08

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.90

Share a skill with all your agents in one click, plus a round of file-viewer improvements.

New

  • Share a skill with every agent in one switch. The skill detail view now has a simple Synced / Not Synced control — turn it on to make a skill available to all your other code agents (Codex, Hermes, OpenCode), off to keep it to itself. It shows which agent owns the skill, and works just like the agent Auto on/off control.
  • Reveal in Finder from the file viewer. The file viewer header now has a Reveal in Finder button.

Fixes

  • Open files refresh live. A file you're viewing now updates automatically as an agent writes to it on disk.
  • Cleaner skill-sharing warnings. Sharing an ordinary skill no longer shows a spurious "lossy" warning — that warning is now reserved for skills that genuinely lose Claude-specific behavior when shared to another agent.
  • ⌘K stays scoped to the inspector. Sending a selection to the terminal with ⌘K now works only from the file inspector, not the terminal itself.
  • The file inspector tidies up. It closes when you open a new tab, and drops files that were renamed, moved, or deleted.

Notes

This update is available whenever you choose Check for Updates… from the Push menu.

v1.9.89

2026-06-08

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.89

Agents now keep running through a runtime outage with automatic failover, plus a couple of Codex terminal fixes.

New

  • Automatic runtime failover. Push now keeps a backup runtime ready and switches to it automatically if the primary one becomes unavailable, so your agents keep running instead of stalling.

Fixes

  • Codex tab spinner stops correctly after a review turn. Fixed a case where a Codex tab could keep showing as active after a sub-agent — like a code review — finished its turn.
  • Codex file edits show up in the inspector again. The session file list now understands Codex's apply_patch format, so files Codex changes appear in the inspector as expected.
  • Clearer recurring-issue marker. The recurring marker on issue cards now uses a blue rotate icon.

Notes

This update is available whenever you choose Check for Updates… from the Push menu.

v1.9.88

2026-06-07

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.88

The terminal status now follows Claude correctly through prompts and questions, plus a new source badge on issue cards and more performance work.

New

  • Source badge on issue cards. Issues now show a small icon for where they were captured — Mac, iPhone, or browser — as the last badge in the card's top row.

Fixes

  • The terminal spinner tracks Claude's prompts and questions correctly. While Claude is waiting on you — a permission prompt, a plan approval, or a multiple-choice question — the tab keeps showing as active, and it stops as soon as the turn actually ends. Previously the spinner could stop early while Claude was still working, or keep spinning after you answered or canceled.
  • New terminal tabs open at the right width. Fixed a case where opening a new tab while a file panel was open could leave it in a too-narrow column until the next resize.
  • More performance under heavy agent load. Continued moving background work off the main thread, so the interface stays smooth even with many agents streaming at once.

Notes

This update is available whenever you choose Check for Updates… from the Push menu.

v1.9.87

2026-06-06

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.87

Performance and stability: Push stays smooth even with many agents running, plus fewer background hiccups.

Fixes

  • Push stays responsive under heavy agent load. Fixed a periodic stutter where the interface could freeze for a few seconds while lots of agents were active. Push now does its network and data work off the main thread, so scrolling and switching views stay smooth no matter how busy your agents are.
  • Much less background disk activity. Fixed a runaway loop that could write gigabytes per day to disk in the background when a leftover Hermes session marker was present.
  • Quick Capture: Enter inserts a newline instead of selecting all the text.
  • Terminal file panel no longer animates on tab switch. Switching terminal tabs restores each tab's file inspector instantly, without a reflow.

Notes

This update is available whenever you choose Check for Updates… from the Push menu.

v1.9.86

2026-06-06

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.86

Navigation and terminal polish: move back and forward through the app like a browser, ⌘W closes the right thing, and each terminal tab remembers its file panel.

New

  • Back and Forward, app-wide (⌘[ / ⌘]). Step backward and forward through the views you've visited — sections, agents, files, issues — just like a browser's back button.
  • Each terminal tab remembers its file panel. The side file inspector's open/closed state and width are now saved per tab, so switching tabs keeps each one the way you left it.

Fixes

  • ⌘W now closes the active terminal tab, not the whole window. Previously it could close the entire window out from under you.
  • Smoother with many terminals running. Reduced background UI work when lots of agents are streaming at once, so the app stays responsive.

Changes

  • The terminal tab search box was removed. Filter your terminal tabs with the folder chips instead.

Notes

This update is available whenever you choose Check for Updates… from the Push menu.

v1.9.85

2026-06-06

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.85

The terminal mirror from 1.9.84 now works end to end — your open terminals appear reliably on your other devices, and you can watch (and type into) any of them from your phone, not just issue runs.

Fixes

  • Your live terminals now show up reliably on your other devices. The terminal mirror added in 1.9.84 could come up empty; your open tabs now register dependably, so they appear on your phone and the web as you open and close them.

New

  • Watch any open terminal from your phone — on demand. Open a terminal on another device to see its live screen and type into it, for any open tab, not only issue runs. It streams only while you're actually watching — nothing runs in the background for terminals nobody's looking at — so it stays light on your Mac and works on tabs you already have open.

Notes

This update is available whenever you choose Check for Updates… from the Push menu.

v1.9.84

2026-06-06

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.84

Operate your Mac's terminals from anywhere — every live terminal now mirrors to your other devices, and you can type into them remotely.

New

  • Operate your Mac terminals remotely. Every open terminal tab now mirrors to your other devices (your phone, the web) as a live session — not just issue runs. From an authenticated device on your workspace you can watch and type into a running terminal on your Mac, so you can answer a prompt or nudge an agent without being at your desk. Tabs sync automatically as you open and close them, and a background sweep tidies up sessions a crashed Mac left behind.

Polish

  • Continue a finished issue from any comment path. Commenting to continue a closed issue's session (added in 1.9.83) now behaves the same no matter how the comment is posted — including from the CLI's push issue update --comment.

Notes

This update is available whenever you choose Check for Updates… from the Push menu.

v1.9.83

2026-06-05

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.83

Terminal and History improvements — type in any language, filter your tabs, continue issues by commenting, and keep your sessions from disappearing.

New

  • Type Chinese, Japanese, and Korean in the terminal. IME composition (Pinyin, Japanese, Korean) now works — candidate windows and multi-keystroke input land correctly instead of dropping characters.
  • Filter terminal tabs with ⌘F. A search field filters your open terminal tabs by name; ⌘F focuses it from anywhere, and the field is pinned to the toolbar's trailing edge across the app.
  • Comment to continue a finished issue. Posting a new comment on a closed issue now resumes that issue's session, so you can pick a conversation back up just by replying — no need to reopen and re-run it.

Fixes

  • Sessions no longer vanish from History. A conversation you started or resumed during the current run used to show under Live Terminal while its tab was open, then disappear from both columns the moment you closed the tab — only reappearing after a restart. History now keeps its session index fresh as you work, so closing a tab moves the session straight into Sessions in the same refresh, with no restart needed. The same fix covers resuming, /clear, and /compact, and keeps each conversation to a single card.
  • Cleaner tab close. Closing a terminal tab now releases its session file handle right away instead of holding it open until unrelated activity cleared it.

Polish

  • The History, Issues, and agent-detail boards use calmer, neutral section-title icons.

Notes

This update is available whenever you choose Check for Updates… from the Push menu.

v1.9.82

2026-06-05

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.82

Visual polish across the History board, the cards, and the agent view — clearer columns, one consistent "Auto" mark, and better-aligned badges.

Improvements

  • Clearer History board. The three columns now read Live Terminal, Sessions, and Files, each with a refreshed icon, and the Files column is tinted yellow so the board's lanes are easy to tell apart at a glance.
  • The agent view matches the History board. An agent's detail view now shows Active Issues, Sessions, and Files columns with the same icons and colors — and the Active Issues lane shows only open work.
  • One consistent "Auto" mark everywhere. An agent set to run on its own now shows a single, consistent Auto marker — on issue cards, in the agent toolbar, and in the sidebar — replacing the older mismatched badges.
  • Recurring issues read more clearly. A recurring issue shows a simple clock marker at the front of its card (no more tinted background); its schedule still lives in the issue's Repeat control.
  • Tidier agent toolbar. The Auto/Manual control is simpler, and the runtime picker uses short names — Claude, Codex, OpenCode, Hermes.
  • Better-aligned badges. Icons in card badges now sit on the same line as their text instead of floating slightly low.

Notes

This update is available whenever you choose Check for Updates… from the Push menu.

v1.9.81

2026-06-04

Silent release — Sparkle finds this one on its own and installs it on next quit. You're seeing the notes because you clicked Check for Updates while it was queued.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.81

Terminal runs link to their issue correctly, finished sessions stop showing as running, plus a few History and folder-color fixes.

Fixes

  • Working an issue in a terminal links it correctly. Running /push work <id> now starts the session on the issue's assigned agent and tracks it as a real run — so the issue's Run status lights up and the session is bound to the tab — instead of failing silently and running untracked.
  • A finished session no longer looks like it's still running. When a terminal session's process exits, its run is released promptly, so it stops showing as a live "running" session — on this Mac and on a paired iPhone — once it's actually over.
  • Terminal-mode History shows the true folder for resumed Claude sessions, instead of a lossy placeholder path.
  • Folder-color polish. History's Files-column folder badge is colored in Terminal mode, and folder colors stay applied in the toolbar's overflow dropdown.

Notes

This update is available whenever you choose Check for Updates… from the Push menu.

v1.9.80

2026-06-04

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.80

Remote-access status you can actually read, a Find shortcut, and a couple of fixes.

What's new

  • See your remote-access status at a glance. Settings → Connection now always shows whether your iPhone can reach this Mac — Reachable from anywhere, Remote access is off, Connecting…, or a clear reason when it can't connect — in plain language, with the relay address shown as detail. (Previously this could disappear from Settings entirely right when the tunnel was having trouble.)
  • ⌘F focuses search. Press ⌘F to jump straight to the search field in whatever view you're in.
  • The New Issue popup remembers your draft if you cancel and reopen it.

Fixes

  • Resuming a terminal session from History restores its folder, focus, and scroll position instead of losing track of where you were.
  • Files written with a working-directory-relative path now open correctly in the file viewer instead of showing "File not found."

Notes

This update is available whenever you choose Check for Updates… from the Push menu.

v1.9.79

2026-06-04

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.79

Session-resume fixes, file-viewer polish, and a tunnel reliability fix.

Fixes

  • Resuming a session from History opens in the right folder. Reopening a Claude session from the History board now reliably lands in that session's original working directory — so it resumes the actual conversation instead of starting fresh in the wrong place.
  • The file viewer reads better. YAML frontmatter renders as a clean properties table, long list values show as a bulleted list (not cramped pill chips), and Markdown spacing was retuned for reading.
  • History files open in their own view in Terminal mode, instead of taking over the terminal's file inspector.
  • Switching to Terminal mode is instant — no confirmation alert.
  • Tunnel reliability. Fixed a case where the pairing tunnel could keep showing "Connecting…" / report itself disconnected even though it was actually working, with no automatic recovery.

What's new

  • Colorful runtime logos in the terminal tab strip.

Notes

This update is available whenever you choose Check for Updates… from the Push menu.

v1.9.78

2026-06-04

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.78

Colorful folders, plus a few Terminal-mode refinements.

What's new

  • Color your folders. Folder chips across the toolbar, Files, and History can now carry a color. A new folder is auto-assigned a distinct color the first time you open or cd into it, and you can recolor any folder yourself: select its chip, then press-and-hold to pick from the palette. Colors persist and stay in sync between Terminal and Full mode.
  • ⌘Y opens History from anywhere in Terminal mode.

Fixes

  • Cmd+Click a file path inside a full-screen terminal app (a TUI) now opens it in Push's built-in file viewer.
  • Terminal-mode History no longer lists Push's own internal/system sessions — just the folders you work in.

Notes

This update is available whenever you choose Check for Updates… from the Push menu.

v1.9.77

2026-06-03

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.77

A Terminal-mode navigation refresh.

What's new

  • Cleaner Terminal mode. Terminal mode no longer shows a sidebar — it's just your terminal, full width. Jump to History with the clock button in the toolbar, and return with the standard back button. (Full mode is unchanged — its sidebar stays.)

Fixes

  • ⌘N (New Issue) keeps working after switching between Terminal and Full mode.

Notes

This update is available whenever you choose Check for Updates… from the Push menu.

v1.9.76

2026-06-03

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.76

This release reshapes the History surface (formerly "Sessions") into one board that brings your terminals, past sessions, and the files behind them together.

What's new

  • Files, right next to your sessions. The History board now has a third column — Files — showing the artifacts your agents wrote and the files you've opened. Pick a folder chip or type a search and all three columns (Live · Sessions · Files) narrow together. Click any file to open it in the inspector.
  • One History board. "Sessions" is now History in the sidebar, and completed agent runs share the Sessions column instead of living in a separate one — so a folder's terminals, sessions, and files all sit side by side. The standalone Files sidebar entry has been retired now that files live on the board.
  • Files in Terminal mode. The file inspector and the per-session Files list work in Terminal mode too, not just Full mode.

Fixes & polish

  • The "you've opened this" badge on file cards is quieter — no longer accent-colored.
  • Switching from Full to Terminal mode no longer asks for confirmation.
  • Folder and system chips in the terminal tab strip show the right glyphs.

Notes

This update is available whenever you choose Check for Updates… from the Push menu.

v1.9.75

2026-06-03

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.75

A maintenance release — no user-facing changes. Internal improvements to the release pipeline's post-publish health check (better diagnostics; a CI-only check no longer falsely flags otherwise-valid releases).

Notes

This update is available whenever you choose Check for Updates… from the Push menu.

v1.9.74

2026-06-03

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.74

Performance and reliability polish for the Files surfaces.

What's changed

  • Improved: File tracking across the Files gallery and agent views now does its disk work off the main thread and caches reads — opening files, searching, and switching agents stay smooth even with a lot of files.
  • Fixed: A brief flicker that could momentarily show the previous agent's files when switching agents quickly.

Notes

This update is available whenever you choose Check for Updates… from the Push menu.

v1.9.73

2026-06-03

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.73

The Files surfaces now highlight what you've actually opened — not just everything an agent wrote.

What's changed

  • New: An agent's Files column now leads with the files you've opened (clicked or selected) for that agent, with a "Show all files" toggle for the rest — so the files you care about are up top instead of buried in everything the agent produced.
  • New: The Files gallery has an "Opened" filter chip — click it to show only files you've opened — and opened files get a small eye badge so they stand out at a glance.
  • Improved: Opening a file anywhere — a terminal, the file inspector, or the gallery — now consistently marks it as "seen" across all of these surfaces, live.

Notes

This update is available whenever you choose Check for Updates… from the Push menu.

v1.9.72

2026-06-03

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.72

The file preview now follows what your agents are working on — it keeps a per-session list of the files each agent creates or edits, and lets you jump between them.

What's changed

  • New: The file preview tracks the files an agent writes or edits during a session, updated live as it works — no clicking required. Open the preview and it shows the most recently changed file by default.
  • New: A chevron menu in the preview header lists every file a session has touched. Pick one to open it — the same as clicking its path in the terminal. Clicking a file pins it, so the preview stays on your choice while the agent keeps editing other files.
  • New: Works across every agent — Claude, Codex, OpenCode, and Hermes.
  • Changed: The preview header now wraps the file path in a rounded chip matching the active terminal tab, with the file switcher tucked in at the end.

Notes

This update is available whenever you choose Check for Updates… from the Push menu.

v1.9.71

2026-06-02

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.71

The file-preview toggle now lives in the terminal tab row, next to the theme button.

What's changed

  • Changed: The button that opens the file preview moved from the window toolbar to the right end of the terminal tab row — a small file-icon chip beside the theme picker, in the same glassy style. It shows only while the preview is closed; once it's open, the preview's own close button takes over.

Notes

This update is available whenever you choose Check for Updates… from the Push menu.

v1.9.70

2026-06-02

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.70

The file viewer can now hand a selection straight to your agent — by shortcut, by a button under the text, or by right-click — and a copy from it carries where it came from.

What's changed

  • New: Select text in a file and send it straight into the terminal with its source location attached (path:line), so the agent knows exactly what you're pointing at. Three ways to do it: press ⌘K, click the Send to Terminal button that pops up under the selection, or right-click → Send to Terminal. Focus jumps to the terminal so you can start typing your question right away.
  • New: Copying text from a file viewer now brings its origin along — paste into the terminal and the file path and line range are added above it automatically. Pasting into any other app is unchanged.
  • Changed: ⌘K no longer clears the terminal. Wiping a running agent's output by accident was too easy; Clear is still available from the terminal's right-click menu.
  • Changed: The file viewer's right-click menu is tidier — Look Up, Translate, Speech, Search with Google, and Services are gone, since they weren't useful for reading code or output.

Notes

This update is available whenever you choose Check for Updates… from the Push menu.

v1.9.69

2026-06-02

Silent release — Sparkle finds this one on its own and installs it on next quit. You're seeing the notes because you clicked Check for Updates while it was queued.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.69

Agents can now close out their own finished work, the system agent shows its real icon everywhere, and the Sessions board attributes history to the right folder.

What's changed

  • New: Agents can mark a finished one-time issue complete on their own — with a stated reason — instead of leaving every done task for you to close by hand. It's judgment-based (a merged PR is supporting evidence, not the trigger), recurring issues are never auto-closed, and it's on by default. Toggle it in Settings → General.
  • Fixed: The system agent now shows its distinct icon (a folder badged with a gearshape) everywhere its identity appears — session cards, issue cards, the assignee badge, the sidebar, and the detail header — not just on the terminal filter chip.
  • Fixed: Historical sessions on the Sessions board now attribute to the real folder agent they belong to, instead of occasionally inventing a stray agent from a folder's name.
  • Fixed: The Sessions board's per-folder history budget is keyed by the resolved agent, so a busy folder can no longer crowd a quieter one out of view.

Notes

This update installs automatically the next time you quit Push.

v1.9.68

2026-06-02

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.68

System-agent sessions now show up under Settings → Terminal → "Show system sessions" — previously the toggle surfaced nothing.

What's changed

  • Fixed: Turning on "Show system sessions" now actually lists the system agent's sessions under the system chip on the Sessions board. They were being filtered out as duplicates of background runs that are themselves hidden — so they appeared nowhere. This only affected machines where the system agent had already done work, which is why it slipped past the earlier fix.

Notes

This is a manual update — open Push and choose Check for Updates… to install it.

v1.9.67

2026-06-02

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.67

Reopening an issue that's already open in a terminal now takes you straight to that tab — no more duplicate.

What's changed

  • Fixed: When an issue's session is already open in a terminal tab, clicking it now jumps to that tab instead of opening a second one into the same session. This was most noticeable after relaunching the app, where reopening could spawn a duplicate terminal.
  • Clearer: The issue card's button now reads Open — and jumps to the tab — whenever the session is already open, and Reopen only when it needs to be resumed into a new tab.

Notes

This is a manual update — open Push and choose Check for Updates… to install it.

v1.9.66

2026-06-02

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.66

The Sessions view no longer caps out — every folder's sessions stay visible, however many you accumulate.

What's changed

  • Improved: Sessions are now indexed locally, so the board no longer stops at a fixed number of rows. Every working folder is represented — a busy folder can't crowd a quieter one off the list anymore — and your full set of recent sessions shows without the old "200 most recent" ceiling.
  • Faster: session titles, recaps, and stats are cached as they're read, so reopening the Sessions view doesn't re-scan your history each time.

Notes

This is a manual update — open Push and choose Check for Updates… to install it.

v1.9.65

2026-06-02

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.65

"Show system sessions" now actually shows them — grouped under their own "system" chip.

What's changed

  • Fixed: turning on Settings → Terminal → Show system sessions surfaced nothing. The system agent runs in a workspace folder whose path the Sessions filter couldn't match (a quirk of how Claude encodes session directory names), so every system session was silently dropped. They now appear correctly.
  • New: system sessions group under a dedicated "system" chip with the system icon (a folder badged with a gearshape), instead of scattering into "Other".

Notes

This is a manual update — open Push and choose Check for Updates… to install it.

v1.9.64

2026-06-01

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.64

The workspace system agent now has its own icon so it's easy to tell apart from your own folder agents.

What's changed

  • New: the workspace system agent — the behind-the-scenes runner Push uses for enrichment and internal automations — now shows a distinct icon (a folder badged with a gearshape) everywhere it appears: the Sessions board, the folder filter chips, and an issue's earlier-sessions list. Previously it borrowed an ordinary folder agent's plain folder icon, so the two looked identical. Turn on Settings → Terminal → Show system sessions to see it.

Notes

This is a manual update — open Push and choose Check for Updates… to install it.

v1.9.63

2026-06-01

Silent release — Sparkle finds this one on its own and installs it on next quit. You're seeing the notes because you clicked Check for Updates while it was queued.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.63

The terminal spinner now stops the moment an agent finishes or you interrupt it, plus issue-card and workspace-naming fixes.

What's changed

  • Fixed: the Claude activity spinner could keep animating after a turn was already done — e.g. after /goal finished or after you pressed ESC to interrupt. Push now treats the agent's session transcript as the source of truth for when a turn ends, so the spinner stops promptly while still spinning correctly through a permission prompt that's waiting on you.
  • Fixed: clicking an active issue that already has a session now reopens that session instead of spawning a fresh run.
  • New: the workspace name is now editable in Settings → General, and the issue prefix follows the rename — renaming a workspace re-derives the prefix and updates existing issue identifiers to match.

Notes

This update installs automatically the next time you quit Push.

v1.9.62

2026-06-01

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.62

Assign agents straight from an issue card, plus terminal keyboard and mouse fixes for full-screen apps like Claude Code.

What's changed

  • New: assign an agent to an issue right from its card — the assignee picker (Auto / a specific agent / Unassigned) is now on the card itself, not just the detail view. Cards also show an "Enriching…" indicator while Push is auto-filling an issue's details.
  • Fixed: in a full-screen terminal app (e.g. Claude Code's full-screen view), ⌘C now copies the selection (or does nothing when there's no selection) instead of typing a stray "c" into the prompt. The same fix covers other ⌘-key combos, so an unrecognized shortcut can no longer leak a stray character into the terminal.
  • Fixed: ⌘-clicking a link now opens it even while a full-screen terminal app has the mouse — previously links only worked in the normal (inline) view.
  • Fixed: Codex terminal tabs now show a proper title instead of an internal thread ID.
  • Changed: the Folders group in the sidebar is now a native collapsible section.

Notes

This is a manual update — open the Push menu and choose Check for Updates… to install it.

v1.9.61

2026-06-01

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.61

A round of Settings cleanup: connection-related controls now live in one place, and the in-progress Skills gallery is opt-in so it doesn't clutter the sidebar.

What's changed

  • Changed: the Apple Account settings pane is now Connection and folds in the former Devices pane — your Apple account, the pairing tunnel, and paired devices all live in one place. It still only appears in Server + Tunnel mode. (Disconnecting is now handled entirely by the mode switch, so the standalone Disconnect button is gone.)
  • Changed: the Skills sidebar section is now off by default while it's still evolving. Turn it on anytime under Settings → Experimental (Mac) or the Labs tab (web).
  • Fixed: the count badge on folder-filter chips (Issues, Sessions, Terminal, Files, Skills) no longer floats above the title — it now sits on the same baseline.

Notes

This is a manual update — open the Push menu and choose Check for Updates… to install it.

v1.9.60

2026-06-01

Silent release — Sparkle finds this one on its own and installs it on next quit. You're seeing the notes because you clicked Check for Updates while it was queued.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.60

Background jobs stay visible while they're still running, and Push now has optional, anonymous usage and crash reporting so we can find and fix problems faster.

What's changed

  • Fixed: background processes that Claude spawns no longer disappear from the active list while they're still running. The liveness check is now layered and debounced, so long-running background work stays tracked instead of being marked finished too early.
  • New: optional, anonymous usage and crash reporting. Push can send a once-a-day "active" signal (to count how many people use it) plus crash reports — no account, no personal data, and never any of your terminal contents. It's on by default to help us improve Push; turn it off anytime in Settings → Privacy → Send anonymous telemetry.

Notes

This update installs automatically the next time you quit Push.

v1.9.59

2026-06-01

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.59

Switching between Terminal, Server, and Server + Tunnel modes is reliable again, and Sessions are usable in Terminal mode. This release fixes a cluster of issues around mode switching, the workspace runtime, and how local sessions are grouped.

What's changed

  • Fixed: switching modes no longer breaks. The server could fail to start (Relay is not available in local_trusted mode) when a stray relay setting was left in the environment — it now ignores it and starts cleanly in local mode. The tunnel also reliably ends up in the state that matches the mode you picked, even when the switch restarts the server.
  • Changed: choosing Server mode + Tunnel no longer immediately opens an Apple sign-in window. Push just turns on, and the sidebar shows a Connect Apple Account button you tap when you're ready. Your sign-in stays connected across mode switches and restarts as before.
  • New: leaving Server + Tunnel mode while signed in now asks whether you'd like to disconnect your Apple account — so signing out is a choice, not automatic and not stuck-on.
  • Fixed: Settings → Runtimes no longer shows "Couldn't load default runtime." The workspace runtime now self-heals, so the default runtime always loads and can be changed.
  • Fixed: in Terminal mode, Sessions are now grouped by their folder (with folder icons) instead of collapsing into one giant Other pile — making the Sessions list usable with no server running.

Notes

This is a manual update — open the Push menu and choose Check for Updates… to install it.

v1.9.58

2026-05-31

Silent release — Sparkle finds this one on its own and installs it on next quit. You're seeing the notes because you clicked Check for Updates while it was queued.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.58

Claude terminal tabs no longer lose track of a turn after you answer a permission prompt. Previously, when Claude paused mid-work to ask for permission, the tab's working indicator could go dead the moment the prompt appeared — and stay dead even after you answered, which let the tab be closed while Claude was still generating. The spinner now stays live through the entire turn, and the close/reload/quit guard keeps protecting the tab until the work actually finishes.

What's changed

  • Fixed: the working glyph on a Claude tab now keeps spinning through a permission prompt and after you answer it — it only stops when the turn truly ends. The tab can no longer be closed without warning while Claude is still working.
  • The Terminal "Active" filter, the sidebar count, and the Dock badge now consistently mean the same thing — tabs that need attention (running or just finished) — and the Terminal toolbar matches the other views.

Notes

This is a silent update — Push installs it automatically the next time you quit.

v1.9.57

2026-05-30

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.57

Recurring work is now fully part of Issues — the standalone Recurring sidebar is gone, repeating work lives on the board like any other issue, and a new Recurring filter plus a "dormant" badge make scheduled work legible at a glance. Issue completion stays human-owned (agents do the work and report; you decide when it's done). The main window also shrinks much further for small or scaled displays, and a file-inspector resize crash is fixed.

What's changed

  • Recurring, fully unified — the separate "Recurring" sidebar item is gone; repeating work is just an issue on the board. A new Recurring filter (right after All) shows every scheduled issue across your folders, and recurring work shows up in each agent's Issues column instead of a separate strip.
  • Dormant schedules are obvious — a recurring issue's badge now shows · dormant (dimmed) when its agent is on Manual: the schedule won't fire until you switch that agent to Auto. One switch, made visible — no hidden second toggle.
  • Completion is human-owned — agents do the work and report via a comment; only you mark an issue done or archived (one-time or recurring). A recurring issue keeps running on its schedule until you retire it.
  • The window fits smaller screens — the main window now resizes down to 800×500, so it works on small displays and with "Larger Text" / scaled-resolution setups.
  • File-inspector resize fix — dragging the file inspector very wide no longer triggers a resize-storm crash.

Notes

This is a Manual update — install it from Push → Check for Updates.

v1.9.56

2026-05-30

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.56

Recurring work now lives inside Issues: an issue can repeat on a schedule (Hourly/Daily/Weekly) and a human decides when it's done, so there's one model to learn instead of separate Jobs and Issues. Terminal panes stay live when you drag the window between monitors or change resolution, the file viewer header collapses to a single compact path row, and "Auto" assignees now resolve to a real agent (or Unassigned) once routing runs.

What's changed

  • Recurring issues — any issue can now repeat: pick Never/Hourly/Daily/Weekly with a new Repeat control, and a recurring badge shows up in the row, list, and kanban views. Completion stays human-owned — agents report via comment and never auto-close a recurring issue.
  • Terminal multi-display sync — dragging a terminal pane to another monitor no longer leaves it blank or frozen, and switching screen resolution no longer leaves a stale cell grid. The surface re-points its display and re-syncs geometry on every screen change.
  • Tighter file viewer header — collapsed to a single path row pinned to the tab-strip height (the duplicate bold filename is gone), so the file-viewer chrome lines up with the terminal tab strip.
  • Auto-assign resolves cleanly — an issue created with Auto now resolves to the best-fit agent, or to Unassigned when no agent matches, instead of showing "Auto" forever after enrichment.

Notes

This is a Manual update — install it from Push → Check for Updates.

v1.9.55

2026-05-30

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.55

The assignee dropdown gets a clearer model: a new Auto option (with a spark icon) sits first and lets Push route the issue to the best-fit agent for you, your folder agents show their own colored icons, and Unassigned moves to the bottom. The same dropdown now appears in both the new-issue popup and the issue detail view. The floating "+" button is now an opt-in Experimental feature.

What's changed

  • New Auto-assign option in the assignee dropdown (spark icon, first) — Push picks the best-matching agent by its description during enrichment
  • Agents in the dropdown now show their own colored, type-aware icons; Unassigned is last
  • Same dropdown shared by the new-issue popup and the issue detail view
  • "Auto" vs "Unassigned" are now distinct: Auto routes automatically, Unassigned (or a specific agent) leaves the assignee alone
  • The floating new-issue "+" button is now an Experimental feature, off by default (enable it in Settings → Experimental)

Notes

This is a Manual update — install it from Push → Check for Updates.

v1.9.54

2026-05-30

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.54

A simpler Settings window: one-click skill install for every code agent, a streamlined Server pane, and smoother scrolling. Manual tier.

What changed

Install the push skill for all four code agents

  • Settings → Server → Skills now lists Claude Code, Codex, OpenCode, and Hermes, each with its own install status and an Install / Reinstall button. Installing copies the /push skill into that agent's skills location so it can drive Push issues — OpenCode also gets its slash-command shim written automatically.
  • Previously only Claude's skill could be installed from the app.

Streamlined Server pane

  • The Integrations (push CLI, Launch at login) now live in the Server pane as a clean status table — each shows whether it's installed, with Install / Reinstall only (no more Remove).
  • The Port is fully app-managed: it's fixed per install and auto-reclaimed if something stale is holding it, so there's nothing to misconfigure. The editable Port field and the developer-only Project Directory field are gone.
  • The server now always auto-starts and auto-restarts — those toggles are gone. Show notifications moved to General.

Less clutter

  • Removed the Secrets settings pane (secrets are still managed via the CLI; nothing about how agents use them changed).
  • Removed two experimental features: the Quick Terminal popup and the Cost tab/sidebar.

Smoother Settings scrolling

  • When a Settings pane is long enough to scroll, content now fades softly under the toolbar instead of overlapping the back/forward buttons and title — matching the native macOS look.

Notes for upgrading

Nothing to do. This is a Manual update — Push won't surface it on its own; you'll get it the next time you click Check for Updates….


If you went looking for this in Check for Updates: the Settings window dropped .windowStyle(.hiddenTitleBar) so the macOS 26 scroll-edge effect has a toolbar Liquid Glass surface to fade content into (.toolbarBackgroundVisibility(.automatic) + .scrollEdgeEffectStyle(.soft, for: .top)); InstallerService gained per-agent skill install across ~/.claude/skills, ~/.agents/skills, ~/.hermes/skills, and the OpenCode ~/.config/opencode/commands/push.md shim; AppConfig.load now coerces port/autoStart/autoRestart to app-managed values and ServerManager.start reclaims a stale-held port.

v1.9.53

2026-05-29

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.53

HTML files now preview the way they look in a browser. Manual tier.

What changed

Full-fidelity HTML preview

  • Opening an .html file in the Files viewer now renders it live by default — its CSS and JavaScript run, so slide decks, interactive docs, and styled pages look the way they do in a browser instead of as flattened, unstyled text.
  • Web fonts load too: Google Fonts is allow-listed, so a deck shows in its intended typeface rather than a serif fallback.
  • The render runs inside a sandboxed, isolated frame. The page has no access to your files, cookies, or the app, and it can't reach the network beyond the allow-listed fonts — so a file written by an agent can run its own scripts to display itself, but it can't read anything on your machine or phone home.
  • The header still has the Preview / Live / Source toggle: Preview is the sanitized, scripts-stripped view; Live is the new faithful render; Source is the highlighted HTML.

Notes for upgrading

Nothing to do. This is a Manual update — Push won't surface it on its own; you'll get it the next time you click Check for Updates….


If you went looking for this in Check for Updates: FilePreviewView's default HTMLViewMode is now .live, which routes HTML through a new FileViewerContent.htmlLive case. shell.html renders it into an <iframe sandbox="allow-scripts"> (opaque origin, no allow-same-origin); the inherited CSP keeps connect-src 'none' with fonts.googleapis.com / fonts.gstatic.com host-allow-listed for style-src / font-src — fidelity without an exfiltration channel.

v1.9.52

2026-05-29

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.52

A cleaner agent detail toolbar. Manual tier.

What changed

Agent detail toolbar

  • The agent toolbar is now three clear, independent controls: Open Terminal, the code agent (runtime) selector, and the Auto/Manual selector.
  • Open Terminal opens a terminal in the agent's folder running its selected runtime — the same path as spawning a tab from the Terminal section's + button. It's hidden for agents that have no folder.
  • Auto/Manual is now a pull-down that matches the runtime selector's look (icon + label + chevron), instead of the old chip pair. Auto = the agent picks up issues and fires its Jobs on its own; Manual = it runs only when you wake it.

Pick an agent color from its folder icon

  • The agent color picker moved off the toolbar onto the folder glyph in the detail header — click the colorful folder icon next to the agent name to pick a color. The glyph itself shows the chosen color, and it's wrapped in a circular Liquid Glass button.

Notes for upgrading

Nothing to do. This is a Manual update — Push won't surface it on its own; you'll get it the next time you click Check for Updates….


If you went looking for this in Check for Updates: the AgentDetailView toolbar Run button was replaced by an Open Terminal button (reusing newAgentTab with a new cwd: param), the FilterChip autonomy pair became AgentAutonomyMenu (a structural copy of AgentRuntimeMenu), and the color picker is now the header folder glyph wrapped in a custom ClearGlassCircleButtonStyle (.glassEffect on the button label — the built-in .buttonStyle(.glass) is dropped to thin material through a Menu's AppKit bridge).

v1.9.51

2026-05-29

Silent release — Sparkle finds this one on its own and installs it on next quit. You're seeing the notes because you clicked Check for Updates while it was queued.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.51

A tidier agent toolbar. Silent tier.

What changed

Agent detail toolbar cleanup

  • The Run button is gone from the agent detail toolbar — it was rarely used, so the toolbar now leads straight into the Auto/Manual, runtime, and color controls.
  • The project Set up button (shown for folders that declare a .push/project.json) moved off the toolbar to sit inline right after the folder path, where it reads as the advanced, project-specific affordance it is.

Notes for upgrading

Nothing to do. This is a Silent update — Push installs it automatically the next time you quit, and the sidebar pill will light up briefly to let you know.


If you went looking for this in Check for Updates: the toolbar Run button in AgentDetailView is commented out (kept, not deleted, for possible future restore), and the .push/project.json provisioning button plus its state/helpers moved from the toolbar into AgentHeaderSection's header row, grouped with the working-directory card.

v1.9.50

2026-05-29

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.50

Feed becomes opt-in, and a workspace that ever produced a file can be deleted again. Manual tier.

What changed

Feed is now opt-in

  • The Feed sidebar entry is hidden by default. Sessions and Issues are the primary work surfaces; Feed is the raw stream for those who want it. Turn it back on under Settings → Experimental → "Feed sidebar entry" (Full mode only), alongside the other experimental flags.
  • Your view never strands on a hidden Feed: if Feed was your launch default with the flag off you land on Terminal instead, toggling Feed off while viewing it bounces you to Terminal, the notification "View" action falls back to Sessions, and the "Open on launch" picker drops Feed when it's off.

A file-producing workspace can be deleted again

  • Deleting a workspace whose agents had ever produced a file (a report, deliverable, etc.) failed with a 500 and left the workspace stuck with no removal path. Workspace deletion now clears those agent files first, so the delete goes through cleanly.

Notes for upgrading

Nothing to do. This is a Manual update — Push won't surface it on its own; you'll get it the next time you click Check for Updates….


If you went looking for this in Check for Updates: Feed is gated behind ExperimentalFeatures.feedSidebar (default false) with launch-time + live selection clamps; and workspaceService.remove() now deletes agent_files (scoped by workspaceId) before the agents delete, fixing the agent_files_agent_id_agents_id_fk 23503 violation that rolled back the whole transaction.

v1.9.49

2026-05-28

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.49

Settings → Runtimes tells you at a glance whether each runtime is ready to use — installed and signed in — instead of just "is the binary there." Server + Status merge into one pane, and the confusing "Coder Agent" pane is gone. Manual tier.

What changed

Runtimes show real readiness, not just "installed"

  • Each runtime (Claude / Codex / OpenCode / Hermes) now shows one status pill: Ready (installed + signed in), Sign-in needed (installed but not logged in), or Not installed. Previously the green dot only meant the binary was on PATH — you couldn't tell from this screen whether you'd actually be able to run anything until a task failed.
  • The sign-in check is cheap and instant — no waiting on the slow "Diagnose" probe. (Diagnose is still there, per-runtime, for the deep round-trip check.)
  • The default runtime is now shown and chosen right in the table: a DEFAULT badge marks it, a compact callout up top states which runtime is default and whether it's ready, and you set a new default from a runtime's ⋯ menu. The separate "Default runtime adapter" dropdown is gone.
  • Version moved to a caption under each runtime's name, so the table reads cleanly.

Server + Status settings merged

  • The separate Status sidebar item is gone. It was a single read-only line about the same local server the Server pane configures.
  • The Server pane now opens with a live Status section (Running · PID · Port) plus a Restart Server button, with Configuration below — the same status-header-then-config shape as the Runtimes pane. One subject, one pane.

"Coder Agent" settings pane removed

  • The Settings → Coder Agent pane is gone. It was a leftover re-run button for first-launch setup, and its "Status: Not run" never reflected whether you actually had an agent — it was just confusing.
  • Nothing is lost: opening a terminal in any folder already creates an agent for that folder, and the push setup command still exists in the CLI.

Notes for upgrading

Nothing to do. This is a Manual update — Push won't surface it on its own; you'll get it the next time you click Check for Updates….


If you went looking for this in Check for Updates: Runtimes fuses install + auth into one readiness pill (server GET /runtimes now returns auth, computed by a cheap prompt-free probe in runtimes.ts decideAuth/gatherAuthSignals); the default runtime is selected in-table; and the orphaned Coder Agent / SetupRunner / SetupChecklist cluster was deleted.

v1.9.48

2026-05-28

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.48

The Sessions cards drop a redundant chip and fill in their body with the tied issue's description when no recap is available. Manual tier.

What changed

Cleaner Agent History cards

  • The "Agent" and "/push" chips on each card in the Agent History column are gone. The column header already says "AGENT HISTORY", so the per-card chip was restating the column's classification.
  • Card header now reads: [issue] [folder] [Claude] Reopen. Less visual noise, same information density for actionable identity (issue, folder, provider).

Cards fill in their body from the tied issue

  • Before: cards without a recap (most Codex/OpenCode/Hermes runs, plus Claude sessions that never idled out) rendered an empty body block under the title.
  • After: the card body falls back to the tied Push issue's description when no recap is available. The same body slot, the same italic-secondary styling, the same 4-line visual truncation.
  • The fallback only fires when there's a real issue tied to the card (push-run cards with run.issue?.identifier); cards with no issue tied still show no body (unchanged).
  • Performance: a single hash lookup per card. The issue descriptions are pre-cached in a map on the workspace store, rebuilt whenever issues change, so render-time work stays O(1) per card.

Notes for upgrading

Nothing to do. This is a Manual update — Push won't surface it on its own; you'll get it the next time you click Check for Updates….


If you went looking for this in Check for Updates: cards in Agent History drop the redundant Agent//push chip and fall back to showing the tied issue's description when no recap exists. Cache is WorkspaceStore.issueDescriptionsByIdentifier, rebuilt by a Combine sink on $issues.

v1.9.47

2026-05-28

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.47

Follow-up to 1.9.46: fixes a second source of Sessions column-2 latency that only shows up when you have many live Claude tabs. Manual tier.

What changed

Sessions column 2 is fast again on machines with lots of live tabs

  • 1.9.46 fixed the cold-cache cost on the first Sessions navigation after launch. But on machines with many live Claude tabs (20+), opening Sessions a second time often felt just as slow as the first — alternating fast/slow with each navigation.
  • Root cause: the helper that loads per-card metadata for the Live column (liveMetadata) was, as a side effect, wiping the entire Sessions-column metadata cache every time it refreshed its candidate inventory. With many live tabs the wipe happened constantly, so the next time you opened Sessions, column 2 had to re-parse every session from disk again.
  • After 1.9.47: that helper now refreshes only the candidate inventory and leaves the metadata cache alone. Column 2 stays warm regardless of how busy column 1 is.

Notes for upgrading

Nothing to do, and your data is untouched. This is a Manual update — Push won't surface it on its own; you'll get it the next time you click Check for Updates….


If you went looking for this in Check for Updates: a helper that loaded Live-column metadata was unintentionally wiping Sessions-column cache as a side effect (pruneCacheToTouched({}) interpreted "nothing touched" as "everything stale"). Fixed by making the prune a no-op when nothing was touched.

v1.9.46

2026-05-28

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.46

The Sessions page opens faster on the first navigation after launch (about 750 ms shaved off), and Settings → General now shows the correct mode when you open it. Manual tier.

What changed

Sessions: column 2 stops being empty on first open

  • Before: navigating to Sessions for the first time after launching Push showed Live + Agent History almost immediately, but the middle "Sessions" column stayed blank for about a second while Claude's per-session metadata cache warmed.
  • After: an eligibility-aware warmup runs in the background right after Push connects (low priority, off the main thread), so by the time you click Sessions the cache is hot. Typical first-nav now completes in around 90–130 ms instead of around 900 ms. Identical mechanism on subsequent navigations within the same launch.
  • Internals: the old all-providers warmup at app launch was racing the new eligibility-aware one, and each was pruning the other's writes out of the on-disk cache. The legacy warmup is now gated to Terminal mode (where it's still useful); Full mode runs only the eligibility-aware path. Folder agents and the "Show system sessions" toggle from 1.9.45 are unaffected.

Settings: Mode picker now reflects reality on every open

  • Before: opening Settings → General sometimes showed "Server mode (local only)" even when Push was actually running with the tunnel up. The picker only refreshed when the Apple Account pane was visible — but that pane is hidden in non-tunnel modes, so the data underneath the picker stayed stale.
  • After: the deployment state is shared across the whole app and re-checked every time Settings opens. The picker shows the real mode without you having to navigate elsewhere first. As a side effect, flipping from Server mode (with tunnel) to Server mode (local only) now actually disables the tunnel — previously the disable was skipped when the picker was reading stale state.

Notes for upgrading

Nothing to do, and your data is untouched. This is a Manual update — Push won't surface it on its own; you'll get it the next time you click Check for Updates….

If you ever want to see the per-stage Sessions perf attribution, the diagnostic HUD is opt-in via defaults write ai.massless.push sessions.showPerfHud -bool true (no Settings entry).


If you went looking for this in Check for Updates: Sessions's middle column now populates in around 100 ms on first open instead of around 900 ms (fixed two warmups racing on the same on-disk cache). Separately, Settings → General's Mode picker now shows the correct mode on every open instead of sometimes lagging behind the real state.

v1.9.45

2026-05-28

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.45

The Sessions page now defaults to your own coding work — workspace scratch and other non-folder sessions are filtered out at scan time, so the page loads faster and reads cleaner. A new Settings toggle brings them back if you want them. Manual tier.

What changed

Sessions page focuses on folder-bound work

  • The three Sessions columns (Live, Sessions, Agent History) now show only sessions whose working directory belongs to one of your folder agents. Sessions in unrelated directories — .cache dirs, hidden folders, the workspace scratch dir, codex test stand-ins — no longer appear by default.
  • The filter happens at scan time, not at chip-render time: each provider (Claude / Codex / OpenCode / Hermes) skips the title / recap / aggregation parse for ineligible sessions entirely. On a busy machine this trims a noticeable amount of work from each refresh.
  • The folder-filter chip strip at the top of the Sessions page also collapsed cleanly: instead of one chip per raw resolved id (which sometimes surfaced random UUIDs or hidden-dir basenames), every non-folder, non-system session falls into one shared "Other" bucket and every system entry collapses into one "system" chip.

New toggle: Settings → Terminal → Sessions → "Show system sessions"

  • Off by default. When on, sessions from Push's internal workspace agent (the workspace_runtime scratch dir — enrichment one-shots, internal automations) come back into the Sessions page and chip strip.
  • Flip it without restarting Push; the Sessions historical scan reruns on toggle.

Notes for upgrading

Nothing to do, and your data is untouched on disk — only what the Sessions page shows is narrowed. If a session you previously saw is no longer there, it's still there on disk; either add a folder agent that covers its directory, or turn on "Show system sessions" in Settings.

Hermes historical sessions specifically: Hermes doesn't record a working directory in its sessions table, so Hermes rows are hidden in Full mode under the new filter. Terminal mode is unaffected.

This is a Manual update; Push won't surface it on its own. You'll get it the next time you click Check for Updates….


If you went looking for this in Check for Updates: Sessions now defaults to folder-agent sessions only; the workspace-scratch + non-folder sessions are dropped at scan time (faster + cleaner). A new "Show system sessions" toggle in Settings → Terminal → Sessions opts those back in.

v1.9.44

2026-05-28

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.44

The issue's Run button now lights up when an agent works on it from an already-open terminal tab — not just when you launched via Run. Manual tier.

What changed

Run button reflects what's actually happening in your tabs

  • Before: the issue card's green "● Open" indicator only lit up when you clicked Run on that issue. If an agent was already sitting in a tab and you (or it) typed /push work <id> to start working, the button stayed dark — the issue had no idea its work was live.
  • After: typing /push work <id> in any existing tab now lights the same green indicator on the issue's card. Clicking Open focuses that tab (not a new one); closing the tab cleanly completes the run; the Stop button reaches the right session.

Multiple issues in the same tab — handled

  • The same terminal can host work on more than one issue over time. Each issue tracks its own button state independently. When you switch the tab from PU-A to PU-B, PU-A's button drops back to idle and PU-B's lights up — without you closing or reopening anything.

Notes for upgrading

Nothing to do, and your data is untouched. This is a Manual update, so Push won't surface it on its own; you'll get it the next time you click Check for Updates…. The new behavior applies to terminal tabs you start (or /push work calls you make) after updating.


If you went looking for this in Check for Updates: when an agent runs /push work <id> in an existing tab, the issue's Run button now goes green ("Open") and Open focuses that tab — closing it ends the run, and Stop reaches it. Multiple issues per tab work as you'd expect.

v1.9.43

2026-05-28

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.43

The live terminal mirror reads right on a phone now — no more repeated lines, no keyboard popping up. Manual tier.

What changed

Live terminal mirror: no more repeated/garbled lines

  • When you watch a running agent's terminal from your phone, the mirror now matches the Mac's full grid — both columns and rows. Before, it matched the width but guessed the height, and because Claude Code redraws its live area by moving the cursor up and rewriting, the wrong height meant frames piled up instead of overwriting — so you'd see the same line (often an ID/UUID) printed over and over while the agent worked. It now scrolls in lockstep with the Mac, so the live view stays clean.
  • The Mac terminal is usually larger than a phone screen, so the mirror stays at a readable size and you swipe to see the rest.

No keyboard over the terminal

  • The remote terminal is read-only — there's nothing to type into it — so it no longer brings up the on-screen keyboard (and its navigation bar) when you open it on a phone.

Notes for upgrading

Nothing to do, and your data is untouched. This is a Manual update, so Push won't surface it on its own; you'll get it the next time you click Check for Updates…. The mirror fix applies to terminal runs you open after updating.


If you went looking for this in Check for Updates: the phone's live terminal mirror now matches the Mac's full grid (so redraws stop piling up into repeated lines) and no longer pops the on-screen keyboard over a read-only view.

v1.9.42

2026-05-28

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.42

"Set up" now tells you when a project has changed, plus a deployment-mode settings fix. Manual tier.

What changed

"Set up" shows when a project needs re-setup

  • A repo's .push/project.json can now carry a version token. Once you've set a project up, if the repo later bumps that token — because its dependencies or setup changed — the project's agent shows Update instead of Set up, so you know to re-run it rather than discovering a stale install through an error. The project picks the scheme: a plain version number, or an automatic hash of its dependency files.

Deployment-mode settings gating

  • The Apple and Devices settings panes now appear only in Server + Tunnel mode, where they apply — continuing the local-first refinements from 1.9.39–1.9.41.

Notes for upgrading

Nothing to do, and your data is untouched. This is a Manual update, so Push won't surface it on its own; you'll get it the next time you click Check for Updates….


If you went looking for this in Check for Updates: a project set up from source now shows an Update badge when its .push/project.json version changes, and the Apple/Devices settings panes are gated to Server + Tunnel mode.

v1.9.41

2026-05-27

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.41

Set up a cloned project from source in one click. Manual tier.

What changed

Set up a project from source

  • When a repo you've opened declares a .push/project.json (a skill to register plus a setup command), its agent now shows a Set up button. One click registers the project's skill into your Claude skills — so Push and your agents can use it — and runs the project's setup command in a terminal to install its dependencies. It turns "clone a repo, then manually wire up its skill and install deps" into a single action. Re-running is safe.

Notes for upgrading

Nothing to do, and your data is untouched. This is a Manual update, so Push won't surface it on its own; you'll get it the next time you click Check for Updates….


If you went looking for this in Check for Updates: a repo that declares .push/project.json now gets a one-click Set up action on its agent — registering its skill and running its setup command.

v1.9.40

2026-05-27

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.40

Fixes the new Run Instance button and refines the local-first deployment modes from 1.9.39. Manual tier.

What changed

Run Instance now launches in your shell

  • The Run Instance button (added in 1.9.39) ran the start command in a bare shell that didn't load your PATH — so tools like npm (nvm) or a project's CLI weren't found, and multi-step start commands didn't run. It now launches in your normal login shell, so your full PATH is available and the command runs exactly as it would in a terminal you opened yourself.

Deployment-mode refinements

  • Following 1.9.39's local-first modes, the sidebar sign-in now appears only in Server + Tunnel mode (where it applies), and existing installs are migrated to the right mode automatically. No action needed.

Notes for upgrading

Nothing to do, and your data is untouched. This is a Manual update, so Push won't surface it on its own; you'll get it the next time you click Check for Updates….


If you went looking for this in Check for Updates: the Run Instance button now launches in your normal shell (PATH + multi-step start commands work), and the local-first deployment modes are refined so sign-in shows only in Server + Tunnel mode.

v1.9.39

2026-05-27

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.39

Push now runs fully local by default — Apple Sign-In and the relay tunnel are opt-in — and skills can start their companion app in one click. Manual tier.

What changed

Local-first by default

  • A fresh or unconfigured instance now runs fully locally — no Apple Sign-In, no relay tunnel. The three product modes are Terminal, Server (local), and Server + Tunnel; the server and CLI default to local instead of relay. Relay is opt-in: connecting Apple turns the tunnel on, and disconnecting turns it back off. A one-tap toggle pauses or resumes the tunnel (local ↔ tunnel) without fully disconnecting Apple, and lossy mode switches ask for confirmation first.
  • Push self-heals a missing or corrupt config.json at boot instead of stranding you on an "Apple required" prompt or breaking push doctor. A config that's present but invalid is now surfaced loudly (logged at boot and in /api/health) rather than silently ignored.

Run a skill's instance when it's down

  • A skill can point at a companion app — its "instance" — with a URL. Before, if that app wasn't running, the Open Instance button just dead-ended on a "can't connect" page. Now, when the instance is down and the skill declares how to start it, the button becomes Run Instance: one click opens a terminal in the right folder, runs the start command, and switches you to it so you can watch it boot. Push re-checks every few seconds, so the button flips back to Open Instance the moment the app answers. Auto-start is limited to your own skills, so an installed skill can't run a command on click.

Agent names self-heal to match their folder

  • Following 1.9.38's "agent names follow their folder," any agents whose names had drifted from their folder are now corrected automatically. No action needed.

Notes for upgrading

Nothing to do, and nothing changes about your data — tabs, sessions, runs, agents, and files carry over untouched. Existing instances keep their current mode; the new local-first default only applies to fresh or unconfigured ones. This is a Manual update, so Push won't surface it on its own; you'll get it the next time you click Check for Updates….


If you went looking for this in Check for Updates: Push now runs fully local by default with Apple Sign-In and the relay tunnel made opt-in, skills can start their companion app for you (a new Run Instance button), and drifted agent names self-heal to match their folder.

v1.9.38

2026-05-27

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.38

Agent names now follow their folder, and you can install skill-powered apps straight from a URL or a hosted registry. Manual tier.

What changed

Agent names follow their folder

  • An agent's name is now always its folder's name — set automatically and kept in sync. Move an agent to a new folder and its name follows along. The name is no longer hand-editable (and the old free-text job-title line is gone): the folder is the single source of truth. The workspace system agent and skill-instance runners keep their own managed names.

Install skill-instances from a URL or a hosted registry

  • You can now install a skill-powered app hosted on Push directly from an http(s) URL, or from a hosted registry — gated by an Ed25519 signature on the bundle. This makes the host / verify / install groundwork from 1.9.37 reachable end to end.

Notes for upgrading

Nothing to do, and nothing changes about your data — tabs, sessions, runs, agents, and files carry over untouched. Existing agents keep working; their names now simply reflect their folders. This is a Manual update, so Push won't surface it on its own; you'll get it the next time you click Check for Updates….


If you went looking for this in Check for Updates: agent names are now derived from their folder (and no longer hand-editable), plus you can install skill-instances from a URL or a hosted registry.

v1.9.37

2026-05-27

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.37

Groundwork for hosting skill-powered apps on Push, plus a tidier agents list. Manual tier.

What changed

A new, invisible "skill" agent kind

  • Push now recognizes a third agent role — skill — for the hidden runner behind a skill-powered app hosted on Push (alongside ordinary agents and the workspace system agent). Skill runners are invisible plumbing: they never show up in the agents list or sidebar, and can't be assigned issues. If you don't host a skill-instance, nothing about your workspace changes.

Foundation for hosting skill-instances

  • Internal groundwork so a skill (a capability with a CLI) can become a hosted, packaged, signed, shareable app on Push. The build → package → distribute recipe is now documented for agents working in Push.

Notes for upgrading

Nothing to do, and nothing changes about your data — tabs, sessions, runs, agents, and files carry over untouched. This is a Manual update, so Push won't surface it on its own; you'll get it the next time you click Check for Updates….


If you went looking for this in Check for Updates: it's mostly invisible groundwork — a hidden "skill" agent kind (for skill-powered apps hosted on Push) kept out of your agents list, plus the foundation for hosting and packaging skill-instances.

v1.9.36

2026-05-26

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.36

A new "Open Instance" action on skill pages, plus snappier, fairer session lists. Manual tier.

What changed

Open a skill's app right from its page

  • When a skill ships its own web app, Push's skill detail view now shows an "Open Instance" button in the toolbar, with a dot that tells you whether the app is reachable. Click it to jump straight to the running app. Push learns this from the skill itself — any skill that declares a web app gets the button, with no Push-side configuration.

Faster, fairer session lists

  • The Sessions and agent views now refresh on the events that actually change them instead of polling on a timer — so updates land quicker and the UI does less idle work. This also fixes a case where Codex sessions could be starved out of the columns when other providers were busy; every provider now gets its fair share.

Notes for upgrading

Nothing to do, and nothing changes about your data — tabs, sessions, runs, agents, and files carry over untouched. This is a Manual update, so Push won't surface it on its own; you'll get it the next time you click Check for Updates….


If you went looking for this in Check for Updates: skill pages can now open a skill's web app ("Open Instance"), and session lists refresh faster and treat Codex fairly.

v1.9.35

2026-05-25

Silent release — Sparkle finds this one on its own and installs it on next quit. You're seeing the notes because you clicked Check for Updates while it was queued.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.35

Readability polish for the live terminal on phones, plus a cleaner sidebar account chip. Silent tier.

What changed

Live terminal reads better on phones

  • When you watch a running agent's terminal from your phone, the text now renders at a legible font size, and you can scroll horizontally when a line runs wider than your screen instead of having it clipped. Builds on the width-fidelity fix from 1.9.33 — the mirror keeps the Mac's exact grid, and now the parts that don't fit are reachable rather than cut off.

Sidebar account chip refinements

  • The signed-in account chip at the bottom of the sidebar is tuned to sit more naturally: its left/right margin now matches its bottom margin, and the avatar sits balanced inside the capsule's rounded end. Small, but it makes the footer feel more "in place."

Notes for upgrading

Nothing to do — this is a Silent update. Push installs it the next time you quit, and the sidebar's update pill lights up briefly to let you know. Your tabs, sessions, runs, agents, and files carry over untouched.


If you're seeing this, Push picked up a Silent update on quit. Polish release — the phone live-terminal view is more readable (legible font + horizontal scroll), and the sidebar account chip sits more naturally.

v1.9.34

2026-05-25

Silent release — Sparkle finds this one on its own and installs it on next quit. You're seeing the notes because you clicked Check for Updates while it was queued.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.34

A round of native-feel polish: the sidebar account area, the issue detail view, and the file/Skills panels all sit better in macOS now. Silent tier.

What changed

Native sidebar account footer

  • The account area at the bottom of the sidebar is now built from native macOS controls instead of hand-rolled look-alikes. The signed-in row (your avatar, email, and pairing status) is a single Liquid Glass capsule, and the Connect Apple Account button is a native prominent-glass capsule.
  • The line that separates the footer from the list above it is now the system scroll-edge divider — it appears only while rows actually scroll under the footer and stays out of the way (immersive, no divider) when the list is short. Previously this was a hand-drawn fade.

Issue detail opens as a resizable sheet

  • Opening an issue now presents its details in a resizable modal sheet you can size to fit the work, rather than a fixed panel.

File and Skills panels match the app background

  • The file viewer and the Skills views are now transparent, so they share the app's background instead of showing a mismatched panel behind the content.

push doctor is mode-aware

  • The push doctor CLI command now tailors its checks to how you're running Push — terminal mode vs. full mode — so it stops reporting things that don't apply to your setup.

Notes for upgrading

Nothing to do — this is a Silent update. Push will install it the next time you quit, and the sidebar's update pill will light up briefly to let you know. Your tabs, sessions, runs, agents, and files carry over untouched.


If you're seeing this, Push picked up a Silent update on quit. This release is native-feel polish — the sidebar account footer, issue detail sheet, and file/Skills panels all sit more naturally in macOS.

v1.9.33

2026-05-24

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.33

The live terminal now renders correctly on your phone (and any remote viewer). Manual tier.

What changed

Faithful live terminal on remote screens

  • When you watch a running agent's terminal from your phone or another device, the mirror now renders at the Mac's exact terminal width instead of guessing its own. Previously a narrow screen fit itself to ~40 columns and replayed a 140-column TUI into it — so text overlapped, status bars doubled up, and Claude Code's in-place animations (the spinner, folded tool output) stacked into a wall instead of overwriting.
  • The viewer now shrinks the font to fit your screen width while keeping the producer's grid, so the layout matches what you see on the Mac — folding, colors, and live redraws all come through faithfully.
  • It follows the Mac live: resize the terminal window mid-run and the remote view re-fits.

Notes for upgrading

Nothing to do — Manual update. The fix applies to new Run-in-Terminal runs started after updating; re-open a live terminal on your phone to see it. Your tabs, sessions, runs, agents, and files carry over untouched.


If you're reading this in Push, you clicked Check for Updates. Manual tier — the live terminal mirror now renders at the Mac's real width on phones and remote viewers, so it no longer garbles.

v1.9.32

2026-05-24

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.32

Every agent can carry a color, and folder attribution reads the same everywhere. Manual tier.

What changed

Pick a color for each agent

  • Open an agent's detail view — there's now a color picker in the toolbar, right next to the runtime picker. Choose from a muted palette, or "Default" for none.
  • The agent's folder icon adopts that color everywhere it appears: the sidebar row, the folder-filter chips on every section's toolbar, the cards (issues, sessions, jobs, files), and the agent detail header. One color per agent, consistent across the app.
  • Colors are opt-in — agents start neutral until you pick one, and a colored agent updates live across every surface the moment you change it.

One consistent folder pill

  • The "which folder / which agent" tag — folder icon + name — now renders as the same capsule badge across issues, sessions, jobs, the feed, and files. It used to be a loose icon-and-text that sat differently on each card.
  • On cards it leads the top row (right after the issue number), so attribution scans at a glance.

Skills open immersively

  • Clicking a skill now renders its detail in the full WebKit file viewer, matching how files preview.

Notes for upgrading

Nothing to do — Manual update. Your tabs, sessions, runs, agents, and files carry over untouched. Agents you haven't colored stay neutral.


If you're reading this in Push, you clicked Check for Updates. Manual tier — agents can now carry a color (set it in the agent's detail toolbar), and folder attribution reads as one consistent pill everywhere.

v1.9.31

2026-05-23

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.31

Agents are folders, end to end — and the runtime list is trimmed to what we actually support. Manual tier.

What changed

Add an agent by adopting a folder

  • The "+" on the Agents sidebar now opens a folder picker and tracks that folder as an agent — the same thing that already happens when you open a folder in a terminal. An agent is a folder, so creating one means pointing Push at a directory.
  • The old "new agent" form (name + adapter + archetype) is gone. It created folder-less agents that no longer fit the model.

Runtimes trimmed to what's supported

  • Settings → Runtimes and the workspace runtime picker now list only the runtimes Push actually supports: Claude Code, OpenAI Codex, OpenCode, and Hermes.
  • Gemini, Pi, and Cursor — already hidden, and never fully wired for terminal use — have been retired. Existing agents on those types keep loading (they fall back gracefully); you just can't create new ones.

Notes for upgrading

Nothing to do — Manual update. Your tabs, sessions, runs, and files carry over untouched. Any agent bound to a retired runtime keeps working.


If you're reading this in Push, you clicked Check for Updates. Manual tier — the Agents "+" now adopts a folder as an agent, and the runtime list is trimmed to Claude / Codex / OpenCode / Hermes.

v1.9.30

2026-05-23

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.30

Markdown, code, and HTML files now render in a real web view. Manual tier.

What changed

File Viewer rebuilt on WebKit

  • The file preview now renders Markdown, source code, and HTML through one fast WebKit view instead of the old native text stack.
  • Markdown gets GitHub-grade styling and selectable-across-the-whole-document text — previously you couldn't select past a single paragraph.
  • Code gets syntax highlighting with a line-number gutter.
  • HTML files now render as a sandboxed page, with a Preview ⇄ Source toggle in the header.
  • Everything renders offline and sandboxed — no network, and any embedded scripts are stripped (DOMPurify + a strict content-security policy).

Files: dead entries no longer look openable

  • Files whose path no longer exists — e.g. an agent wrote them in a temporary worktree that was later cleaned up — are now greyed out and marked "File no longer exists" in the Files gallery, instead of looking like normal files that dead-end at "File not found" when you open them.

Notes for upgrading

Nothing to do — Manual update. Your tabs, sessions, runs, and files carry over untouched.


If you're reading this in Push, you clicked Check for Updates. Manual tier — the file viewer is now WebKit-backed (Markdown / code / HTML with full-document selection), and missing files are clearly marked in the Files gallery.

v1.9.29

2026-05-23

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.29

Cleaner, more consistent boards and sidebar. Manual tier.

What changed

Recurring jobs get their own place again

  • Recurring jobs moved back out of the Issues page into their own Recurring sidebar item (with a new bolt icon). The Issues board is a clean board again — no jobs strip on top.

One consistent issue-number tag

  • The issue ref (like DEE-42) now shows as the same neutral pill everywhere — leading both the issue cards and the agent-session cards — instead of plain text in one place and a tag in another. The session card's badges (issue / agent / model) all read as one matched set now.

Tidier Sessions and boards

  • The Sessions, Agent detail, and Issues boards now share the exact same column spacing, card padding, and corner radius, so they line up identically.
  • Sessions columns are color-keyed: the Live lane is tinted blue; the history lanes stay neutral grey so the active lane stands out.
  • Dropped the redundant "Sessions" title above the board — the sidebar already names the page.

Quieter sidebar

  • Agent rows in the sidebar no longer show the runtime-name pill (Claude / Codex / …). It was noise in a narrow column; the runtime is still in each row's tooltip.

Notes for upgrading

Nothing to do — Manual update. Your tabs, sessions, runs, and files carry over untouched.


If you're reading this in Push, you clicked Check for Updates. Manual tier — UI consistency pass across the boards and sidebar, plus Recurring jobs back as their own sidebar item.

v1.9.28

2026-05-22

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.28

Buttons across sessions and issues now say what they do. Manual tier.

What changed

Clear actions on sessions and issues

  • The terminal, play, and dot icons scattered across the Sessions and Issues views are now labeled buttons that tell you what will happen before you click:
    • Run (green ▶) — start fresh work in a new terminal tab.
    • Open — jump to a terminal tab you already have. A green dot means it's live; a hollow dot means it's sleeping and wakes when you open it.
    • Reopen — pick a finished session back up in a new tab, continuing where it left off. Same wording and icon as the Terminal strip's "Reopen" button.

The Live column gets an Open button

  • Running and sleeping sessions in Sessions → Live now show an explicit Open button, instead of only a status dot you had to know was clickable.

Smaller touches

  • The issue detail's terminal toggle now reads Show (it reveals the terminal panel), so it doesn't clash with the new Open button.
  • Dropped the redundant "Active" label on live sessions and quieted the "Sleeping" label — the button's dot now carries that state.

Notes for upgrading

Nothing to do — Manual update. Your tabs, sessions, runs, and files carry over untouched.


If you're reading this in Push, you clicked Check for Updates. Manual tier — relabels the session and issue actions (Run / Open / Reopen) so each click is no surprise.

v1.9.27

2026-05-22

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.27

The Feed no longer freezes when you scroll through a lot of activity — plus a handful of UI refinements. Manual tier.

What changed

The Feed stays responsive on large feeds

  • Scrolling to the bottom of a busy Feed could freeze the app for several seconds — it looked like a crash or beachball. The Feed now lays out and scrolls smoothly no matter how much activity has piled up. Long sections (Recent activity, Resolved decisions) load in pages with a Show more row, so the view never has to render everything at once.

Sessions columns adapt to your window

  • The Sessions view columns now resize to fit your window width, matching the issue board.

Issue board columns are color-coded

  • Each board column is now tinted with its status color, so the board is easier to scan at a glance.

Terminal folder filter follows you

  • The Terminal's folder-filter chips now track the folder you're actually in as you move between directories, instead of staying pinned to the one a tab started in.

Sidebar shows the runtime

  • Agent rows in the sidebar now show a small pill with the runtime name (Claude, Codex, OpenCode, Hermes).

Notes for upgrading

Nothing to do — Manual update. Your tabs, sessions, runs, and files carry over untouched.


If you're reading this in Push, you clicked Check for Updates. Manual tier — mainly a fix for the Feed freezing on large feeds, plus some UI polish.

v1.9.26

2026-05-22

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.26

Run-in-Terminal from your phone now actually starts on your Mac. Manual tier.

What changed

Running an issue in a terminal from iOS no longer gets stuck

  • When you tap Run in Terminal from iOS — or an agent picks up assigned work on its own — the agent now starts running on your Mac immediately, even when you're not on the Terminal tab and the window is in the background. Before, the new terminal tab would sit there doing nothing until you manually clicked it: the agent's first command was typed but never submitted. Now it just runs, and you can come back to the tab whenever you like.
  • This works the same across every runtime — Claude, Codex, OpenCode, and Hermes.

Remote runs stay out of your way

  • A run you start remotely no longer steals your current terminal tab or pulls the Mac to the front. Your selected tab stays put; the new run's tab simply lights up with an unread badge.
  • For a run you explicitly kicked off (e.g. from your phone), Push posts a notification so you can find it later. Silent, autonomous pickups stay quiet.

A safety net when no Mac is hosting

  • If you start a terminal run but no Mac is running Push to host it, the run now falls back to running in the background (and still streams to your phone) instead of staying stuck.

Notes for upgrading

Nothing to do — Manual update. Existing tabs, sessions, runs, and files carry over untouched. Dormant terminal tabs still resume exactly as before (a quick reload when you click them).


If you're reading this in Push, you clicked Check for Updates. Manual tier — remote Run-in-Terminal now starts on its own, across all four runtimes.

v1.9.25

2026-05-22

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.25

A cleaner agent page, and per-agent budgets retired from the UI. Manual tier.

What changed

The agent page leads with what matters

  • An agent's header is stripped down to just its name and its working directory. The subtitle/title line, the Created · Updated · Agent ID metadata row, and the "Last seen" line are gone — they were clutter on a page that's really about the agent's work.
  • The working directory now sits inline right after the name and shows the full path, with a one-click copy-path button right beside it.

Per-agent budgets are no longer shown

  • The per-agent monthly budget is retired from the interface — it no longer appears on the agent page, the sidebar, the web app, or the CLI. Every agent now runs unlimited.
  • Workspace-level budgets and the Costs page are unchanged — this only affects the per-agent cap.

Notes for upgrading

Nothing to do — this is a Manual update. Existing agents, issues, jobs, runs, sessions, and files carry over untouched. Any per-agent budget you had set is cleared to unlimited; your historical spend totals are kept.


If you're reading this in Push, you clicked Check for Updates. Manual tier — agent-page cleanup plus the per-agent budget retirement.

v1.9.24

2026-05-22

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.24

The agent page feels like one space, and the Recurring section always shows. Manual tier.

What changed

Agent detail reads as one immersive page

  • On an agent's page, the header (name, status, budget, ID) and the columns below it (Jobs · Issues · Sessions · Files) no longer look like two separate panels. The divider is gone and they share one background, so the page reads as a single continuous space with sections — not a header bolted onto a board.

Recurring section is always there

  • On the Issues page, the Recurring section now shows even when it's empty (count 0) — filtering to an agent with no recurring jobs, for example. It stays a stable anchor instead of popping in and out.

Notes for upgrading

Nothing to do — this is a Manual update. Existing agents, issues, jobs, runs, sessions, and files carry over untouched.


If you're reading this in Push, you clicked Check for Updates. Manual tier — layout polish on the agent and Issues pages.

v1.9.23

2026-05-22

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.23

The Issues-page agent filter now counts your recurring jobs too. Manual tier.

What changed

The agent filter sees jobs, not just issues

  • On the Issues page, the agent chips at the top now count an agent's issues and recurring jobs together — the badge reflects everything that agent has on the page.
  • An agent that has only recurring jobs (no issues) now gets its own chip, so you can filter straight to it. Before, it had no chip and was only visible under "All".

Clearer section labels

  • The two sections on the Issues page are now labeled Recurring (the strip of recurring job cards) and Issues (the board).

Notes for upgrading

Nothing to do — this is a Manual update. Existing jobs, issues, agents, runs, sessions, and files carry over untouched.


If you're reading this in Push, you clicked Check for Updates. Manual tier — a fix so the Issues filter accounts for recurring work.

v1.9.22

2026-05-22

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.22

Jobs now live on the Issues page. Manual tier.

What changed

Jobs and Issues, one page

  • Your agents' Jobs now appear as a horizontal strip of cards at the top of the Issues page, right above the board — issues and jobs are the same kind of work, so they share one screen.
  • One filter for both. The agent chips (and the search box) at the top filter the jobs strip and the issue board together. Pick an agent to see just its jobs and issues.
  • The two sit as clean, labeled sections — the same look as the Feed dashboard.

Simpler sidebar

  • The standalone Jobs tab is gone, now that jobs live on the Issues page. One less place to look.

Notes for upgrading

Nothing to do — this is a Manual update. Existing jobs, issues, agents, runs, sessions, and files carry over untouched. If your default screen was set to Jobs, it now opens the Issues page.


If you're reading this in Push, you clicked Check for Updates. Manual tier — jobs and issues, together at last.

v1.9.21

2026-05-22

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.21

The agent filter works the same way everywhere now — and service agents stop hiding. Manual tier.

What changed

One consistent agent filter across Sessions, Issues, Files, and Jobs

  • All four lists now share the exact same chip strip: All, one chip per agent, then Other. Whatever you learn on one screen carries to the rest.
  • The chips group by agent. A folder-bound agent shows its folder; a folderless service agent (Gmail, planner, and the like) now gets its own chip instead of being swept into "Other". Previously, on the Issues board, anything assigned to a service agent vanished into a single catch-all — now you can filter straight to it.
  • Each chip's icon matches its row in the sidebar — folder agents and service agents read the same wherever you see them.

Simpler buckets

  • The separate "Unassigned" chip on Issues is gone. Anything with no owning agent — unassigned, or an agent that was deleted — lands in one Other bucket, the same catch-all every list uses.

Notes for upgrading

Nothing to do — this is a Manual update. Existing agents, issues, runs, sessions, jobs, and files carry over untouched; only the filter-chip drag order resets once.


If you're reading this in Push, you clicked Check for Updates. Manual tier — a consistency pass on how every list filters by agent.

v1.9.20

2026-05-22

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.20

The Agents sidebar reads cleaner at a glance. Manual tier.

What changed

Tidier Agents sidebar

  • Each agent row used to carry its runtime logo pill — twelve near-identical marks that added little signal. That's gone, so the list stays focused on agent names.
  • Agents running on Auto now show a small green AUTO badge next to their name. Manual agents show nothing, so the ones working on their own stand out at a glance.
  • Service agents (the ones not bound to a folder) get a refreshed icon.

Notes for upgrading

Nothing to do — this is a Manual update. Existing agents, issues, runs, and sessions carry over untouched.


If you're reading this in Push, you clicked Check for Updates. Manual tier — a small polish pass on the Agents sidebar.

v1.9.19

2026-05-22

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.19

Quick-captured ideas now reliably get cleaned up and routed for you. Manual tier.

What changed

Capture enrichment actually finishes the job

  • When you dump a thought into the quick-capture popup (the + button / ⌘N) or save a page from the browser extension, Push wakes its workspace runtime to rewrite the placeholder title, fill in a description, and pick the right agent to own it. That step was silently stopping halfway — the issue stayed with its raw first-line title, an empty description, and no assignee. Now it runs to completion.
  • Enrichment also understands plain notes and voice memos, not just saved web pages — a typed thought gets distilled into a crisp title and summary instead of being treated like a page that doesn't exist.

Quieter timeline

  • Background enrichment no longer leaves a stray "completion" comment on the issue it just tidied up, and no longer briefly looks like the issue is "being worked on" while it runs.

Notes for upgrading

Nothing to do — this is a Manual update. Existing agents, issues, runs, and sessions carry over untouched.


If you're reading this in Push, you clicked Check for Updates. Manual tier — a bug fix so quick captures organize themselves the way they were meant to.

v1.9.18

2026-05-22

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.18

Scheduled jobs now run in a terminal like issues, and you can watch any live run's terminal from the web and iOS — an immersive, full-screen xterm mirror. Manual tier.

What changed

Jobs run in a terminal, like issues

  • A scheduled job owned by one of your agents now runs in a terminal tab — foreground and streaming, exactly like an assigned issue — instead of headless in the background. Only the system runtime stays headless. You watch a job work the same way you watch an issue.

Watch a live run's terminal — from the web and iOS

  • A running agent's terminal can now be streamed live to a browser or the iOS app: the real terminal — full color, cursor, TUI redraws — rendered with xterm.js, read-only.
  • A new immersive viewer fills the whole screen with just the terminal: one background, no toggles, no chrome — built for embedding inside the native apps.
  • Webview embeds now authenticate with an injected token, so the stream works inside the iOS/macOS app shell.

Lighter on the wire

  • The raw terminal mirror now streams only while someone is actually watching a run — no byte firehose for runs nobody has open. The live transcript and durable log are unaffected.

Notes for upgrading

Nothing to do — this is a Manual update. Existing agents, jobs, runs, and sessions carry over untouched.

v1.9.17

2026-05-21

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.17

A new Settings → Runtimes health view, plus follow-ups to the terminal-first run model: a cleaner Sessions layout, a Stop button that actually stops, and a tidier agent UI. Manual tier.

What changed

New: Settings → Runtimes

  • A machine-level view of the local CLI runtimes Push can drive — Claude, Codex, OpenCode, Hermes, and friends. Each row shows whether the binary is installed and runnable (a green/gray health dot), its version, how many agents use it, and its live workload (idle or how many runs are active), all behind size-normalized brand logos.
  • A per-runtime Diagnose… action runs a deep environment probe and lists exactly what passed, what warned, and what failed — so a misconfigured CLI is one click away from an explanation.
  • The default-adapter picker (which runtime handles capture enrichment, folder descriptions, and unassigned-issue execution) now lives at the bottom of this view.

Sessions, re-cut into three columns

  • The Sessions surface is now Live · Sessions · Agent History — running work, your own sessions, and an agent's past runs each get their own column instead of being mixed together.
  • Re-clicking a session's resume no longer opens a duplicate tab — it jumps back to the one that's already open.
  • An issue's session surfaces now hide system and auxiliary runs, so you only see the runs that are actually yours.
  • Detached runs now recover their session for Codex, OpenCode, and Hermes too — previously only Claude runs reconnected after detaching.

Stop a running agent — and it stays put while it runs

  • The Stop button on a terminal run now genuinely interrupts it (sends Ctrl-C to the tab and keeps the session alive). Previously, stopping a terminal run silently did nothing.
  • While an agent's run is active, reassigning or unassigning its issue is locked — no more pulling an issue out from under a running agent.

One agent, one terminal

  • Visible terminal runs are now serialized per agent — an agent runs in one terminal at a time, so a single agent can't fan out into competing terminal tabs.

A tidier agent UI

  • The agent detail header is decluttered and the redundant status badge is gone.
  • Sidebar agent rows now show a gearshape icon for folderless agents and a per-agent runtime badge.
  • An agent's Auto/Manual switch moved back to the toolbar as folder-filter-style chips.

Internal

  • Consolidated the repo's engineering invariants into a single CONVENTIONS.md pointer.

Notes for upgrading

  • No action needed, and no data migration runs on upgrade.

If you're reading this in Push, you clicked Check for Updates. Manual tier — a new Runtimes health view, Sessions cleanup, a working Stop button, and a tidier agent UI.

v1.9.16

2026-05-21

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.16

A simpler, more consistent run model: your agents always work in a real terminal — and that terminal streams to your other devices automatically. Manual tier.

What changed

Agents run in the terminal — and stream automatically

  • The "Run in Terminal" vs "Run in Background" choice is gone for your agents. Every agent now runs in a real terminal tab — and that terminal streams to your other devices (web, phone) automatically, with nothing to switch on. Glance at it, or walk away and let it run; it's watchable from anywhere.
  • The experimental "Stream terminal runs to viewers" toggle has been removed — streaming is always on now.
  • The headless/background mechanism still exists under the hood — as the automatic fallback when no Mac is online to host the tab, and for Push's own system runtime. It's just no longer a per-agent choice you have to make.

Run an issue only when it's assigned

  • To run an issue, it must be assigned to an agent. Unassigned issues no longer quietly run on Push's system agent — assign one first, and the Run button tells you when you need to.

A clearer issue card

  • The issue card's run control now shows live state at a glance: a green dot while it's running (click to jump straight to that terminal), a hollow gray dot when its session is sleeping, or the terminal icon to start a run — matching the Sessions column.

Fixes

  • Fixed unassigning an agent from an issue — it was silently doing nothing on the Mac.

Internal

  • Removed the legacy "CEO agent" concept.

Notes for upgrading

  • The per-agent Background/Terminal mode is gone; agents run in the terminal, with an automatic headless fallback when no Mac is online to host the tab. No action needed, and no data migration runs on upgrade.

If you're reading this in Push, you clicked Check for Updates. Manual tier — a simpler, more consistent run model.

v1.9.15

2026-05-21

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.15

A fix that keeps the experimental terminal-streaming feature from ever slowing the app, plus two model simplifications. Manual tier — most of this is invisible unless you'd turned streaming on.

What changed

Terminal streaming can no longer slow the app

  • Fixed the lag. With the experimental "Stream terminal runs to viewers" toggle on, an active terminal run could make the whole app stutter and beachball (it cleared when you closed the tab). The live byte stream was being broadcast to every connected surface — including the Mac itself, which doesn't need it — and each frame kicked off a full refresh. That's gone.
  • The mirror is now structurally isolated. The literal-terminal stream rides a dedicated per-run channel as compact binary frames, with a slow-viewer guard and a GPU-accelerated renderer on the web side. A device that isn't watching a given run receives none of its bytes — so even with many runs streaming at once, the streaming feature can't degrade the core app. It stays a non-essential, opt-in extra.

Simpler issues and agents

  • Issues no longer have a priority field. The priority scheme is removed.
  • Agents no longer have a "reports to" org-chart. That hierarchy is removed.

Notes for upgrading

  • Two small schema migrations run automatically on first launch (drop issue priority, drop the agent reports-to relationship). Aside from those fields going away, behavior is preserved — your issues, agents, adapters, folders, Auto / Manual state, and Jobs are untouched.
  • The streaming change is a fix and needs no action. Terminal streaming itself remains an opt-in Experimental toggle, off by default.

If you're reading this in Push, you clicked Check for Updates. Manual tier — a streaming-stability fix plus a bit of model cleanup.

v1.9.14

2026-05-21

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.14

Internal cleanup: agents no longer carry a "Reactive / Proactive" classification. Manual tier — behavior-preserving, nothing for you to do.

What changed

Agents are just environments now

  • Dropped the Reactive / Proactive label. An agent used to be tagged "Reactive" or "Proactive" — a leftover from before Jobs existed. Now that scheduling lives entirely in Jobs and autonomy is a single Auto / Manual switch (since 1.9.13), that label did nothing but take up space in the agent detail view. It's gone.
  • Scouts are an agent + a Job. Creating a deliverable "scout" no longer bakes a hidden timer into the agent. The agent holds the environment (its folder + deliverable output); a Job holds the schedule and the prompt that runs each tick. The --scout CLI preset still sets up the agent side — add a push job for the cadence.

Notes for upgrading

  • A small schema migration runs automatically on first launch: it drops the unused agent classification field and tidies agent metadata. Behavior-preserving — your agents keep their adapters, folders, Auto / Manual state, and Jobs. Nothing to do.

If you're reading this in Push, you clicked Check for Updates. Manual tier — a quiet internal cleanup.

v1.9.13

2026-05-21

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.13

Simpler agent autonomy with a built-in concurrency cap, plus an experimental way to watch a terminal run from another device. Manual tier — the autonomy change is automatic; the streaming is opt-in.

What changed

Simpler autonomy + a fleet-wide concurrency cap

  • Auto / Manual, nothing else. An agent's autonomy is now a single Auto/Manual switch — the separate per-agent check interval is gone. Standing instructions become Jobs.
  • One pickup cadence for assigned issues. Auto agents pick up their assigned issues on a single shared schedule (~10 minutes) instead of each running its own timer.
  • A cap on concurrent runs. Push now caps how many background runs execute at once across the whole fleet (default 3), so a roomful of Auto agents can no longer spawn a pile of subprocesses at the same time and bog down your machine.

Experimental: stream a terminal run to other devices

  • New Experimental toggle, Settings → Experimental → "Stream terminal runs to viewers" (off by default). When it's on, a Run-in-Terminal session is mirrored to remote viewers (web/iOS) over the existing relay — as a structured transcript and, for the literal terminal, a live xterm view. The local terminal is untouched; this only adds an outbound mirror.

Notes for upgrading

  • Autonomy: existing per-agent intervals fold into the single global pickup cadence; agents keep their Auto/Manual state. No action needed for normal use.
  • Streaming: additive and off by default — nothing changes unless you enable the Experimental toggle.
  • No data migrations are applied automatically on upgrade.

If you're reading this in Push, you clicked Check for Updates. Manual tier — the autonomy simplification lands quietly; the terminal-streaming piece is opt-in.

v1.9.12

2026-05-21

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.12

The Files cards read cleaner: full file names, no mid-name "…", and a source label that matches the folder filter at the top of the Files tab. Manual tier — small UI polish; nothing demands your attention unless you go looking.

What changed

Files cards show the whole name and a clearer source

  • Full file names. Long names like run-artifact-extractor.test.ts now show in full instead of being cut off in the middle.
  • Source matches the toolbar. The label at a card's top-left is now the same folder source shown by the Files toolbar filter — the agent or folder the file lives in (or "Other" for files outside any agent's folder). Gone is the older mix of raw folder names plus a tap glyph for ⌘-clicked files.

Notes for upgrading

  • No migrations, no action needed. Purely a Files-tab presentation change.

If you're reading this in Push, you clicked Check for Updates. Manual tier — routine UI polish; no forced dialog.

v1.9.11

2026-05-21

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.11

One clear rule for which agents can take work. Manual tier — almost entirely under the hood; nothing demands your attention unless you go looking.

What changed

A single definition of "which agents are assignable"

Until now, "can this agent be given work?" was decided in a dozen different spots, each slightly differently — which let your folder agents (the ones for push-macos, push-ios, and every other folder you work in) quietly disappear from some surfaces, like the iOS app's agent list and voice routing.

Now there's one rule, computed in one place: an agent is assignable unless it's the workspace's internal system agent. Folder agents are real, assignable agents — they show up and can take work everywhere, carrying the description from their folder's CLAUDE.md.

  • Folder agents are back wherever agents are listed or routed to (most visibly in the iOS companion app's Settings and voice routing).
  • The internal workspace runtime can no longer be handed work directly. It was never meant to be a normal assignee; assigning to it now returns a clear error instead of silently doing the wrong thing. You won't notice this unless you were poking at it on purpose — it never appeared in the assignee picker.

Notes for upgrading

  • No migrations, no action needed — everything is additive. Existing assignments, terminal runs, and background runs behave exactly as before.

If you're reading this in Push, you clicked Check for Updates. Manual tier — this is internal plumbing plus a small guardrail; no forced dialog.

v1.9.10

2026-05-20

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.10

One Run button instead of two, and a tidier agent detail view. Manual tier — UI changes you'll find when you go looking; nothing demands your attention.

What changed

One Run button, driven by the agent's mode

An issue used to have two Run buttons — Run in Terminal and Run in Background. Now there's one button that follows the agent's run mode: it reads "Run in Terminal" or "Run in Background" and does exactly that.

You set the mode on the agent (the Run in Terminal / Run in Background selector in the agent's toolbar), and every issue assigned to that agent inherits it. One control, one mental model — the agent has a run mode; its issues follow it.

Auto-mode respects the run mode

When an agent on Auto picks up an assigned issue on its own:

  • a Background-mode agent runs it headlessly with output in the Feed (as before), and
  • a Terminal-mode agent now opens a terminal tab for it, so you can watch and steer — instead of always running headless.

If the app isn't around to open the tab, the run quietly falls back to background so the work still happens.

Agent detail view: tidier

  • Auto / Manual, the check interval, and the standing task moved out of the toolbar into the agent's info area, next to its description — where the rest of "how this agent behaves" already lives.
  • Agent ID and the agent Link are now click-to-copy fields in the info grid (the old "⋯" menu is gone).
  • The toolbar is now just Run plus the Run in Terminal / Run in Background selector.

Notes for upgrading

  • No migrations, no action needed — everything is additive. Existing terminal runs, background runs, and assigned issues behave exactly as before.

If you're reading this in Push, you clicked Check for Updates. Manual tier — these are UI refinements you'll find when you go looking; no forced dialog.

v1.9.9

2026-05-20

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.9

Your open terminal sessions now show their status at a glance. Manual tier — polish following 1.9.8.

What changed

Sessions: "Sleeping" tabs are visible again

Since 1.9.8, Push wakes only a few terminal tabs at launch and resumes the rest the moment you open them. That made the Sessions view's terminal column show just the handful actively running — your other open tabs looked like they'd slipped away into "history."

Now every open agent tab shows in the Terminal column with a clear status:

  • Active (green dot) — running right now.
  • Sleeping (hollow dot) — open and ready; resumes the instant you click it.

Click any Sleeping session to jump to its tab and wake it. "Terminal History" now means only sessions whose tab you've actually closed.

Under the hood

A few defensive guards keep terminal-mode launch behavior robust (no user-facing change).

Notes for upgrading

  • No migrations, no settings changes.

If you're reading this in Push, you clicked Check for Updates. Manual tier — polish, so no forced dialog.

v1.9.8

2026-05-20

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.8

Faster, lighter launch when you keep many terminal tabs. Manual tier — nothing to do; the change is internal.

What changed

Launch no longer wakes every saved tab at once

Push remembers your open terminal tabs across launches. Previously, on every launch it eagerly spawned a live shell and re-launched that tab's agent (claude --resume, codex, …) for every saved tab — fine for a handful, but with dozens of agent tabs that meant dozens of agent processes starting in the same few seconds. On a large enough set this could saturate the machine right after launch: a pile of login shells pegging CPU, blank tabs, and an app that felt stuck.

Now Push warms only the tabs you're about to see — the active one plus your two most recent — and brings the rest back the moment you open them. Opening a tab cold-starts its shell and resumes its session on the spot (typically ~1–2 s), exactly as before; nothing is lost, it just happens on demand instead of all at once.

  • A cold launch now starts at most 3 terminal surfaces, regardless of how many tabs you have saved.
  • Your sessions still resume — when you click the tab.

Quieter shell startup

A debugging probe that had been writing to two log files on every shell start (and could grow them without bound) is now off unless you opt in with PUSH_DEBUG=1.

Notes for upgrading

  • No migrations, no settings changes. Existing tabs, issues, and background runs are unchanged.
  • If you keep many terminal tabs, launch will feel faster and lighter.

Reference

Root cause + fix design live in the writing repo: documentation/push/macos/investigations/2026-05-20-terminal-mode-login-process-storm-prewarm-autoresume-herd.md and documentation/push/macos/plans/2026-05-20-bound-prewarm-to-mount-set-fix-terminal-login-storm.md.


If you're reading this in Push, you clicked Check for Updates. Manual tier — the change is internal, so no forced dialog.

v1.9.7

2026-05-20

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.7

Simplifies how Jobs and Agents fit together, and tightens one folder ⇄ one agent. Manual tier: changes are mostly under the hood — nothing demands your attention unless you go looking.

What changed

Jobs: one switch instead of two

Before, a Job had its own on/off and its agent had Auto/Manual — two overlapping ways to ask "does this run on its own," which got confusing (a Manual agent would still fire its jobs).

Now there's one switch: the agent's Auto/Manual. A Job is a recurring event on its agent's calendar — it runs when the agent is on Auto and the job is due, exactly like an assigned issue gets picked up only when the agent is on Auto. A Manual agent does nothing on its own.

  • Jobs no longer have a per-job on/off toggle. To park a job without deleting it, set its trigger to manual (push job update <id> --manual); to run it again, give it a schedule (--interval).
  • The Jobs gallery and the agent's Jobs column are now view + delete; scheduling changes go through the push job CLI.
  • push job enable / disable are gone — there's nothing to toggle.

One folder, one agent

A folder is now claimed by at most one agent per workspace (an agent can still have no folder). Pointing a second agent at a folder another agent already owns is refused with a clear message instead of silently creating a duplicate. Folderless service agents are unaffected.

Notes for upgrading

  • Two DB migrations (0063, 0064) apply automatically on first launch after upgrade — no action needed.
  • Any Job you'd previously disabled becomes parked (kept, but not firing) rather than suddenly running. Re-schedule it any time with push job update <id> --interval <seconds>.
  • Everything else is behaviorally additive; existing terminal runs, issues, and background runs are unchanged.

Reference

Full design + rationale (incl. the "calendar event" mental model and the research behind it) lives in the writing repo at documentation/push/macos/investigations/2026-05-19-environments-and-jobs-agents-schema-split-design.md §23.


If you're reading this in Push, you clicked Check for Updates. Manual tier — the changes are mostly internal, so no forced dialog.

v1.9.6

2026-05-20

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.6

Introduces Jobs — recurring, self-triggering work for your agents — plus a reliability fix for agent deliverables and some CLI polish. Manual tier: Jobs ships as infrastructure (empty until you create one), so nothing changes for you unless you go looking.

What changed

Jobs — recurring work-units owned by an agent

An Issue is one-time work you assign to an agent. A Job is its sibling: persistent, recurring work an agent runs itself — on a schedule (daily, hourly, weekly) or only when triggered. Toggle a job on/off with a flag; a folder-bound agent runs its jobs in that folder's context.

  • New Jobs section in the sidebar — a gallery of every job across your agents, grouped by folder exactly like Files.
  • A Jobs column on each agent's detail view (first column), showing that agent's jobs.
  • A push job CLI: create / list / get / update / enable / disable / delete. Example:
    push job create --agent <id> --name daily-digest --interval 86400 --standing-instruction "…"
    
  • The heartbeat scheduler now fires due jobs alongside assigned issues — one unified loop. A job's own enabled flag is the gate, so an agent can run jobs even with its periodic heartbeat off.

Jobs are created via the CLI for now; the gallery is view + enable/disable/delete. Creation UI lands in a later release.

Agent deliverables no longer silently dropped

Agents that produce a deliverable (publish-to-XHS/X/etc.) write it to .push/deliverable.json. Adapters read that file from the agent's working directory — but some older scout agents' instructions wrote it to $AGENT_HOME instead, which diverges from the working directory once an agent is folder-bound. The result: the deliverable was written but never picked up.

Adapters now check both locations (working directory first, then the agent home), so a deliverable is surfaced regardless of which convention the agent's instructions used. Affects all four local providers (Claude, Codex, OpenCode, Hermes).

CLI: set an agent's default run mode

push agent create / push agent update gained --default-run-surface <terminal|background> — sets which Run button is primary on that agent's issue pages (parity with the toolbar picker added in 1.9.5). The deprecated agent cwd add --primary/--name flags (no-ops since single-cwd) were removed.

Notes for upgrading

  • DB migration 0062 adds the jobs table and a job_id column on heartbeat_runs. It's additive and applies automatically on first server boot after upgrade — nothing changes for existing agents, issues, or runs.
  • No jobs exist until you create one (push job create) — the Jobs gallery shows an empty state with guidance until then.
  • Everything else is behaviorally additive; existing terminal and background runs are unchanged.

Reference

Full design + as-built implementation status lives in the writing repo at documentation/push/macos/investigations/2026-05-19-environments-and-jobs-agents-schema-split-design.md (§5.5 canonical model, §22 as-built).


If you're reading this in Push, you clicked Check for Updates. Manual tier — Jobs is additive infrastructure (empty until you create a job), so no forced dialog.

v1.9.5

2026-05-19

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.5

A bigger PATCH than usual — the auto-mode loop finally does what the name implies, plus a small clutch of new ambient affordances around runs.

What changed

Auto-mode actually picks up assigned issues now

If an agent had heartbeat.enabled=true and you assigned three issues to it, the old behavior was: the agent ticked on schedule, ran its generic prompt, and unless that prompt was hand-coded to scan its issue queue, none of the assigned issues got worked on. Push's "automatic mode" silently did nothing useful in the common case.

The fix changes the periodic tick from "one generic wake" to "one wake per assigned-active-unlocked issue." Each wake carries the issue context, acquires the issue's execution lock, runs the agent against that specific issue, posts the completion comment, and releases the lock. The CLI receives explicit work; the audit trail (heartbeat_runs.issue_id, issues.completed_by_run_id) is complete; the standard mental model — "auto-mode picks up my assigned issues" — finally matches the implementation.

Capped at 5 wakes per tick to keep queues sane; overflow drains on subsequent ticks. Gated by a new per-agent flag heartbeat.pickupAssignedIssues (default true) so you can opt out if your CLI prompt was already doing its own scanning.

Live output pane on the issue page

When a background run is in flight on an issue, an inline streaming pane now appears below the active-run badge. It shows the agent's output live as the run progresses — no separate terminal tab, no pre-declaring "I want to watch." The pane is read-only, monospace, auto-scrolls to the bottom, and self-collapses when the run finishes.

The pane subscribes to a new per-run WebSocket endpoint at /api/workspaces/:wid/runs/:runId/stream. The same endpoint backs the web UI's transcript view, so a run started from your Mac shows up live if you flip over to the web on another device.

Per-agent "default run mode" toolbar picker

The agent detail toolbar gained a small picker that lets you choose whether Run in Terminal or Run in Background is the primary button on this agent's issue pages. Both buttons stay available everywhere — only the visual emphasis (bordered prominent vs. plain) changes based on the agent's preference.

Useful for agents you generally want to babysit (set to Terminal) vs. agents you generally let run autonomously (set to Background).

"Open in Mac app" on web run cards

The web UI's completed-run cards now include an "Open in Mac app" link. Clicking it triggers a push://open-session?… URL that the Mac app handles by spawning a terminal tab with --resume <session_id>, picking up the agent's conversation where the run left off. Works in any browser; Mac silently handles the scheme.

Run completion now sends a push notification

When a run reaches a terminal status (succeeded / failed / cancelled / timed out), the server fires a push notification to your registered iOS devices. iOS-side delivery comes in the next iOS update; today this is the server half of the loop.

Small fixes

  • Agent detail picker dropdown wasn't switching from Background to Terminal — clicks looked like they did nothing. SwiftUI was deduping the agent prop on id only, so updates to runtimeConfig weren't propagating through view diffing. Fixed by reading the live agent from the workspace store inside the toolbar body.
  • The 15-minute idle reaper that sweeps stale interactive runs as "detached" now uses a 24-hour threshold for visible-surface runs — these are server-owned and legitimately survive long idle periods without that meaning the run was orphaned.

Notes for upgrading

  • DB migration 0060 adds a surface column to heartbeat_runs and backfills it from invocation_source. Migration 0061 adds the new agent_working_sessions table. Both apply automatically on first server boot after upgrade.
  • Existing terminal runs are byte-for-byte identical in behavior — the only change you'd notice is the bumped 24-hour idle threshold (previously 15 minutes).
  • The pickupAssignedIssues flag defaults to true. If you have agents whose CLI prompts handle their own issue queue scanning and you don't want the server fanning out wakes per-issue, set heartbeat.pickupAssignedIssues = false on those agents.

Reference

Full design + architecture audit lives in the writing repo at documentation/push/macos/investigations/2026-05-19-heartbeat-issue-pickup-and-run-modes-deep-dive.md. The §17 section captures the converged UX (keep both buttons, add ambient affordances) that this release implements.


If you're reading this in Push, you clicked Check for Updates. Manual tier — the auto-mode work is the headline but it's behaviorally additive (existing agents work the same), so no forced dialog.

v1.9.4

2026-05-19

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.4

A focused PATCH that fixes one bug: fresh Codex tabs were showing a 36-character thread UUID as the tab title instead of the folder name.

What changed

Codex tabs no longer show thread UUIDs as titles

If you opened a new Codex tab in a folder and hadn't yet run /rename, the tab strip would display the Codex thread's internal identifier — something like 019e41a4-5ae9-74a2-b… — instead of the folder name. That regression rode along with the v1.7 change that added /rename support: we configured Codex's tui.terminal_title = ["thread"] so user-set thread names would flow through to the tab strip, but Codex's thread_name for an un-renamed thread falls back to the thread UUIDv7, which we were faithfully accepting as the title.

The fix adds a defensive guard in the OSC route that handles plain-text titles: if the incoming title is a bare UUID, drop it. The chip falls through to the folder basename instead, matching the pre-/rename-support behavior. Once you /rename a thread to anything that isn't UUID-shaped, that name surfaces in the tab strip as before.

The guard sits at the same routing layer for both Codex and OpenCode (the two providers whose plain-text OSC emissions route into the agent-title slot). OpenCode's own plugin already filters its placeholder titles, so this is a second-line defense for that provider; for Codex it's the only line of defense. Claude is unaffected — its titles always carry the prefix glyph and route through a different path, and Claude's auto-haiku is descriptive English, not an identifier. Hermes is unaffected too — it doesn't emit OSC at all; titles come from a SQLite watcher with empty/null filtering at every layer.

Notes for upgrading

  • No DB changes, no schema migration. Pure Mac-side defensive filter.
  • Existing renamed Codex tabs are unaffected. Only the pre-/rename placeholder display changes.
  • The fix is defense-in-depth — if a future Codex release reverts to a placeholder shape that isn't UUID-shaped (e.g. the older "<spinner> <project-name>" default), that string flows through normally and the chip shows whatever Codex emits.

If you're reading this in Push, you clicked Check for Updates. Manual tier, as usual.

v1.9.3

2026-05-19

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.3

A surfaces-and-grouping PATCH. Three unrelated UX cleanups that all sharpen the same idea: folders are first-class, and the chip strips / sidebar lists should reflect that uniformly.

What changed

Files now group by folder, not by writer agent

The Files gallery's chip strip used to bucket files by the agent that wrote them. After today, it buckets by the folder the file lives in — computed live from the file's path against your workspace's current agent cwds. Same path-prefix mental model the Terminal toolbar and the Sessions view already use.

Concretely:

  • A file at ~/workspace/foo/output.ts clusters under the chip for whichever agent currently owns ~/workspace/foo — or under a hollow-folder chip if that path exists in your workspace but no agent owns it right now.
  • A file at ~/workspace/foo/sub/output.ts clusters under the chip for the agent owning ~/workspace/foo/sub if there is one, falling back to the parent ~/workspace/foo otherwise. Longest matching prefix wins.
  • A file outside any agent's folder (e.g. something in /tmp or ~/Downloads) clusters under "Other" with the tray.full glyph — honest orphan instead of being misattributed to whatever agent happened to write it.
  • The dedicated "Terminal" chip is removed. Files you ⌘-clicked from a terminal now join the same per-folder buckets via the same path-prefix rule. A click on a file in ~/projects/bar/log.txt from a terminal anywhere lives under bar's chip, with the file in bar — the file's home, not the click's context.

The grouping is live: rename a folder agent, retarget its cwd, or add a new folder agent inside an existing claimed folder, and files re-cluster on the next render. There's no schema migration and no server change — files outlive their writer because the grouping is computed against the live workspace state rather than something frozen at insert time.

Sidebar's Agents section: folder icon for folder-bound agents

Folder-bound agents (most auto-folder agents — push-macos, push-browser, etc.) now render with a folder.fill glyph next to their name, matching the convention the Terminal toolbar's folder chips and the Files gallery's folder chips use. Folderless service agents (planner, scouts, future voice helpers — anything with no cwd) keep the existing AgentAvatar brand mark, because for them the adapter identity is the load-bearing visual signal.

Before today, the Agents section showed an AgentAvatar for every row. With most workspace rows being auto-created folder agents whose name is the folder basename, that meant twelve nearly-identical Claude brand marks crowding out the few service agents whose brand really matters. The visual mix is now meaningful again.

Sidebar's Agents section: alphabetical

The Agents section now sorts case-insensitively by name. Previously it showed agents in the server's created_at DESC order, which put recently-created agents at the top — useful for "what did I just make?" but not what most users want when scanning their workspace at a glance. Alphabetical is what the rest of the app's similar lists use; the Agents section now matches.

Terminal tab chip: full-context tooltip

Hovering the tab title in the chip strip now reveals a tooltip with the agent name, provider, full folder path, bound issue (if any), background process count, and generation state — anything that doesn't already fit visibly in the chip. The tab strip stays clean at default zoom; the long-press affordance is there when you want it.

Notes for upgrading

  • No schema migration, no DB changes. The Files grouping refactor is pure Mac-side; the underlying agent_files rows are unchanged.
  • The Agents sidebar's drag-pin ordering is unchanged — only the default unsorted layout shifted to alphabetical. Drag any folder chip to pin it where you want; that order persists across launches.
  • If you have files captured under agents that were once given a cwd and then detached (the planner case from v1.9.2), those files will now cluster under whichever agent currently owns the matching folder — automatically, without re-capture. Files captured by genuinely folderless agents (no cwd at any point) cluster under "Other."

If you're reading this in Push, you clicked Check for Updates. Manual tier, as usual.

v1.9.2

2026-05-19

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.2

A targeted PATCH that closes a stale-binding edge case in the v1.9.1 reconcile flow. Surfaced today when a real workspace's planner service agent was found to be squatting on the push-macos folder cwd, causing every terminal tab opened in that folder to cluster under a "planner" chip instead of the agent the folder should actually belong to.

What changed

Reconcile now wipes bindings where the agent's cwd doesn't match the tab's cwd

The v1.9.1 reconcile pass preserved any binding where tab.agentId matched a live agent in cwdIndex. It did not check whether that agent's cwd actually matched the tab's cwd. As a result, if an agent was renamed, retargeted, or had its cwd cleared on the server, a tab's binding survived the reconcile pass — and the chip strip kept rendering the tab's snapshot under that agent's (now stale) identity.

v1.9.2 adds the missing check: a binding is preserved only when agent.cwd == tab.cwd, with an explicit exemption for isDefault=true system agents (workspace_runtime), whose bindings are valid even when their cwd doesn't appear in cwdIndex by design. Any mismatch is now treated as a tombstone — the binding is wiped, and the existing autoheal pass picks up the orphaned tab on the same launch, calling ensureAgentForFolder for its cwd and rebinding it to the correct folder agent (creating a fresh one if none exists).

In practice, this means: when you launch this release for the first time after the misbound planner.cwd is cleared server-side, the tabs that were grouped under "planner" wipe their stale binding, autoheal creates a fresh push-macos auto-folder agent, and the tabs re-cluster under a proper "push-macos" chip. The original planner agent stays in the workspace as a folderless service agent with its persona and history intact.

What this isn't

  • Not a behavior change for tabs whose binding is correct. If tab.agentId points at an agent that does own the tab's folder, the reconcile path is unchanged.
  • Not a new server contract. Same ensureAgentForFolder flow as before; the only new check happens locally during reconcile.
  • Not a Terminal-mode change. Reconcile and autoheal remain Full-mode-only by structural gating. Terminal-mode tabs continue to render from their durable cached snapshot.

Brief glitch on the first restart that triggers a wipe

On the launch where a mismatched binding gets detected, the affected tabs spend ~100-300ms each between wipe and autoheal completion. During that window the chip strip groups them under "Other" instead of the correct folder chip. One-time, per restart, per affected folder. Subsequent launches converge to the right state.

Notes for upgrading

  • If your workspace had any agents with a cwd field that didn't match a folder they're really meant to own (a manually-created agent given a cwd, a folder agent retargeted via SQL, etc.), the first launch after upgrade will detect those mismatches and rebind the affected tabs to fresh folder agents. The "old" agent stays in the workspace, just without a folder claim.
  • Drag-pin chip order resets for the affected chips on first launch after rebinding. Other chips' order is preserved.

If you're reading this in Push, you clicked Check for Updates. Manual tier, as usual.

v1.9.1

2026-05-19

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.1

A targeted PATCH for Terminal-mode users. Before today, every terminal tab and every historical Claude/Codex session collapsed into a single "Other" chip whenever the app ran in Terminal mode — no matter how many folders you'd worked across. v1.9.1 makes Terminal mode group by folder the same way Full mode does, without ever calling the server.

What changed

Terminal mode now groups chips by folder

The terminal toolbar's chip strip and the Sessions view's chip strip both clustered everything under one "Other" pill in Terminal mode. The cause was structural: the only path that ever wrote tab.agentId was the server-side ensureAgentForFolder call, and that's Full-mode-only by design. A Terminal-mode-only user accumulated no per-folder identifiers, so the chip grouping had nothing to key on.

v1.9.1 synthesizes a per-folder identity at tab-spawn time in both modes, entirely client-side. The identifier is a deterministic UUIDv5 hash of the folder path — same input always produces the same UUID, so multiple tabs in the same folder share one chip; different folders get different chips. The cached snapshot carries the folder name (the basename) for chip rendering.

In Full mode, the existing ensureAgentForFolder call overwrites the synthesized identity with the server's real one a moment after spawn — your Full-mode workflow is unchanged. In Terminal mode, the synthesized identity is what the chip strip groups on. Either way, the toolbar shows one chip per folder, labeled with the folder's basename.

Upgrade-time backfill

Pre-this-release Terminal-mode tabs that have no agentId on disk get their identity backfilled on the next launch — either inheriting a sibling tab's identity at the same folder (so your existing Full-mode chip stays one chip after a Terminal-mode session) or synthesizing fresh. One-shot normalization at restore-time; idempotent thereafter.

What this isn't

  • No new server calls in Terminal mode. The synthesis is a pure client-side SHA-1 of the folder path. No HTTP, no DB, no LLM.
  • No data-model changes. Agent, TerminalTab, and AgentSnapshot all use existing fields. No new schema, no migration.
  • No Full-mode behavior change for already-bound tabs. Tabs that were ever in Full mode keep their server-issued identity; the chip strip renders them exactly as today.
  • No changes to which paths are eligible. The chip-eligibility denylist (Downloads, Library, .ssh, .docker, Push runtime roots, system paths, …) mirrors the server's denylist exactly. The same paths cluster under "Other" in both modes.

When you flip modes

Going Terminal → Full: tabs whose synthesized identity exists only client-side get promoted to a real server agent on next Full-mode bootstrap via the existing autoheal pass. Going Full → Terminal: tabs keep their server-issued identities and the chip strip continues to render them via the per-tab cached snapshot, even if the server-side agent has since been deleted.

Notes for upgrading

  • On first launch after upgrade, Terminal-mode tabs whose chip used to collapse into "Other" will move to a per-folder chip automatically. Drag-to-pin order resets for those chips (they're new chip IDs); you can re-pin once.
  • Existing Full-mode tabs are not touched. The chip strip you see today stays the chip strip you see tomorrow.
  • Sessions view in Terminal mode now shows a chip per folder for historical Claude/Codex/OpenCode/Hermes sessions on disk, instead of one shared "Other" bucket.

If you're reading this in Push, you clicked Check for Updates. Manual tier, as usual.

v1.9.0

2026-05-19

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.9.0

A MINOR-bump release wrapping the day's data-model unification work into one shippable artifact: every folder a terminal tab opens in is now a real agent end-to-end, the workspace's hidden "ghost" runtime and the auto-created scratch folder agent are now the same row, and the Terminal toolbar's "Other" / "(unknown agent)" leaks are structurally closed.

What's new

Folder-as-agent autoheal is complete

Three orthogonal gaps that left tabs stranded as "Other" are closed:

  • Denylist relaxation. Tabs spawned in $HOME itself and inside ~/.push/, ~/.push-dev/ workspace subdirs now get a real folder agent. The literal ~/.push/ and ~/.push-dev/ roots stay denied (those hold server config + logs); only the subdirs that users actually work in are eligible. .ssh, .gnupg, top-level Library/Downloads/Desktop, and system paths remain denied.
  • Synchronous description fallback for new workspaces. When the workspace has no default agent to drive LLM enrichment (fresh installs, no workspace_runtime provisioned yet), auto-created folder agents fall back to a synchronous read of CLAUDE.md → AGENTS.md → .push/agent.md → README → package.json. No more permanent "(describing…)" placeholder for new users.
  • Bootstrap autoheal retry. On every Full-mode launch, tabs whose previous ensureAgentForFolder call failed (denylist refusal, network blip, server down) get a fresh retry. Tabs that should now succeed bind to a real agent; tabs that should still fail (true denylist, network still down) stay in "Other" by policy.

After these three layers + the chip-rendering hardening below, the toolbar chip group is a faithful representation of the workspace's agent state in every configuration: fresh install, dogfooding in .push-dev/, $HOME-based shells. Every chip is either named (real agent) or labeled "Other" with a tray.full glyph (truly-denied paths) — never empty, never lying about background enrichment.

workspace_runtime is now the workspace scratch folder agent

Until v1.9.0 the workspace had two parallel concepts pointing at the same physical folder: the hidden workspace_runtime "ghost" agent (used for unassigned-issue runs, capture enrichment, voice routing) and a separate createdVia="auto_folder" agent auto-created when a terminal tab opened in the workspace scratch dir. Two DB rows, two adapter spawn paths, same physical folder.

v1.9.0 unifies them in-place. The workspace_runtime row gains an explicit cwd.path pointing at the workspace scratch dir. ensureAgentForFolder short-circuits: if the requested path is the workspace scratch dir, it returns the workspace_runtime row rather than minting a duplicate. A boot-time migration sweep absorbs any pre-1.9.0 duplicate rows (soft-deleted with metadata.absorbed_into for audit), zero FK churn — workspace_runtime's UUID is preserved across the migration, so heartbeat runs, agent task sessions, and agent files referencing it stay valid.

The user-facing contract is unchanged. workspace_runtime remains hidden from the agents list (isDefault=true filter); it cannot be deleted, edited beyond its adapter, or assigned issues directly. It's the workspace's catch-all execution context — a system-managed agent that should never need user attention. Terminal tab chips in the scratch dir render as "scratch" (the cwd basename), not as the internal "workspace_runtime" label.

Cleaner data model end-to-end

The L3 refactor that's been accumulating across the recent v1.8.x patches is now done at the type-system level:

  • SessionDisplayItem.localLive and .localHistorical cases carry an Agent value (resolved at discovery time) instead of a cwd: String. The "is this a string or an agent?" cognitive overhead is gone from filter and chip rendering hot paths.
  • TerminalTabsStore.agentOrder: [UUID] is the sole chip-ordering surface. The legacy folderOrder: [String] field stays on disk as a decode-only migration source (consumed once on next Full-mode bootstrap, then dropped) but is no longer written by v1.9.0+. Existing user data is preserved through the migration.
  • TerminalTab.cachedAgentSnapshot carries the agent's name, cwd, and adapter type as durable per-tab state, so chip labels render correctly even before WorkspaceStore.bootstrap() arrives — including in Terminal mode where bootstrap never runs. The snapshot now also stamps isSystemManaged: Bool, suppressing internal labels like "workspace_runtime" from any user-visible chip in any mode.

Sessions toolbar + cards: no more system-label leaks

The Sessions view's chip group and card rendering both went through the same hardening. Workspace_runtime sessions (capture enrichment runs, folder describe runs, unassigned-issue runs) used to surface as "(unknown agent) N" in the chip, and as cards with title + attribution both reading "workspace_runtime". In v1.9.0:

  • Chip label resolution recognizes system-managed agent IDs and falls back to the cwd basename ("scratch") via session items.
  • Card title for workspace_runtime push runs uses the issue title → invocation source ("Browser capture enrichment", "Folder describe") → cwd basename, in that order. Never "workspace_runtime".
  • Card attribution uses the cwd basename with a folder glyph (instead of the person.fill avatar) — signals "this is a workspace folder bucket, not a persona."

Terminal mode

No behavior changes for Terminal-mode-only users. Auto-creation, reconcile, migration, and autoheal are all Full-mode-gated by WorkspaceStore.bootstrap(). Terminal mode preserves all tab bindings and persisted state through mode flips losslessly.

The pre-L3 folderOrder chip ordering is now persisted through Terminal-mode launches via an in-memory migration buffer — earlier in the v1.9.0 dev cycle the encoder destroyed unconsumed data on the first save in Terminal mode; this ships with that resolved. The system-managed agent flag is persisted in the per-tab cached snapshot so chip labels resolve correctly in Terminal mode without needing the server fetch.

Notes for upgrading

  • The boot-time migration sweep runs automatically on first Full-mode server boot after upgrade. Existing duplicate scratch auto_folder rows are absorbed in-place with no FK rewrites; user-facing chip labels for those tabs self-heal on the next reconcile pass (~0-2 seconds after bootstrap).
  • Pinned terminal toolbar chip order from pre-L3 snapshots migrates to the new agentOrder shape on the next Full-mode bootstrap. The migration is one-shot, idempotent, and self-recovers if a tab spawns during the migration window.
  • The /workspaces/:wid/runtime endpoint continues to surface workspace_runtime's adapter for the Settings picker. Adapter changes via the existing Settings → Runtime adapter UI are unchanged.

If you're reading this in Push, you clicked Check for Updates. Manual tier, as usual.

v1.8.18

2026-05-18

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.8.18

Patch follow-up to v1.8.17. Closes a gap in the documented "refresh an existing folder-agent's description" recipe.

What changed

Re-fire enrichment when reopening a tab in a pending folder

v1.8.17 introduced the always-AI description-enrichment pass for new auto-folder agents. The release notes also documented a recipe for refreshing an existing agent whose description was thin or outdated:

curl -s -X PATCH "http://localhost:3100/api/agents/<id>" \
  -d '{"metadata": {"descriptionSource": "pending"}}'
# Then open a tab in the folder, or run `push agent wakeup <id>`.

The "open a tab in the folder" half of that recipe didn't actually work. The existing-row branch of ensureAgentForFolder (which the tab-spawn hits) bumped tabSpawnCount and returned early without ever calling triggerFolderDescribe. So a stuck-on-pending agent had no built-in path back to a real description short of a manual PATCH.

v1.8.18 fixes it. When ensureAgentForFolder hits an existing row whose descriptionSource is "pending", it now fires triggerFolderDescribe on the way out — same fire-and-forget shape as the new-agent branch. The LLM's prompt still guards on descriptionSource === "pending" before PATCHing, so concurrent edits during the ~30s enrichment window remain safe.

Net effect: the documented refresh recipe works end-to-end again, and any folder-agent that was created before v1.8.17 (when descriptionSource defaulted to a real file source) can be re-enriched by toggling its metadata back to pending and opening a tab.

What did NOT change

  • New-agent enrichment trigger (unchanged since v1.8.17)
  • Idempotency model — LLM prompt still guards descriptionSource === "pending" before PATCH
  • Adapter pinning (haiku for claude_local, gpt-5-nano for codex_local, …)
  • Cost envelope (~$0.005–0.02 per refresh, capped by workspace_runtime budget)
  • Denylist, stale-path daemon, usage tracking — all unchanged

Notes for upgrading

If you're upgrading from v1.8.17 and have an auto-folder agent with a thin description (the canonical case was push-ios landing on the CLAUDE.md badges line), the manual refresh path is now:

# 1. Reset descriptionSource → pending
curl -s -X PATCH "http://localhost:3100/api/agents/<id>" \
  -H "Content-Type: application/json" \
  -d '{"metadata": {"descriptionSource": "pending"}}'

# 2. Open a terminal tab inside the folder (or trigger ensure-for-folder by any other route).
#    Tab spawn → ensureAgentForFolder → existing-row branch → triggerFolderDescribe.
#    ~30s later the description fills in.

If you're reading this in Push, you clicked Check for Updates. Manual tier, as usual.

v1.8.17

2026-05-18

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.8.17

Two changes — both finishing patterns started in earlier releases.

What changed

Folder-agent descriptions: always-AI, always background

When you open a terminal tab in a new folder, Push auto-creates an agent for it (v1.8.14). Until now, the agent's description came from synchronously reading the first paragraph of CLAUDE.md / AGENTS.md / README, with a workspace_runtime LLM pass only firing for folders that had nothing to extract. That worked most of the time — and produced a thin, useless description when a folder's CLAUDE.md happened to lead with metadata or badges (push-ios was the canonical case: its description landed as just "Bundle ID: ai.massless.push | Website: https://pushto.do | Version: 0.7.4").

v1.8.17 closes that gap. Every auto-created folder agent now follows the same pattern Push already uses for browser-captured issues and voice transcriptions:

  • Tab spawns instantly. Server creates the agent with a placeholder description: "Folder: <name> (describing…)". Sidebar shows the agent immediately; the placeholder is the loading state.
  • workspace_runtime is woken. Same triggerFolderDescribe call as before, just now unconditional instead of fallback-only. The LLM reads CLAUDE.md / AGENTS.md / .push/agent.md / README / package.json plus ls and git log, decides whether to use the file's prose verbatim or synthesize from broader context.
  • ~30 seconds later, the description fills in. The LLM PATCHes the agent with real routing prose AND tags the source it used ("CLAUDE.md", "synthesis", etc.). Sidebar updates via WebSocket.

If the LLM run fails (network, budget, etc.) the placeholder stays — the agent is still functional, just not as accurately routable until you manually edit or until a retry succeeds. Same graceful-failure stance as capture enrichment.

If you've already manually edited an agent's description (CLI, Mac UI, anywhere), the next time enrichment runs against it the LLM will see descriptionSource !== "pending" and exit silently. Your edits are preserved structurally — no flag to set, no opt-out — just write what you want and the system stops trying to "improve" it.

The pattern is borrowed directly from capture enrichment (which has been in prod for ~9 months handling browser-captured issue title/description/assignee). Same fire-and-forget shape, same model pinning (Haiku for claude_local, gpt-5-nano for codex_local), same suppression rules (no completion comment, no timeline noise, fresh session). Cost is ~$0.005-0.02 per new folder, capped by the workspace_runtime's monthly budget.

Soft-deleted agents stay deleted

Two narrow places in the codebase didn't filter deletedAt properly, surfaced when push-dev and coder were soft-deleted during the v1.8.16 shake-out:

  • findMentionedAgents (the regex that resolves @name in issue/comment bodies) was matching against the full agents table, so typing @push-dev after deletion would still resolve to the deleted row's id.
  • enqueueWakeup's status guard checked terminated and pending_approval but not deleted agents. If a stale id from anywhere (old assignment, Mac tab binding, hand-typed) was passed in, the wake would proceed and try to spawn a heartbeat run against the deleted agent.

Both close together with a single deletedAt check. Pre-existing latent gaps — not new with v1.8.17 — but they were the only paths a soft-deleted agent could leak back into active workflows. After this release the soft-delete contract is airtight: deleted agents are invisible to every routing surface.

What did NOT change

  • The trigger (every Mac terminal tab spawn in Full mode) — same
  • The denylist (system paths, $HOME-itself, ~/.push/, dot-caches) — same
  • Adapter inference order (CLI in tab → workspace default → claude_local) — same
  • Stale-path daemon (hourly sweep marking missing CWDs) — same
  • Single-cwd per agent shape (collapsed in v1.8.16) — same
  • Usage tracking (metadata.tabSpawnCount + lastTabSpawnAt bumped on every hit) — same
  • Backward-compat shim for legacy cwds: [] payloads from pre-v1.8.16 clients — still in place

Notes for upgrading

Existing auto-agents (from v1.8.14 / v1.8.15 / v1.8.16) are NOT automatically re-enriched. They have descriptionSource set to a real file source already, so the new idempotency check correctly skips them. If you have a specific auto-agent whose description is thin (e.g. push-ios), the manual refresh path is:

# Reset descriptionSource to "pending", then a future tab spawn or
# explicit wakeup will re-trigger enrichment.
curl -s -X PATCH "http://localhost:3100/api/agents/<id>" \
  -H "Content-Type: application/json" \
  -d '{"metadata": {"descriptionSource": "pending"}}'
push agent wakeup <id>

A first-class push agent describe --refresh <id> CLI subcommand will likely land in a future Manual release.


If you're reading this in Push, you clicked Check for Updates. Manual tier, as usual.

v1.8.16

2026-05-17

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.8.16

One agent, one folder. The cwds[] array is gone.

What changed

The model: single CWD per agent

Until today, every agent could claim a list of working directories — primary + secondaries — with a per-entry primary boolean to disambiguate. The pattern existed because the schema permitted it, not because it was load-bearing. In practice almost every agent had exactly one CWD; the rare multi-CWD "umbrella" (push-dev claiming push-macos + push-ios + push-website) was a holdover from before folder-as-agent shipped.

v1.8.16 collapses this. Agents now have a single cwd field (an object with path, repoUrl, repoRef, worktreePolicy), or no folder at all. The mental model becomes one-to-one: an agent corresponds to a folder you work in, or to no folder (for service agents like Gmail that have no filesystem meaning).

This isn't just cosmetic. It eliminates an entire class of subtle bugs — the v1.8.15 dedup fix existed because the prior query scanned only position 0 of the array and missed secondary-CWD matches. With no array, that bug is structurally impossible. The dedup query becomes a single scalar JSONB read; indexable, faster, simpler.

The migration

A new database migration (0058_agents_single_cwd.sql) runs at server startup the first time you launch v1.8.16. For every agent:

  • If it had one CWD (the common case — coder, planner, journal, refs, almost everything): the path, repoUrl, repoRef, and worktreePolicy fields move over cleanly. name and primary are dropped (the former is redundant with agent.name, the latter was the array's tie-breaker). No semantic change for these agents.

  • If it had multiple CWDs (almost certainly only push-dev on your prod): the primary entry is preserved as the agent's cwd. The full original array is preserved in metadata.legacyCwds for forensic recovery. The dropped secondary CWDs are no longer claimed by this agent — they become orphaned, available for the auto-create system to register as their own folder-bound agents the next time you open tabs there.

  • If it had no CWDs (gmail, journal, workspace_runtime): nothing changes — cwd: null.

What this means for push-dev specifically

If your prod has push-dev claiming push-macos + push-ios + push-website (as the canonical example does), here's the post-migration picture:

  • push-dev keeps push-macos as its cwd. Its persona, description, runtime config, history — all unchanged.
  • metadata.legacyCwds preserves the full original [push-macos, push-ios, push-website] array on push-dev's row for audit.
  • Next time you open a terminal tab in ~/workspace/product/push/push-ios, the auto-create system creates a fresh push-ios agent (description sourced from push-ios's CLAUDE.md if present, fallback otherwise). Same for push-website.
  • push-dev's description ("Push product engineer across all three surfaces") will be misleading post-migration. Consider editing it to focus on Mac. The fix is a single line: push agent update <id> --description "Push macOS engineer …".

If you don't care about push-ios or push-website being separately addressable agents, do nothing. They just become orphan folders that auto-register the moment you next open a tab there. Lazy is the friendly default.

Backward compatibility

The server's create/update endpoints accept BOTH the legacy cwds: [...] array shape AND the new cwd: {...} object shape on input. Internally everything normalizes to single. This means v1.8.15 (or earlier) Mac clients still working against a v1.8.16 server won't 422 during the rollout window. The shim stays in place for at least one more minor release.

The CLI's agent cwd add/list/remove subcommands are deprecated but kept working with degraded single-cwd semantics — add becomes idempotent set (replaces any existing cwd, warns), list shows the one cwd or "(none)", remove clears the cwd. They print deprecation notices pointing at push agent update --cwd <path>. The subcommand group will be removed in a future release.

What did NOT change

  • The auto-create flow (folder = agent, fires on terminal tab spawn). Same trigger, same denylist, same description-sourcing order, same enrichment-via-workspace-runtime for fallback descriptions.
  • The routing graph. Voice routing, browser-capture enrichment, @-mention resolution — all unchanged.
  • The heartbeat / adapter execution path. Adapters never directly read agent CWDs; they receive a resolved path from the heartbeat service. The resolver got slimmer (no more array-pick), but the contract with adapters is identical.
  • Service agents (Gmail, future Slack, etc.) — unchanged, still folder-less.
  • metadata.tabSpawnCount + metadata.lastTabSpawnAt (added in v1.8.15) keep tracking on every ensure-for-folder hit.
  • The stale-path background daemon (added in v1.8.15) keeps marking missing CWDs. Reads the single cwd.path instead of scanning an array — same observable behavior.

If you're reading this in Push, you clicked Check for Updates. Manual tier, as usual.

v1.8.15

2026-05-17

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.8.15

Three improvements to folder-as-agent auto-creation — the feature that shipped in v1.8.14. All three came out of dogfooding the v1.8.14 ship and thinking carefully about how the system should behave on a busy prod instance over time.

What changed

1. Multi-CWD agent dedup (bug fix)

If you have an agent that claims multiple folders — like push-dev, which lists push-macos, push-ios, and push-website all as CWDs — the v1.8.14 dedup query only checked the primary CWD position. Opening a tab in a secondary CWD (push-ios or push-website) would silently create a duplicate auto-agent for that folder, fragmenting your routing graph.

v1.8.15 fixes this: the dedup query now scans the entire cwds[] array. Opening a tab in any of push-dev's three folders binds to push-dev. No duplicates, no fragmentation.

This bug never visibly fired in v1.8.14 because nobody opened a tab in those secondary folders during the shipped lifetime — but it would have fired the first time anyone touched push-ios or push-website after the upgrade. Fixed before it could hit anyone.

2. Usage tracking on every ensure hit

Every time a terminal tab spawns in a folder Push has already registered, the server now bumps two fields on the agent's metadata: lastTabSpawnAt (ISO timestamp) and tabSpawnCount (cumulative integer). Both fields are also set on first creation. Free signal — no schema migration, no extra server work.

You won't see these fields directly in the UI yet. They power what's coming next: recency-based sorting of the agent list, visual de-emphasis of folders you haven't touched in a while, and a "you've worked here a lot, want to customize this agent?" nudge for high-traffic folders with placeholder descriptions.

3. Background daemon: stale-path watcher

A new pattern in Push. A hourly background sweep walks every auto-created folder agent and stat()s its primary CWD. If the folder vanished from disk (you renamed it, moved it to a different drive, deleted it), the sweep writes metadata.staleSince with the discovery timestamp. If the folder comes back (you restored it, mounted the drive again), the same sweep clears the mark.

The daemon is observe-only. It never deletes, archives, or hides anything. It just marks. UI consumers can read staleSince to fade or sort stale rows; cleanup actions are always user-initiated.

This is the first "background daemon" feature in Push — a system process that quietly watches over the agent graph without ever interrupting the user. Same architecture will host future observer features: description-freshness tracking when CLAUDE.md changes, duplicate-claim detection across agents, promotion suggestions for high-traffic folders, routing-miss diagnostics. All same shape: silent, idempotent, non-acting. The system thinks; the user decides.

What did NOT change

  • Mode gate (AppMode.full only) is still structural.
  • Trigger is still TerminalTabsStore.newTab() — every gesture, no AI-mediation.
  • Description generation order (CLAUDE.md → AGENTS.md → .push/agent.md → README → package.json → fallback) is unchanged.
  • LLM enrichment of fallback descriptions runs once per fallback agent, capped on workspace_runtime's budget. Unchanged from v1.8.14.

If your prod instance is on v1.8.14 today, you'll feel zero difference until you open a tab in a folder that intersects an existing multi-CWD agent's secondary CWD — which is when the v1.8.14 bug would have fired and v1.8.15 silently prevents it.


If you're reading this in Push, you clicked Check for Updates. Manual tier, as usual.

v1.8.14

2026-05-17

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.8.14

Every terminal tab you open is now an assignable agent.

What's changed

Folders auto-register as agents

Until today, "I open a terminal in this folder" and "I want to assign work to this folder" were two separate gestures. You had to remember to run push agent create for every folder you actually worked in — name it, write a description, pick an adapter. Most folders never got that treatment, so they stayed invisible to the orchestration layer: voice captures, browser captures, @-mentions, the iOS picker — all blind to a folder you'd been working in all week.

Now, every terminal tab you open in Full mode silently registers the folder as a routable agent. Open a tab in ~/workspace/refs? An agent named refs exists, ready to receive issues. Open one in ~/workspace/product/push/push-website? Same — push-website is now a real agent. No push agent create step.

These auto-agents have heartbeat disabled by default and wakeOnDemand: true, so they don't run on a timer. They wake when something assigns to them (an issue, an @-mention, a voice capture routed by the workspace runtime). Same identity model as the rest of Push; just registered for you instead of by you.

You can promote any auto-agent into a fully-configured one — change its description, give it a SOUL.md, turn on the heartbeat — by editing it like any other agent. The auto-create is a starting point, not a special class.

Folders without CLAUDE.md get a description anyway

Auto-agents pick up a description from the folder itself: first paragraph of CLAUDE.md (or AGENTS.md, .push/agent.md, README.md, package.json's description field, in that order). If you wrote the prose, Push uses it.

For folders with none of those files — a notebook folder, a scratch directory, anywhere without a project-canonical readme — the description used to fall back to "Workspace folder at /Users/you/workspace/refs. Adapter: claude_local." Honest, but useless to the routing AI, so voice captures and browser captures couldn't auto-route to those folders.

v1.8.14 closes that gap. When the fallback description triggers, Push silently wakes the workspace runtime with a one-shot task: look at the folder (ls, git log, a couple of file reads), write a useful 2-4 sentence routing description, PATCH the agent. About 30 seconds, roughly one or two cents on Haiku, no comments posted, no timeline noise. Next time you say "send this article to refs," routing works.

The workspace runtime uses the same enrichment-pattern infrastructure that's been handling browser-capture title rewrites — folder-describe is another auxiliary wake reason in the same family. If you've already customized a folder's description, the LLM checks first and exits without overwriting. Your prose always wins.

Terminal mode is unaffected

Folder-as-agent is a Full-mode feature. In Terminal mode the server isn't running, there's no orchestration layer, and nothing about your terminal sessions changes — opening tabs is the same plain experience it's been.

Mode gate is structural

The auto-create call only fires when AppMode.current == .full. If you switch from Terminal mode to Full mode mid-session, only folders you open after the switch get auto-registered. We don't backfill historical CWDs on mode flip — that would surprise you with a populated agent list right after a mode change. New folders, going forward.


If you're reading this in Push, you clicked Check for Updates. Manual tier, as usual.

v1.8.13

2026-05-16

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.8.13

Performance fix for Sessions navigation freeze introduced by v1.8.12.

What was wrong

v1.8.12 shipped a local-state fallback so detached interactive runs whose /complete POST never landed could still be resumed by looking up the matching JSONL on disk. The lookup ran on the main thread, once per heartbeat run, on every Sessions tab navigation. On a workspace with hundreds of runs (typical heavy use), that turned into a one- to two-second freeze every time you clicked Sessions.

Two compounding causes:

  • The per-run lookup revalidated the ~/.claude/projects/ directory fingerprint on every call — a contentsOfDirectory syscall plus ~100 attribute fetches, even when the cache was already warm. Multiplied by a few hundred runs, that's the freeze.
  • The surrounding Sessions cache rebuild (historical reads across four providers + per-run resolution + filter and sort) had always run synchronously on the main thread. With v1.8.12 it crossed the perception threshold.

What changed

Two layers:

  • The detached-run sid lookup now reads the cache snapshot directly. The fingerprint pass is skipped on warm reads — we're looking for files that existed at the run's mtime window, not newly created ones, so stale-cache reads are correct for this use case. Cold cache still triggers one populate.
  • The Sessions cache rebuild moved off the main thread. Inputs are snapshotted on main; FS walks, sid resolution, filtering, and sorting all run inside a detached task; the main thread is only re-entered to write the resulting @State arrays. The view body stays a cache reader — no filtering or sorting ever blocks main.

Practical effect: clicking Sessions feels instant again. First-launch cold cache pays the FS-walk cost off the main thread, so the view mounts immediately and populates a frame or two later. Subsequent navigations hit warm caches and are imperceptible.


Routine release. Surfaces only via Check for Updates.

v1.8.12

2026-05-16

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.8.12

Three small fixes that tighten up session resume across the Sessions board, issue detail, and the issue board.

Resume a completed issue's last session from its card

On the issue board, the terminal icon on a completed issue used to be greyed out — the assumption was "issue is closed, no point starting a new run." That left no obvious way to revisit the conversation that closed the issue.

Now the icon stays clickable on completed issues. Clicking it spawns a fresh agent tab with --resume <sid> pointed at the issue's most recent resumable session — no new heartbeat row, no claim on executionRunId. Same gate the Sessions board and issue detail's "Earlier sessions" badge use, so whether a card is resumable means the same thing everywhere.

Archived issues stay disabled. Archived is the "stop touching this" signal.

"Agent History" no longer shows runs that never ran

Workspace-runtime accepts a lot of one-shot background RPCs that aren't really sessions — for example, the Chrome extension's webpage-save flow wakes a haiku to fill in a title and an assignee. Those used to clutter the Agent History column even when they failed before spawning a child process.

Now any terminal-state run with no started_at is filtered out of both the Sessions board's Agent History column and the issue detail's Earlier sessions section. They have no Claude session on disk to resume, so showing them was just noise.

Detached interactive runs can find their session on disk

When the Mac quits between Claude's session-start marker and the /complete POST that records the session id back to the server, the run gets swept by the reaper with status=detached and session_id_after=null. The conversation itself is still on disk under ~/.claude/projects/, but the server has no pointer.

Push now scans the local Claude session inventory as a fallback: when a run has no server-recorded session id but does have a started_at and a known cwd, it looks for a JSONL whose modification time falls inside the run's lifetime window and offers that as the resume target. Forward-only — runs created before this version don't have the cwd in their context snapshot. Claude-only for now; Codex/OpenCode/Hermes use different on-disk layouts.


Routine release. Surfaces only via Check for Updates.

v1.8.11

2026-05-16

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.8.11

Eight-commit cleanup of the interactive-run lifecycle and the issue↔session resume bridge — one evening's worth of audit work that shook out a stack of latent bugs around Run-in-Terminal, session resume, and the Sessions board's "running" buckets.

What's changed

Run-in-Terminal sessions actually resume now

Pre-fix: clicking Run in Terminal on an issue with prior conversation history started a fresh session every time. The previous sid was sitting in agent_task_sessions but four layers between that table and the spawn command were never wired up:

  • Interactive run completion endpoints didn't write back to agent_task_sessions (only the background path did), so closing the tab forgot the session.
  • The workspace-resolve probe computed hasPreviousSession and logged it, then dropped it on the return path.
  • The Mac always passed sessionId: nil to composeAgentCommand, even when the server knew a prior sid existed.
  • push session begin --issue X from inside a shell never surfaced the resume command.

All four gaps closed. Run-in-Terminal on an issue with prior history now spawns claude --resume <sid> / codex resume <sid> / opencode --session <sid> / hermes chat --resume <sid> automatically. Auto-resume is silent — matches the background path's behavior. If you want a fresh session, close the tab and start one from outside the issue (a dedicated Run-fresh affordance is deferred until the in-practice friction is observed).

Earlier sessions on issue detail

New Earlier sessions section on issue detail, below the active run / completed-by-run rows. Reuses the same SessionColumnCard used by the Sessions board — same chip layout, same provider glyphs, same resume badge. Filters by issue, excludes the active/completed-by rows above it to avoid double-rendering, sorts newest-first, capped at 10.

Closes MAS-3526.

Session-card resume moved to an explicit terminal-glyph badge

Pre-fix: tapping anywhere on a card in Agent History pushed SessionDetailView — a 250-LOC second-tier view that existed mostly to host a resume button. Tapping a card in Terminal History silently resumed the session. Mis-aimed clicks were doing surprising things.

Now: card body is informational; the explicit terminal-glyph badge in the card header is the only resume affordance. Badge greys out when the run isn't resumable (no sid, or a non-local adapter like gemini / cursor / pi). Terminal (Running) cards still focus the bound tab on tap — the card IS the live tab there, so card == focus is the right gesture.

SessionDetailView deleted (341 LOC). Live transcript surfaces via the bound terminal tab when one exists; per-run log is on the agent's detail page.

Four-CLI auto-type parity for Run-in-Terminal first turn

v1.8.10 documented that Claude and Hermes auto-type /push work <id> [title] on the first turn after a Run-in-Terminal spawn. This release brings Codex and OpenCode to parity:

  • OpenCode — uses /push work (commands shim resolves the slash dispatch).
  • Codex — uses $push work (Codex's mention codec rewrites $push to an internal markdown link; trailing text stays free-form prompt content).

Net result: every Run-in-Terminal click on every local-adapter agent now guarantees a first turn, which guarantees a sid bridge back to the heartbeat-runs row, which lights the resume badge instead of leaving the run sid-less and stuck idle at the TUI prompt.

Ghost "Running" cards finally cleared

If you'd been looking at the Sessions board for a few days, you may have noticed cards stuck in Agent (Running) for hours or days after the underlying session was clearly dead. That bucket should have been empty most of the time; instead it accumulated.

Root cause: the server has been writing a 7th heartbeat-run status value (detached) since 2026-04-14, but the shared constants list and the Mac's RunStatus enum only knew about 6. A detached run decoded as the default fallback (queued), which the Mac's session projection routed to Agent (Running). Every reaper-detached run since April has been a ghost.

Fixed by adding detached everywhere: shared constants, Mac enum, both session-projection switches. Swift's exhaustive switch on the enum immediately surfaced both routing sites the moment the case was added — the kind of compile-time invariant that would have caught this on day one if the enum had been complete from the start.

The matching issue-detail string check (isInflight) also left detached out, so the "Running in terminal" pill on issue detail was sticky after the reaper finalized a run server-side. Same fix.

Duplicate interactive-run rows fixed

If you'd run a Mac-side query of heartbeat_runs over the last week, you'd have seen pairs of rows for the same (tab, issue) pair — created 50–60ms apart, identical sessionKey, only one of them ever received a sid PATCH. Same root cause across two failure modes:

  • Spawn path — Phase 1 of unified-runs (2026-05-08) wired the Mac to create an invocationSource: "interactive" run at Run-in-Terminal spawn time, but didn't inject PUSH_RUN_ID into the spawned shell's env. The skill's push session begin --issue X then fell through env-adoption and POSTed a second row alongside the Mac's. Fixed by reordering the call sites to pre-create the run, then thread the runId into the tab struct and the shell env before spawning.
  • Caller pathopenIssueTab was returning the stale local tab struct after mutating the array element. Callers checked spawnedTab.interactiveRunId == nil on the stale copy, saw nil, and fired a second createInteractiveRun POST as lazy-fallback. Swift struct semantics: the array mutation didn't propagate back to the local variable. Fixed by returning tabs[idx] so post-mutation state is visible.

UUID case-sensitivity regression

A regression introduced and fixed in the same evening. The Mac was writing contextSnapshot.issueId using Swift's UUID.uuidString (uppercase). The server's JSONB JOIN was string equality, and Postgres issues.id::text produces lowercase canonical form. Every Mac-created run between the regression and the fix lost its issue linkage on the wire — run.issue came back null in listRuns, the new "Earlier sessions" filter missed them, and the resolve-workspace previous-session lookup couldn't find them either.

Same case-sensitive comparison existed in four other server-side JOINs — all five now cast both sides to ::uuid, which normalizes regardless of input case. Mac-side defense-in-depth: write .uuidString.lowercased() so future clients also send canonical form.

Workspace-runtime agents now surface their provider on the Sessions board

workspace_runtime — the default agent on every workspace, is_default = true, hidden from GET /agents per design — was producing runs whose adapter type couldn't be derived on the Mac. The Sessions board fell back to the run's adapterType field, but the heartbeat.list query didn't LEFT-JOIN agents, so that field came back blank too. Net: the resume badge greyed out on every workspace_runtime run, even genuinely-resumable Claude ones.

Fix: LEFT JOIN on agents in both heartbeat.list and getRunWithIssue, mirroring the symmetric /live-runs endpoint. Now the provider and the resume badge resolve correctly.

Stale-tab keep-alive cleanup

If you'd force-quit Push (or used dev-rebuild during iteration), restored-from-disk terminal tabs whose Ghostty surface was no longer materialized were keeping their server-side run rows alive forever — the keep-alive PATCHes fired regardless of whether the agent process was actually running. Now gated on the actual presence of a Ghostty surface for the tab.

Plus: reconcileInteractiveRunsOnLaunch now actively fires complete(status: "detached") on rows still running whose tab no longer exists locally. The previous app process is gone, so the agent process is gone too. Cross-machine safety: the loop only iterates over THIS Mac's local tabs, so it can never close another machine's active session.

The reaper's threshold for invocationSource: "interactive" runs is also shortened from 1h to 15min as a backstop for cases where the Mac lost its tab binding entirely (closeTab fired complete fire-and-forget but the POST didn't land before Push exited).

Notes for operators

  • This release is Manual — invisible to Sparkle's scheduled background timer. Sparkle picks it up when you click Push → Check for Updates in the menu bar. Most of these fixes only matter the next time you click Run in Terminal anyway, so reaching for the menu is the natural moment.
  • The duplicate-row fix is forward-only — existing duplicate pairs in your DB aren't auto-cleaned. They're harmless (only one of each pair was ever live), but visible in heartbeat.list queries. A separate one-shot dedupe script is future work if it ever matters.
  • Sessions-board's Agent (Running) bucket may shrink visibly on first launch after upgrade as previously-ghost detached runs route to Agent History where they belong.
  • One known gap remains (Gap 2 from the audit thread): the closeTab race where a session that produced a sid marker right as the user closed the tab can still lose the sid because the complete POST fires before the FSEvents/marker drain. Separate fix.

Investigation + audit documents in the writing repo:

  • investigations/2026-05-15-issue-session-resume-audit.md — the four gaps (A/B/C/D), audit method, verification.
  • investigations/2026-05-15-issue-session-relationship-audit.md — duplicate-row analysis + the Phase 1 PUSH_RUN_ID gap.
  • investigations/2026-05-15-evening-session-bug-cascade.md — full eight-commit story arc.

v1.8.10

2026-05-15

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.8.10

A maintenance release — six commits' worth of terminal-tab and capture-flow polish bundled into one quiet update. Mostly invisible improvements unless you spend time running issues from the Run-in-Terminal path.

What's changed

Terminal tabs now show the issue title, not the identifier

Before: clicking Run in Terminal on an issue gave you a tab where both the badge chip and the tab title said the same thing — e.g. badge MAS-3518, title also MAS-3518. The identifier was shown twice in the strip; the title slot carried no semantic information about the actual work.

Now: the tab title carries the issue title (e.g. Refine terminal auto-type to include issue title) while the badge still anchors the identifier (MAS-3518). Two complementary layers — machine ID on the badge, human meaning on the title.

The mechanism differs by provider but the result is uniform:

  • Claude tabs — the title flows in via claude --name "<issue title>". Properly shell-escaped now, so titles with spaces, quotes, or punctuation survive the shell parse. Claude's own haiku may eventually overwrite the title as conversation evolves; the badge stays put either way.
  • Codex / OpenCode / Hermes tabs — none of those CLIs has a --name equivalent for the interactive TUI (verified against each upstream's source). Push falls back to a Mac-side direct write to the tab's manual-title slot (userTitle), the same slot the right-click → Rename affordance uses. The tab title shows the issue title from frame zero regardless of what the agent emits.

If you want the agent's own title back (Codex's spinner, Hermes's auto-derived label, etc.), right-click the tab → Use Automatic Title clears the override and the tab falls through to whatever the agent is asserting.

/push work <id> [title] carries the issue title too

The auto-typed slash command that fires after the agent's TUI is ready now includes the issue title as advisory context: /push work MAS-3518 Refine terminal auto-type to include issue title. The skill parses first whitespace-delimited token as the issue ref (canonical), rest of line as the optional title. The push CLI accepts the same shape — push work MAS-3518 Fix login bug works without quoting.

Mirrors the same shape the slash command itself documents in skills/push/SKILL.md. Agent gets the title twice (once via slash, once via --name-derived prompt header on Claude) — redundant in the prompt but useful defense in depth.

Four-CLI skill parity — Hermes now slash-invocable

Hermes was the surprise of this release cycle: it has first-class /skill-name slash dispatch identical to Claude's, but we'd been treating it as preload-only via the -s push flag. That flag is just eager-load; without a skill installed at ~/.hermes/skills/push/, interactive Hermes users couldn't /push at all.

Fixed in two places:

  • The hermes-local adapter now mirror-symlinks Push skills into ~/.hermes/skills/ on every execute (same pattern Codex already uses for $CODEX_HOME/skills/).
  • push agent local-cli extended to install the Hermes skill alongside Claude and Codex when you onboard an agent identity.

Net: /push work <id> works inside hermes chat the same way it works inside Claude.

OpenCode /push slash command shim

OpenCode skills are tool-discovered (the model auto-invokes via a skill tool when descriptions match), not slash-invocable. For slash dispatch OpenCode has a separate commands/ mechanism.

The opencode-local adapter now writes ~/.config/opencode/commands/push.md on every execute — a thin delegating shim that instructs the model to load the full push skill via the skill tool. Users get a deterministic /push slash command in OpenCode sessions instead of relying on the model to auto-discover.

Memory-dump capture UX in the new-issue popup

The new-issue popup now has a one-click capture entry-point — pull text from clipboard or speech-to-text into the issue body without leaving the popup. Companion server change extends the create-issue endpoint with a capture mode for richer body content (voice/screenshot/clipboard provenance).

Quick-capture popup stripped the advanced-options disclosure to keep the surface focused on the primary path; advanced fields are still available from the issue detail after creation.

Background-run banners — quick recap

(Carrying over from v1.8.7, in case you missed it.) Run-in-Background runs surface the same orange "running in workspace sandbox" and yellow "Configured CWD missing" banners that Run-in-Terminal does. The pre-flight /resolve-workspace probe is now shared across both paths and across views; a run started from the sidebar surfaces its banner when you later open the issue detail.

Notes for operators

  • The Manual tier (default since v1.8) means this release is invisible to scheduled background update checks. Sparkle picks it up when you click Push → Check for Updates in the menu bar, or when you reach for that menu after a few weeks of accumulated quiet releases.
  • The /push work <id> [title] slash command and push work <id> [title] CLI accept the title as variadic positional — no quoting required. The CLI shows a (provided: "...") annotation when the typed title differs from the server-fetched title (you'd see this if the issue was retitled after you ran the command).
  • Right-click on a non-Claude terminal tab → Use Automatic Title is the escape hatch back to agent-asserted titles (useful if you want to see Codex's spinner-prefix or Hermes's auto-derived label).

Investigation + plan documents in the writing repo:

  • investigations/2026-05-15-four-cli-skill-invocation-research.md — full per-CLI breakdown of skill mechanism + tab-title strategy.
  • plans/2026-05-15-issue-title-as-claude-name-deduplicate-tab-title-vs-badge.md — design rationale for --name + userTitle fallback hybrid.

v1.8.9

2026-05-15

Silent release — Sparkle finds this one on its own and installs it on next quit. You're seeing the notes because you clicked Check for Updates while it was queued.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.8.9

Bridge release for the new Manual update tier. Same code as v1.8.8 — shipped Silent this time so it actually reaches your Mac.

Why two releases this close together

v1.8.8 introduced a third update tier called Manual — invisible to Sparkle's scheduled background timer, surfaces only when you click Check for Updates. We made it the new default. v1.8.8 itself was tagged Manual, on the theory that "every release ships under its own tier."

That created a chicken-and-egg: existing Push installs (v1.8.7 and earlier) don't yet know how to opt into the manual Sparkle channel, even when you click Check for Updates. They saw v1.8.8 in the appcast, filtered it out as "channel not allowed," reported "You're up to date." Every existing install was stuck on v1.8.7.

v1.8.9 carries no channel tag (Silent tier), so Sparkle's scheduled timer on v1.8.7 finds it normally and silent-installs it on next quit. Once you're on v1.8.9, every future Manual release works as designed — your Mac opts into the manual channel for the duration of every user-initiated Check for Updates, and not otherwise.

What's actually new (since v1.8.7)

New release tier: Manual (the new default)

Push has had two release tiers for the last 30-odd ships:

  • Notable<sparkle:criticalUpdate />. Forces an Update Available dialog on every user. Reserved for security fixes, breaking changes, data-loss fixes, and headline features.
  • Silent — Sparkle's scheduled timer finds the release and silent-installs it on next quit. A small "Update ready" pill lights up in the sidebar in the meantime. This was the default since v1.8.0.

v1.8.9 adds and defaults to a third, even quieter tier:

  • Manual<sparkle:channel>manual</sparkle:channel>. Sparkle's scheduled background timer doesn't see it at all. The release surfaces only when you explicitly click Check for Updates. This is now the default tier. Most ships from here on will use it.

The point: at the current cadence (multiple ships per week), even silent-install-on-quit was interrupting users at the moment they tried to quit, and the sidebar pill was getting noisy enough to be ignored. Manual flips the floor — Push behaves like an app that doesn't update unless you ask.

You always get the cumulative result when you do ask. Sparkle compares versions, so manually checking finds whatever the latest tagged version is — every Manual ship between checks is rolled into one upgrade.

Notable and Silent are still available for the ~20% of releases that actually warrant attention.

Board card Run button is now a Terminal button

On the issue board, each card's quick-action button used to read "Run." It now reads "Terminal," matching the same button in issue detail. Same behavior — opens a Push terminal tab bound to the issue — but the label finally tells you what's about to happen instead of leaving you to guess between "background heartbeat run" and "terminal session."

Notes for operators

  • The Sparkle appcast now carries <sparkle:channel>manual</sparkle:channel> on Manual-tier items. If you're polling the appcast yourself for any reason, an updater whose allowedChannels is empty will only see Notable + Silent items. Push's own updater seeds ["manual"] into the allowed set for the duration of a user-initiated Check for Updates, then clears it.
  • All three tier trailers (Notable: true, Silent: true, Manual: true) are mutually exclusive at every layer of the release pipeline. The CI workflow's derive step fails fast if more than one is set on a tag.
  • v1.8.8 is still live on R2 as an immutable artifact (we never delete published DMGs — Sparkle entries pointing at it would break). Nobody upgrades to it in practice because v1.8.9 is strictly newer by CFBundleVersion. The appcast entry for v1.8.8 stays as historical record.

Full design + the bootstrap lesson are captured in the writing repo: documentation/push/macos/.

v1.8.8

2026-05-15

Manual release — Push didn't surface this on its own; you found it by checking for updates. Routine improvements like this ship quietly in the background.

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.


Push 1.8.8

The first Push release that's invisible by default. If you're reading this, you clicked Check for Updates — exactly as designed.

What's changed

New release tier: Manual (and it's the new default)

Push has had two release tiers for the last 30-odd ships:

  • Notable<sparkle:criticalUpdate />. Forces an Update Available dialog on every user. Reserved for security fixes, breaking changes, data-loss fixes, and headline features.
  • Silent (was the default since v1.8.0) — Sparkle's scheduled timer finds the release and silent-installs it on next quit. A small "Update ready" pill lights up in the sidebar in the meantime.

Both still exist. v1.8.8 adds a third, even quieter tier:

  • Manual<sparkle:channel>manual</sparkle:channel>. Sparkle's scheduled background timer doesn't see it at all. The release surfaces only when you click Check for Updates, like you just did. This is now the default tier. Most ships from here on will use it.

The point: at the current cadence (multiple ships per week), even silent-install-on-quit was interrupting users at the moment they tried to quit, and the sidebar pill was getting noisy enough to be ignored. Manual flips the floor — Push behaves like an app that doesn't update unless you ask.

You always get the cumulative result when you do ask. Sparkle compares versions, so manually checking on, say, 1.8.7 finds whatever the latest tagged version is (1.8.8, 1.8.9, 1.8.10, …) and offers the newest one — every Manual ship between checks is rolled into one upgrade.

Notable and Silent are still available for the ~20% of releases that actually warrant attention.

Board card Run button is now a Terminal button

On the issue board, each card's quick-action button used to read "Run." It now reads "Terminal," matching the same button in issue detail. Same behavior — opens a Push terminal tab bound to the issue — but the label finally tells you what's about to happen instead of leaving you to guess between "background heartbeat run" and "terminal session."

Notes for operators

  • The Sparkle appcast now carries <sparkle:channel>manual</sparkle:channel> on Manual-tier items. If you're polling the appcast yourself for any reason, an updater whose allowedChannels is empty will only see Notable + Silent items. Push's own updater seeds ["manual"] into the allowed set for the duration of a user-initiated Check for Updates, then clears it.
  • All three tier trailers (Notable: true, Silent: true, Manual: true) are mutually exclusive at every layer of the release pipeline. The CI workflow's derive step fails fast if more than one is set on a tag.
  • No behavior change for users on Notable or Silent — those tiers still work exactly as before. Only the default-when-unflagged shifted.

Full design + audit lives in the writing repo: documentation/push/macos/ (release-tier overhaul commits 994949de and 613fa3fd). The /release-mac skill at ~/.claude/skills/release-mac/SKILL.md is the canonical decision tree for picking a tier on any future ship.

v1.8.7

2026-05-15

A maintenance release — mostly invisible improvements to how runs, renames, and voice transcripts behave. Eight commits' worth of polish bundled into one silent update.

What's changed

Renaming an agent (or workspace) now keeps disk in sync

Before: renaming an agent in the Mac UI or via push agent update --name <new> only updated the database row. The on-disk dir under ~/.push/.../agents/ kept its creation-time slug forever. Power users browsing the filesystem saw stale names; new agents created with previously-used slugs had to live next to old siblings.

Now: rename atomically fs.rename(2)'s the dir from <old-slug>__<uuid> to <new-slug>__<uuid> and rewrites the agent's AGENT_HOME + instructionsFilePath in the same database update. The UUID inside the dirname stays canonical; the slug just stays current. Capitalization-only edits (e.g., "Writing" → "writing") detect the same canonical slug and skip the FS rename — only the DB name changes.

Active-runs guard. If a heartbeat run is queued or running for the agent when you try to rename, the API returns 409 conflict — wait for the run to finish (or cancel it) and try again. There's no --force; the guard exists because spawned adapters bake AGENT_HOME into their process env at spawn time, and renaming the dir under a live adapter breaks every $AGENT_HOME/… read in the process.

Workspace rename cascades. Renaming a workspace also rewrites AGENT_HOME for every non-deleted agent in it (the workspace dirname appears inside every agent path).

Background runs now surface the same banners as terminal runs

Before: clicking Run in Terminal on an unassigned issue showed an orange "running in sandbox" banner — the Mac pre-flighted /resolve-workspace and used the resolver's source field to decide. Clicking Run in Background skipped that probe entirely, so identical runs got no UI signal. Users had no way to tell a background run had landed in the workspace scratch dir instead of a project.

Now: both Run-in-Background paths (the issue detail's button and the sidebar's quick-run) pre-flight /resolve-workspace and surface the same banner pattern. A run started from the sidebar surfaces the banner when you later open the issue detail — the resolver state is shared across views.

New "configured CWD missing" banner

If you've assigned an agent to an issue but the agent's configured project directory (cwds[0].path) was deleted or moved, the resolver falls back to the agent's read-only identity dir under ~/.push/…/agents/. Previously the Mac logged a warning to the run log and otherwise stayed silent — users had no clue their "Run on this issue in ~/my-repo" had actually landed in an empty system dir.

Now: a yellow "Configured CWD missing — running in agent home" banner fires next to the active run, quoting the missing path so the fix is obvious (update the agent's CWD, don't reassign).

workspace_runtime gets a real persona

The hidden workspace_runtime agent (which handles unassigned-issue runs) used to be created with no persona files at all — claude-code's --append-system-prompt-file silently no-op'd because the target didn't exist. Unassigned-issue runs were vanilla claude-code sessions with zero Push context.

Now: a new runtime archetype writes a single minimal AGENTS.md tailored for the scenario — "you're in a sandbox dir, the issue description has everything you need, post the result as a comment, then exit." Existing workspace_runtime agents get the file written retroactively on first boot of v1.8.7 via an idempotent backfill.

Enrichment runs no longer post comments

The new runtime persona above told the agent to "post the result as a comment on the issue, set status: 'done'." That's correct for user-initiated unassigned-issue runs but contradicted the capture-enrichment flow's "PATCH-only, no side effects, no comments" contract. Capture-enrichment runs now suppress the system-prompt file (via instructionsFilePath: "" in the adapter config override) so the persona stays out of the enrichment path.

Background-run pipeline polish

Four lower-risk fixes from the pipeline audit:

  • Cancel a run with TERM→KILL escalation. cancelActiveForAgent previously sent SIGTERM only; if the child trapped TERM (codex during a long tool call, claude mid-streaming), the process leaked indefinitely. The TERM→KILL ladder is now shared with the per-run cancel path.
  • 16 MB capture cap, up from 4 MB. Long verbose runs (codex --json after ~10 min, claude stream-json with many tool calls) were losing their session because the early thread.started / system.init events that carry the session ID got sliced off the tail when the buffer overflowed.
  • Deliverable parity across the four adapters.
  • Hermes sqlite warning if the local DB is missing/corrupt instead of a silent fail.

Full investigation: writing repo → investigations/2026-05-14-background-run-pipeline-audit-four-adapters.md.

Voice-recording transcripts render as speaker turns

Mac mirror of the iOS edge function's v123 → v124 → v125 evolution of speaker-turn merging. Comments now render one chunk per real speaker turn instead of one per Whisper segment, matching what you see in Otter / Fireflies. Legacy comments stored before the merge logic shipped on iOS self-heal on the Mac (the merge runs client-side as a defensive pass).

Notes for operators

  • The runtime archetype is internal-use; it doesn't surface in the Mac's New Agent picker. The blank archetype is preserved for future use (bring-your-own-instructions agents).
  • The active-runs rename guard applies to heartbeat runs — interactive terminal sessions don't block rename. If you really need to force-rename through an active run, cancel the run first (gh / API), then rename.
  • The 16 MB capture cap is a process-memory ceiling; long runs still complete fine. The cap exists to prevent unbounded growth if an adapter produces megabytes of debug output.

Investigation + plan documents in the writing repo:

  • plans/2026-05-14-slug-uuid-folder-naming.md §6 — atomic rename design (revised decision from v1.8.6's "drift is fine").
  • plans/2026-05-15-unassigned-run-banners-and-runtime-persona.md — the three-gap fix.
  • investigations/2026-05-14-background-run-pipeline-audit-four-adapters.md — the pipeline audit.
  • investigations/2026-05-14-mac-voice-recording-comment-fragmented-segments-render.md — the diarization mirror.

v1.8.6

2026-05-14

A quiet release with two unrelated cleanups. Voice transcripts that get regenerated on iPhone now actually propagate to the Mac, instead of being silently rejected by the server — the 12 voice recordings stuck with empty transcripts since 1.8.4 start healing the next time you touch each todo on iOS. Separately, workspace and agent dirs under ~/.push/ carry human-readable names on disk now (coder__<uuid> instead of just <uuid>), so ls and cd<TAB> finally do something useful when you're poking around the filesystem.

What's changed

iOS regenerate finally syncs through to Mac

Before: when you tapped Regenerate on a voice todo on iPhone, iOS would extract a fresh transcript and POST it to the Push server with the same idempotency key (externalId) as the original recording. The server saw the matching key and short-circuited — it returned the original (empty-transcript) row and silently discarded the new payload. iOS had no way to know the update was rejected; iPhone kept showing the right transcript from its own local CloudKit copy, so the bug was invisible until you opened the same issue on Mac and saw "Transcript not available."

Now: the server upserts on key match instead of short-circuiting, but only for comment types that legitimately have late-binding content (today: voice_recording; the list is config-table-driven). When iOS re-POSTs a voice comment with a fresh transcript, the server merges the new body and metadata onto the existing row and updates updatedAt. Other comment types — links, screenshots, normal text comments — keep today's strict idempotency.

The 12 stuck transcripts on existing issues (MAS-3498, MAS-3515, and 10 siblings) heal automatically the next time you edit or regenerate any of them on iOS. No manual backfill or operator action required — just touch the todo on your phone and reload the issue on Mac.

Defenses against accidental clobber

Three guardrails sit alongside the new merge path so it can't accidentally overwrite real content with empty:

  • Empty body short-circuits. A re-POST with an empty body never replaces a real transcript.
  • Placeholder body short-circuits. A re-POST with the legacy "Voice recording (Ns)" shape never replaces a real transcript either.
  • Identical-payload re-post is a no-op. A genuine network-retry replay of the same payload doesn't bump updatedAt or generate a spurious issue.comment_updated audit event.

Combined with iOS's existing 2026-05-13 client-side guard (which prevents posting empty transcripts in the first place from up-to-date iOS builds), this is belt-and-suspenders: even if a pre-fix iOS build is still in circulation somewhere, it can't create stuck transcripts anymore — the next non-empty post heals the row server-side.

Audit-trail update

Late-binding-content updates now emit an issue.comment_updated activity event alongside the existing issue.comment_added. No consumer currently filters on it; it's there for future web UI / agent integrations and for general visibility into post-creation updates.

Workspace and agent dirs on disk are human-readable now

Before: every workspace and agent directory under ~/.push/instances/default/workspaces/ was named by a bare 36-char UUID. ls gave you a wall of opaque hex; tab-completion was useless; debugging anything that touched the filesystem started with a DB query to map UUID → name.

Now: each dir carries a creation-time slug as a prefix joined to the UUID by __. The shape becomes:

~/.push/instances/default/workspaces/
└── massless-inc__781c1dc8-1bbe-478f-900c-8bb4975ea72d/
    ├── scratch/
    └── agents/
        ├── coder__4e2edaad-4430-420f-ad92-33ce7419f9ed/
        ├── ai-dev-scout__57546d70-34ad-4341-9c63-5a729763ba85/
        └── …

The UUID is still authoritative — code looks dirs up by trailing UUID, the slug is decoration. A one-shot migration runs on first boot after this update: every bare-UUID workspace and agent dir gets renamed in place to its canonical <slug>__<uuid> form, and each agent's AGENT_HOME env var is rewritten to match. Idempotent; safe to run again. No user action required, no marker file to clean up.

Renames in the Push UI or via push agent update --name … deliberately do NOT rewrite the directory on disk afterwards. The slug captures the creation-time name and may drift if you later rename; the UUID inside the dirname is what identifies it. If you care about the drift, recreate the agent — but in practice you don't need to.

Notes for operators

The scripts/backfill-voice-transcripts.ts script that shipped in 1.8.4 still works for impatient fixes — if you want all 12 stuck rows healed immediately without waiting for organic iOS sync, run it with --apply and a TOGETHER_API_KEY. Otherwise organic sync (the user opening each affected issue on iOS and tapping anything) will heal them gradually as they're touched.

Investigation + plan documents live in the writing repo:

  • investigations/2026-05-14-voice-recording-regenerate-doesnt-sync-server-externalid-shortcircuit-no-update-path.md
  • plans/2026-05-14-comments-upsert-on-externalid-for-late-binding-types.md
  • plans/2026-05-14-slug-uuid-folder-naming.md (L3-physical, the dirname rework)
  • investigations/2026-05-13-push-path-and-workspace-architecture-deep-dive.md §18 (the architectural framing)

v1.8.5

2026-05-14

A foundational cleanup. Agents finally have an honest, system-managed home dir under ~/.push/, and unassigned issues stop running in your literal $HOME. The change is mostly behind-the-scenes — most users won't notice anything except that Run-in-Terminal on an unassigned issue now opens in a Push-scoped sandbox with an explanatory banner, instead of dropping you into /Users/<you>/ next to your Documents and dotfiles.

What's changed

Unassigned-issue Runs land in a sandbox, not your $HOME

Before: if you clicked Run-in-Terminal on an issue with no agent assigned, Push's resolver fell through four heuristic layers (one of which guessed a project by counting agent CWDs across the workspace) and eventually landed the terminal at whatever it thought was a reasonable directory. In multi-repo workspaces the guess was usually wrong; if all the heuristics missed, the floor was os.homedir() — your literal user home, with full view of your Documents, browser data, .ssh, mail, and everything else personal. The agent could (and would) read whatever was at pwd to "understand the project," producing surprising completions for non-repo issues like Chrome-extension captures.

Now: the resolver has two layers for unassigned runs — the CLI's trigger CWD if you came from push work in a terminal, and a per-workspace scratch dir at ~/.push/instances/default/workspaces/<wid>/scratch/ as the floor. Your literal $HOME never appears as a runtime CWD anymore. When the sandbox-floor case fires, the issue-detail view surfaces a small orange banner — "No agent matched — running in sandbox" — so you understand why the agent started in an empty directory.

For assigned issues with cwds[] configured, nothing changes — the agent still lands in its configured project.

Per-workspace, per-agent filesystem layout

The pre-1.8 ~/.push/instances/default/workspaces/<uuid>/ directory was misleading: the UUIDs were agent IDs, not workspace IDs, and there was no per-workspace dir at all. 1.8.5 restructures it into a real hierarchy:

~/.push/instances/default/
└── workspaces/
    └── <workspace-uuid>/
        ├── scratch/                     ← workspace runtime CWD floor
        └── agents/<agent-uuid>/         ← per-agent home dir
            ├── AGENTS.md
            ├── SOUL.md
            ├── HEARTBEAT.md
            ├── TOOLS.md
            └── memory/

A one-shot migration runs on first boot after this update. It moves scaffold files (SOUL.md, HEARTBEAT.md, TOOLS.md, AGENTS.md, memory/, CHANGELOG.md, plus anything else that was sitting in an agent's old home dir) from wherever each agent's AGENT_HOME previously pointed — typically ~/.push/agents/<name>/ for CLI-onboarded agents or ~/workspace/agents/<name>/ for --cwd-created ones — into the new canonical location. Once verified, the legacy ~/.push/agents/<name>/ source dirs are deleted; the user-side ~/workspace/agents/<name>/ source dirs have only their scaffold files removed (any user project content stays in place).

The migration is idempotent and marker-guarded. A pre-state record is written to ~/.push/instances/default/.agent-home-aligned containing each agent's pre-migration AGENT_HOME value, for rollback.

If you want a defensive backup before upgrading, run:

cp -R ~/.push/agents ~/.push/agents.bak.$(date +%Y%m%d) 2>/dev/null
cp -R ~/workspace/agents ~/workspace/agents.bak.$(date +%Y%m%d) 2>/dev/null

Agent home is now system-managed; cwds[] is your business CWD

Two concepts that were tangled together in --cwd are now separate:

  • AGENT_HOME (system-managed) — the agent's identity dir under ~/.push/.../agents/<aid>/. Holds the scaffolded persona files. Read by the agent at runtime via the $AGENT_HOME env var. You don't need to think about this; the server sets and maintains it.
  • cwds[] (user-managed) — the directories the agent reads inputs from and writes outputs to. Your repo, your drafts folder, wherever your work lives. Configured via push agent create --cwd <path> or the Mac UI.

Before 1.8.5, push agent create --cwd <path> set both cwds[0].path AND adapterConfig.env.AGENT_HOME to the same path — coupling identity to project, which is why the eight agents in the prod workspace each had AGENT_HOME pointing at a different user-side directory. From 1.8.5: --cwd only sets cwds[0].path. AGENT_HOME is always the canonical system path.

Persona scaffold templates now reflect this: SOUL.md / HEARTBEAT.md / TOOLS.md / AGENTS.md tell the agent to read persona files from $AGENT_HOME and write memory + deliverables to $PWD/.push/ (which is the project dir for agents with cwds[], or the agent's home as a fallback otherwise).

New Agent dialog gains an Archetype picker

The Mac sidebar's "New Agent" form has a new picker — Engineer (default), Observer, Bridge, or Manager — that selects the persona scaffold template the server generates at the agent's home. Engineer matches the implicit pre-1.8.5 default; the other three were previously only reachable through CLI flags.

CLI changes

push agent create flags simplify:

  • --cwd <path> now sets only the business CWD (cwds[0].path). No more silent AGENT_HOME assignment.
  • --home <path> is gone. The server picks the agent home automatically.
  • --archetype <type> is the new way to select a persona scaffold template: engineer | observer | bridge | manager | blank.

push setup no longer scaffolds persona files locally — it POSTs archetype: "engineer" and lets the server do the scaffolding at the canonical path.

The /push skill is rewritten to teach the AI the new two-concept model (identity vs. CWD) so AI-driven agent creation produces correct results.

Notes for operators

  • Migration is non-fatal. If anything in the alignment phase fails, the server logs and starts anyway. Marker file at ~/.push/instances/default/.agent-home-aligned records pre-state for rollback. Re-run by deleting the marker and restarting the server.
  • Rollback. Revert this version via Sparkle, delete the marker, and the pre-state in the file is enough to manually rewrite agents.adapterConfig.env.AGENT_HOME back to the old values. Filesystem content for ~/.push/agents/<name>/ source dirs is deleted during migration; restore from the backup recommended above.
  • External scripts that read $AGENT_HOME at the legacy paths will break. From 1.8.5, $AGENT_HOME always resolves to a UUID-keyed path under ~/.push/instances/default/workspaces/<wid>/agents/<aid>/.

v1.8.4

2026-05-13

A quiet release. Issue voice recordings finally render properly on the Mac — speaker chips for multi-person audio, a play button when the audio file is around, and an honest "transcript not available" stub when it isn't.

What's changed

Voice recording comments render with speaker chips + audio playback

Before: any voice_recording comment on an issue went through the catch-all Markdown renderer in the issue detail view. For single-speaker transcripts that looked fine. For multi-speaker transcripts produced by the iOS edge function (which started shipping inline [SPEAKER_02 0:00-0:06] body markers a couple of days ago), the markers leaked into the rendered text — you'd see [SPEAKER_03 0:13-0:17] every few seconds of conversation, breaking up otherwise-readable prose into bracket noise. Worse, comments where the transcript hadn't filled in yet (older recordings stuck with body = "Voice recording (Ns)" and an empty metadata.transcript) rendered as just that bare string — no play button, no duration, no way to listen back, even when the audio file was still on the server.

Now: voice_recording comments get a dedicated renderer. Multi-speaker transcripts parse the SPEAKER markers into chips — a colored dot, an uppercase Speaker 01 label, and the M:SS – M:SS range — with each speaker's body text below as a clean block. Single-speaker transcripts render as plain prose with no SPEAKER junk. When the audio asset is present (either because iOS uploaded it or because the comment carries one for any other reason), a play/pause control appears above the transcript with the recording's duration next to it; tapping the button streams the audio directly from the Push server. When the transcript is genuinely missing but the audio file is there, you see the play button and a small "Transcript not available" line instead of the placeholder string.

The chip palette uses adaptive Apple system colors so it flips correctly between light and dark mode. Long transcripts (the iOS edge function can produce 150+ segments on a 5-minute multi-person recording) render lazily so the issue detail stays responsive while scrolling.

Notes for operators

Pre-1.8.4 voice_recording rows on the server that landed with an empty transcript will continue to show the audio-only UI until someone runs a one-shot backfill — there's a new scripts/backfill-voice-transcripts.ts for that. It scans for voice_recording comments with empty metadata.transcript and a non-null asset_id, re-runs Whisper Large v3 (with diarization) against the local audio, and writes the resulting transcript back to the row. Dry-run by default; requires TOGETHER_API_KEY for --apply. Twelve such rows existed at release time across the prod workspace.

v1.8.3

2026-05-12

Hotfix for v1.8.2. Issues board failed to load with a 500 from /api/workspaces/.../issues for anyone who upgraded an existing install — both the Mac app's Issues view and the web UI's issue board stayed blank. v1.8.3 fixes the underlying cause and applies the missing schema change automatically on next launch.

What's changed

Issues board no longer 500s after upgrade

Before: v1.8.2 introduced a new completed_by_run_id column on the issues table to power the Completed by · View session row in issue detail. The migration script (0057_issue_completed_by_run.sql) shipped with the build, but the server's startup migration runner was reading the PUSH_MIGRATION_PROMPT=never env that the Mac app passes — that env was intended to mean "don't block on an interactive prompt nobody can answer" but the code path actually short-circuited to "skip applying entirely." Result: the new code selected a column the DB didn't have, every /issues request 500'd, and the Mac + web boards rendered empty.

Now: the server applies pending migrations silently when running headless (which is every Push.app startup). On next launch after upgrading, the completed_by_run_id column gets added to the existing issues table without any user interaction — the boards come back immediately. No data loss; the column is nullable and only populated for issues completed after this release.

If you were affected by the empty-board bug on v1.8.2, upgrading to v1.8.3 and relaunching is the entire fix. No manual pnpm db:migrate step needed.

Future-proofing: migration env semantic is now safe-by-default

Same root cause, broader fix: any future schema change shipped in a future release will apply on first relaunch instead of being silently swallowed. The headless-startup code path no longer has an "off switch" for migrations that the Mac app accidentally toggled — there is no scenario where a non-interactive Push.app server has pending migrations and refuses to apply them.

v1.8.2

2026-05-12

A quiet release. Issues now remember which agent run completed them, Hermes joins the terminal-provider lineup, and the dev variant stops scribbling into the prod marker tree. Nothing here changes the day-to-day flow.

What's changed

Completed issues now know which run finished them

Before: when an agent run flipped an issue to Done, the issue detail had no way to surface the session that did the work. The heartbeat's clean-up step cleared execution_run_id immediately at run-finish, so by the time the issue's "Done" state synced to the Mac, the link back to the session was already gone. You could see that an issue had been completed but not who or what finished it — which made post-hoc auditing ("how did this resolve?") impossible without digging through agent logs.

Now: completed issues persist a permanent completed_by_run_id foreign key. In the issue detail, a new Completed by · View session row appears alongside the existing assignee row when an issue is in the Done state. Clicking View session opens the bound session in a terminal tab — the same path that the assignee's run button uses while the issue is still active. The link survives across run cleanup, app restarts, and CloudKit sync.

If you re-open an issue (Done → Active), the completed_by_run_id clears, so the row only appears for issues that are currently Done. No retroactive backfill — only issues completed after upgrade get the row populated; older completed issues continue to show their normal completion state without the session edge.

Hermes joins the terminal provider lineup

Before: when picking a terminal provider for an agent (or starting a session manually), the choices were Claude, Codex, OpenCode, plus a long tail of older / experimental options — gemini_local, pi_local, cursor — that hadn't been actively maintained for a while.

Now: hermes_local is a first-class option in the agent picker (Mac + web), and it's wired through to the server side so Push heartbeats can spawn Hermes runs end-to-end. The Hermes adapter calls hermes chat -q -Q --resume under the hood, parses session IDs from stderr, and reads usage/cost from Hermes's own ~/.hermes/state.db. Same shape as the OpenCode adapter — no special cases.

At the same time, the picker UI now hides gemini_local, pi_local, and cursor. Existing agents that were already configured with one of those types keep working (the underlying adapter packages, server registry entries, and validators are all unchanged) — they just stop showing up as new-agent choices to avoid steering people toward providers we're not maintaining. Zero rows existed in prod for any of the three before the change, but the back-compat path is cheap insurance.

Dev variant stops polluting the prod marker tree

This one only affects developers running the Push-Dev variant (see DEVELOPING.md). If you don't have a Push-Dev.app in desktop/, ignore.

Before: per-provider terminal hooks (Claude / Codex / OpenCode / Hermes) write per-tab marker files under $PUSH_MAC_DIR. The Mac app passes the variant-aware path (~/.push-dev/mac/ for dev, ~/.push/mac/ for prod) when it launches a terminal-side shell — but the embedded server, which spawns agent adapters during heartbeat runs, was reading PUSH_MAC_DIR from the user's shell environment instead. Most developers have PUSH_MAC_DIR=~/.push/mac exported from their terminal-integration setup, so the dev-variant server propagated that to its adapter children, and every dev-variant heartbeat ended up writing markers into the prod tree. Quiet bug, but it polluted prod's running/notify/generating markers with rows that prod's app didn't expect.

Now: buildPushEnv() in adapter-utils resolves PUSH_MAC_DIR with PUSH_HOME-first priority. When the Mac app's ServerManager sets PUSH_HOME=~/.push-dev for the dev variant, the server now derives PUSH_MAC_DIR=~/.push-dev/mac/ and ignores any inherited value. Prod behavior is unchanged (no PUSH_HOME → falls through to the inherited value or the ~/.push/mac/ default). Verified end-to-end on Push-Dev: post-fix runs write markers into ~/.push-dev/mac/ and the prod tree stops receiving them.

v1.8.1

2026-05-11

A quiet release that completes the v1.8 silent-update story with a low-key in-app surface, fixes a long-standing bug where iOS-ingested screenshots rendered as captions instead of images, and canonicalizes which surface created each issue. Most of this is invisible unless you go looking.

What's changed

Updates surface in the sidebar footer

Before: v1.8.0 shipped silent-by-default updates. Working as designed — but with one open question. If Push updates itself silently, how does a curious user know an update is queued (or install one immediately if they want to)?

Now: when Sparkle has silently downloaded an update and queued it for next-quit install, a small row appears at the bottom of the sidebar:

Version 1.8.2 ready                        [Update]

The left text is just text — not interactive, not loud, sized to match the existing sign-in row's typography. The small [Update] button on the right is the only click target. Tapping it triggers an immediate quit + relaunch into the new version. If you don't tap it, Sparkle still installs on next quit anyway — the button just moves it up.

The row is hidden entirely when no update is pending — so most of the time, you'll never see it. In Full mode it sits below the existing sign-in / avatar / relay-dot row. In Terminal mode (where the rest of the footer chrome doesn't render), it surfaces on its own when an update is queued and nothing else otherwise.

This intentionally stays out of the menubar. The Push menubar P glyph remains untouched.

iOS-ingested screenshots now render as images, not captions

Before: when you captured a screenshot from iOS Push (Share extension from Photos, WeChat, X, etc.), the Mac app's issue detail would render it as a plain text caption — "Screenshot from Photos" — instead of the actual image. Voice recordings on the same issues worked fine; only screenshots were broken. This had been latent since 2026-03-30.

Now: the server auto-resolves iCloud-synced screenshots into asset records at comment-create time, the same way it has been doing for voice recordings since v1.6.x. The Mac app's existing image renderer takes over automatically once the asset row exists — no Mac changes were needed.

If you have historical issues with caption-only screenshots, this release alone doesn't fix them retroactively; a one-shot backfill script (scripts/push-unpack/ingest-binary-assets.ts) walks existing comments and links iCloud files. Per-workspace; run on demand.

Root cause for the curious: when the iCloud-symlink architecture was introduced in late March, only the voice-recording path got a corresponding runtime auto-resolve in the comment-create route. The screenshot equivalent was never written — a one-time backfill at the time masked the gap by retroactively linking 235 existing screenshots, but every iOS-captured screenshot since accumulated as assetId: null. This release closes the gap with a parametric helper so the next iCloud-backed media type lands as a one-row config addition rather than another forgotten branch.

Issue source_client canonicalized to three buckets

Mostly invisible, but worth flagging for anyone who filters issues by where they came from.

Before: the issues.source_client field was inconsistently populated. The Mac said "macos" (and rarely got that to the database in practice), the iOS app said nothing, the Chrome extension said "chrome", and the CLI said "cli". Three different conventions, plus a lot of null.

Now: three canonical values — mac, ios, browser. The server canonicalizes legacy values it receives (macos and climac; chromebrowser) and infers from external_source when the client didn't send anything (browserbrowser, pushios), so cross-repo deploys can lag without breakage. The Mac, CLI, and web UI all send the canonical values directly; iOS and Chrome ext are covered by inference until their repos pick up the convention.

A one-shot backfill at deploy time recovered 1252 historical rows (mostly iOS). After the migration, the workspace source_client distribution went from 98% null to 75% classified.

Edge cases worth knowing

  • The update row renders only when Sparkle has fully downloaded an update. Not during download, not before. If you check at the wrong moment and see nothing, that's correct — there's no half-progress indicator (a "downloading…" state would mislead since you can't act on it).
  • Tapping [Update] does not show a confirmation. Sparkle silent-installs and relaunches immediately. Worst case if you click by accident: you relaunch slightly earlier than you would have on your next normal quit. The reassurance copy in Sparkle's relaunch alert ("your Claude Code session will resume") still applies.
  • The update row never renders if you've collapsed the sidebar (⌃⌘S) in Full mode. Accepted gap — silent-install on next quit Just Works regardless. If you want the indicator, expand the sidebar.
  • The historical-screenshot backfill is opt-in. New screenshots automatically work post-update. Old broken ones stay caption-only until someone runs the backfill script for that workspace.

Compatibility

  • iOS app — no change required to benefit from the screenshot fix or the source_client cleanup. Server-side inference covers both.
  • Chrome extension — no change required. Server canonicalizes its existing "chrome" value to "browser".
  • CLI — sends sourceClient: "mac" on push issue create now (was nothing prior, which the server inferred as null).
  • Web UI — sends sourceClient: "browser" on the New Issue dialog now (was nothing prior).
  • Existing installs on v1.8.0 — first silent install of v1.8.1 happens on next quit. No preference migration. The update row is empty until v1.8.2 or later starts being available.

What's not in 1.8.1 yet

  • Menubar P-glyph update indicator. Considered and intentionally rejected during the Phase 3 design — the menubar stays clean. If sidebar-collapsed or Terminal-mode users report missing the in-app indicator, it can land as a follow-up sharing the same state holder.
  • Release notes inside the update popover. Tapping the [Update] button is direct — no intermediate popover with "what's new" text. The App menu → "What's New in Push…" remains the place for release-notes prose.
  • HEIC transcode for the web UI. iOS-captured screenshots come as HEIC; the Mac app's AsyncImage renders them natively via Image I/O. The web UI doesn't — browsers don't decode HEIC. If web-side rendering of HEIC screenshots becomes a priority, a ?format=jpeg query-param on /api/assets/:id/content is the cleanest path. Out of scope here.
  • Auto-retry / sweep for null-assetId voice recordings. The 20% null rate on voice recordings (iCloud sync race at comment-create time) is the same shape as screenshots had pre-fix, but the screenshot fix doesn't address it. A periodic backfill or iOS-side retry would close it. Tracked separately.

Internals

For anyone reading the source after the release:

  • Update row state holderdesktop/Sources/Push/Updater/PushUpdateState.swift. @MainActor ObservableObject exposing pendingUpdate: PendingUpdate? and an immediateInstall: (() -> Void)? closure. Set from PushUpdaterDelegate's callbacks; cleared on real download errors (SUNoUpdateError ignored — that just means no newer update than the one we already have queued).
  • Sparkle delegate hookSPUUpdaterDelegate.updater(_:willInstallUpdateOnQuit:immediateInstallationBlock:). Returns true to "take control" so Sparkle stalls its impatient-timer fallback while still installing on quit per its contract. Skips isCriticalUpdate items (Sparkle's dialog fires regardless) and isInformationOnlyUpdate items (no install action makes sense).
  • Sidebar rowsidebarFooterUpdateRow ViewBuilder in SidebarView.swift. Naked inline content (no background, no border) so it reads as ambient status rather than a CTA. Text uses .caption.weight(.regular).foregroundStyle(.secondary) matching the signed-in row's typography. [Update] button is .buttonStyle(.bordered).controlSize(.small). Whole row gated on updateState.pendingUpdate != nil; the .safeAreaInset block extends to render in Terminal mode when the row is non-empty.
  • iCloud asset resolverdesktop/../server/src/services/icloud-asset-resolver.ts (path is in the push-macos repo's server tree, not the client). ICLOUD_MEDIA_TYPES config map keys media-type → (filename field, storage folder, content-type). To add a new iCloud-backed type, add a row; don't add another ad-hoc branch in routes/issues.ts. CLAUDE.md "iCloud asset resolve" rule pins the convention.
  • source_client canonicalizationresolveSourceClient in server/src/services/issues.ts. Single function called at the top of POST /workspaces/:wid/issues that maps legacy values + infers from external_source. The Mac (Models.swift:612), CLI (commands/client/issue.ts:140), and web UI (NewIssueDialog.tsx:396-405) all send canonical values directly. CLAUDE.md "Issue source_client" rule pins the three buckets.

Design documents

  • documentation/push/macos/plans/2026-05-10-release-update-ux-overhaul.md §8 — Phase 3 design + the six-option survey that landed on the naked sidebar row
  • documentation/push/macos/plans/2026-05-11-phase-3-sidebar-update-pill-impl.md — Phase 3 implementation plan + execution log capturing the iteration history (popover dropped, chevron dropped, icon dropped, copy fine-tuned)
  • documentation/push/macos/investigations/2026-05-10-ios-screenshot-comments-null-assetid-no-runtime-icloud-resolve.md — iCloud screenshot bug investigation
  • documentation/push/macos/plans/2026-05-10-extend-icloud-asset-resolve-to-screenshots.md — iCloud screenshot fix plan + parametric helper design
  • documentation/push/macos/plans/2026-05-10-issue-source-client-three-buckets.mdsource_client canonicalization plan + backfill

v1.8.0

2026-05-11

The headline of this release is silent-by-default updates: from now on, most Push releases install themselves quietly the next time you quit the app, instead of interrupting you with a "ready to install" dialog. We'll keep the dialog for releases that genuinely warrant your attention — security fixes, breaking changes, headline features — and let everything else just land. Plus: localhost issue links open in-app instead of bouncing through Safari, the window has a sane minimum width so the sidebar can't be squeezed into negative coordinates, and a fresh Matrix-style demo mode under Experimental for anyone who wants ambient agent-activity visualization.

What's changed

Updates install silently by default

Before: every Push release showed a "Push X.Y.Z is ready to install" dialog. Bug fixes, polish, dependency bumps — all of it interrupted you to ask permission to relaunch. Releases the team genuinely wanted you to see (security, data-loss, breaking changes) blended into the same stream of dialogs as a copy tweak, so the signal-to-noise ratio of the dialog steadily eroded.

Now: most releases install themselves the next time you quit the app. No dialog, no relaunch prompt, no interruption — Push just notices you've quit, applies the update, and the next time you open it you're on the new version. The release notes are still there if you want them (App menu → "What's New in Push…"), and Sparkle still does its full signed/notarized verification before swapping bytes. The only thing that's gone is the modal.

The dialog is reserved for notable releases — security fixes, breaking changes, data-loss-class fixes, or headline features the team genuinely wants every user to discover. Each release explicitly opts in to dialog behavior at tag time via a Notable: true trailer in the tag message; absence means silent. v1.8.0 itself is not notable (the silent-update mechanism is shipping silent on purpose — surfacing a dialog to announce "no more dialogs by default" is the wrong kind of irony).

If you preferred the old behavior, this release adds nothing to undo it for now — silent is the new default, full stop. We'll watch for feedback.

Localhost issue links open in-app

Before: clicking an http://localhost:3100/issues/ABC-1 URL inside a terminal tab would open Safari, which would then show the web UI, which doesn't always have your local issue context. The URL was useful for sharing or copying — it was annoying as a click target.

Now: Push intercepts those clicks and focuses the issue inside the app's own Issues surface. The Safari hop is gone for in-app links. External URLs and non-localhost links still go to the browser as before.

Main window has a 1400pt minimum width

Before: dragging the main window narrower than the sum of (sidebar + content + active inspector) minimums would put AppKit's layout into an unsatisfiable state. Its fallback was to slide the sidebar to negative origin — visible as truncated long item names ("checkpoint-research" → "earch") even though plenty of column space appeared available.

Now: the window enforces a 1400pt content minimum (with .windowResizability(.contentMinSize) so it's a real NSWindow resize floor, not just a SwiftUI view-size hint). The floor is sized to fit the worst-case combination of sidebar (140pt) + issue-detail content (~720pt) + terminal inspector at its ideal width (520pt) + 20pt safety margin. 13" MBA fullscreen is unaffected. There's no scenario now where you can resize into the broken state.

A new CLAUDE.md rule pins the rationale so future inspector additions raise the floor in lockstep instead of regressing.

Matrix-style demo mode (under Experimental)

A new sidebar entry renders a full-screen Matrix-style digital rain — falling katakana, digits, and symbols at 24fps — with floating event cards drifting upward over the rain showing live agent activity from the workspace feed. Cards display agent name, title, detail, and timestamp; they age over ~12 seconds with fading opacity.

It's off by default and lives behind a new toggle in Settings → Experimental → "Matrix demo sidebar entry". Visible only in Full mode (Terminal mode never shows it regardless of the flag). For ambient orchestration visualization, demo screens, or just enjoying the aesthetic.

Mac speaks camelCase end-to-end with the server

Before: the Mac was the lone outlier among Push clients (CLI, web UI, iOS) sending convertToSnakeCase JSON bodies. The server is uniformly camelCase. v1.7.27 shipped a regression where POST /workspaces/:wid/heartbeat-runs returned 400 "agentId is required" because the Mac was sending agent_id. That visible failure was the tip of the iceberg — for months prior, the same encoder had been silently dropping typed fields like assigneeAgentId and triggerDetail because the server destructured camelCase keys and zod's .strip quietly removed the snake_case ones.

Now: the snake_case encoder/decoder are gone from PushAPIClient. Every outbound body is camelCase. Three previously-untyped interactive-runs endpoints gained zod schemas so future drift fails loud at the route boundary. A new CI guard (scripts/check-mac-api-casing.sh, wired into pr-verify) blocks any future PR that re-introduces the snake_case strategies; bypass per-call with a // casing-ok: annotation if you're genuinely proxying to an external snake_case API.

Several Mac flows that had been silently dropping fields for months now work correctly. The DEE-3 lifecycle, iOS build, and CLI session begin all verified end-to-end.

Issue identifier passed as --name to Claude sessions

When you start a Claude session bound to an issue, Push now passes the issue identifier as --name <ABC-123> so the session shows up with a meaningful name in Claude's own session listings instead of the directory-name fallback. Useful when you claude resume from outside Push — you can identify the right session by the issue it was working on instead of having to recognize cwds.

Codex glyph animation now correct in dev

The agent glyph spinner in dev variant tabs wasn't animating reliably for Codex sessions. Fixed; the spinner now cycles correctly. Debug logging that was added to chase the issue has been trimmed.

Edge cases worth knowing

  • The first silent install you experience won't have any indication. You'll quit Push on v1.7.27, open it later, and you'll just be on v1.8.0. Check About to confirm. (This is by design — that's what silent means — but it can feel disorienting the first time.)
  • If a notable release ships, you'll see a dialog again. That's the new signal: a dialog now means "we genuinely think you should see this," not "Sparkle has a new version."
  • The Experimental section's Matrix demo only renders in Full mode. If you're in Terminal mode, the toggle has no visible effect. Switch to Full from Settings → Mode if you want to see it.
  • Window minimum width may feel tight if you've never resized aggressively narrow. 1400pt is the floor; on a 13" MBA non-fullscreen at default scaling, that leaves no room to dock anything alongside Push. Use fullscreen or a larger display if you need adjacent windows.

Compatibility

  • iOS app, web UI, CLI, relay, adapters — unaffected by the camelCase fix (they were already camelCase).
  • Existing installs on v1.7.27 — first-time silent install happens on the next quit after this release lands. No preference migration; nothing to do.
  • Existing experimental flags (Quick Terminal popup, Memory sidebar, Cost sidebar) — settings preserved, behavior unchanged. Matrix demo is the fourth entry under the same pattern.
  • Existing Mac flows that had been silently dropping snake_case fields — now work correctly. If you noticed assigneeAgentId or triggerDetail going unset on Mac-initiated runs, that's fixed.

What's not in 1.8.0 yet

  • A user-facing toggle to opt back into "always show update dialog" — silent is the default and there's no escape hatch in this release. If feedback warrants one, it'll come in a follow-up.
  • Telemetry on silent-install adoption — we don't track whether your install was silent or dialog-driven. The decision is purely a function of the tag's Notable: trailer.
  • Window minimum width auto-adjustment when new inspectors land — the 1400pt floor is hardcoded. New inspectors that push the worst-case wider need a manual bump in PushApp.swift plus a sanity check that 13" MBA fullscreen still fits.

Internals

For anyone reading the source after the release:

  • Silent-by-default plumbing lives in .github/workflows/release-mac.yml's "Derive version" step (greps the annotated tag's message body for ^Notable: true$ → forwards PUSH_NOTABLE=1) and scripts/mac-publish-release.sh (passes --notable to mac-appcast-add.py). The appcast-side flag is <sparkle:criticalUpdate /> on the item — Sparkle's own mechanism for "show this even if user has automatic updates on." Absence = silent. The release-mac skill enforces an explicit notability question per release; default is no.
  • Localhost URL interceptWKNavigationDelegate.decidePolicyFor checks if the URL is localhost:3100 matching /issues/<identifier> and dispatches via IssueRouter instead of allowing the browser hop. Non-matching URLs fall through to default .allow behavior.
  • Window minimum widthPushApp.swift uses .frame(minWidth: 1400, minHeight: 600) on the main scene's root view + .windowResizability(.contentMinSize) on the WindowGroup. The latter is required for .frame(minWidth:) to be honored as the NSWindow's resize floor; without it the floor only governs the SwiftUI view's intrinsic-size lower bound and AppKit will still let you drag past it. Settings + Logs scenes use the same pattern.
  • Matrix demoViews/Demo/MatrixRainView.swift (Canvas-backed katakana/symbol rain at 24fps via TimelineView) + Views/Demo/DemoModeView.swift (rain at 60% opacity over black with floating event cards from WorkspaceStore.feed). Sidebar gating: if appMode == .full && matrixDemoEnabled { sidebarRow(.demo, ...) }. Storage key: experimental.matrixDemo in ExperimentalFeatures.Key.
  • camelCase symmetry — the two-line removal of keyEncodingStrategy = .convertToSnakeCase and keyDecodingStrategy = .convertFromSnakeCase from PushAPIClient. CI guard at scripts/check-mac-api-casing.sh greps the source for the strategies and fails the PR if found. Per-call bypass via // casing-ok: <reason> annotation. Three interactive-runs endpoints gained zod schemas (server/src/routes/interactive-runs.ts) so unknown-field rejection is loud rather than silent.
  • Claude --name flagSessions/ClaudeAgent.swift includes "--name", issueIdentifier in the launch argv when an issue is bound. No effect when run unbound (no flag passed).

v1.7.27

2026-05-08

The headline of this release is the unified Run model: clicking Run in a terminal on an issue now opens at the same directory that Run in Background would use, and the resulting session shows up in the Agent columns of Sessions like every other tracked run. The trigger button becomes a visibility choice (watch it vs. let it run headless), not a categorization one. Plus a quiet bug fix to Codex / OpenCode / Hermes tabs so they show up in Sessions the moment they're opened, not after the first prompt.

What's changed

Run-in-Terminal lands in the right directory

Before: Run-in-Terminal read the agent's primary working directory on the client, with no existence check and no fallback. If the agent had no CWD configured, the directory didn't exist on disk, or the agent had session continuity to honor, the terminal opened wherever the active tab had been pointing — so a tab opened from issue ABC-1 might inherit the cwd from the unrelated tab you were just looking at. Run-in-Background did none of this, because the server resolved CWD using a multi-tier fallback chain (primary → previous-session → agent-home, with fs.stat checks and warnings).

Now: Run-in-Terminal calls a new server endpoint that runs the exact same code path Run-in-Background does. CWD resolution is byte-identical across both buttons. If the agent's primary CWD is missing on disk, you land in the agent's home directory with a warning logged — same as Background. If the agent has a previous session for this issue, you land in that session's directory — same as Background. No more silent inheritance from whichever tab happened to be active.

Internals: the server's processWakeup had a 40-line CWD-resolution prefix; that block was extracted into prepareWorkspaceContext() and exposed as POST /agents/:id/resolve-workspace. Both the wakeup path and the new endpoint call the helper. Drift between the two paths is impossible by construction.

Issue-bound terminals are tracked as agent runs

Before: Run-in-Terminal opened a tab and that was it — no record on the server, no Active Run light on the issue, and the session showed up in Sessions cols 1/2 ("Terminal" / "Terminal History") instead of cols 3/4 ("Agent" / "Agent History"). The same (agent, issue) would land in different Sessions columns based on which button you clicked, even though both buttons did the same conceptual thing.

Now: an issue-bound Run-in-Terminal creates an invocationSource: "interactive" heartbeat run, claims issues.executionRunId (so the issue's Active Run section lights up), and lives in Sessions col 3 (Agent / running) while the tab is alive, then col 4 (Agent History) when the tab closes. The dedup query already in place (which hides JSONLs from cols 1/2 when a heartbeat row claims the sid) automatically routes the session card to the right column — no double-cards.

The shape is identical to the existing push session begin CLI flow used inside Claude Code; we're just adding a new trigger to the same backend lifecycle.

Active Run section distinguishes interactive vs background

When the Active Run section shows for a tab you opened with Run-in-Terminal:

  • Label reads "Running in terminal" instead of "Agent running" — the in-flight copy reflects user-driven, not server-spawned.
  • Status icon switches from the activity spinner to the apple.terminal glyph.
  • A new Open button appears on the right; clicking it pops the issue-detail terminal inspector pane open with the bound tab, the same path Run-in-Terminal takes after spawn. So if you've closed the inspector and you want the terminal back, click Open instead of finding the tab manually.

For Run-in-Background, nothing changes — same spinner, same "Agent running" copy.

Lifecycle that survives quits, reaper sweeps, and relaunches

The interactive heartbeat run carries the tab's full lifecycle:

  • Sid PATCH — when Claude / Codex / OpenCode / Hermes writes its session id into its marker file, the Mac reports it to the server. Now the Sessions dedup kicks in and the JSONL drops out of col 1/2 (col 3 stays as the canonical card).
  • Keep-alive — every 10 minutes, a tiny PATCH bumps the row's updatedAt so the server's 1h reaper doesn't sweep the run while you're actively using it.
  • Tab close — fires complete with status: succeeded. Card moves to col 4.
  • App quit (graceful) — best-effort complete-all with status: detached, capped at 2 seconds so shutdown stays bounded.
  • App quit (force / SIGKILL) — server's reaper detaches the row after 1 hour of no PATCH activity. No orphan rows.
  • App relaunch — the Mac reconciles each restored tab's interactiveRunId against the server. If the row was reaped while you were gone, the binding clears so a future Run click attaches a fresh row instead of pointing at a dead one.

All of this exists in the data model already (the CLI's push session begin has been using these endpoints for months); the Mac is now a second client to the same lifecycle.

Codex / OpenCode / Hermes tabs appear in Sessions immediately

Before: a tab where you typed codex (or opencode, or hermes) and hadn't yet submitted a prompt had no marker on disk, because each provider's session-start hook fires later than process start (Codex's hook runs at turn-start, not session-start; OpenCode publishes inside Session.createNext gated on submit(); Hermes once a session enters live state). So the Sessions "Terminal (running)" column quietly omitted the tab until you sent your first prompt — making it look like Push hadn't noticed.

Now: each agent's wrapper stakes a placeholder marker on launch (carries a "writtenBy":"wrapper" discriminator field) and releases it on exit only if the hook hasn't promoted it to a real marker yet. The provider's hook overwrites the placeholder when its real session-start event fires; the cleanup grep on wrapper exit looks for the discriminator, so promoted markers persist correctly across exit (preserving the existing resume contract). The Sessions column now shows the tab from the moment you typed the agent's name.

Claude is intentionally not on this path — Claude's marker semantic is transient (delete-on-exit), not durable. Mixing the two would either preserve stale claude markers (causing spurious auto-resume) or delete real codex/opencode/hermes markers (losing resume continuity).

Logging instrumentation for the new flow

The unified-run flow crosses 5 layers (Mac → API client → server route → heartbeat service → DB) and has lifecycle that spans days. Every state transition now writes a log breadcrumb with consistent correlation IDs (tab, runId, agent, issue, sid). A single tab's full lifetime can be reconstructed days later from log show --predicate 'subsystem == "ai.massless.push"' plus ~/.push/logs/server.log.

This is not debug-only — it's permanent monitoring. Diagnostic guide lives in the writing repo at documentation/push/macos/INTERACTIVE-RUN-DEBUGGING.md (correlation-IDs table, the 14-step lifecycle, greppable predicates, three diagnostic playbooks). When something goes sideways in prod, that doc plus a single grep should be enough to reconstruct what happened.

Edge cases worth knowing

  • Brief col-1 flash before sid lands. The first ~500ms after Run-in-Terminal, the heartbeat row exists but no sid is reported yet, so the JSONL on disk shows in col 1 alongside the canonical row in col 3. Clears as soon as the marker drain runs.
  • Re-clicking Run focuses the existing bound tab without re-resolving CWD. If you want a different CWD, close the tab first.
  • Run-in-Background while a Run-in-Terminal tab is live for the same issue. Already gated by today's "no second active run on the same issue" rule — Background is disabled while interactive is in flight.
  • A tab open and idle for over 1 hour with the app closed. Reaper marks the row detached. On next launch, Mac's reconciliation clears the stale interactiveRunId and the tab returns to acting like an ad-hoc terminal.
  • Network unreachable when you click Run-in-Terminal. Tab still spawns; logged warning. The tab just won't be tracked in Sessions until next reconcile cycle.
  • Power-user CLI flow: push session begin inside a Push-spawned terminal tab. The CLI's idempotency rule reads PUSH_RUN_ID from env and adopts our row instead of creating a duplicate. So the /push session begin you might still run from inside Claude is harmless when wrapped by Run-in-Terminal.
  • Codex/OpenCode/Hermes wrapper exit without a real session. The placeholder marker carries the "writtenBy":"wrapper" discriminator and gets cleaned up on exit. Sessions col 1 doesn't get stuck on a dead marker.

Compatibility

  • iOS app, web UI, CLI, relay, adapters — unaffected. The interactive-runs endpoints used by push session begin are the same ones the Mac now calls; existing CLI users see no change.
  • Existing heartbeat_runs rows — no migration needed. invocationSource: "interactive" was already a valid value; we're just adding a new client that creates rows with it.
  • Cross-Mac installinteractiveRunId and lastReportedSessionId on TerminalTab are local to each Mac install. They don't sync across devices.

What's not in 1.7.27 yet

  • Worktree-per-issue isolation for interactive runs. Run-in-Background can spawn under an isolated git worktree when the agent's cwdWorktreePolicy === "per_issue". Run-in-Terminal does not — the lifecycle questions (where the worktree lives, when it gets cleaned up, what happens if you keep the tab open across days) need their own design.
  • Server-side cancel-on-issue-close. Marking an issue done while an interactive run is live currently leaves the run in flight (the reaper sweeps it after 1h). An explicit hook to complete active runs on issue closure is a behavior change for the bg path too; flagged as future work.
  • Per-turn rows for interactive runs. One row covers a tab's full session, not one row per user prompt. Closer to the CLI's push session begin semantics; matches user expectation of "a session is a session."
  • Token usage / cost capture for interactive. No per-prompt usage extraction yet. Claude session JSONL has token totals at session-end if we ever want to surface this.

Internals

For anyone reading the source after the release:

  • CWD parity lives in server/src/services/heartbeat.ts's new prepareWorkspaceContext() helper, called from processWakeup and from the new resolveWorkspaceForAgent service method. The route is at server/src/routes/agents.ts:POST /agents/:id/resolve-workspace. The Mac client method is PushAPIClient.resolveAgentWorkspace(_:issueId:) in Networking/PushAPIClient.swift.
  • Interactive heartbeat run lifecycle on the Mac flows through three new fields on TerminalTab: interactiveRunId, lastReportedSessionId, and the existing boundIssueIdentifier. All three are persisted via Codable (with decodeIfPresent for forward-compat with old terminals.json snapshots). The store helpers attachInteractiveRun(tabId:runId:), clearInteractiveRun(tabId:), reportSessionIdToInteractiveRun(tabId:sessionId:), pingInteractiveRunsKeepAlive(), and reconcileInteractiveRunsOnLaunch() cover the lifecycle calls.
  • Sid PATCH wiring — each provider's drain (Claude / Codex / OpenCode / Hermes) calls reportSessionIdToInteractiveRun(...) immediately after assigning to lastSeenAgentSession[tabId]. Idempotent: a second drain with the same sid is a no-op via lastReportedSessionId suppression. Failure rolls back the suppression so the next drain re-tries.
  • App-quit complete-all lives in AppDelegate.performAsyncShutdown between flushPendingSave() and discardAll(). Hard 2-second cap on the network calls; on timeout, the reaper is the safety net.
  • Active Run UI branch lives in IssueDetailView.activeRunSection(_:). The Open button consults terminalTabsStore.boundTabId(for: issueId) != nil (hasBoundTerminal) so a missing bound tab doesn't show a non-functional button.
  • Wrapper marker staking_push_stake_marker <name> and _push_release_marker_if_placeholder <name> in desktop/Resources/ghostty/shell-integration/zsh/.zshenv, called from each of the codex / opencode / hermes wrappers. Placeholder JSON carries both cwd+threadId and directory+sessionId so each provider's readMarker finds the field shape it expects. To add a fifth provider: call the two helpers and verify the provider's hook does NOT include "writtenBy":"wrapper" on its real writes.
  • Logging convention — Mac uses os.Logger at category issue-run, issue-inspector, or terminal; all UUIDs and short strings tagged , privacy: .public. Server uses pino's structured logger with "heartbeat.interactive.*" event names. The categories already exist; this release adds new event names within them. See INTERACTIVE-RUN-DEBUGGING.md for the full catalog.

v1.7.26

2026-05-07

The headline of this release is the Issues board overhaul: cards now show the issue description so you can scan a column without clicking in, the filter dropdown is replaced by an inline chip strip that matches how Files filters by agent, the priority field is gone from the UI, and the sort menu collapses to a single-tap toggle.

What's changed

Description on every board card

Before: a card showed only the identifier, the title, the assignee, and a timestamp. The title alone often wasn't enough — "Fix login flow" gives you nothing to triage on without opening the detail.

Now: if the issue has a description, it renders as a 3-line clamped subhead under the title in caption-secondary styling. Card height grows with the description (within reason); cards for description-less issues stay compact, so you don't pay layout cost for empty fields.

Filter dropdown → inline chip strip

The ≡ Filter button + popover is gone. The filter is now an inline chip strip in the toolbar, matching the agent-group filter on the Files surface and the folder-group filter in Sessions.

  • All chip on the left, with a count of every issue in the workspace (search-respected).
  • Unassigned chip when at least one issue has no assignee.
  • One per-agent chip for each agent that has issues assigned, showing the brand mark + agent name + count. Same monochrome glyph treatment as the terminal tab strip.
  • Single-select: clicking a chip filters the board to that one slice, clicking another swaps. Click All to clear. The previous popover's multi-select model is gone — single-select keeps the chip row honest about what you're looking at.
  • Drag-to-reorder: per-agent chips are draggable. Order is persisted across launches under push.issues.assigneeChipOrder. New agents that aren't pinned land at the right of the strip, sorted by issue-count desc.

Sort: menu → single-tap toggle

The sort dropdown only ever held two options (Newest, Oldest), so it's now a single button that toggles between them. Icon shows the current state — arrow.down when newest is at the top of each column (default), arrow.up when oldest. Tooltip carries the explanation.

Sort moved to the leading edge of the chip cluster

The sort button used to live at the trailing edge of the toolbar, after the chips. It's now at the leading edge — right before the All chip — so the toolbar reads left-to-right as: + new-issue, gap, sort, then the filter strip. The chip strip and the sort button cluster as one related "view configuration" block; the + sits apart as a creation control.

Priority retired from the Issues UI

Priority is no longer shown anywhere in the Mac app's Issues surface:

  • Board cards: the priority icon is gone. Cards now lead with identifier + activity dot, not identifier + priority severity.
  • Issue detail: the toolbar Priority menu is gone, and the priority badge in the header row is gone. Detail header is now just the status badge + assignee.
  • Filter popover (now retired entirely, see above): the PRIORITY section it used to carry is gone.
  • New-issue sheet: the Priority picker is gone. New issues are created without a priority value.
  • The PriorityBadge shared component is deleted.

The data schema is intact: Issue.priority and the IssuePriority enum still exist on the model, the API still accepts and returns priority on Create/Update requests, and existing priority values on issues created before 1.7.26 are preserved server-side. We're just not surfacing them. If a priority-aware view ever returns, the data is ready for it.

Status quick-filter retired

The popover used to have All / Active / Done / Archived chips at the top for quick status filtering. Removed: status is already the primary axis the board groups on (each status is a column), so a status filter just hid one or more columns — which is rarely what you want. Use the columns themselves.

Edge cases worth knowing

  • Drag-released-outside or Escape during a chip drag clears the dragged-state cleanly via the same DragCleanupMonitor Files / Sessions use. No stuck "ghost" opacity.
  • A chip whose agent stops having issues disappears from the strip on the next rebuild but stays remembered in the persisted curated order. If that agent gets new issues later, the chip snaps back to its prior slot.
  • Selecting a chip whose agent then loses all its issues silently snaps the selection back to All so you don't stare at an empty board with no obvious recovery.
  • Search interacts with chips in the same direction as on Files: the chip counts are search-filtered (so "All" shows how many issues match your search across all agents), but selecting a chip doesn't shrink the chip row (the row stays stable as you click through).
  • Pre-1.7.26 priority values assigned via the iOS app, the web UI, or the Mac UI before this version are still on the issues — they're just invisible in the Mac app. Server-side queries and the API still see them.

Compatibility

  • iOS app and web UI are not changed by this release. Both still surface priority. If you need to set or change priority on an issue, do it from those surfaces; the data round-trips through to the Mac app correctly.
  • Cross-client filter persistence: the chip order stored under push.issues.assigneeChipOrder is local to this Mac install. It does not sync to other Macs or to iOS / web.
  • Chrome extension, CLI, relay, and adapters are unaffected — purely a UI refactor.

What's not in 1.7.26 yet

  • Per-column sort (each column independently). Sort is currently global across all four columns.
  • Multi-select chip filtering. We deliberately collapsed to single-select to match Files / Sessions; if a multi-issue filtering need surfaces, we'll likely add it back as a modifier-key augment (⌘-click to add to selection) rather than reinstating the popover.
  • A "Just me" chip that filters to the local user as assignee. Push doesn't currently model the local human as an assignee identity; the chip strip filters across agent assignees only.
  • Priority back in the UI under a different surfacing (e.g. as a column reorder key). Not planned, but the data is preserved if we ever want it.

Internals

For anyone reading the source after the release:

  • The chip strip is built out of FilterChip + FilterChipCount from Views/Shared/FilterChip.swift — the same components Files (FilesGalleryView.agentFilterToolbar) and Sessions (SessionColumnsView.folderFilterToolbar) use. Three surfaces now render identical chip pills; future polish (hover ring, selection tint, accessibility labels) lands in lockstep.
  • Drag-to-reorder uses FolderChipDropDelegate from Views/Shared/FolderChipReorder.swift (unmodified), with agent IDs serialized as uuidString so the existing String-keyed delegate plumbing works without a generic refactor.
  • IssueFilter (in Views/Issues/IssueFilterView.swift) shrank from a multi-select state container with statuses / priorities / assigneeAgentIds / showUnassigned sets to a single selection: AssigneeFilter enum (.all / .unassigned / .agent(String)) plus chip metadata (orderedAgentChips, agentCounts, unassignedCount, totalCount, draggedAgentId). The deleted IssueFilterPopover and IssueFilterButton have no replacements — the chip strip in MainView.issueAssigneeFilterToolbar is the entire filter UI now.
  • Chip rebuild lives on IssueFilter.rebuildChips(from:searchText:), called from IssueBoardView's existing onReceive(store.$issues) / .onAppear / .onChange(of: searchText) hooks. Same performance contract as FilesGalleryView — never derived in body. The board's column-cache rebuild and the chip rebuild fire from the same hook so they stay coherent.
  • The toolbar separation between the + button and the sort/chip cluster uses ToolbarSpacer(.fixed, placement: .primaryAction) on macOS 26+ to break Tahoe's auto-grouping of adjacent toolbar items into one glass capsule. Pre-Tahoe doesn't auto-group, so the spacer is gated to #available(macOS 26.0, *).
  • PriorityBadge is deleted from Views/Shared/SharedComponents.swift. IssueSortOrder lost its .priority case. Models stayed intact: Issue.priority, Issue.issuePriority, IssuePriority enum, and CreateIssueRequest.priority / UpdateIssueRequest.priority are all still present.

v1.7.25

2026-05-07

The headline of this release is the issue-detail terminal inspector: when you click "Run" on an issue's detail page, the bound terminal now opens in a right-side inspector pane attached to the issue itself, instead of yanking you over to the Terminal section. You can read the issue body and watch the agent work in the same view.

What's changed

Run-in-terminal opens the terminal alongside the issue, not in a different section

Before: clicking Run on an issue detail spawned a terminal tab and immediately switched the sidebar to Terminal, replacing the issue you were reading. To compare what the agent was doing against the issue's acceptance criteria you'd flip back and forth between sections.

Now: the terminal tab opens in a right-side inspector pane on the same issue-detail screen. The issue body stays visible on the left; the agent's terminal scrolls on the right.

The inspector's behavior:

  • Same terminal as before, just a second viewport. The tab still lives in TerminalTabsStore exactly like a Terminal-section tab. Closing the inspector dismisses the viewport; the terminal keeps running and remains findable in the Terminal section. There's only one terminal, with two places it can be displayed.
  • Toggle button at the trailing edge of the issue-detail toolbar (sidebar.right SF Symbol, the same icon Xcode and Mail use). Disabled when the issue has no bound terminal yet.
  • Header inside the inspector shows the agent glyph + tab title + an ↗ Open in Terminal section button (graduates to the full Terminal canvas) + a close .
  • Width persists across close/reopen and across launches. Default ~520pt, drag freely between 400 and 640pt. Beyond that, graduate to Terminal section for the full canvas.
  • Cmd+click on a file path inside the inspector terminal pushes the file preview onto the issue-detail nav stack while the inspector stays mounted on the right. You end up reading the file against the running terminal output — sidebar | file preview | live terminal.
  • Lifecycle controls aren't available inside the inspector by design. ⌘R reload, ⌘W close-tab, ⌘T new-tab, etc., are suppressed when the inspector is the host. The inspector is a peek; lifecycle ops live in the Terminal section. To reload or close the tab itself, click ↗ to graduate to Terminal section first.

Run from the issue list row is unchanged

Clicking Run on an issue list row (without entering issue detail) still navigates to Terminal section as it did before. The inspector is structurally tied to issue detail — it has nothing to attach to when you're on the list. Tap into the issue first, then click Run, if you want the inspector experience.

File-preview inspector in Terminal section: unchanged from prod

A regression introduced mid-development that froze the file-preview inspector on open in Terminal section was caught and reverted before tagging. Cmd+click on a file path inside any terminal tab continues to open the file inspector pane on the right, as in 1.7.24.

Sessions sidebar icon

The Sessions sidebar item now uses a clock-with-counterclockwise-arrow SF Symbol (clock.arrow.trianglehead.counterclockwise.rotate.90) instead of the old list.dash. Visually reads as "look back in time," which matches what Sessions actually shows: historical agent runs.

Edge cases worth knowing

  • ↗ Open in Terminal section does not close the inspector preference — it just switches sections and focuses the bound tab. Returning to Issues remounts the inspector with the same tab automatically.
  • Quitting Push preserves the inspector toggle state across launches via @AppStorage. If you had it open when you quit, navigating back to that issue after relaunch reopens the inspector with the auto-resumed tab.
  • Multiple issues with bound terminals. The inspector follows the current issue. Switching to a different issue dismounts the previous tab's surface and mounts the new issue's bound tab if it has one. The toggle is one global on/off (not per-issue) — you might toggle it on for one issue and find it auto-renders for any other issue with a bound tab too.
  • Fullscreen mode on macOS. A thin dark band may appear above the inspector header in fullscreen. This is macOS's "under-toolbar" inspector style for inspectors attached to navigation children — cosmetic, unavoidable at this placement, accepted as the cost of the inspector working at all when there's an active navigation push.
  • Right-click context menu inside the inspector terminal shows only Copy / Paste / Clear. Reload Tab is omitted (lifecycle op), matching the keyboard-shortcut suppression.

Compatibility

  • No changes required from the Chrome extension, the CLI, or any external tools.
  • All existing terminal tabs (Claude / Codex / OpenCode / Hermes) work in the inspector. The bound-tab system is unchanged — issueTabBindings still maps one issue ↔ one terminal tab.
  • The file inspector in Terminal section is bit-identical to 1.7.24's behavior.

What's not in 1.7.25 yet

  • An inspector for the Sessions detail view (look at a historical agent run, peek at the live terminal it spawned). Same architecture as the issue-detail inspector should generalize cleanly; not yet wired.
  • A way to resize the inspector below 400pt or above 640pt. The 400 floor is a hard constraint (~80 cols at SF Mono 12pt — minimum sane terminal width). The 640 ceiling is an over-cautious cap for now; we may relax it after observing real usage on wide displays.
  • Per-issue inspector visibility memory. Today the toggle is global — flipping it on for issue A means it auto-shows for any issue with a bound tab. Per-issue state is more complex than the value warrants until users ask for it.

Logs

The inspector's lifecycle, width-lock timer, and layout transitions log to the unified system log under subsystem = "ai.massless.push", category = "issue-inspector":

log show --predicate 'subsystem == "ai.massless.push" AND category == "issue-inspector"' --last 5m --style compact

Useful for debugging inspector behavior in the wild — every Run trigger, toggle click, mount/dismount, and graduate event has a log line.

Internals

For anyone reading the source after the release:

  • File inspector uses the prod-d8d67ab5 placement at NavigationStack root in MainView.swift, with a single-source computed Binding<Bool> driving .inspector(isPresented:). Untouched in 1.7.25.
  • Terminal inspector is attached to IssueDetailView's body itself, not to the parent NavigationStack — that placement was load-bearing. SwiftUI's .inspector on a NavigationStack with an active .navigationDestination push mounts the view tree but NSSplitView never gives it column space; SwiftUI tears the view down ~1ms later. Attaching to the pushed view itself is the only placement that actually renders. Cost: macOS gives "under-toolbar" inspector style on navigation children, hence the dark band in fullscreen.
  • InspectorWidthLock helper at desktop/Sources/Push/Views/Shared/InspectorWidthLock.swift is shared by both inspectors. Encapsulates the saved-width @AppStorage, live-width tracking via GeometryReader (with subpixel + range filters), the 200ms widthLocked timer (Apple Dev Forums 735559 workaround), and the synchronous-snapshot-at-close pattern. liveWidth is intentionally NOT @Published — was cascading body re-evals at 60+ Hz during resize drag, freezing the app.
  • TerminalTabsStore.issueTabBindings promoted from private to @Published private(set); new public accessor boundTabId(for:) for reactive lookups. Used by the inspector gate to know whether the current issue has a bound terminal.
  • The keyboard-shortcut suppression for ⌘R / ⌘W / ⌘T / ⌘1..9 falls out for free from the existing \.terminalActions focused-scene gating in PushApp.swift:269-301. When the inspector is the host, StandaloneTerminalView isn't in the view tree, so terminalActions is nil, and every menu item is .disabled(actions == nil).

Full design + saga: ~/workspace/writing/documentation/push/macos/plans/2026-05-06-issue-detail-terminal-inspector.md and *-impl.md. The impl plan's "Implementation saga" section chronicles 9 placement attempts before the breakthrough — useful reading before any future inspector work in this codebase.

v1.7.24

2026-05-06

This release covers everything since v1.7.21: the new-tab button menu reshape from the in-progress 1.7.22 work, and the deep-link arc that ran across 1.7.23 and 1.7.24. The deep-link work shipped iteratively under the hood (a URL-scheme foundation, then an HTTP-driven refinement), but to users it lands as one feature: clicking "Open in Push" from the Chrome extension's duplicate-page banner now jumps straight to the matching issue.

What's changed

Deep links — open issues directly from elsewhere

Push now claims a custom URL scheme so other apps and tools can hand off to a specific issue inside Push:

  • Production: push://
  • Dev variant (Push-Dev.app): push-dev:// — distinct so the two variants never compete for Launch Services dispatch on a Mac that has both installed.

Today the verb is issue:

push://issue/MAS-1234

Clicking such a URL from anywhere local — the Chrome extension popup, osascript -e 'open location "..."', a future OSC 8 hyperlink in push issue list, an AppleScript / Alfred workflow, an in-comment Markdown link — activates Push, switches the sidebar to Issues, and selects the matching issue. The handler is variant-strict: a push-dev:// URL never activates the prod app, and vice versa, even when both are installed.

The verb-in-host URL shape (host = "issue", identifier in path) leaves room for future verbs like push://workspace/<id> or push://session/<runId> without re-shaping existing URLs.

Open Chrome extension issues without leaving your tab

The matching change in @masslessai/push-browser 0.4.0 makes the Chrome extension's duplicate-banner "Open MAS-XXXX in Push →" link feel native: clicking it stays in your current tab, no new tab opens, no OS confirm dialog appears (after the very first time on each Chrome profile). Push activates with the issue selected.

How this works: the extension is already an authenticated HTTP client to your Mac's Push server. Instead of asking the OS to dispatch a push:// URL (which requires opening a new browser tab to dodge a Chrome popup-context bug, plus a one-time "Open Push?" confirm dialog), the extension makes a single localhost POST to a new /api/issues/:id/focus endpoint. The server emits a WebSocket event the SwiftUI side already subscribes to, and the same activation path the URL-scheme handler uses fires.

The URL scheme is still in place as a fallback for the rare case where Push is fully quit when you click — only Launch Services can launch Push from a cold start, so the extension falls back to a push:// dispatch (with the new tab + dialog) when the HTTP call fails.

"Start a new tab" is now an action menu, not a settings menu

Right-clicking the + New Tab button used to open a single inline picker labeled "Set default new tab option." Picking Terminal / Claude / Codex / Hermes / OpenCode flipped the saved default but did not spawn anything — a left-click on + afterwards is what actually opened the tab. The mismatch between an action-shaped surface (a labeled row in a context menu) and a settings-shaped behavior (silent preference flip) was a recurring source of "I clicked Claude in the menu and nothing happened" friction.

The right-click menu now splits into two sections:

  • Top — "Start a new tab": one row per available tab type. Clicking a row opens a tab of that type immediately. No checkmark; this section is purely action.
  • Divider
  • Bottom — "Set Default…" submenu: opens a child menu containing the same five options as an inline Picker. The current default (whatever a bare left-click or ⌘T spawns) shows the native checkmark. Picking a row in the submenu only flips the preference; it does not spawn a tab.

Left-click on + itself is unchanged — still spawns whatever the user has selected as the default. ⌘T also still spawns the default. Only the right-click menu changed.

Why a submenu rather than two flat sections

Two flat sections — top "spawn now," bottom "set default" — tested noisier than the submenu form. Five spawn rows + a divider + five default rows is eleven items, and the eye has to read each row's section context to know what it does. The submenu collapses the bottom half into a single discoverable affordance ("Set Default…" with the standard macOS chevron), which keeps the menu's primary surface short and lets the default-flipping behavior live one level deeper, where it is genuinely a less-frequent action than spawning a tab.

The submenu is implemented as Menu("Set Default…") { Picker(...).pickerStyle(.inline) }. NSMenu renders this as a row with a chevron that expands to a child menu, with the picker's options as inline checkmark rows inside.

Edge cases worth knowing

For the deep-link path:

  • First click of a push:// URL in any Chrome profile shows a one-time "Open Push?" prompt. Standard OS-level affordance; tick "Always allow" once and it goes silent. Only applies to the URL-scheme fallback path; the HTTP focus path has no such dialog.
  • App not running when the link is clicked (URL-scheme path only). Launch Services launches Push, then delivers the URL event; the navigation fires once MainView is ready. If macOS App Translocation triggers a relaunch on first ever launch, the URL event is dropped — the user re-clicks and it works.
  • Identifier not in the active workspace's cached issues. V1 is cache-only: the sidebar still switches to Issues, but no row is selected. Cross-workspace probing is deferred until a real user hits it.
  • Unknown verbs. push://something-else is silently ignored — no crash, no spurious activation.

For the new-tab menu:

  • ⌘T spawns the default tab type, exactly as before.
  • Left-click on + spawns the default tab type, exactly as before.
  • The default itself is still backed by @AppStorage("defaultNewTabAction"), so it persists across launches and stays in sync with any other surface that reads DefaultNewTabAction.
  • The button's icon still mirrors the current default (apple.terminal.fill for Terminal, the brand-mark PDF for Claude/Codex/OpenCode/Hermes), so the user can always tell at a glance what + will spawn.

Compatibility

  • Chrome extension users on @masslessai/push-browser 0.3.0 continue to work via the URL-scheme fallback path — same UX as before. Users on 0.4.0+ get the smoother HTTP focus path.
  • push://issue/<id> from anywhere else (osascript, AppleScript, Alfred, future CLI hyperlinks) is unaffected by which extension version is installed — it always works as long as Push is installed.
  • The new-tab menu's left-click + ⌘T behavior is unchanged. Only the right-click flow is reshaped.

What's not in 1.7.24 yet

  • The push CLI does not yet emit OSC 8 hyperlinks for identifiers in TTY output, doesn't yet ship deepLinkURL in --json projections, and doesn't yet have a push open <id> subcommand. All three are tracked for a follow-up CLI release.
  • Comments inside Push that mention another issue (e.g. "duplicates MAS-2200") still render the identifier as plain text. Intra-Push Markdown linkification is the highest-EV downstream piece — once it lands, every MAS-XXXX in any comment becomes a tap-to-navigate. Tracked as a separate phase of the deep-link rollout; gated on this release shipping first.

Logs

The deep-link handler logs to the unified system log under subsystem = "ai.massless.push", category = "deeplink" at notice level — useful for debugging deep-link issues in the wild:

log show --predicate 'subsystem == "ai.massless.push" AND category == "deeplink"' --last 5m --style compact

Both URL-scheme and HTTP trigger paths emit there. received url= lines come from the URL-scheme path; focus identifier= lines without a preceding received url= come from the HTTP path.

Internals

For anyone reading the source after the release:

  • New route: server/src/routes/issues.tsPOST /issues/:id/focus, ~25 lines next to the existing /enrich route.
  • New helper: desktop/Sources/Push/DeepLinkActivation.swift — owns the activation sequence (NSApp.activate(ignoringOtherApps:) + makeKeyAndOrderFront on first key-capable window + NotificationCenter.post(.navigateToIssue, ...)), wrapped in DispatchQueue.main.async so the calling event unwinds first. Both URL-scheme and HTTP paths converge here so future macOS activation-policy changes get fixed in one place.
  • AppDelegate.routeDeepLink parses the URL and delegates to DeepLinkActivation.focus(identifier:).
  • WorkspaceStore.handleEvent adds a case for issue.focus_request calling the same helper.
  • desktop/Resources/Info.plist registers CFBundleURLTypes for push. The dev variant's plist is patched at assemble-time by scripts/mac-assemble-app.sh to register push-dev instead, so prod and dev never compete for Launch Services dispatch.
  • The new-tab menu lives in desktop/Sources/Push/Views/Sidebar/NewTabButton.swift (or wherever the + button view is defined in your tree) — Menu { ForEach(actions) { Button } Divider() Menu("Set Default…") { Picker(...).pickerStyle(.inline) } }.

Full design + decision log for the deep-link work: ~/workspace/writing/documentation/push/browser/20260506_chrome_extension_deep_link_to_mac_app.md.

v1.7.21

2026-05-06

Hardens the new file inspector (added in 1.7.20) against terminal-mode leakage. The right-side inspector toggle button is now hidden when Push runs in terminal mode (where the file feature is intentionally absent), and a defense-in-depth layer prevents stale state from surfacing the inspector pane on the off chance something else writes the cached path.

What's changed

Inspector toggle is hidden in terminal mode

Push has two app modes — full (the default; everything: Terminal, Sessions, Feed, Issues, Files, Agents, Skills) and terminal (intentionally minimal, just Terminal + Sessions for users who want a focused terminal app without the orchestration surfaces). The file inspector pane shipped in 1.7.20 is a full-mode feature: it pairs with the Files section's preview component and the Cmd+click handler that's already gated to .full at the routing layer.

In 1.7.20, the routing was correct (Cmd+click in a terminal tab while in terminal mode falls through to NSWorkspace and opens the file in the system default app, exactly like before the inspector existed) — but the toolbar toggle button itself was rendered in both modes. If a user had previously used the inspector in full mode and switched to terminal mode, the button could still be clickable and would surface the cached path's preview pane. That broke the "terminal mode is minimal" invariant.

1.7.21 wraps the toggle button (and its companion flexible spacer) in an explicit appMode == .full gate. In terminal mode the trailing edge of the window toolbar simply ends after the folder filter chips — no leftover empty space, no orphan affordance. Switching back to full mode restores the toggle, and any cached state (last-opened file, persisted column width) survives the round-trip via @AppStorage.

Defense-in-depth on the inspector binding

Three additional guards were added so the inspector cannot surface in terminal mode even if some future code path sets the file-path state without going through the gated entry point:

  • fileInspectorPresented (the Bool binding the SwiftUI .inspector modifier observes) now requires appMode == .full in addition to its existing terminal-section + non-nil-path checks.
  • The .openFileInPreview notification listener in MainView ignores the notification when not in .full mode (the router already gates posting it; this is the matching guard on the receiving side).
  • The width-lock animation handler and the last-path cache writer both require .full mode, so terminal-mode state changes can't pollute the persisted inspector cache.

Each of those guards is independent — any one of them blocking a path is sufficient to keep the inspector hidden. They were added together because the cost is one line each and the guarantee — "no surface area whatsoever in terminal mode" — is much stronger than "the visible button is hidden."

There are no user-visible changes in full mode. The 1.7.20 inspector behaves identically: Cmd+click opens it on the right, the toolbar toggle hides and reopens it bidirectionally, and the column width persists across close/reopen and across launches.

v1.7.20

2026-05-06

Cmd+click a file path in any terminal tab and it now opens in a native right-side preview pane — same file viewer the Files section uses, but the terminal stays visible. Two install-time and runtime improvements ride along: SF Mono ships in the app bundle (no first-launch DMG download), and Push now keeps your Mac awake while any agent is mid-turn.

What's changed

Right-side file inspector for the terminal section

Cmd+click a file path written to a terminal tab — say a Read README.md line in a Claude session, or a stack-trace path from a build error — and Push now slides in a native macOS inspector pane on the right edge of the window with the file rendered inside. The terminal stays visible to the left; you can read the file and the conversation that referenced it without losing your place.

A new toggle button at the trailing edge of the window toolbar (the sidebar.right icon, same as Xcode's right-inspector toggle) hides and reopens the pane bidirectionally — close it, click again, the last file you were looking at comes back.

The pane is the same FilePreviewView the Files section uses for its own previews, including STTextView-backed code files, MarkdownUI rendering for .md, and image previews. Files that aren't render-able in-app (binaries, archives) still hand off to the system the way they always have. Cmd+clicking from inside the Files section continues to push the preview onto the navigation stack as before — that surface's whole job is full-canvas preview, so the inspector pattern would just add chrome there.

The pane width persists. Drag it wider, close it, reopen later, drag the divider to your liking — Push remembers across launches via a workaround for a documented SwiftUI bug where inspectorColumnWidth(ideal:) is silently ignored on reopen and the pane snaps to its minimum width. The fix is a 200ms timed lock on the column's minimum width at open time, then a relax — net effect, the inspector reopens at the saved width and stays draggable.

The handful of files we touch is intentionally small: MainView.swift adds the routing and the timed-lock workaround; StandaloneTerminalView.swift adds the toolbar toggle. FilePreviewView.swift itself is unchanged — same component, two different presentation surfaces. The clicked-files tracker (ClickedFilesStore.record) sits upstream of presentation in TerminalLinkRouter, so the Files section's "Recently clicked" view still populates identically whether you previewed the file in the inspector or pushed it onto a nav stack.

SF Mono ships inside the app bundle

Push uses Apple's SF Mono as the terminal font on macs that don't already have it installed (it ships with Xcode, but not with stock macOS). Until now, Push detected its absence on first launch, downloaded SF-Mono.dmg from developer.apple.com, mounted it, unpacked the OTFs into ~/Library/Fonts/, and registered them — all in the background, with a curl + hdiutil + pkgutil + cpio pipeline.

That worked on most Macs but had two real problems. On a fresh MacBook with no font cache pre-warmed, the async download regularly lost a race against the terminal prewarm service, so the very first terminal opened with the system fallback font and only got SF Mono on the second launch. And on enterprise Macs behind a TLS-intercepting proxy or a firewall that blocked developer.apple.com, the install silently failed with no recovery path beyond manually downloading the DMG.

The five OTFs (~660 KB total) now ship inside Push.app/Contents/Resources/SF-Mono/ and Push registers them at launch directly from the bundle. Net effect: SF Mono is available the instant Push finishes launching, every time, with no network dependency. The legacy DMG-download path is gone — the preference no longer reads "downloading…" because there's nothing to download. Existing installs that already have SF Mono in ~/Library/Fonts/ from the old code path keep working unchanged.

Mac stays awake while agents are running

If you launched a long-running Claude or Codex run from a Push terminal tab and walked away, the Mac would still go to sleep on the standard idle timeout — pausing the agent mid-turn until the screen woke up. Push now holds an IOPMAssertion while any of the following is true: a terminal agent is mid-turn, a background job is running in any tab, or a server-driven heartbeat run is active. The assertion releases automatically the moment all work finishes, so battery impact is bounded to actual agent runtime.

The assertion's reason string is "Push agents working", visible to pmset -g assertions if you want to confirm what's holding the Mac awake from the command line. Display sleep is not suppressed — only system idle sleep — so the screen still dims and locks on its normal schedule without interrupting the agent underneath.

There's no UI for this; it's automatic. If you specifically want the Mac to be allowed to sleep while an agent is running (rare, but possible if you're running on a dock that loses power on lid-close), the assertion is a per-process one and quitting Push releases it.

v1.7.19

2026-05-04

The four-column Sessions surface gets clearer headers — Terminal vs Agent, Running vs History — replacing the older "Live / Local / Running / Completed" labels that didn't reflect what each column actually shows. Two under-the-hood improvements ride along: column 2 of Sessions stops re-rendering on every heartbeat tick, and Claude Code resumes that hit a stale image now self-heal instead of failing the whole run.

What's changed

Sessions columns: Terminal (running) · Terminal History · Agent (running) · Agent History

The four-column Sessions surface was labelled Live / Local / Running / Completed. "Local" was misleading on two levels — everything in Push runs on your Mac, so local-vs-cloud isn't the actual axis, and the Local column wasn't even "everything on disk." It specifically subtracts every session that already appears as a server-driven Heartbeat run, i.e. the Local column is the complement of the two right-hand columns rather than a parallel slice. The real axis is who started this session: a human typing in a Push terminal tab (cols 1–2) versus the push orchestrator dispatching it as part of a heartbeat or /push command (cols 3–4).

New labels read straight off that axis:

  • Terminal (running) — sessions you started by typing claude / codex / opencode / hermes in a Push tab, currently live.
  • Terminal History — past tab-started sessions on disk; tap to resume in a fresh tab.
  • Agent (running) — push-orchestrated runs currently executing.
  • Agent History — finished push-orchestrated runs, success or otherwise.

Empty-state copy follows the same vocabulary ("No terminal sessions running" / "No past terminal sessions on this Mac" / "No agent sessions running" / "No completed agent sessions yet"), so the message a user sees when a column is empty matches the column they're looking at.

The header chrome also gets simpler. The leading SF Symbol icon next to each column header is dropped — the live indicator on cards plus the count chip already convey state, and the icon was visual noise inside upper-cased headers. Header padding bumped from 4pt to 11pt so the column name now sits flush with each card's title text rather than floating just-slightly-left of the content edge.

Sessions sidebar stops re-rendering on heartbeat status flips

The middle column of the Sessions surface (terminal history) is built by filtering hundreds of on-disk transcripts against the union of session-IDs currently tracked by the push server, so a server-driven session never appears twice in the UI. That filter was being re-run on every store.runs mutation — which fires for ordinary status flips (queued → running → succeeded), log appends, exit-code updates, and other events that don't change the session-ID projection at all. In a busy workspace with active heartbeats, the filter + re-render of up to 200 cards was firing several times a second, showing up as visible main-thread stalls when scrolling the sidebar with Sessions mounted.

Push now hashes the projection and skips the rebuild when the hash matches the previous one. Status flips, log updates, and timestamp changes — none of which alter the dedupe set — short-circuit before doing any work. The actual session-set additions and removals (a new agent run starting, a finished run rolling off) still trigger a full rebuild as before.

A diagnostic trace fires to Console when any individual rebuild exceeds 5ms, so future regressions in this code path show up in log show --subsystem ai.massless.push --predicate 'category == "sessions-perf"' rather than only as user-perceptible jank.

Claude Code resumes self-heal past unprocessable images

When a Claude Code session's transcript contains an image the API later rejects on resume — corrupted bytes, an unsupported format the server-side has tightened on, or an expired short-lived URL — the claude --resume <sid> invocation fails with Could not process image and returns no work product. Until now, that error tore down the whole adapter run and the only recovery was to delete the session manually and start over.

Push now detects that specific failure and retries once with a fresh session. Both the exit-code-zero path (where the error is in the JSON result field) and the exit-code-non-zero path (where it shows up in the errors[] array) are covered, case-insensitively. The retry runs without --resume so the fresh attempt isn't trying to reload the same broken transcript; the stale session id is also explicitly cleared from the run's persisted resume target so future invocations don't re-pin to the dead session. Other failure modes (rate limits, timeouts, network errors) are unchanged — the retry only kicks in for the "Could not process image" signature.

This affects any push agent that runs Claude Code locally and ends up with an image in its history. The retry adds at most one Claude invocation's worth of latency on the recovery path, and zero overhead on the happy path.

v1.7.18

2026-05-04

New tab placement is now configurable — open new terminal tabs at the beginning (the historical default) or at the end of the strip, with auto-scroll-into-view so a freshly created tab never lands off-screen. Hermes picks up the same theme-mismatch prompt Claude has shipped with since April. The +, Reopen, and tint chips at the edges of the tab strip get a Tahoe glass treatment to read as a matched control set.

What's changed

Configurable new-tab placement + auto-scroll-into-view

Settings → Terminal now carries a single radio picker — "Open new tabs at: Beginning (left) / End (right)" — defaulting to Beginning so existing users see no behaviour change. Pick End and every new tab spawned from the +-button, ⌘T, the +-button's right-click default-action menu, or any future API/CLI path appends to the trailing edge instead of inserting at the leading edge. Existing tabs aren't reshuffled when you flip the preference; only future inserts honour it.

Auto-scroll-into-view is the second half of the change. Whether you're at Beginning or End placement, after a new tab spawns the strip scrolls so the new tab is visible — anchored to the side it was inserted at. This also fixes a latent bug under Beginning placement: previously, if you'd scrolled the strip rightward to inspect tab 25 of 30 and clicked +, the new tab spawned at position 0 — invisible under your current scroll offset. You'd have to manually scroll back to find it. Now both directions just work.

The auto-scroll is filter-aware: if you're filtered to a CWD that doesn't match the new tab's directory, the scroll silently no-ops rather than yanking you out of your filter. ⌘⇧T reopen-closed-tab also participates with a centered anchor — the restored tab scrolls into view at its original position regardless of where it sits in the strip.

The Settings → Terminal pane is visible in both Full and Terminal app modes, so single-window terminal-mode users see it too.

Hermes prompts to fix unreadable amber-on-white text

Push has had a prompt for over a month that detects when your Mac appearance doesn't match Claude Code's theme config (light Push + dark Claude theme = unreadable diff blocks) and offers to flip Claude to auto with one click. Hermes had the same problem in mirror: Hermes's default skin paints UI labels and dim text in amber/gold (#B8860B, #DAA520) — beautiful on a dark terminal background, ~2:1 contrast on a white one (well below WCAG AA's 4.5:1), so much of Hermes's chrome dropped into near-invisible territory in light appearance.

Push now runs the same check for Hermes. When you flip the appearance picker (or launch with a mismatched config), if your terminal is light and your Hermes skin is dark-tuned, you'll get a prompt offering to switch the skin to warm-lightmode. The opposite direction (dark Push + a light-tuned Hermes skin) prompts to switch to default. The check is silent if you're already on a matching skin family or if you've explicitly suppressed it via the prompt's "Don't ask me again" checkbox. Custom or third-party skins are left alone — Push won't second-guess a value it doesn't recognise.

The atomic-merge into ~/.hermes/config.yaml only touches the cli.skin key — every other entry in your config is preserved verbatim, the same boundary discipline Push uses for Claude's theme write.

A small structural difference from Claude's version: Claude's theme: auto is a one-shot fix that lets Claude OSC 11-query the terminal background and pick the right palette automatically. Hermes 0.12 has no auto mode, so the prompt recommends a specific skin matched to your current appearance and re-fires whenever you flip the appearance picker. We've started a separate upstream conversation about adding cli.skin: auto to Hermes itself; once that lands, the prompt will recommend auto instead and stop having to retrigger.

Tab-strip edge buttons get a Tahoe glass treatment

The three control endpoints at the edges of the tab strip — the leading + button, the trailing Reopen button, and the trailing tint/appearance picker — now carry a Glass.regular background tinted with a 20% primary-color overlay on macOS 26 Tahoe, replacing the flat quaternary fill they shipped with. The chips read as denser-than-default-glass capsules so they sit visibly proud of the strip's own ultra-thin substrate without competing with active-tab selection. Pre-Tahoe macOS keeps the quaternary fill (no live blur on macOS 15).

All three chips share a single TabStripChipBackground modifier so the +, Reopen, and tint controls can't drift visually as we evolve the strip.

Sessions card stops mirroring server-driven JSONLs into the Local column

If a session was driven by a server-side adapter (anything that writes to its own JSONL store rather than ~/.claude/projects/...), the Sessions card was showing the same conversation in both the server-side column and the Local column. The Local column now filters out sessions that already appear in a server column, so each conversation surfaces in exactly one place.

v1.7.17

2026-05-03

Hermes (NousResearch) joins Claude Code, Codex, and OpenCode as a fully-supported terminal-tab provider. Tab titles now flow immediately to the chip across every resume path — close + reopen, ⌘R reload, Sessions-card click — for all four providers. Codex background-job tracking gets a small reliability fix.

What's changed

Hermes is a first-class terminal provider

Spawn a Hermes (NousResearch) tab from the new-tab picker the same way you would Claude, Codex, or OpenCode. The chip carries Hermes's brand mark (a yellow-to-orange "H" lettermark sampled from the upstream "HERMES-AGENT" wordmark), the chip animates while Hermes is generating, the Sessions card lists Hermes conversations alongside the others, and ⌘R reload / ⌘W close + ⌘⇧T reopen / quit-and-relaunch all auto-resume the conversation via hermes --resume <session-id>. Background-job tracking surfaces when Hermes invokes its terminal tool with background: true, exactly like Claude's and Codex's bg-job badges.

Setup is one-time: Push atomically merges a managed hooks block into ~/.hermes/config.yaml on launch (Push owns its block; your other hook entries are left alone). The bundled hook script lives inside Push.app and never modifies your Hermes home directory beyond that single config edit.

The +-button picker reorders Hermes ahead of OpenCode so the four providers sit in install-share order rather than alphabetical.

Tab titles flow immediately to the chip — for every provider

Hermes is OSC-mute in REPL mode (its /title <name> slash command writes the title to ~/.hermes/state.db but doesn't emit a terminal title escape), so Push pumps titles via three complementary channels:

  • A marker channel populates the title every time Hermes fires a hook event, so the chip catches up on every turn.
  • A tab-activation channel pulls the latest title from SQLite when you switch to a Hermes tab — covers the case where you rename and walk away without sending another turn.
  • A filesystem channel (FSEvents on ~/.hermes/, debounced at 200ms with a per-tab change-detector) catches /title writes the moment they hit SQLite. The chip flips within a quarter second of you typing the rename — the same speed Claude's /rename and Codex's /rename already had via OSC.

Hermes's own auto-titler (the brief AI-generated session name that appears after your first exchange) flows through the same path, so the chip lights up automatically when Hermes finishes naming the session.

A separate fix that benefits all four providers: clicking a session row in the Sessions card now opens a fresh tab with its title already populated, instead of briefly showing the folder basename until the chip backfills. Reload and reopen already preserved the title — the Sessions-card resume path now matches them. You'll most likely have noticed this on Hermes (where the gap was most visible), but Claude / Codex / OpenCode were exhibiting a milder version of the same flicker. All four are crisp now.

Hermes brand mark renders at proper chip size

The first-cut placeholder Hermes "H" glyph rendered at roughly half the visual mass of every other provider's chip mark — the lettermark only filled 50% × 60% of its canvas, so the chip's aspect-fit shrank it. The bars now span the full canvas height, the crossbar spans the full width, and the gradient (warm gold at the top fading to burnt orange at the bottom — sampled from the upstream wordmark) is baked into the PDF rather than relying on monochrome rendering. The mark sits at the same visual weight as the Claude character, the Codex blob, and the OpenCode "O" frame.

The new-tab +-button picker also picks up a tighter 0.85× scale for the Hermes and OpenCode marks so all four provider buttons read at consistent optical weight in the picker.

Hermes sessions survive close + reopen

An earlier Hermes shipping had its hook script clean up the per-tab running marker on session exit, which broke the resume path: ⌘W close + ⌘⇧T reopen, ⌘R reload, and quit-and-relaunch all landed on a fresh-session bare launch instead of hermes --resume <id>. The hook script now leaves the marker in place — Push owns its lifecycle — so every resume path correctly threads the previous session id and continues your conversation where you left off.

Codex background-jobs counter no longer drifts

Two small reliability fixes in the Codex bg-jobs badge:

  • Markers for completed background commands are now deleted at PostToolUse time (when the call returns to the model) rather than waiting on the lifecycle probe. The probe still finalises markers for jobs that outlive the parent Codex process, but the synchronous-completion case clears immediately so the badge counter doesn't carry stale entries forward.
  • When a background command exits with no recorded exit code (rare but real — typically when Codex is killed mid-execution), the badge renders a neutral grey indicator instead of the red "failed" treatment, since "we don't know how it ended" is genuinely different from "it failed."

Docs

The terminal-chrome skill description now points at the tab strip rather than the toolbar — the tint and appearance pickers moved out of the window toolbar in v1.7.15 (the macOS Tahoe >> overflow chevron was silently swallowing them) and live at the trailing edge of the tab strip now. The skill text was lagging the actual UI.

v1.7.16

2026-05-02

A polish pass on the terminal tab strip — the tint palette is wider, the theme button reads as a clean swatch, and the leading "+" button picks up the same chip frame as Reopen and the tint picker so the strip's three control endpoints match.

What's changed

Full Apple system palette in the tint menu

The tint picker now offers the complete Apple system colour rainbow — Red, Orange, Yellow, Green, Mint, Teal, Cyan, Blue, Indigo, Purple, Pink, Brown — plus the existing Graphite as a neutral grey at the end. Previously only seven hues were available; you now have the same range macOS itself surfaces in its system colour pickers.

Existing tint selections persist untouched: the rawValues for the seven previously-shipped colours haven't moved, so if you'd picked Blue, you still have Blue. Pink keeps its custom soft-rose hex (Color.pink is systemPink, which renders as cherry-red through the wash blend mode at higher strengths — the custom hex stays recognisably pink at every step).

Theme button reads as a swatch

The theme button's icon swapped from paintpalette.fill to circle.fill. It now reads as a plain circle of solid colour in your selected tint — a swatch, not a tool — which matches the dot-bullet styling the menu's tint rows already use. Less semantic load for a control whose only job is to surface "this is the current colour."

Leading "+" button now wears the same chip frame

The new-tab button at the leading edge of the tab strip used to be a small icon with no chrome. It now wears the same .quaternary chip frame as the trailing Reopen and tint controls — 24pt tall, 10pt horizontal padding, 6pt corner radius — so the three endpoints of the tab strip read as a matched set instead of three different shapes competing in the same row.

The SF-Symbol plus glyph in .terminal mode renders at primary colour weight now (was secondary), matching Reopen's actionable state. The Claude / Codex / OpenCode adapter brand-mark PDFs keep their fixed brand colours regardless — they don't pick up the surrounding foreground style — so their identity reads exactly the same as before, just inside a slightly more prominent capsule.

v1.7.15

2026-05-02

A small terminal-chrome reshuffle. The tint picker has moved out of the window toolbar into the tab strip, and the trailing controls share a consistent frame now.

What's changed

Tint picker moved to the tab strip

The "Default / Blue / Green / …" terminal tint picker used to live on the right side of the window toolbar. As your folder-filter chip group on the left grew (many open folders, an Active chip when something's running), macOS 26 Tahoe's iOS-style ">>" overflow chevron started silently swallowing the tint picker into the overflow menu — sometimes the picker was reachable in two clicks instead of one, sometimes you couldn't tell where it had gone.

This is a documented dead-end in SwiftUI: there's no public API to pin a custom toolbar item. .searchable() survives the overflow because Apple privileges the search role internally; everything else competes for width with the chip group, and NSToolbarItem.visibilityPriority (the AppKit-level pin) isn't consulted by the SwiftUI-layer overflow on Tahoe (verified in this codebase with a bridge probe).

So the picker now lives at the trailing edge of the terminal tab strip, next to the "Reopen" button. It's always visible regardless of window width or chip count, and it's conceptually a section setting more than a window-chrome action — a better fit for the tab-strip row anyway.

Trailing controls share the active-chip frame

The tint picker (now icon-only — a paintpalette.fill glyph that wears the currently-selected tint colour) and the "Reopen" button (renamed from "Reopen closed", tooltip still spells out the full action) now share the exact frame of an active terminal tab chip: 24pt tall, 10pt horizontal padding, 6pt corner radius, neutral-grey .quaternary fill. The two trailing controls and a selected tab now read as a matched set instead of three different shapes competing in the same row.

The "Reopen" button still dims its text to secondary when there's nothing to restore — same disabled affordance, just inside the new chrome.

v1.7.14

2026-05-02

A small but visible fix to capture enrichment introduced in v1.7.13 — the workspace runtime no longer posts "I finished" comments on captured-page issues. Plus a tiny terminal-tab polish.

What's changed

Captured-page enrichment is silent now

The browser-capture enrichment shipped in v1.7.13 was producing two completion comments on every saved page — one from the agent ("Enriched title and description from captured page") and one from the heartbeat system ("Done. Issue enriched: …"). Neither served a real purpose: the title/description PATCH itself is what the user sees, and the comments were just noise on the timeline.

Both removed. After this update, saving a page produces a clean timeline with only the user's captured artifacts (the URL as a link comment, your typed note as a comment, and the screenshot if you ticked the toggle). Title and description still fill in silently within ~30 seconds. Run telemetry still exists in the run history for debugging — it just doesn't pollute the issue.

The fix applies to both successful and failed enrichment runs. Failures keep the placeholder title; the user's graceful-degrade signal stays the absence of an LLM-generated title, not a comment about it.

Terminal tab close button — subtler when inactive

The × button on a terminal tab now renders at 20% opacity when the tab isn't focused, matching the macOS convention for inactive controls. Reduces visual noise in the tab strip when you have several tabs open.

v1.7.13

2026-05-02

A new Settings pane for picking which agent runs your workspace's system tasks, plus the workspace-runtime infrastructure to auto-enrich captured webpages into scannable issues.

What's changed

New Settings pane: Workspace Runtime

Settings → Workspace Runtime lets you pick which adapter handles capture enrichment and any unassigned-issue work for this workspace. Defaults to Claude Code; switchable to Codex, Gemini, OpenCode, Cursor, Pi, or any custom HTTP/process adapter you've set up.

The pane was previously invisible — the workspace runtime was hardcoded to claude_local, which silently broke any system task for users without Claude Code installed and authenticated. The new pane exposes it explicitly so the choice matches whatever LLM CLI you actually use.

Capture enrichment for saved webpages

A capture client (anything that POSTs an issue with sourceClient: "chrome" and sourceApp: "chrome-extension") can now hit POST /api/issues/:id/enrich, and the workspace runtime will read the issue's typed link / comment / screenshot timeline entries and rewrite the title + description into a clean, list-scannable summary in your language.

The enrichment is a separate concern from any "Run now" or assigned-agent execution — captures don't take the issue execution lock, so a Run-now you fire on the same issue isn't blocked. Each enrichment is also fully isolated:

  • Fresh session every time — no continuity from prior runs or your interactive agent conversation.
  • Runs in a neutral working directory, so it doesn't summarize your current project's git log instead of the saved page.
  • Self-contained directive prompt — small models on a fresh session know exactly what to do without asking clarifying questions.

End result: captures land as background metadata work that doesn't trip over your foreground.

Cheapest-tier model picked automatically for enrichment

Internal cost control: enrichment runs use the cheapest reasonable model for whichever adapter your workspace runtime is set to (Haiku for Claude Code, gpt-5-nano for Codex, gemini-2.5-flash-lite for Gemini, etc.). Not user-configurable — it's automatic per adapter, and only affects enrichment runs. Real work the runtime does keeps the model configured in its adapterConfig.

For Claude Code specifically the pin uses the bare haiku alias, so when Anthropic ships a new Haiku version it picks up automatically without a Push update.

New endpoints

  • POST /api/issues/:id/enrich — triggers an async enrichment run on the workspace runtime. Returns 202 { runId, queued } immediately; the issue's title and description fill in over ~10–60 seconds via the standard live-event stream.
  • GET /api/workspaces/:wsId/runtime — narrow projection of the workspace runtime's adapter config, used by the new Settings pane. Updates go through the existing PATCH /api/agents/:id.

v1.7.12

2026-05-02

The Issues page "Run" button is no longer a single autonomous-only trigger — it now opens a terminal tab on the issue (or runs in the background, your call). Closes the gap between "I see a task" and "I'm working on it."

What's changed

Two buttons: Run and Background

The toolbar on each issue now has two distinct actions:

  • Run (terminal icon) opens a visible terminal tab on the issue, at the assigned agent's primary cwd, with the right CLI launching automatically (claude / codex / opencode per the agent's adapter). Disabled when the assignee's adapter has no terminal surface (process / openclaw_gateway), with a tooltip explaining why.
  • Background (play icon) does the headless heartbeat run — same path iOS voice-create uses. Output lands in the run log surfaced via the Feed and Sessions view.

Re-clicking Run on the same issue focuses the existing tab instead of spawning a duplicate. The binding survives a quit + relaunch, so re-Running on PU-1 after restart still finds the same tab.

Auto-typed /push work <id> for Claude tabs

When you click Run on an issue assigned to a claude_local agent, Push spawns the tab AND types /push work <identifier> as the first user message into Claude's TUI — the room is set up before you walk in. The push skill activates with the issue context already loaded, so Claude knows what it's working on without you typing a thing.

Wired via a dedicated claude-ready/<tab> marker that the SessionStart hook writes only after Claude has rendered (distinct from claude-running/, which the .zshenv wrapper writes pre-launch and isn't a TUI-up signal).

Tab chip shows the bound issue identifier

A small monospaced badge (PU-1) appears between the agent glyph and the title for tabs spawned via Run. Find-the-tab-for-this-issue is a glance now, not a search.

Live run state on the issue page

The Active Run panel under the issue description now reflects actual run status — green spinner while inflight, blue checkmark on success, red exclamation on failure with the dispatcher's verbatim error inline (text-selectable so you can copy it into a bug report). Previously the panel rendered "Agent running" unconditionally, so Background-run failures were invisible.

Inline error banner for action failures

Any user-initiated action that hits the network (Run, comment post, status change) now surfaces failures via a transient red banner at the top of the issue, instead of disappearing into stderr. Auto-dismisses after ~6 seconds; manual close button cancels the timer.

Bug fix: wakeAgent endpoint

The Mac client was POSTing to /agents/:id/wake for headless runs; the server route has always been /agents/:id/wakeup. The 404 had been swallowed silently for a release cycle — Background runs from the Mac never actually fired. Caught by the new error banner the moment we exercised the new button.

Upgrading

brew upgrade push or the in-app Sparkle updater. No migration steps needed. Tabs spawned before this release won't have an issue badge until you click Run on the corresponding issue once — the focus path heals the binding.

v1.7.11

2026-05-01

Three small UI polish wins on the Sessions surface — adapter chips no longer break mid-word, server cards finally match local cards' compact label style, and the column layout sits fixed when content fits instead of jiggling on phantom horizontal scroll.

What's changed

Chip rows wrap whole instead of breaking mid-word

The header chips on session cards (adapter / model / turn count) used to truncate mid-letter at narrow column widths — "Claude" rendering as "Claud / e" stacked on two lines. Each chip now flow-wraps as a complete unit: if a chip doesn't fit on the current row, the whole chip drops to the next row. The trailing live-dot and background-job indicators stay pinned to the top-right of the card.

Compact adapter labels on server cards too

Push-run cards (Running / Completed columns) now show "Claude", "Codex", "Gemini" instead of "Claude Code", "OpenAI Codex", "Gemini CLI" — matching the local-card behavior shipped in 1.7.9. The brand-mark logo already identifies the vendor; the labels just had to catch up. Long-form names are preserved in non-chip contexts (settings, agent detail, error messages).

Fixed column width — no more phantom horizontal scroll

The Sessions surface previously used a fluid [min, max] column model that coupled four constants (window minWidth, sidebar range, column min, column max) into layout arithmetic that was unpredictable to tune. We replaced it with a single columnWidth: 300 constant and a Trello/Linear-board style layout: when columns fit the viewport, the surface sits dead-fixed with no horizontal panning at all; when they overflow (4 columns at narrow windows), it horizontally scrolls. The 300pt cards left-align with breathing room on the right at any reasonable window size.

Bonus: the recap blurb now wraps to 4 lines (was 3) so longer summaries don't truncate as aggressively.

Upgrading

brew upgrade push or the in-app Sparkle updater. No migration steps needed.

v1.7.10

2026-05-01

A focused bug-fix release: file preview reliably renders TypeScript / shell / Python / JSON / etc. on macOS 26 Tahoe (was blank in 1.7.9), modern code extensions get real source-snippet thumbnails in the Files tab, and the terminal tracks cd more accurately in tab titles.

What's changed

File preview now actually shows code

Opening a .ts, .sh, .py, .json, or any other code file from the Files tab — or via Cmd+Click in a terminal — now renders the source. Markdown was the only thing that consistently worked in 1.7.9; on macOS 26 Tahoe everything else routed through our hand-rolled NSTextView wrapper would silently render zero glyphs (a bright black pane with line numbers but no text). Six attempts to fix the wrapper from the inside failed.

We replaced the rendering primitive with STTextView — a proven TextKit 2-based NSTextView replacement, used by other production Mac apps. ~300 lines deleted, syntax highlighting re-enabled for TypeScript / shell / etc., line numbers and selection both preserved. If you flipped to "Open externally" as a workaround in 1.7.9, you don't need it anymore.

Source-snippet thumbnails for modern code extensions

The Files tab thumbnail tile now shows a tiny monospaced preview of the source for .ts, .tsx, .go, .rs, .kt, .scala, .proto, and other extensions that macOS Quick Look has no built-in text-preview generator for. Previously these showed a generic file-type badge while .sh and .md showed real previews — now everything Push knows how to open shows a content thumbnail.

Terminal: track cd in the tab title's cwd basename

When you type cd somewhere in a terminal tab, the title's folder hint now updates immediately rather than waiting for the next OSC 7 emission. Less stale labeling on tabs you've navigated around in.

Under the hood

  • Reworked the syntax-highlighter timeout race from withTaskGroup + cancelAll to withCheckedContinuation with a one-shot resume guarded by a lock. Same semantics (highlight on success, plaintext on 2-second timeout or error), clearer once-only fire.

Upgrading

brew upgrade push or the in-app Sparkle updater. No migration steps needed.

v1.7.9

2026-05-01

Two small but high-frequency wins on the Sessions surface: a search field that filters cards by what's visible on them, and a tighter adapter chip label on local cards.

What's changed

Search the Sessions surface

The Sessions tab now has a search field in the toolbar (⌘F to focus, same as Files). Typing narrows the cards to ones whose visible content matches your query — case-insensitive, across:

  • Title — Claude haiku / Codex thread / push issue title
  • Recap — the italic blurb under the title
  • Folder — the basename shown in the footer
  • Agent name — for push runs
  • Issue identifierPUSH-NN for push runs

Search ANDs with the folder chips: pick a folder, then type to narrow further. Folder chip badges show search-narrowed counts so you can see at a glance how many matches are in each folder. "No matches" empty state when the query has zero hits across all four columns.

By design, search only looks at what's rendered on the card — not the underlying JSONL or SQLite payload. Transcript search is a separate feature with different perf concerns; not in scope here.

Compact adapter labels on local cards

Local session cards (Live + Local columns) now show "Claude" and "Codex" instead of "Claude Code" and "OpenAI Codex". The brand-mark logo already carries the vendor identity, and the columns are tight; cropping the redundant words gives the title and recap more room to breathe. Push run cards (server-side attribution) keep the full labels.

Upgrading

brew upgrade push or the in-app Sparkle updater. No migration steps needed.

v1.7.8

2026-04-30

Session cards in the left column now tell you what's actually inside each session — turn count, duration, branch, dominant tool, model, recap blurb — all across Claude Code, Codex, and OpenCode. Switching to the Sessions tab is also genuinely instant now: ~7 ms warm, ~5 ms first-switch-after-launch (used to be 500 ms – 5 s on heavy histories).

What's changed

Session cards show what's inside the session

Every card in the Sessions column now carries a header chip strip and a stats row:

  • Header: brand-mark logo + agent name (Claude Code / OpenAI Codex / OpenCode) · model name (Opus 4.7, GPT-5.3, etc., when non-default) · turn count (the strongest "how big is this session?" signal, prominent type)
  • Recap: the agent's most recent wrap-up message (Claude's away_summary, Codex's task_complete.last_agent_message, OpenCode's last text part — same idea, three different sources)
  • Stats row: duration · git branch · dominant tool used (pencil for Edit/Write, magnifying glass for Read/Grep, terminal for Bash/exec, etc.) · bypass footnote when permission/sandbox guardrails were loosened

Most fields are silent when they don't apply (sub-agent count chip only on multi-agent sessions, model chip only when ≠ adapter default, branch only when not detached). The card collapses the stats row entirely on truly blank sessions instead of leaving an empty line.

Codex sessions finally have recaps

Codex cards used to render with the title and timestamp only — no recap blurb. The structural analogue of Claude's away_summary lives in Codex's task_complete.last_agent_message, which Push now reads from the rollout JSONL. Same italic secondary blurb you've been seeing on Claude cards, now on Codex too.

OpenCode parity

OpenCode cards get the same treatment via per-session aggregations against opencode.db: turn count from user-message rows, dominant tool from the part-table histogram, latest model from the most recent assistant message, recap from the latest text part. All cached by (session_id, time_updated) so warm switches are sub-millisecond.

The Sessions tab is now instant

Three independent caching fixes took the Terminal → Sessions switch from 500 ms – 5 s on a heavy history (hundreds of Claude project dirs, thousands of JSONLs) down to ~7 ms warm:

  • The per-session metadata cache used to interpret "no recap recorded" as "we haven't checked yet," which forced re-parsing every session-without-a-recap on every tab switch (about 70% of Claude sessions). Now keyed on parser version, not value presence.
  • The cache file used to be rewritten to disk on every tab switch even when nothing changed. Now writes only on actual cache misses.
  • The phase-1 directory walk (105 project dirs / 600+ JSONLs on heavy users) used to re-run every switch. Now in-memory cache, invalidated only when files are actually created or deleted.

Cold first-switch after launch is also fixed: a background warm-up populates all three providers' caches in three concurrent off-main tasks during app launch, so the very first time you click Sessions in a fresh Push process, the cache is already populated. ~5 ms cold, same as warm. The warm-up never blocks the main thread — all three readers' state is now thread-safe via lock-guarded structs.

Header chip redesign

  • Adapter chips (Claude Code, OpenAI Codex, OpenCode) now share a uniform neutral gray background — the per-adapter color signal moved into the colorful brand-mark logo prepended before the label.
  • Turn count gets header real estate as the strongest single signal of session importance, with prominent typography.
  • The bypass indicator was a loud red header capsule. Demoted to a tertiary text segment in the stats row — most users run agents in bypass mode by choice, so the red was crying wolf.

Upgrading

brew upgrade push or the in-app Sparkle updater. No migration steps needed.

The session metadata cache schema bumped to v7 — old v6 caches load with title and recap intact; only the new stats fields re-parse on first launch (cheap, off-main via the warm-up).

v1.7.7

2026-04-30

OpenCode polish pass: the tab chip now animates while generating, the brand mark sits at the same baseline as Claude Code and Codex, and the chip title shows the actual session name instead of the "OC | …" prefix that was leaking through from upstream.

What's changed

OpenCode chip animates while generating

OpenCode tabs now show a rotating braille spinner (⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏) while a turn is in flight, matching the Claude and Codex chip animations. At idle, the chip returns to the OpenCode brand mark. The spinner cadence and glyph set match what OpenCode itself uses for its dialogs and loading indicators upstream — so what you see in the tab strip is the same beat as what OpenCode shows inside its own UI.

OpenCode chip aligned with Claude and Codex

The OpenCode logo previously sat ~1 line higher than the Claude and Codex chips because the asset I copied from the iOS catalog had asymmetric padding inside its bounding box and a stripped inner cutout. Replaced with a regenerated asset built from the official upstream brand SVG: the new "O" frame fills the chip's full vertical height with symmetric horizontal padding, sitting at the same baseline as the other two providers.

The OpenCode mark is also now monochrome-aware: since the upstream brand has no inherent color (only "light mode" and "dark mode" appearance variants of the same shape), it's a single asset that template-renders in every surface — the chip, the + button picker, the sidebar, agent rows. Auto-adapts to light/dark mode without a separate inverse asset.

OpenCode chip title shows the session name, not "OC | …"

OpenCode's TUI emits its own terminal title with an "OC | " prefix as an identity affordance for terminal apps that don't have a brand glyph. Push's tab chip already carries the OpenCode brand mark, so the textual prefix was redundant — and it was landing in the wrong layer of the title model, hiding the session name behind path-or-shell-output noise.

Push now suppresses OpenCode's own title emission via the upstream-provided OPENCODE_DISABLE_TERMINAL_TITLE env flag, and the bundled plugin emits the raw session title directly. Result: the chip shows the actual session name (auto-generated by OpenCode's title agent, set by /rename, or the cwd folder name pre-name) — same display layer as Claude haikus and Codex rename values.

Upgrading

brew upgrade push or the in-app Sparkle updater. No migration steps needed.

v1.7.6

2026-04-30

OpenCode joins Claude Code and Codex as a first-class agent in Push. The new-tab + button has an OpenCode option; tab chips show the OpenCode brand mark; the Sessions view mixes OpenCode threads in alongside the others; ⌘⇧T-reopen continuity now works for unprompted Codex and OpenCode tabs that previously came back as plain shells.

What's changed

OpenCode as a third agent provider

The + new-tab menu now offers OpenCode Session alongside Claude and Codex. The chip uses OpenCode's brand mark at idle and animates while a turn is producing. Sessions in the left column mix OpenCode threads with Claude and Codex sessions, sorted by most recent — click a row to resume the conversation in a fresh tab at the recorded directory.

Resume integration mirrors the existing providers: opencode --session <id> <cwd> is auto-typed when you reopen a closed session or relaunch Push. Background-job badges, generating-spinner cues, and ⌘W close warnings all light up for OpenCode the same way they do for Claude and Codex.

Distribution: Push installs a small Bun plugin into ~/.config/opencode/opencode.json on launch (atomic merge, idempotent across upgrades). The plugin runs inside OpenCode's TUI and keeps Push's existing chip / Sessions / generating / bg-job machinery in sync — no additional configuration on your end.

⌘⇧T reopen now restores unprompted Codex and OpenCode tabs

If you opened a Codex or OpenCode session via the + button, didn't type a prompt, and closed the tab, ⌘⇧T-reopen used to bring back a plain zsh shell — even though the chip glyph still showed the agent. The reopened tab now relaunches the agent fresh, matching the chip's promise.

(Codex publishes its session-creation hook only at the first turn; OpenCode publishes its session.created event only at first submit. So when you close before submitting, no marker exists for Push to consume on reopen. Push now falls back to a fresh agent launch when a tab's persisted provider says it's an agent tab but the marker is absent — same behavior the app-relaunch path has had since v1.7.0.)

Claude tabs were never affected — Claude's hook fires immediately at process launch, so the marker has always been on disk before close was possible.

Upgrading

brew upgrade push or the in-app Sparkle updater. No migration steps needed.

v1.7.5

2026-04-30

The chip's spinner now actually stops when you hit ESC inside Claude Code or Codex.

What's changed

  • Tab spinner stops on ESC. Press ESC in a Claude or Codex tab to interrupt mid-turn and the chip's animated glyph stops cycling immediately, instead of spinning forever (or until your next prompt completes naturally).
  • Works for both providers, every interrupt phase. Codex covered end-to-end via its rollout file. Claude covered for ESC during streaming and tool execution via the session JSONL, and ESC during the pre-streaming setup phase via a side-channel observation of Claude's terminal title transition. Permission prompts and long-running tool calls still keep the spinner going correctly — those aren't interrupts.

Upgrading

brew upgrade push or the in-app Sparkle updater. No migration steps needed.

v1.7.4

2026-04-29

A polish pass on the chip row and the app icon. No new features — just the bits that were a pixel off.

What's changed

  • Agent brand marks render monochrome in chip rows. Fixes the colored-glyph-against-cream-pill clash from v1.7.3, so the per-agent chips read as a uniform set instead of five different brand colors fighting for attention.
  • Agent chip glyphs center on the chip, not the text baseline. Previously the mark sat ~1px low next to the label; now it's vertically centered.
  • Folder filter chips use folder.fill. Solid-fill folder glyph replaces the outline variant for the Sessions and Terminal folder filter chips — matches the rest of the chip-row vocabulary.
  • App, DMG, and dev icons refreshed. Re-exported from the latest master so the Finder icon, DMG window icon, and dev-build icon all match what's shipping.

Upgrading

brew upgrade push or the in-app Sparkle updater. No migration steps needed.

v1.7.3

2026-04-29

Cmd+Click any file path in a Push terminal — it opens inside Push's in-app viewer instead of leaving for an external editor. And every file you click is quietly tracked so you can find it again from the Files tab, alongside the files your agents produced.

What's changed

⌘-Click in a terminal now opens files inside Push

Compiler errors, grep output, claude tool-call paths — anywhere a file path appears in a terminal — Cmd+Click now opens it in Push's file viewer (the one with line numbers, syntax highlighting, and the polished typography from v1.7.2). Browser URLs continue to open in your default browser; binaries and other types Push can't render still hand off to NSWorkspace.

The router handles the messy real-world inputs:

  • view.swift:42 style (compiler / grep output) — strips the :line[:col] suffix and opens the file. Line position is ignored for now (no scroll-to-line yet).
  • file:// URLs — common in OSC 8 hyperlinks; unwrapped and routed through the file branch instead of treating them as generic schemed URLs.
  • Trailing sentence punctuationREADME.md. with a literal period from the end of a sentence now resolves correctly.
  • Schemeless URLsgithub.com/foo gets a courtesy https:// prepend before opening in your browser. Previously dropped silently.
  • Relative pathsview.swift resolves against the terminal tab's current working directory if you're in .full mode.

In .terminal mode, all clicks fall through to NSWorkspace as today — no in-app viewer surface to land on.

Files you ⌘-click are tracked locally

The Files tab used to show only files agents produced during runs. Now it also includes files you explicitly engaged with via Cmd+Click in a terminal — your attention is the curation signal, not a filesystem-watch firehose.

The chip row gains a Terminal chip alongside All and the per-agent chips:

  • All = every file Push knows about for this workspace, sorted by latest activity.
  • Terminal = files you ⌘-clicked from a terminal. Always visible (even at count 0) — Cmd+Click is the only way to populate it.
  • Per-agent chips = unchanged.

A file that's both an agent artifact AND a terminal click shows once in All (with both source glyphs on the row), and appears under both Terminal and the matching agent chip. Right-click → Forget this file removes a click record.

The store is purely local — no DB, no server round-trip, no cross-device sync. Same architecture sessions already use (~/.push/mac/clicked-files/<sha1-of-path>, one plaintext file per row, mtime is the timestamp). Bounded to the last 200 entries OR 30 days, whichever cap fires first. Pruned on every write.

Architecture: server-independent

Both pieces — routing and tracking — work in .terminal mode, where there's no embedded server, no DB, no relay. The routing logic is a pure-function module (TerminalLinkRouter.swift); the click store is filesystem-only. When we eventually surface the Files tab in .terminal mode, the data path is already there.

Notes

  • The click store mirrors ClaudeSessionMarker's exact pattern: filesystem-as-database, one file per row. Filename is SHA1 of the path; content is 3 lines (path / source / workspace UUID or empty). Symmetric with how live sessions are tracked.
  • TerminalLinkRouter lives at desktop/Sources/Push/Services/Terminal/TerminalLinkRouter.swift — single public entry point route(_:kind:pwd:), no stored state, four-branch routing that reads top-to-bottom (file:// → local file candidates → schemed URL → schemeless courtesy upgrade).
  • Design docs in the writing repo: documentation/push/macos/plans/2026-04-29-cmd-click-route-files-into-push.md and 2026-04-29-user-clicked-files-tracking.md. The latter walks through the deliberate three-option comparison (server table vs widening vs filesystem-only) and why we chose to mirror sessions.

Upgrading

brew upgrade push or the in-app Sparkle updater. No migration steps needed.

v1.7.2

2026-04-29

A focused readability pass on the in-app file viewer. Click a file in the Files panel or open a Markdown / source file from a result, and it now reads like a real editor — line-number gutter, status footer, Finder's real file-type icon in the header, and properly tuned typography across code and prose.

What's changed

Line-number gutter

Source files now render with a left-side gutter showing line numbers (Xcode/VS Code style — wrapped continuation lines stay blank, gutter widens automatically for large files). Same canonical AppKit NSRulerView mechanism Xcode and TextEdit use.

Status footer

Language · N lines · 12.4 KB at the bottom edge of every text-bearing preview. Pretty-prints typescriptTypeScript, objectivecObjective-C, and so on. Hidden for images and the binary-fallback card where the data doesn't apply.

Finder file-type icon in the header

The generic blue document glyph is gone — the header now shows Finder's actual icon for the file (Swift bird, JS, .md quill, etc.) via NSWorkspace. No more "is this a Swift file or JS file" confusion at a glance.

Typography across reading surfaces

A single source-of-truth Typography.swift token file now drives both the file viewer's NSTextView and the in-app Markdown renderer. Numbers sourced from Apple HIG, GitHub's markdown CSS, and swift-markdown-ui's built-in .gitHub theme — body 14pt × 1.5 line-height for prose, 13pt × 1.4 for full-file code, fenced code blocks drop one notch (12pt) so they don't break prose flow. Heading rhythm tightened: 24pt top / 12pt bottom margins on H1/H2/H3, asymmetric the way every well-typeset doc system does it.

The practical effect: long Markdown files breathe, code reads less cramped, and headings actually feel like section breaks.

Notes

  • Read-only viewer — no editing surface. Selection + copy work via NSTextView default; "active line" tint was considered then dropped because there's no caret in a read-only view.
  • The 500 KB file-size cap and the long-line guard for syntax highlighting are unchanged — pathological files still drop to the plaintext / fallback tiers.
  • Design rationale + numbers + sources: documentation/push/macos/plans/2026-04-28-typography-tokens-and-file-viewer-readability.md in the writing repo.

Upgrading

brew upgrade push or the in-app Sparkle updater. No migration steps needed.

v1.7.1

2026-04-29

Polish on the v1.7.0 Codex track. The user-typed agent path — opening a plain terminal tab and typing claude or codex yourself — now matches the New-button path end to end: clean folder-name title, brand-mark chip, and reliable resume across app relaunch.

What's changed

claude / codex typed in a plain tab

Before: the tab title would briefly flash claude / codex (the literal command name from zsh's preexec) or the abbreviated cwd path before the agent emitted a real title — and for Codex pre-first-turn, never settle. The chip glyph would stay hidden because Push didn't know the tab was an agent tab until much later.

Now: the moment you press Enter on claude<Enter> or codex<Enter>, Push recognizes the launch command, snaps the tab title to the folder name (e.g. push-macos), shows the brand-mark chip immediately, and durably remembers the tab's provider for every continuity boundary that follows.

Resume on relaunch when the agent never had a first turn

Codex's SessionStart hook fires at the start of a turn, not at codex launch. So a Codex tab where you opened codex but never submitted a prompt had no marker file written, and on app relaunch Push had nothing to resume — the tab opened to a plain shell despite the chip claiming Codex.

Now: if the tab's snapshot says it's an agent tab but there's no marker on disk, Push queues a fresh-session launch. The chip glyph's promise is honored — open Codex back where Codex was, even if codex never reached its first turn before the previous quit.

Single source of truth for "is this an agent tab?"

Internally, three signals — boot bridge, live presence, durable persisted intent — now resolve through one helper. The chip glyph and the title-OSC suppression read from the same chain, guaranteed to agree. This was the root cause of the chip-without-title and title-without-chip mismatches that bit through v1.6.x and v1.7.0.

Notes

  • The unconditional shellCwdTitle scrub at provider promotion clears any pre-promotion residue (the cwd path zsh wrote into the title before you typed your agent command). One-shot, fires only at the moment of promotion.
  • Idempotency guard on the new bare-launch fallback: restoreFromDisk runs twice on each launch (prewarm + ensureFirstTab fallback), and the second pass would have clobbered a real --resume <sid> with a bare-launch without the guard. Verified live.
  • Design rationale + addendum trail: documentation/push/macos/plans/2026-04-27-claude-terminal-session-audit-for-codex.md (Addendum 16 covers this release).

Upgrading

brew upgrade push or the in-app Sparkle updater. No migration steps needed.

v1.7.0

2026-04-28

Codex is now a first-class agent in Push, alongside Claude Code. The Sessions view shows your Codex thread history; the new-tab + button has a Codex option; tab chips identify Claude vs Codex with their own brand glyphs and a sensible folder-name fallback while the agent boots.

What's changed

Codex sessions in the Sessions view

The Local column now mixes your Codex threads with your Claude sessions, sorted by most recent. Each row carries a brand badge — "OpenAI Codex" or "Claude Code" — and the same click-to-resume behavior. Tap a row, Push opens a fresh terminal at the recorded cwd and runs the right resume command (codex -C <cwd> resume <thread-id> for Codex, claude --resume <session-id> for Claude). Folder filter chips include cwds from both providers automatically.

Behind the scenes, Push reads from ~/.codex/state_*.sqlite (Codex's thread index) for historical sessions and <PUSH_MAC_DIR>/codex-running/<tab> markers for live ones — same shape as the existing Claude path, just glued onto the column-1 feed.

Tab chip identity

Each agent tab's chip now shows the agent's brand mark next to the title:

  • Claude tabs — monochrome Claude Code glyph at idle, animated ✳/⠂/⠐/⏸ rotation while a turn is streaming.
  • Codex tabs — monochrome Codex sigil at idle, braille spinner ⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏ while a turn is generating.

The brand mark and the spinner are mutually exclusive — one or the other, never both — so the chip stays calm at idle and unambiguously alive during a turn. The + new-tab button preview matches the chip's brand mark for whichever provider is set as the default new-tab action.

Folder name as the early-state title

While an agent tab is booting (Claude before its first haiku, Codex before /rename), the chip title now shows the cwd's last component (e.g., push-macos) instead of "Claude Code", "codex", or the abbreviated path with slashes. As soon as the agent asserts a real name — Claude's auto-haiku, Codex's /rename value — the agent's name takes over.

The fallback survives reload (⌘R), close + reopen (⌘W → ⌘⇧T), and app relaunch. Push tracks each agent tab's provider as durable per-tab state, so the right title shows across every continuity boundary.

Notes

  • Stale tab titles self-heal on first launch. If a tab was stuck on a path with slashes (…/some/path) before this release, the new build clears that on first launch. The next shell prompt repopulates correctly.
  • The right-pane "Resume Session" button on a server-side Push run still routes through claude --resume; provider-aware run resume is tracked but not in this release. Local Codex history (your Codex thread index on this Mac) is unaffected.
  • Codex hooks (SessionStart, UserPromptSubmit, PreToolUse, PermissionRequest, PostToolUse, Stop) drive the chip's busy state, the unread badge on inactive tabs, and the marker that lets Push resume a Codex thread after reload/relaunch.
  • Design rationale + addendum trail: documentation/push/macos/plans/2026-04-27-claude-terminal-session-audit-for-codex.md (Addendums 10–14 cover this release).

Upgrading

brew upgrade push or the in-app Sparkle updater. No migration steps needed.

v1.6.7

2026-04-27

Files gallery now has an agent filter. Same chip-row UI as the Sessions and Terminal folder filters — just grouped by the agent that produced each file instead of by working directory.

What's changed

Filter Files by agent

The Files surface used to be search-only — fine when you had a handful of artifacts, less fine once a few agents had been working in parallel for a while. Now there's a chip row in the toolbar with one chip per agent. Click a chip, the grid narrows to that agent's output. Click "All" to clear.

Search and the agent chip are independent inputs, AND-ed at filter time. Type a query in the search field, the chip badges shrink to match — agents with no matches drop out of the row entirely. Pick a chip while a query is active, the grid narrows to "this agent AND this query."

Drag chips to reorder; the order persists across launches. Same drag behavior, persistence model, and self-heal (active filter resets to "All" if the agent disappears) as the folder chips in Sessions and Terminal.

Notes

  • Pure feature add to the Files surface. No behavior changes elsewhere.
  • The chip row stays visible even on a fresh workspace ("All [0]") so the affordance is discoverable on first launch.
  • Performance budget is the same as Sessions': filter and chip rebuild happen once per input change, never inside body. No measurable scroll cost added.
  • Plan + perf rationale: ~/workspace/writing/documentation/push/macos/plans/2026-04-26-files-agent-filter-chips.md.

v1.6.6

2026-04-27

Push 1.6.6

New app icon. Same logo treatment is now used for the Dock icon, the DMG, and (already in 1.6.5) the menubar status icon — Push finally has one consistent mark across all three surfaces.

The old wordmark was a flat blue tile. It looked fine in the Dock because Tahoe clips app icons into a squircle silhouette automatically, but on the mounted DMG (where macOS composites the volume icon raw onto the desktop) it stood out as a hard-edged rectangle next to every native squircle around it. The new icon bakes the squircle silhouette and drop shadow into the asset itself, so it reads as a proper Mac icon in every context.

Notes

  • No behavior changes. Pure visual refresh.
  • If your existing install still shows the old icon after updating, that's macOS's IconServices cache — it'll refresh on its own within a few minutes, or you can speed it up with killall Dock Finder.

v1.6.5

2026-04-25

Hide/show the sidebar with ⌃⌘S no longer hitches. The lag scaled with how many terminal tabs you had open — at 17 tabs it was clearly stuttering; at a few it was fine. Now it's smooth at any count.

What's changed

Sidebar toggle is fast again

Push's terminal section used to keep every tab mounted in the SwiftUI tree, hidden via opacity. That kept tab-switch instant, but it meant any layout-affecting event — sidebar toggle, window resize, NavigationSplitView animation — fanned out to every libghostty surface in parallel: each one re-flowed text, rebuilt its Metal render targets, and re-drew. With 17 tabs that's 17× the work, and it dominated the toggle animation budget.

Now Push mounts only the active tab and the two most-recently-used. Inactive tabs' libghostty surfaces and PTYs stay alive in the background — switching back to a recent tab is still instant — but they don't react to the sidebar resize, so the toggle has 1/N the work to do.

In numbers: pre-fix prod with 17 tabs spent 11 main-thread frames on Metal draws and 5 on render-target rebuilds during a single 10-second toggle window. Post-fix dev with 10 tabs spends 0 of each. The toggle just animates.

Tabs in the background still get their Claude session resumed on relaunch

A side effect of the mount narrowing was that we had to move Claude/shell event routing (the OSC 7 cwd hook that drives auto-resume on relaunch, plus title and unread-badge updates) from the SwiftUI representable to the surface manager. So when you quit and relaunch Push with 10 Claude tabs, all 10 tabs' claude --resume fires correctly — even the ones that aren't in the active mount window.

Notes

  • Pure perf fix, no new features.
  • The full investigation, cmux comparison (whose portal pattern inspired the mount-narrowing), and timeline are at ~/workspace/writing/documentation/push/macos/investigations/2026-04-25-sidebar-toggle-lag-all-tabs-mounted.md.
  • Cold-mounting a tab outside the recent cache (the 4th-most-recently-used tab on first switch back) costs a remount round-trip. In practice it's well under a frame; if you notice flashing, let me know.

v1.6.4

2026-04-25

Extends the 1.6.3 reload-resize fix to a second path that had the same bug: relaunching Push and resuming your previously-open Claude Code tabs.

What's changed

Relaunch + resize is now also clean

In 1.6.3 we fixed reload-then-resize: ⌘R'ing a Claude Code tab and then maximizing the window no longer left a blank band at the top. Same week we found a third launch path with the exact same bug — when you quit Push and relaunch it, the auto-resumed Claude Code tabs were re-spawning through a different code path than open-from-Sessions or reload, and that path produced the same broken layout when you then resized the window.

Now: every Push-side path that brings Claude Code into a tab — open from the Sessions UI, ⌘R reload, app-relaunch auto-resume — uses the same launch flow. Resize behaves identically across all three. No blank band on relaunch + maximize.

Same brief shell prompt flash on relaunch

You'll briefly see your shell prompt for ~half a second per Claude tab on relaunch, just before Claude takes over each one — same as the reload flow shipped in 1.6.3. That's intentional: it's the cost of routing the launch through the path that has always rendered correctly.

Notes

  • Pure bug fix, no new features.
  • The launch-flow consolidation is also a small refactor — the marker-to-launch-command logic now lives in one place instead of two. Risk-reviewed in the investigation writeup.
  • 1.6.3's note about Claude Code 2.1.120's startup crash on --resume still applies. That's an upstream Anthropic issue (anthropics/claude-code#53041) and unrelated to anything Push does. If your auto-update grabbed 2.1.120, pin to 2.1.119 until Anthropic ships a fix.

v1.6.3

2026-04-24

Fixes a layout bug where reloading a Claude Code tab (⌘R) and then resizing the window left a clean blank band at the top of the viewport with the conversation pinned to the bottom half.

What's changed

Reload + resize is now clean

Before: if you reloaded an active Claude Code tab and then maximized the window (or dragged a window edge), the conversation would jump to the bottom of the visible area and a tall stripe of empty cells would appear above it. Reloading the tab again would temporarily fix the display until the next resize. 100% reproducible on long-running sessions.

Now: reload-then-resize renders cleanly. The conversation stays anchored where it should be; no blank band.

The bug only ever showed up after a reload — fresh sessions opened from the Sessions sidebar always rendered cleanly through any number of resizes. That asymmetry was the whole story: opening a session and reloading a session were taking two different code paths into Claude Code, and one of them happened to interact poorly with a Claude Code 2.1.119+ rendering optimization. Reload now uses the same launch flow the Sessions sidebar uses, so both paths behave identically.

Tiny visible side effect of the fix

You'll briefly see your shell prompt for ~half a second when you reload a Claude Code tab, just before Claude takes over. Previously the reload was prompt-less. The new flow needs the shell to draw its prompt before Push types claude --resume <id> into it — that's the half-second flash. It's the cost of routing reload through the same path that has always worked correctly. If you ever care about removing the flash, ping us; it's solvable but not free.

Notes

  • This release is purely a bug fix; no new features, no UI changes.
  • If you're running Claude Code 2.1.120, you may also be hitting an unrelated upstream bug where claude --resume crashes immediately on startup with g9H is not a function. That's filed at anthropics/claude-code#53041 and will be fixed by Anthropic in a coming release; pin to 2.1.119 in the meantime if your auto-update grabbed 2.1.120.
  • Full investigation writeup with the back-and-forth methodology — every wrong hypothesis, every narrowing experiment, the breakthrough observation that pinpointed the asymmetry — lives in the writing repo at documentation/push/macos/investigations/2026-04-24-claude-code-resize-layout-gap.md.

v1.6.2

2026-04-24

Safety rails for destructive tab actions — ⌘R reload, close tab, and ⌘Q now all warn before they kill a Claude turn, a backgrounded job, or a foreground process you forgot about.

What's changed

One warning, three actions

Before: closing a tab warned you about Claude-spawned background jobs, but would happily kill a mid-response Claude session or your pnpm dev without a word. Reloading with ⌘R warned about nothing. Quitting the app warned about nothing. Three different actions, three different levels of "are you sure?".

Now: ⌘R reload, closing a tab (× / ⌘W / right-click → Close), and quitting the app (⌘Q) all run the same check. If anything is busy, you get a confirmation dialog listing exactly what's about to get killed:

  • Claude is generating a response. You started a turn and it's still in flight.
  • N background jobs are still running. Things Claude kicked off with run_in_background:true (or anything you Ctrl+B'd into the background mid-run).
  • A process is running in the terminal. A foreground vim, btop, ffmpeg, pnpm dev, whatever.

Idle tabs still reload/close/quit instantly, no dialog.

"Claude is active" now means mid-turn, not just running

Before: any tab with Claude open was flagged as busy — even when Claude had finished responding and was sitting at its prompt waiting for your next message. Closing the tab in that state popped an alert asking if you were sure.

Now: Push tracks whether Claude is actually generating right now. An idle Claude at its prompt is not busy. Start a turn, though, and the tab becomes busy until Claude finishes — at which point closing or reloading goes through without a prompt again.

Under the hood: we listen to Claude Code's UserPromptSubmit and Stop hooks, so the signal matches exactly when you see the response start and stop.

Notes

  • The new hook behavior only applies to Claude sessions started after upgrading. Any Claude already running from a pre-1.6.2 launch still uses the old per-process signal until you restart it.
  • Theme-switch auto-repaint (shipped in 1.6.1) still runs its own safety check and won't trigger the new prompt — it already refuses to touch busy tabs silently.

v1.6.1

2026-04-24

Quiet polish release — Claude tabs now repaint themselves correctly after a light/dark theme switch, without you having to reload each one by hand.

What's changed

Claude tabs re-theme themselves on the next click

Before: flipping macOS between light and dark left every open Claude tab stuck at the old palette — Claude's boxes, dim text, and chrome were all baked in at the colors they had when the conversation first rendered. The only fix was ⌘R on every tab, one at a time.

Now: Push watches for appearance changes, notices which tabs still have stale colors, and silently reloads them the moment you switch back to each tab. The reload uses claude --resume under the hood, so your conversation comes back intact — just in the new theme.

A few rules we're deliberate about:

  • Running Claude sessions are never interrupted. If Claude is mid-response when you flip theme, the tab keeps its current colors until you exit — we won't yank the shell out from under a live session. On your next visit after that, the repaint happens.
  • Tabs with backgrounded jobs stay put too. If you've got pnpm dev & or a long ffmpeg running, we won't SIGKILL it to change a color.
  • Plain shell tabs (no Claude history) are left alone. Reloading them would wipe your scrollback with nothing to restore from. Flip theme, type clear, or ⌘R manually when you care.

No new UI, no settings to toggle. It just works on the next tab switch.

Notes

  • First flip after upgrading will repaint whichever Claude tab you switch to first; subsequent tabs repaint as you visit them.
  • ⌘R still forces an immediate reload if you want the repaint now.

v1.5.30

2026-04-24

A small follow-up to v1.5.27's background-process surfacing: Claude Code shells you background mid-run with Ctrl+B now show up in Push's Sessions list, the same as ones started with run_in_background:true.

What's changed

Sessions: Ctrl+B'd Bash now visible

v1.5.27 introduced background Claude shells in the Sessions list, but only for Bash calls that started backgrounded via tool_input.run_in_background:true. Calls that started foreground and got backgrounded mid-run with Ctrl+B were invisible — Claude Code only fires the pre-tool hook once before the call runs, so the transition itself emits no signal.

v1.5.30 detects Ctrl+B by diffing Claude's workspace tasks directory between the pre- and post-tool hooks: if a new .output file appeared during a foreground call, Claude spawned a task for it, which means Ctrl+B fired. The post-hook then promotes the marker into the visible bg-tracker so the Sessions UI picks it up. Marker JSON gains a bg_via_ctrlb:true field on those entries.

Shell-side change only — no native code changes, no migration, no preferences.

Upgrade notes

  • No data migration required.
  • Stale background markers from older builds are cleaned up automatically on the next post-tool hook for a foreground Bash call.

v1.5.29

2026-04-23

Two big shifts: a new Terminal mode that strips Push down to just the bundled Ghostty when you don't need the agents, and a fresh visual pass across the web UI built on the One design system. Plus the DMG installer now mounts with a proper Mac-shaped icon.

What's changed

Terminal mode

A new lightweight runtime mode you can flip from Settings. In Terminal mode Push runs as a pure Ghostty wrapper — no server, no agent runtime, no Issues/Feed/Approvals chrome. The menubar icon drops its server-status badge (there's nothing to badge), the Server menu hides itself, and ⌘⇧R is unbound. Switching modes is live — no relaunch. Full mode keeps everything as before.

Use Terminal mode when you want Push as your day-to-day terminal and only need the agent surfaces occasionally.

Sessions: Live vs Completed split

The Sessions column now splits "Local" into two cards — Live (sessions backed by an open terminal tab, orange dot icon) and Completed (historical JSONLs on disk, grey terminal icon). Easier to see what's actually running right now versus what's resumable. Empty columns auto-hide so the surface stays compact, and remaining columns flex into the freed space (clamped between 240–400pt so cards keep a comfortable reading width).

Web UI: One design system

Push's web UI has fully adopted the One design system. The visible changes:

  • New top bar replaces the old breadcrumb bar
  • New sidebar with a workspace switcher chip
  • ⌘K opens a Spotlight-style command palette instead of the old CommandPalette
  • Settings now opens as a modal dialog, not a side panel
  • Color tokens consistent across every page (no more raw status colors)
  • All 40 agent icons migrated from lucide to Phosphor
  • DM Sans / DM Mono everywhere

Pure visual refresh — no functional changes to anything you do in those surfaces.

DMG installer icon

The DMG you download now mounts with a proper Mac-shaped disk icon (squircle silhouette, not the previous flat full-bleed tile). First-impression polish at install time.

Bug fixes

  • Server menu pruned to Restart-only and the redundant View menu dropped — macOS already provides View.
  • Sidebar Sessions icon swapped to list.dash (the previous waveform read as audio).

Upgrade notes

  • No data migration required.
  • Mode preference (pushAppMode) persists in UserDefaults; default is Full mode for existing users.
  • Web UI theme + color tokens are computed at runtime — no cache invalidation needed.

v1.5.28

2026-04-23

Session cards get richer at a glance — Claude Code's most recent ※ recap: shows under each card title, and the titles now match what the live terminal tab displays. Tap-to-resume also opens the tab at the session's real working directory instead of silently falling back to $HOME. Folder filter chips in both the terminal strip and the Sessions toolbar are drag-reorderable.

What's changed

Recap on session cards

Every historical session card now shows Claude Code's most recent ※ recap: under the title — the same one-paragraph summary Claude prints when you return to an idle session. Parsed from the tail of each JSONL and cached per file, so scrolling stays cheap.

Session card titles match the live tab

Cards now prefer the real Claude-assigned session name instead of a first-user-message snippet. Priority matches what the live terminal tab already shows: the custom-title you set via Claude /rename wins, then Claude's auto-generated haiku (ai-title). If neither has been recorded yet, the old user-message fallback still applies.

Tap-to-resume goes to the session's real cwd

Clicking a historical session card now opens the resume tab in the directory the session actually ran in, read straight from the JSONL. Previously the encoded ~/.claude/projects/<slug> folder name was decoded by swapping every - for / — so any project path containing a hyphen (like push-macos) decoded to a non-existent directory and the shell silently fell back to $HOME.

Drag-reorder for folder filter chips

The folder filter chips above the terminal strip and in the Sessions toolbar are now drag-reorderable. Pin the folders you use most to the front; order persists across launches. Same "live slide on hover, persist once on drop" feel on both surfaces.

Upgrade notes

  • No data migration required.
  • Per-session metadata (title, recap, cwd) is cached under ~/Library/Caches/ai.massless.push/SessionMeta/; schema-versioned so prior caches re-populate cleanly on first Sessions-view open after the update. Warm re-enters stay zero-I/O.
  • Sessions folder-chip order persists under push.sessions.folderOrder in UserDefaults; the terminal strip uses its existing on-disk order.

v1.5.27

2026-04-23

Sessions get first-class placement in the sidebar, Claude background processes surface alongside terminal sessions, and the sidebar's unread/inbox badges switch from red to blue.

What's changed

Sessions row promoted above Feed and Issues

Sessions is now the second row in the sidebar (after Terminal), ahead of Feed and Issues. Reflects how often you actually jump to a running session versus scanning the inbox.

Claude background processes in Sessions

Background Claude runs (spawned by agents or standing instructions) now show up in the Sessions list alongside your terminal sessions, with an adapter chip on each card so you can tell at a glance which runtime is behind a given session.

Column view capped at 200 most-recent items

The Sessions column no longer tries to render every historical session at once. Caps at the 200 most-recent entries to keep scroll budget available for the active ones.

Sidebar badges now blue

The Terminal unread badge and Feed inbox badge both render in blue instead of red. Red is reserved for actual errors and warnings; unread counts are just unread counts.

Upgrade notes

  • No data migration required.
  • If the Sessions row's new position feels off, it'll still respect your last-selected sidebar target across relaunches.

v1.5.26

2026-04-23

Terminal tab titles now survive reload (⌘R), close + ⌘⇧T reopen, and full app relaunch — haikus no longer get overwritten by Claude's ✳ Claude Code placeholder on resume. Also extends the UI responsiveness work from 1.5.25 to more surfaces.

What's changed

Terminal tab names survive reload, relaunch, and close+reopen

Tab titles were getting clobbered by ✳ Claude Code whenever Claude restarted a session. Claude Code doesn't restore the haiku title when it resumes a transcript — it re-emits its fallback placeholder instead — and Push was treating that fallback as an authoritative new title, overwriting whatever real haiku we had cached.

Push now classifies each incoming OSC title emission by content pattern and stores it in one of five per-tab layers (manual rename → Claude-side name → cached haiku → booting placeholder → shell cwd → generic fallback). The display name is resolved by priority. Claude's Claude Code fallback is recognized as a "no content" signal and ignored when a real haiku is cached — so reload, close + reopen, and app relaunch all preserve the tab's haiku.

Fresh Claude sessions still show ✳ Claude Code as a transient placeholder while Claude boots and generates the first haiku. That placeholder is ephemeral: it's never persisted and clears the moment Claude emits a real title.

Manual rename + Claude /rename now co-exist cleanly

Double-click rename on the tab chip sets a user title that wins the display immediately. When Claude asserts a new title (generating a fresh haiku or running /rename to a different value), the manual rename is cleared and Claude's new name takes over. Animation-frame re-emissions (⠂ / ⠐ / ⠠ cycling through the same title) don't trigger the clear — your manual rename persists through Claude's normal streaming.

Note: if you /rename to the exact same value Claude was already asserting, nothing changes on Claude's side and the manual rename stays. If that's not what you want, right-click the tab → Use Automatic Title.

UI sweep: second pass

Extends 1.5.25's performance work to more surfaces that had the same compositor-cost antipatterns — live-filter materials on list rows, auto-ticking relative timestamps, and computed-property array builds re-running on every scroll frame. Affected areas: Feed, agents list, issues list, skills list, files, issue detail, devices.

Also adds a CI guard (scripts/check-swiftui-perf.sh) that fails future PRs which reintroduce these patterns in the desktop/Sources/Push/Views/ tree.

Upgrade notes

  • Tabs that were previously stuck on claude or ✳ Claude Code self-heal on first launch — stale data in the deprecated session-title slot is promoted back to the shell slot where it belongs.
  • No data migration required. The on-disk tab format is forward- and backward-compatible: rolling back to an older build still sees a usable name per tab.
  • Full investigation + design discussion: writing/documentation/push/macos/investigations/2026-04-22-tab-title-haiku-lost-reload-and-resume.md.

v1.5.25

2026-04-22

UI responsiveness pass. Three performance fixes targeting the Sessions view and the terminal tab-persistence path — together they eliminate the lag that several users saw after 1.5.24 shipped the three-column Sessions view, particularly with many live Claude tabs or hundreds of historical sessions.

What's changed

Terminal tab list no longer writes to disk on every Claude animation frame

Claude Code animates its terminal title (the ✳ / ⠂ / ⠐ prefix cycle) during responses, emitting an OSC title sequence per frame. Each one used to trigger a synchronous disk write of Push's full tab snapshot on the main thread — with 20+ tabs that became many main-thread writes per second, cascading SwiftUI invalidation into the sidebar and visibly hitching the UI.

Now the save is debounced to a background queue, and title updates that change only the animation glyph are dropped at the source — no mutation, no disk write. App quits still flush the final state synchronously so nothing is lost.

Sessions view scales to hundreds of historical sessions without janking the sidebar

Scrolling the sidebar with the Sessions section active used to get choppy once the Local column populated with a few hundred past Claude sessions. Root cause was compositor-side: each session card had a live Gaussian-blur material background, a shadow filter, and an auto-ticking relative timestamp (Text(_, style: .relative) runs a 1-second TimelineView per card). The macOS window server had to recomposite those filters every frame, stealing budget from the sidebar's own scroll animation.

Cards are now flat-filled with a simple stroke border, timestamps are pre-formatted strings that don't auto-invalidate, and the column itself uses a native List (backed by NSTableView) so only visible rows have backing views — the old LazyVStack kept every row alive once you'd scrolled past it.

Visual difference: cards look slightly flatter (no translucent blur, no drop shadow). Selection is still obvious from the accent-color stroke.

Sessions view no longer re-sorts on every scroll frame

The three column arrays were computed properties evaluated from the SwiftUI body, which meant sorting and filtering 600+ items on every layout pass the view touched. They're now cached in view state and rebuilt only when the source data changes (tab list mutates, server runs update, view re-entry).

Upgrade notes

  • No data migrations, no user action required.
  • Full investigation + measurements: writing/documentation/push/macos/investigations/2026-04-22-mac-app-ui-lag-terminal-tabs-persistence-churn.md.

v1.5.24

2026-04-22

The Sessions view now shows every Claude session on your Mac — not just the ones Push's agents ran. Your own terminal-driven work is finally first-class alongside agent runs.

What's changed

Unified Sessions view — three columns

The old status-grouped grid (Running / Needs attention / Recent) is replaced by three columns that reflect how you actually think about sessions:

  • Local — every Claude session on this Mac, whether it's a live tab you're typing into right now or an archived conversation from last week. Read directly from ~/.claude/projects/; nothing touches the server.
  • Running Push — agent runs and /push sessions currently in flight.
  • Completed Push — agent runs and /push sessions that have finished, failed, cancelled, or timed out.

Cards migrate between columns automatically as state changes — columns are groups, not drop targets. The layout is responsive: below a minimum column width, the three columns scroll horizontally instead of squeezing unreadably.

Tap a local session to pick it back up

  • Live session (its PTY is still running in a tab) → Push switches to the Terminal section and selects that tab. Your transcript and cwd are right where you left them.
  • Historical session (tab long gone) → Push opens a fresh tab in the original cwd and auto-runs claude --resume <session-id>. Same resume mechanism Push already uses for Claude Code sessions across app quits — now surfaced one tap away from anywhere on your Mac.

The asymmetry that drove this: Push was built to track AI-driven work, but the AI-driven work you personally did in a terminal tab was invisible to it. Friday's 90-minute pair-programming session with Claude now lives in the same place as Friday's agent runs.

Upgrade notes

  • No data migrations, no user action required. Local sessions are read from disk on view appear; nothing is uploaded.
  • Full design: writing/documentation/push/macos/plans/2026-04-21-unified-sessions-visibility.md.

v1.5.23

2026-04-21

Agent detail view gets a Podcasts-style redesign, and the sidebar gets a proper "+" button for adding new agents without leaving the app.

What's changed

Agent detail view — Podcasts-style

The old two-tab (Info | Files) layout is gone. In its place: a scrolling single column that mirrors Apple Podcasts' show detail page and surfaces every useful field the old view hid behind clicks.

  • Hero — 96pt animated avatar with a status overlay.
  • Sessions — a horizontal strip of recent sessions. Tap a card to deep-link into the full session detail view. Hides entirely when the agent has no sessions.
  • About — the agent's description, full-width.
  • Information grid — template, adapter, status, last seen, created, updated, budget, reports-to, and ID in one glance.
  • Working Directories — each CWD with its repo badge + right-click menu.
  • Files — a disclosure group that expands inline without leaving the page.

Side effect: the old view's duplicate-title bug is gone.

"+" button to add new agents

Hover the Agents section header in the sidebar — a "+" fades in on the trailing side. Click it to open a native popover with name, adapter, and an optional description. Template, runtime defaults, and the full description (if you leave it blank) are inferred server-side from the name. Hit Return, the new agent is created and selected; you land on its detail page.

Skills section is intentionally unchanged — skills are disk-scanned from ~/.claude/skills/, not something you add from a sidebar click.

Upgrade notes

  • No data migrations, no user action required.
  • Full plans:
    • writing/documentation/push/macos/plans/2026-04-21-agent-detail-view-podcast-redesign.md
    • writing/documentation/push/macos/plans/2026-04-21-sidebar-agent-add-popover.md

v1.5.22

2026-04-21

Tab UX overhaul — real keyboard shortcuts that match the Mac terminals and browsers you already know, a Terminal menu in the menu bar, a Keyboard Shortcuts window at ⌘/, right-click polish on the terminal pane, and a quiet acknowledgment when Push restores your Claude session across a quit.

What's changed

Terminal menu in the main menu bar

All tab-management shortcuts now have a proper home under Terminal in the menu bar. Help → Search indexes them, VoiceOver reads them, and the items are focus-scoped so the shortcuts only fire when the Terminal section is visible.

  • New Tab ⌘T · Close Tab ⌘W · Reopen Closed Tab ⌘⇧T · Reload Tab ⌘R
  • Next Tab ⌘⇧] · Previous Tab ⌘⇧[
  • Select Tab 1–8 — ⌘1 through ⌘8, jump to a tab by position
  • Select Last Tab — ⌘9 (matches Chrome/Firefox: last tab regardless of count)

Keyboard Shortcuts window (⌘/)

Opens from Help → Keyboard Shortcuts or ⌘/ anywhere in Push. One-screen reference grouped into Terminal and App sections. Non-resizable, content-sized.

Terminal pane right-click polish

Right-click inside a running terminal pane now shows:

  • SF Symbol icons next to Copy, Paste, and Clear — matches macOS design language, no longer a flat text list.
  • A new Reload Tab (⌘R) entry with arrow.clockwise icon. Browsers surface Reload in their page context menu; terminals should too. Session-detail panes (where "reload" has no meaning) don't get the item.

Tab chip Reload shows its shortcut

Right-click an active tab — the Reload item now reads Reload Tab (⌘R) so the shortcut is discoverable from the menu, not just from the Terminal menu bar.

"Picked up where you left off"

Push has always restored Claude sessions across quits — same transcript, same cwd, same in-flight conversation — but the reattach was completely silent. Starting this release, when your Claude session auto-resumes on relaunch, a small popover appears once on the restored tab: "Push restored this Claude session with its transcript, cwd, and live state. Quit anytime; it'll still be here." Fires once per install.

Sidebar polish

  • Agent rows now render each agent's avatar character.
  • Agents and Skills sections get an always-visible left chevron, a full-row hit target for fold/unfold, and default to expanded on first launch.

Upgrade notes

  • No data migrations, no user action required.
  • Full design doc for the tab UX work: writing/documentation/push/macos/plans/2026-04-20-terminal-tab-ux-and-discoverability.md.

v1.5.21

2026-04-20

Sidebar cleanup — Agents and Skills become collapsible groups that list every entity by name, clicking a name jumps straight to its detail view, and the sign-in footer at the bottom is now a single continuous pane of glass instead of a stack of overlapping materials. A handful of other items get tidier homes in Settings and the Server menu.

What's changed

Agents and Skills are now foldable lists in the sidebar

Before: the sidebar had a single Agents row. Click it, land on a list view, click again to open an agent. Same shape for Skills.

Now: Agents and Skills are collapsible sections (click the header to fold/unfold, same chevron Mail uses for Mailboxes). Each section lists your agents / skills by name. Clicking a name opens that agent's or skill's detail view directly — one click instead of two. The fold state survives relaunch.

Native SwiftUI Section(isExpanded:) under the hood. The List's selection binding now carries a polymorphic target, so the native row highlight and arrow-key navigation work across both section rows and per-entity rows in the same column.

Sign-in footer is one pane of glass

The footer at the bottom of the sidebar (avatar, email, relay status) was overlapping scroll content — you'd see rows bleed through behind it. Fixing the overlap naively stacked an extra material layer and left a subtle tonal seam.

Now: no extra material layer. The sidebar's existing glass flows uninterrupted from top to bottom, and scrolling content fades to transparent over the last ~24pt before reaching the footer region. Same scroll-edge effect Apple Music and Notes use.

Secrets and Devices moved to Settings

Secrets and Devices were set-once config surfaces living in the sidebar's Manage section — they don't belong next to daily views like Issues or Sessions. They're now panes in Settings, alongside Apple Account and Server. The backend is unchanged.

Logs moved to the Server menu

Server logs were a sidebar entry too. They're a debugging surface, not a workspace view. Server → Open Logs now opens a standalone resizable log window with standard macOS chrome. The log backend is unchanged.

Memory sidebar entry is opt-in

The Memory graph sidebar entry is still evolving, so it's gated behind Settings → Experimental → Memory sidebar entry. Off by default; flip it on to get the row back. No relaunch needed.

Costs moves into the top group

Minor reshuffle: Costs is now in the top group alongside Terminal, Feed, Issues, Sessions, and Files — it's a glance surface more than a settings one.

Upgrade notes

  • No data migrations, no user action required.
  • Launch defaults: the "Open on launch" picker in Settings → Appearance no longer lists Agents / Skills (they're not standalone sidebar destinations anymore). If you had one of them set as your default, the app will open to Terminal on first launch after upgrade — reselect your preferred default once.
  • Full technical investigation: writing/documentation/push/macos/investigations/2026-04-20-swiftui-sidebar-patterns.md.

v1.5.20

2026-04-20

Quick Terminal popup moves behind an opt-in flag. If double-tapping ⌘ kept summoning a floating terminal you didn't ask for, this release makes that stop — by default, for everyone.

What's changed

Experimental settings pane

Settings has a new Experimental section. It's where we'll stage features that aren't ready to be on for everyone yet. Everything inside is off by default; you enable each one explicitly.

Quick Terminal is now opt-in

The Spotlight-style popup terminal (summoned by double-tapping ⌘) was previously always active. It's a neat idea — hit the hotkey, type, dismiss — but ⌘⌘ overlaps with muscle memory for a lot of people, and there was no way to turn it off.

Starting with 1.5.20, Quick Terminal is off by default. Go to Settings → Experimental → Quick Terminal popup to turn it on. The toggle takes effect on the next keystroke — no app restart.

How it's wired (for the curious)

One flag (experimental.popupTerminal), one source of truth (UserDefaults), read live at the keystroke-handling boundary. No notification bus, no teardown plumbing, no in-memory mirror to drift out of sync. When the flag is off, the keystroke monitor resets its state on every event and never fires. When it's on, double-tap works the same way it always did.

Upgrade notes

  • No user action required. After updating, ⌘⌘ will stop summoning the popup on all existing installs.
  • If you liked the Quick Terminal: re-enable it in Settings → Experimental.
  • No data migrations, no onboarding prompts.

v1.5.18

2026-04-20

Housekeeping release. Ships the in-app references to our new primary domain — push.computer — as part of the ongoing brand migration off pushto.do.

What's changed

Apple sign-in fallback URL now points at push.computer

The CLI-pairing flow's fallback URL — the one the server hands back when the Mac can't reach Supabase but can reach our web origin — now resolves to push.computer/auth/cli instead of pushto.do/auth/cli. No behavior change for users on the happy path; the old URL still works and stays reachable.

Docs and in-app links updated

The in-app sidebar footer's "Docs" link and the README now point at the GitHub /doc folder rather than the retired docs.pushto.do subdomain, which was already dead. No content was lost — the docs have always lived in the repo; only the link target changed.

Notes

  • No data migrations, no user action required.
  • The Sparkle update feed (releases.pushto.do/appcast.xml) intentionally stays on the old domain — it's baked into every installed binary and has to keep resolving indefinitely. New installs after this release still check the same URL.
  • Relay traffic continues to flow through push-relay.com (unchanged since 1.5.13).

v1.5.17

2026-04-20

When Apple sign-in succeeds but the pairing tunnel can't come up — corporate firewall, TLS-intercepting proxy, managed-Mac file block — Push now tells you so, immediately, with the specific cause and something actionable to send to IT.

What's changed

All post-sign-in failures produce the same honest UX

Previously, only one of the three common corp-Mac failure modes (the managed-Mac local-write case) auto-opened a dialog explaining what went wrong. The other two — corporate firewall blocking push-relay.com, or a TLS-intercepting proxy breaking our handshake — landed silently in the pairing-status endpoint and the only user signal was a quiet red relay dot in the sidebar corner. Browser said Success, sidebar showed your avatar and name, and there was no obvious reason the iPhone still couldn't find the Mac.

1.5.17 closes the loop.

After a successful sign-in poll, the Mac briefly watches the pairing tunnel come up (up to ~4s). If it enters a blocked state — network unreachable, TLS verification failure, auth rejection, or a 5xx from our pairing server — the failure sheet auto-opens immediately, while you still have the browser's "Success" page fresh in mind. The sheet carries per-case copy: the actual fs path when it's a local write, the specific firewall hint when it's a network block, the TLS exclusion guidance when it's a proxy re-signing certs. The same diagnostic you'd need to hand your IT team is right there, copy-selectable.

Sidebar now honestly reflects what's working

Before 1.5.17, a sign-in that succeeded server-side but failed to bring up the tunnel left the sidebar showing "you're signed in" (avatar + name + red dot). That matched what the config file said, not what the user actually cared about — their iPhone still can't reach the Mac.

Now the sidebar branches on "effectively connected" instead of "api key present." If the sign-in didn't bring up the pairing tunnel, the footer collapses back to the same blue "Connect Apple Account" button a fresh install shows, plus an orange info-i that re-opens the failure sheet. The api key stays in config so a retry after IT unblocks doesn't need a fresh Supabase round-trip, but the UI stops claiming success until the tunnel is actually up.

Net effect: a user hitting any of the three known corp-Mac walls sees the same shape. Sign-in button, info-i beside it, auto-opened sheet with specific cause. No guessing which surface has the real information.

Notes

  • No data migrations. No user action required beyond updating.
  • The sheet's network/TLS copy is new; the existing local-save copy is unchanged from 1.5.11.
  • Full investigation and case taxonomy: push/macos/investigations/2026-04-20-corp-mac-pairing-failure-taxonomy.md.

v1.5.16

2026-04-20

Settings window gets a proper detail toolbar: browser-style back/forward chevrons on the leading edge, the current pane's name as plain text beside them — the same chrome shape Xcode Settings uses on macOS 26.

What's changed

Back/forward navigation in the Settings detail column

Sidebar taps now push onto a history stack. Two chevron buttons sit at the leading edge of the detail toolbar and shift a cursor through that stack — exactly like the back/forward buttons in a browser. Each chevron is its own ToolbarItem so Tahoe renders them as two distinct glass capsules with a divider between (rather than the fused pill ToolbarItemGroup produces), matching Xcode Settings.

Programmatic sidebar moves are guarded by an isNavigatingHistory flag so using the chevrons doesn't re-push the same entry.

Pane title rendered as plain text, not a toolbar button

The current pane's name (Apple Account, Integrations, etc.) now appears as plain text immediately after the chevrons. Liquid Glass backing is stripped via .sharedBackgroundVisibility(.hidden) (macOS 26+), and the title uses .title3.weight(.semibold) — the same weight Apple uses for Xcode Settings pane headings.

Settings scene: plain Window(id:) instead of Settings {}

SwiftUI's Settings {} scene silently ignores .windowStyle(.hiddenTitleBar) and most resizability / toolbar modifiers, which is why 1.5.15 needed an AppKit bridge to match the main window's nestled-traffic-lights chrome. 1.5.16 switches to a plain Window("Settings", id: "settings") scene that honors those modifiers natively — no bridge required.

Cmd+, is rewired via CommandGroup(replacing: .appSettings) + openWindow(id:), with NSApp.activate(...) first to handle the menu-bar-extra activation quirk. .toolbar(removing: .title) replaces the old titleVisibility = .hidden AppKit tweak.

Notes

  • No data migrations. No user action required beyond updating.
  • Behavior of every Settings pane is unchanged — this release only reworks the window chrome and navigation affordances.

v1.5.15

2026-04-20

The Settings window is rebuilt as a native macOS 26 sidebar — same shape as Xcode and System Settings — and the sidebar's connection popover now tells you about your paired iPhone, not just your Apple Account.

What's changed

Settings window: native macOS 26 sidebar

Push Settings used to be a single scrolling stack of glass cards. 1.5.15 ports it to the System-Settings / Xcode layout: a NavigationSplitView with a permanent left sidebar — Apple Account, Integrations, Coder Agent, Appearance, Server, Status, Uninstall — each driving its own pane on the right.

To get the Xcode look — sidebar extending edge-to-edge with traffic lights nestled inside it — SwiftUI's Settings {} scene needs the backing NSWindow reconfigured for full-size content view + transparent titlebar. A small NSViewRepresentable bridge does that once on attach. The window is resizable and supports full screen, but the sidebar-collapse button is hidden (Xcode's Settings doesn't have one either) and the sidebar can't be dragged closed.

Every pane uses native Form layout

Every pane is now Form(.grouped) with LabeledContent rows, so alignment, spacing, and section chrome come from SwiftUI itself rather than hand-rolled boxes:

  • Apple Account: dropped the glass-wrapped VStack and the redundant header row. Status, Apple ID, Owner ID, and iPhone are LabeledContent rows; Relay tunnel sits in its own section.
  • Integrations: custom HStacks replaced with LabeledContent { buttons } label: { Label + caption }.
  • Coder Agent: flattened the mixed header/status into a single Section("Setup") with a Status row.
  • Uninstall: removed the custom circled-number badges and the blue notice box. Two native Sections instead.

The result reads the same way Apple's own settings windows do.

Settings window chrome no longer doubles on macOS 26

The first cut of the new Settings window also attached an empty NSToolbar() with .unified style. On macOS 26, an empty unified toolbar still reserves ~44–52pt of chrome — so the Settings window had a top band roughly twice the height of the main window's. Fixed by matching the main window's pattern: transparent titlebar + full-size content view, no toolbar.

Sidebar connection popover: iPhone pairing state

The avatar popover that shows Account / iPhone / Pairing tunnel now actually tells you about your iPhone. The iPhone row is two-line, mirroring the Account row: device name on top, "Last active X" caption below. The "active" timestamp is parsed from the lastUsedAt already returned by /api/devices — it's request-driven (bumped when the iOS app talks to the server), not a heartbeat, so it honestly reads as "app last talked to the server."

The status indicator also gets smarter: when the relay is up and an iPhone is registered, the green dot becomes iphone.badge.checkmark — a green check on a secondary phone glyph. So "fully set up" reads visibly different from "ready, nothing paired yet." All failure states (amber retrying, red blocked, gray unknown) keep the dot regardless of iPhone presence.

Notes

  • No data migrations. No user action required beyond updating.
  • All Settings panes that existed in 1.5.14 still exist; they've moved from scrolling cards into named sidebar entries.
  • The Settings window is resizable and supports full screen now; previous version was fixed-size.

v1.5.11

2026-04-19

Bug-fix release — kills the "claude sits on the prompt and never runs" flake when you spawn a Claude Code session from the + button or ⌘T.

What's changed

"New Claude Code Session" actually runs every time

Since the Claude auto-spawn landed, the + button (and, as of 1.5.9, ⌘T with Claude as default) would occasionally open a tab with claude typed at the prompt but never submit — cursor stuck on the word, no Claude Code banner. Quitting and relaunching the app papered over it until the next slow tick.

Root cause. Auto-spawn queued claude\n into the tab's PTY on the first prompt-ready signal. The trailing \n went through ghostty's text path, which bracket-wraps multi-line writes once the shell has enabled bracketed-paste mode. When main-queue scheduling landed the write after zsh's line editor turned bracketed-paste on, the \n arrived as a literal newline inside a paste block instead of firing accept-line. Whether you hit the race depended entirely on how loaded the main loop was at shell startup — hence the random failures.

Fix. Split the write: claude goes through the text path as before, then a synthetic Return key press goes through the key path. The key path is never bracket-wrapped — ghostty's input encoder emits CR, zsh binds ^M to accept-line unconditionally — so the submit is deterministic whether the shell's line editor is up or not.

No behavior change in the common case; existing installs just stop flaking.

Notes

  • No API or data-format changes; upgrade is transparent.
  • Same fix pattern applies to any future "auto-type a command" path (for example, adapters beyond Claude Code): send the text, then press Return as a key event. Don't embed \n in the text write.

v1.5.10

2026-04-19

Small but important reliability fix for first-time pairing.

What's changed

Mac-signed-in now guarantees iPhone-ready

Before 1.5.10, if the very first handshake with the relay hit any transient issue (network blip, relay hiccup, or — most commonly — a rate-limit backoff from the pairing service), the tunnel stayed offline and the only way to recover was quitting and relaunching the Mac app. The Mac thought it was paired; your iPhone saw nothing.

Now the Mac keeps trying in the background with exponential backoff (30s → 1m → 2m → 5m → 10m cap) until the tunnel comes up. Also raised the pairing rate limit from 5 to 100 mints per day so normal reconnection cycles don't trip it.

The net UX contract: once your Mac has signed in with Apple, your iPhone can sign in with the same Apple ID any time later and pair automatically. No restart required on either side.

Notes

  • Existing installs picked up the rate-limit bump silently (server-side).
  • No new integrations, no data migrations.

v1.5.9

2026-04-19

Small polish release — a new preference for what the tab + button spawns, plus a tidier default workspace name.

What's changed

Choose what + (and ⌘T) opens

The tab strip's + button used to hardcode "new terminal tab", with a right-click menu offering Claude Code as a one-off. That menu is now a setting, not an action:

  • Right-click the + → "Set default new tab option" with two checkmarked rows:
    • Terminal Tab (default)
    • Claude Code Session
  • Left-click the + and ⌘T both dispatch through the chosen default — flip the preference once and both entry points follow.

The + glyph itself mirrors the choice so you can tell at a glance what a click will do: the plain plus icon for Terminal Tab, the pixel-art Claude adapter character (same one you see in agent avatars) for Claude Code Session. Menu rows are plain text on purpose — the checkmark is all the signal the menu needs.

Default workspace name is "Workspace"

The onboarding quick-setup defaulted to "Push Workspace", which reads as repetition inside a Push app. It's now just "Workspace". Existing workspaces are untouched; the pu CLI and /pu skill still map both "Workspace" and legacy "Push Workspace" to the PU issue prefix so nothing downstream breaks.

Notes

  • Existing installs keep whatever default they had (the preference reads as Terminal Tab on first launch of 1.5.9).
  • No server-side changes beyond the workspace-name/prefix mapping.

v1.5.8

2026-04-19

Big onboarding cleanup + several relay/pairing fixes that landed since 1.5.7.

What's changed

Onboarding trimmed to 4 steps, zero engineer-speak

The old flow had five screens, two of which asked nothing of you — a 10-row pu setup checklist that ran through system-health probes under engineer-facing names ("Check the /pu skill", "Detect-cwd"), and a three-card finish screen that duplicated things you'd already seen. Both are gone.

Now:

  • Welcome — hero screen, one button
  • Finish setup — CLI shortcut + optional Launch at Login (the only step that asks you anything about the Mac itself)
  • Sign in with Apple — primary path; a small secondary link ("Use Push locally without iPhone") lets you skip if you don't want an iPhone connection
  • You're all set — single confident line, one CTA, you're in the app

The agent-provisioning script still runs (same pu setup), just silently in the background while you're clicking through. By the time you reach step 3, your default agent is set up and a starter task is seeded.

Back buttons now work on every step (hidden during the Apple sign-in browser round-trip so clicking back doesn't orphan an open auth window).

Relay activates in-process after Apple Sign-In

Before 1.5.8, a fresh install required a manual restart of the Mac app before the iPhone could connect — boot-time relay registration fired before Apple Sign-In had completed, failed, and nothing re-triggered it. Now apple-connect kicks the registrar the moment sign-in succeeds, so the tunnel is live without touching anything.

Apple email visible in Settings

The Settings → Apple Account card used to show only an opaque Owner UUID (80bf601c-8de…). It now shows the email you're connected as (e.g. you@example.com), pulled from the Supabase auth row. Existing installs show the email after the next sign-in or disconnect/reconnect cycle.

Dev/prod boot parity

Historical dev seed wrote deploymentMode: local_trusted to let the CLI pu setup short-circuit auth; that created a dev/prod divergence where dev needed a restart after Apple Sign-In and prod didn't. Dev now seeds relay mode by default. Internal cleanup; not visible to end users.

iOS sync fix + settings ghost-entry cleanup

  • iOS instances list used to accumulate stale "Pending setup" entries when the Mac re-registered under a new relay subdomain (e.g. after a disconnect). iOS now reconciles local cache against Supabase on each auto-connect, pruning local rows and their Keychain tokens when they're no longer in the authoritative list. (iOS change; ships with the next App Store/TestFlight build.)
  • Relay allowedHostnames guard now picks up subdomains added at runtime. Previously a fresh first-sign-in hit a 403 ("hostname not allowed") until the next restart.

Notes

  • No new integrations. Existing Apple Sign-In users don't need to re-pair.
  • deploymentMode: "local_trusted" is no longer the onboarding default anywhere — prod defaults to relay, dev seeds relay. local_trusted is still a valid mode for advanced users who set PUSH_DEPLOYMENT_MODE explicitly.

v1.5.7

2026-04-19

Fixes a real-time sync bug between the iPhone app and the Mac UI.

What's changed

Marking a to-do complete on iPhone now updates the Mac instantly

Before 1.5.7, completing a to-do on the iPhone would save to the server but the Mac UI wouldn't move the card to the Done column until you quit and relaunched the app. The reverse direction (Mac → iPhone) worked fine, which is what made the bug easy to miss: one direction of the sync was silently dropping every event.

The cause was a case-sensitivity mismatch in the live-events channel. The Mac app subscribed to its workspace's event stream using the uppercase form of the workspace UUID (Swift's default), while the server published events using the lowercase form (Postgres's default). The channel dispatch in Node's event system uses strict string matching, so the Mac's listener was never called. HTTP lookups survived the mismatch because Postgres normalizes UUIDs internally — only the real-time path was affected.

The fix normalizes the workspace UUID on both sides: the server lowercases on publish and subscribe (defensive against any client), and the Mac client now lowercases its subscription URL to match the iPhone's existing behavior.

If you had the first Mac paired and a to-do marked on iPhone refused to update, it will now update live.

For developers

  • server/src/services/live-events.ts — added channelKey() canonicalization around publishLiveEvent and subscribeWorkspaceLiveEvents.
  • desktop/Sources/Push/Networking/PushWebSocketClient.swift — lowercased workspaceId.uuidString in the WebSocket URL, matching push-ios/App/Push/PushWebSocketService.swift.
  • New reference doc: writing/documentation/push/macos/LIVE-EVENTS.md describes the realtime protocol, event types, and the UUID-case invariant with this bug as a worked example.

Installation

  • Download Push-1.5.7.dmg from releases.pushto.do.
  • Drag Push.app to /Applications, replacing the existing copy.
  • Open it.

Your local data in ~/.push/ is preserved across the update.

System requirements

  • macOS 15 Sequoia or newer
  • Apple Silicon Mac
  • ~200 MB disk space

v1.5.5

2026-04-19

Small terminal-strip additions.

What's changed

Right-click the + to start a Claude Code session

The + button above the tab strip now accepts a right-click. You get a two-item menu:

  • New Terminal Tab — same as the left-click (inherits the active tab's folder, opens your usual shell).
  • New Claude Code Session — opens a tab in the inherited folder and launches Claude Code for you.

The Claude option is deliberately not a shortcut through a new code path — it opens a regular terminal tab and types claude once the shell is ready. Every Push integration behaves identically to when you type it yourself: the tab's PUSH_TAB_ID binds to Claude's --session-id, the session marker is written, and the tab auto-resumes its Claude session after an app relaunch. Any custom flags you've pinned into your own claude() wrapper in .zshrc (like --dangerously-skip-permissions) still flow through.

Reopen closed — breathing room

When the tab strip overflows horizontally, the Reopen closed button on the right used to press right up against the last tab chip, visually reading as the strip's trailing end. Added a few points of leading padding so it registers as its own affordance.

Installation

  • Download Push-1.5.5.dmg from releases.pushto.do.
  • Drag Push.app to /Applications, replacing the existing copy.
  • Open it.

Your local data in ~/.push/ is preserved across the update.

System requirements

  • macOS 15 Sequoia or newer
  • Apple Silicon Mac
  • ~200 MB disk space

v1.5.4

2026-04-19

Two small terminal-strip polishes.

What's changed

Reorder folder chips by drag

The folder-filter chips in the terminal toolbar (the row that starts with All) now accept drag-to-reorder. Grab a folder chip, drag it past a neighbor, and the siblings slide out of the way live under the cursor — same feel as dragging tabs in the strip below. All stays pinned to the first slot and can't be dragged. Your chosen order is persisted across app restarts, and if all tabs in a folder close and that folder later reappears, it snaps back to its remembered position.

Close icon fades on hover

Hovering a terminal tab to reveal its × close button used to pop the icon in and out instantly. It now fades over ~120 ms — gentle enough to read as deliberate, fast enough that flicking across tabs still feels snappy. The 14pt slot is always reserved, so the title text no longer nudges sideways when the icon appears.

Installation

  • Download Push-1.5.4.dmg from releases.pushto.do.
  • Drag Push.app to /Applications, replacing the existing copy.
  • Open it.

Your local data in ~/.push/ is preserved across the update.

System requirements

  • macOS 15 Sequoia or newer
  • Apple Silicon Mac
  • ~200 MB disk space

v1.5.3

2026-04-19

Feed is now a weighted dashboard — a row of tappable glance-metrics at the top, decisions that need you underneath, activity and resolved history below. The separate Dashboard sidebar entry is gone: everything it surfaced now lives on Feed or is one click away. Under the hood Feed gained offline caches, a recoverable Cleared tray, actionable approval notifications, and optimistic actions that roll back with a toast when something fails.

What's changed

Feed is the dashboard

The old four-tab list (All / Action / Update / Approvals) is replaced by a single scrolling view with priority baked into the layout:

  • Metric row at the top — four square tiles: Decisions, Running agents, Open issues, and This month spend. Tap one to jump to the matching sidebar section (Sessions, Issues, Costs) or, for Decisions, scroll-anchor to the hero section below. Running agents gets a red error chip if any agent is in the error state.
  • Needs your decision (hero) — pending approvals, drafts, alerts, and deliverables rendered as amber-accented cards with inline Approve / Reject / Copy & Open actions. On Tahoe, primary actions use .glassProminent with an accent tint.
  • Recent activity — completions, artifacts, and other FYI items as muted secondary cards. Artifacts keep their inline file row with the same "View" affordance.
  • Resolved decisions — a tertiary archive of approved / rejected / cancelled approvals. Muted read-only tiles.

Every section auto-hides when empty. Search at the top of the window filters all three.

Dashboard is gone. Its counts — Active Issues, Pending Approvals, Agents Running, Month Spend — live in the metric row now. Completed issues are one click away in Issues. Cost utilization moved to Costs where it belongs. Running-agent list is covered by Sessions. If you had Dashboard pinned as your default launch section, Push silently routes it to Feed on next open.

Offline, on purpose

Feed now works when the server doesn't.

  • Cache-first render on launch. The last /feed response is persisted to ~/Library/Caches/<bundle>/FeedCache/; opening Feed shows the cached items instantly while the network refresh runs in the background.
  • Artifact pre-fetch. When the feed loads, Push quietly pulls the bodies of the top five artifact items into a local file cache. Tapping an artifact's View sheet after the server stops responding still renders its content.
  • Offline banner. A compact amber capsule sits above the hero whenever the server stops responding, with "Updated Nm ago" so you know how stale the visible data is.
  • Action gating per item. Approve and Reject buttons go disabled with a "Reconnect to act" hint when offline. Copy & Open, View file, and navigation keep working — they don't need the server.

Cleared tray — dismiss is recoverable

Every swipe or X-button dismiss now also writes to a per-workspace Cleared log. When anything is in it, a tray icon appears in the Feed toolbar; tap it to see what you've cleared and restore anything non-terminal (terminal here = approvals you already decided on; the server rejects un-deciding them). Stored for seven days, then auto-purged.

Optimistic actions + toasts

Every mutating action — approve, reject, dismiss, copy & open — now removes the item from the visible feed immediately. If the network call fails, the item pops back into place and a toast surfaces the error ("Couldn't approve", "Couldn't dismiss"). Copy & Open confirms with a success toast. No more silent failures. Toasts live in the bottom-right, auto-dismiss after three seconds, and respect Reduce Motion.

Actionable approval notifications

When an agent requests an approval, the system banner now carries three inline actions:

  • Approve — resolves the approval without bringing Push to the foreground
  • Reject — same, destructive styling
  • View — brings Push to the front and jumps to the Feed

Banners show the agent name, the approval title, and an optional description. Request IDs are deterministic per-approval so duplicate WebSocket events don't stack banners — you get one.

Smaller polish

  • First sidebar group (Feed / Issues / Terminal) no longer shows an "Overview" header. Workspace and Manage groups keep theirs.
  • Dismiss All is removed from the Feed toolbar. The Cleared tray is the safety net; per-row dismiss covers the rare inbox-zero case.
  • All HTTP calls feed a new server-status tracker. Transport failures flip the offline flag instantly; HTTP application errors (4xx/5xx) don't — the server did respond, just with a specific error.

Installation

  • Download Push-1.5.3.dmg from releases.pushto.do.
  • Drag Push.app to /Applications, replacing the existing copy.
  • Open it.

Your local data in ~/.push/ is preserved across the update.

System requirements

  • macOS 15 Sequoia or newer
  • Apple Silicon Mac
  • ~200 MB disk space

v1.5.2

2026-04-18

Feed and Approvals are now one sidebar entry, the terminal tab right-click menu gains a Reload option, and secondary interactions on a tab chip are now scoped to the active tab on a frontmost app — consistent with the rest of macOS.

What's changed

Feed and Approvals, unified

Approvals was duplicating the feed — pending decisions were already surfaced there with inline Approve/Reject. 1.5.2 merges them into a single Feed sidebar entry with four tabs at the top:

  • All — everything.
  • Action — items that want your attention.
  • Update — status changes and notifications.
  • Approvals — the grouped pending/resolved board you know, with the tile layout and inline Approve/Reject decisions unchanged.

Search now filters across both domains. Dismiss All and swipe-to-dismiss remain scoped to feed items (they don't touch Approvals tiles). If you had Approvals set as your default sidebar section, Push silently routes that to the Feed view on next launch — no re-configuration needed.

On macOS 26 Tahoe the primary feed-row action and Approve tile button now use glassProminent with an accent tint, the approvals grid gets .soft scroll-edge treatment, picker counts render in monospaced digits, and the tile border is a notch firmer so tiles read clearly against the brighter material.

Reload a terminal tab in place

Right-click any terminal tab → Reload (also ⌘R when the tab is focused). The surface tears down and is recreated inside the same tab, keeping its UUID, position, title, and cwd. Claude sessions auto-resume via the marker file — same path as quit-and-relaunch, so the conversation picks up where it left off. Plain shells relaunch at the tab's last-known cwd. Browser-style reload semantics: the tab survives, the ephemeral process state does not (scrollback, foreground commands, shell variables).

Useful when a live theme switch leaves stale truecolor cells on screen (the TUI's own redraw is the only way to repaint them cleanly), when Claude gets into a wedged render state, or any time you want a fresh PTY without losing the tab slot.

Chip interactions behave consistently

Double-click rename and right-click context menu are now both gated on the same preconditions: the tab must be active and Push must be frontmost. Two concrete improvements:

  • Right-click on a terminal tab chip while Push is in the background no longer pops the context menu before the window has come forward. Matches Finder and Safari — a click on a background window brings it forward first, then you interact.
  • Right-click on an inactive tab no longer opens the menu at all. You select the tab first (single click), then act on the selection. Same "select, then edit" convention already used for double-click-to-rename.

Single-tap select continues to work everywhere, so tab switching is unaffected.

Installation

  • Download Push-1.5.2.dmg from releases.pushto.do.
  • Drag Push.app to /Applications, replacing the existing copy.
  • Open it.

Your local data in ~/.push/ is preserved across the update.

System requirements

  • macOS 15 Sequoia or newer
  • Apple Silicon Mac
  • ~200 MB disk space

v1.5.1

2026-04-18

Hotfix for a serious 1.5.0 regression — Shift-modified keys (uppercase letters, @, #, $, %, etc.) didn't reach the terminal correctly. Plus two small terminal-workflow polishes that landed alongside.

What's changed

Shift keys work again in the terminal

1.5.0's Ctrl+C fix fed libghostty a keyboard field (unshifted_codepoint) on every keystroke. Ctrl+C got the fix it needed, but the same field silently perturbed Shift+<char> encoding — typing Shift+2 produced 2 instead of @, Shift+A produced a instead of A, and so on.

1.5.1 scopes the Ctrl+C fix to Ctrl+letter only. Non-Ctrl keystrokes (plain typing, Shift combinations, Cmd shortcuts, Option dead keys, arrow keys) now hit libghostty exactly the way they did in 1.4.0. Verified that Ctrl+C, Ctrl+D, Ctrl+L, Ctrl+A, Ctrl+E, Ctrl+W, Ctrl+U all still deliver their usual signals at bare zsh — the 1.5.0 headline fix is preserved.

If you're on 1.5.0, please upgrade immediately. The auto-update prompt should show within a few minutes; you can also force a check via the menu.

Reopen closed tab by name

⌘⇧T and single-clicking the Reopen closed button still pop the newest closed tab. Right-clicking that button now opens a context menu listing every tab on the closed-tab stack by name (newest first) — pick a specific session instead of popping one at a time hoping you'll land on the right one.

Folder-filter keeps your place

Two related polishes to the folder-chip filter in the tab strip:

  • Switching folders used to always snap to the first tab of the destination folder. Now it remembers the last tab you had active in each folder and returns you there.
  • Closing the active tab inside a filtered folder used to occasionally teleport focus outside the filter, leaving the chip and the visible pane disagreeing. Close now picks the closest in-filter neighbor so the chip stays honest.

Installation

  • Download Push-1.5.1.dmg from releases.pushto.do.
  • Drag Push.app to /Applications, replacing the existing copy.
  • Open it.

Your local data in ~/.push/ is preserved across the update.

System requirements

  • macOS 15 Sequoia or newer
  • Apple Silicon Mac
  • ~200 MB disk space

v1.5.0

2026-04-18

Smoother first-run onboarding with a built-in iOS pairing step, plus a quiet fix to Ctrl+C at the terminal prompt.

What's changed

QR code for the iOS companion app

Onboarding now shows a TestFlight QR code right after Apple Sign-In. Scan it from your iPhone's camera to install the Push iOS app — no need to have it pre-installed. The onboarding step stays visible with a live status line that ticks over to ✓ the moment your iPhone pairs with your Push account. Skip it if you don't want the companion app; the desktop works fine on its own.

Silent setup, no more "Run this in Terminal"

The old onboarding showed a dedicated Setup step that flashed progress bars for the local server, database, workspace, and relay. It was noisy and the user didn't have to do anything during it. 1.5.0 runs all of that silently in the background while you're on the Welcome and Sign-In steps — by the time you reach the end, it's done.

The CLI install step also switched to a user-local symlink into ~/.local/bin (no sudo prompt, mirroring Claude Code's installer). If ~/.local/bin isn't on your PATH, the step shows a one-line snippet you can copy-paste (or a Run it button that does it for you) rather than silently editing your shell rc file.

Ctrl+C at the shell now delivers SIGINT

For months, pressing Ctrl+C at a bare zsh prompt in a Push terminal tab would sometimes show the literal characters 5u on the command line instead of sending SIGINT. Invisible inside Claude Code (which has its own key parser), but annoying the moment you exited Claude and typed a shell command. Traced to a mismatch between how macOS pre-translates Ctrl+letter and what Push's terminal engine expects — Push was handing it the already-translated byte, not the letter, so the encoder's lookup table missed. Fix aligns with the reference implementation used by cmux and similar tools. Ctrl+C, Ctrl+D, Ctrl+L, Ctrl+A/E, Ctrl+W, Ctrl+U all now work reliably at the shell.

Installation

  • Download Push-1.5.0.dmg from releases.pushto.do.
  • Drag Push.app to /Applications, replacing the existing copy.
  • Open it.

Your local data in ~/.push/ is preserved across the update.

System requirements

  • macOS 15 Sequoia or newer
  • Apple Silicon Mac
  • ~200 MB disk space

v1.3.1

2026-04-18

Small polish on the Appearance picker shipped in 1.3.0.

What's changed

Appearance picker icons now render in the default text color

The moon / sun / auto glyphs at the top of the color picker were inheriting the parent tinted-glass button's hue — a dark-red moon on a red tint, an orange sun on an orange tint, and so on. 1.3.1 forces the primary text color on each Appearance row's icon so the glyphs stay readable regardless of which tint you've picked.

No behavior change. Picking Light / Dark / System still works exactly as 1.3.0 shipped.

Installation

  • Download Push-1.3.1.dmg from releases.pushto.do.
  • Drag Push.app to /Applications, replacing the existing copy.
  • Open it.

Your local data in ~/.push/ is preserved across the update.

System requirements

  • macOS 15 Sequoia or newer
  • Apple Silicon Mac
  • ~200 MB disk space

v1.3.0

2026-04-18

App-wide Light / Dark / System appearance override — the same per-app toggle pattern Apple ships in Xcode, BBEdit, and Mail, now inside Push. Pick one regardless of your global macOS appearance, and Push (plus the embedded terminal) follows.

What's changed

Light / Dark / System picker

The terminal toolbar's color picker now has a new Appearance section at the top with three choices:

  • System — follow your macOS Appearance setting (default)
  • Light — force light, regardless of system
  • Dark — force dark, regardless of system

Your choice persists across launches and is applied before the first window paints, so there's no flash of system-default appearance on launch. The override cascades to every SwiftUI view, AppKit panel, and the embedded Ghostty terminal — flipping Light or Dark immediately re-themes the terminal palette too.

Built on NSApp.appearance, the Apple-sanctioned hook for per-app overrides. Nothing about the system-wide Appearance setting is touched — only Push's own windows.

Cleaner picker menu

Fixed a stray-divider bug in the same menu where the Strength section showed two dividers between its header and the first row instead of one. Same fix applied to the new Appearance section so both read cleanly.

Installation

  • Download Push-1.3.0.dmg from releases.pushto.do.
  • Drag Push.app to /Applications, replacing the existing copy.
  • Open it.

Your local data in ~/.push/ is preserved across the update.

System requirements

  • macOS 15 Sequoia or newer
  • Apple Silicon Mac
  • ~200 MB disk space

v1.2.1

2026-04-18

Bug-fix release. 1.2.0 introduced a regression where Claude sessions didn't auto-resume after quitting and relaunching Push — that's fixed here. Also bundles the embedded terminal's themes inside Push itself, so live light/dark palette switching no longer depends on having Ghostty.app installed.

What's changed

Claude sessions resume correctly on restart

1.2.0 shipped a bug where quitting and relaunching Push left restored tabs alive but empty — Claude didn't come back. This was a side effect of the new theme work colliding with an internal shell-integration mechanism; the collision silently dropped the per-tab identifier our auto-resume needs to bind each tab to its Claude session. 1.2.1 routes that identifier through a different path and restores the original behavior. Quit-and-relaunch now picks up Claude sessions the same way the in-app Resume button always did.

The in-app Resume / "Reopen Closed" buttons were never affected — they take a different code path that doesn't go through the shell — so if you'd been using those as a workaround, nothing changes for you.

Terminal themes bundled inside Push

1.2.0's live light/dark terminal palette depended on Ghostty.app being installed on your Mac (we used its bundled themes as a bridge). 1.2.1 vendors the theme files (Builtin Tango Light / Builtin Tango Dark) inside Push itself, so the feature works standalone on a clean machine. No behavior change if you already had Ghostty.app.

Installation

  • Download Push-1.2.1.dmg from releases.pushto.do.
  • Drag Push.app to /Applications, replacing the existing copy.
  • Open it.

Your local data in ~/.push/ is preserved across the update.

System requirements

  • macOS 15 Sequoia or newer
  • Apple Silicon Mac
  • ~200 MB disk space

v1.2.0

2026-04-18

Terminal theme work — strength control for the section tint, live light/dark adaptation for the embedded Ghostty surface, and a text-contrast fix so tints don't wash out terminal output at higher intensities.

What's changed

Strength control for the terminal tint

The tint picker now has a Strength section under the color list, with ten discrete steps from 10% to 100%. Default is a conservative 10%, and your color and strength choices persist separately so switching hues preserves your preferred intensity. The Strength section gracefully disables itself when "Default" (no tint) is selected.

Text stays readable at any strength

Previously a high-strength tint washed out terminal output — white text at 50% tint turned cloudy because the overlay sat on top of everything. The overlay now uses .blendMode(.color) which preserves the underlying luminance while only swapping hue + saturation. White text stays white, black text stays black, and the mid-lightness materials (sidebar, detail pane, toolbar glass) pick up the tint properly. Same native SwiftUI primitive used by Apple's own tinted UI.

Polished pink + always-on color indicator

  • Pink was switched from systemPink (reads cherry/red through the blend mode) to a custom rose-pink (#E68BBE) that stays recognizably pink at all strength levels.
  • The toolbar picker button now shows the selected color at full saturation regardless of Strength — it's a selection indicator, not a strength preview. Strength still modulates the actual window wash.

Terminal adapts to macOS Light/Dark mode

The embedded Ghostty terminal now correctly swaps its palette when you flip System Settings → Appearance between Light and Dark. Previously in Light mode the terminal could render blank — text was white on a light NSVisualEffectView material. Fix required wiring up four pieces that Ghostty expects from embedded apps: the resources directory environment variable, a dual-theme config, a RELOAD_CONFIG action handler, and explicit surface refresh on config change.

This currently depends on Ghostty.app being installed on your Mac (we use its bundled themes as a bridge). A follow-up release will ship the themes inside Push itself so this works standalone.

Installation

  • Download Push-1.2.0.dmg from releases.pushto.do.
  • Drag Push.app to /Applications, replacing the existing copy.
  • Open it.

Your local data in ~/.push/ is preserved across the update.

System requirements

  • macOS 15 Sequoia or newer
  • Apple Silicon Mac
  • ~200 MB disk space

v1.1.40

2026-04-17

Terminal polish and a stack of dev-variant isolation fixes that make internal iteration safer without changing prod behavior.

What's changed

Per-section tint in the terminal

Each terminal section can now carry its own tint color, picked from a glass-prominent toolbar picker that matches macOS 26 Tahoe's material language. The picker tints the capsule itself rather than a child icon, so the selected color stays readable regardless of button state.

Terminal tabs

  • Tab titles now reset on Claude Code /clear. The haiku-title OSC that Claude Code emits on startup was sticking around forever after a /clear, so every subsequent session kept the stale title. Push now watches the claude-running/<tab> session_id flip and drops prefix-stripped matches on reset.
  • Tab dividers no longer hide on hover. They stayed visible in all states for consistency.

Dev-variant isolation sealed end-to-end

Push ships as two coexisting bundles (ai.massless.push and ai.massless.push.dev) so the running prod instance hosting the agents is never killed during iteration. Three remaining leaks from dev into prod's data tree are closed:

  • The CLI's run-sidecar, launchd service, and onboarding-wizard agent scaffolder all respected $PUSH_HOME in some paths but still hardcoded ~/.push/ in others.
  • The onboarding wizard's SetupRunner shelled out to the global /usr/local/bin/pu symlink, which may point at a different variant's frozen bundle. It now prefers the bundle-local arch-specific CLI so setup stays in lockstep with the running Swift binary.
  • AppMover's "move to /Applications" prompt is gated behind skipsFirstLaunchOffers, so dev launches no longer nag to relocate.

None of this is visible from prod — the release pipeline is unaffected and your ~/.push/ tree is preserved across the update.

Installation

  • Download Push-1.1.40.dmg from releases.pushto.do.
  • Drag Push.app to /Applications, replacing the existing copy.
  • Open it.

Your local data in ~/.push/ is preserved across the update.

System requirements

  • macOS 15 Sequoia or newer
  • Apple Silicon Mac
  • ~200 MB disk space

v1.1.39

2026-04-17

The file-preview crash is actually fixed this time. v1.1.38 shipped with an assumption that didn't hold in CI. This release vendors the syntax-highlighting dependency and bypasses the SwiftPM generator that was causing the crash.

What's changed

Syntax-highlighting engine vendored + loaded via our own bundle lookup

HighlightSwift is now vendored at desktop/Vendored/HighlightSwift/ — a local fork of v1.1.0 that drops the upstream resources: [.process(...)] declaration and replaces SwiftPM's auto-generated resource accessor with a 5-line BundleLocator.swift that looks up highlight.min.js under Bundle.main.resourceURL. mac-assemble-app.sh copies the JS file directly into Contents/Resources/.

The prior approach (patching the auto-generated accessor in the assemble script, then running swift build a second time) worked on a developer's Mac but didn't survive CI — SwiftPM's "Write sources" step regenerates the accessor on every rebuild, overwriting the patch. The vendored fork side-steps the generator entirely, so the bug can't regress no matter how SwiftPM behaves.

Side-effects:

  • Release pipeline is simpler: one swift build, one cp highlight.min.js, no sed-patching.
  • A build-time guard in mac-assemble-app.sh hard-fails if a new unvendored SwiftPM dependency ever ships resources — catches the same class of bug for future deps before they reach users.

Installation

  • Download Push-1.1.39.dmg from releases.pushto.do.
  • Drag Push.app to /Applications, replacing the existing copy.
  • Open it.

Your local data in ~/.push/ is preserved across the update.

System requirements

  • macOS 15 Sequoia or newer
  • Apple Silicon Mac
  • ~200 MB disk space

v1.1.38

2026-04-17

Double-clicking a code file no longer crashes the app. Every version since v1.0.0 would fatalError when you opened a .ts, .js, .py, .swift, or any other code file from the Files gallery — or previewed a Markdown file with fenced code blocks. The syntax-highlighting engine was missing from the shipped .app; the fix ships it.

What's changed

File previews render without crashing

The file-preview pipeline was also rebuilt around a "fail closed to the next tier, never to a crash" design:

  • Tiered rendering: syntax-highlight → plaintext → fallback card. Every stage wrapped in its own do/catch so failure drops one tier instead of propagating.
  • Binary / long-line / oversize guards pre-filter before the highlighter sees the file, protecting against minified bundles, embedded base64, and pathological regex backtracking.
  • 2 s timeout on the highlighter — catastrophic-backtracking grammars can no longer hang the preview.
  • NSTextView-backed content view replaces SwiftUI Text for large files and long lines. TextKit handles arbitrary content sizes gracefully; CoreText layout crashes are off the table.
  • Per-call highlighter instance — the shared JavaScriptCore context is gone, so two concurrent preview loads can no longer corrupt each other's state.
  • Breadcrumb logging under subsystem: ai.massless.push, category: preview — future crashes leave a trail. Tail with:
    log stream --predicate 'subsystem == "ai.massless.push" AND category == "preview"'
    

Release pipeline hardened against this class of bug

The root cause was that SwiftPM's generated resource-bundle accessor looks for HighlightSwift_HighlightSwift.bundle at the top of the .app (alongside Contents/), but mac-assemble-app.sh never copied it there. Dev binaries happened to work because SwiftPM also bakes a compile-time absolute path as a fallback — and on the dev's own Mac that path resolves. CI-built binaries bake /Users/runner/work/... which doesn't exist on any user's Mac → fatalError on every first code-file preview.

This is the third instance of the same class of bug (after Sparkle v1.0.0, ghostty-shell-integration v1.1.16). The assembly script now carries an explicit copy step AND a post-assemble invariant that hard-fails the build if the bundle is missing. Release pipeline memory updated so SwiftPM resource bundles are treated as first-class shippable assets.

Installation

  • Download Push-1.1.38.dmg from releases.pushto.do.
  • Drag Push.app to /Applications, replacing the existing copy.
  • Open it.

Your local data in ~/.push/ is preserved across the update.

System requirements

  • macOS 15 Sequoia or newer
  • Apple Silicon Mac
  • ~200 MB disk space

v1.1.37

2026-04-17

Terminal tab switching is instant again.

What's changed

Terminal: zero-latency tab switch

Clicking a tab activated it after a ~500 ms pause. The tab chip had both a single-tap (select) and a double-tap (rename) gesture recognizer attached, so AppKit had to wait the system double-click interval on every single click to rule out a double-tap. Modifier declaration order doesn't decouple that wait — the only fix is to never let the two recognizers coexist on the same view.

Inactive chips now carry only the single-tap recognizer; active chips carry only the double-tap recognizer. Selection is instant again. Double-click an already-active tab to rename it (matches Finder / Xcode "select, then edit" convention). Right-click → Rename… still works on any tab.

No hardcoded intervals — SwiftUI reads your system double-click speed from System Settings → Accessibility → Pointer Control at click time.

Installation

  • Download Push-1.1.37.dmg from releases.pushto.do.
  • Drag Push.app to /Applications, replacing the existing copy.
  • Open it.

Your local data in ~/.push/ is preserved across the update.

System requirements

  • macOS 15 Sequoia or newer
  • Apple Silicon Mac
  • ~200 MB disk space

v1.1.36

2026-04-17

Drag or paste N images into a Claude Code session and all N render as attachments. Every drop lands in the tab you're looking at.

What's changed

Terminal: multi-image drop and paste now attaches every image

Dragging or ⌘V-ing two or more images into a running Claude Code session now renders each one as a [Pasted Image] attachment. Previously only the last image survived. Works for Finder multi-select, screenshot thumbnails, and path strings copied from other terminals. Paths under /TemporaryItems/ (macOS's screenshot-thumbnail temp dir) are now stabilized into our own temp dir before insertion so the floating-thumbnail reap race can no longer ghost an attachment.

Terminal: drops always land in the visible tab

Drops used to sometimes disappear — AppKit was hit-testing the drop onto a hidden tab's surface, silently filling a PTY you couldn't see. Drops now route to whichever pane is active, matching how ⌘V has always worked.

Approvals: sectioned gallery with inline decisions

The approvals queue now groups by section and lets you approve or deny inline without leaving the list.

Fixed

  • Double-clicking a skill tile no longer crashes the app.
  • Double-clicking a file tile no longer crashes the app; the gallery and preview layout got a round of polish.

Installation

  • Download Push-1.1.36.dmg from releases.pushto.do.
  • Drag Push.app to /Applications, replacing the existing copy.
  • Open it.

Your local data in ~/.push/ is preserved across the update.

System requirements

  • macOS 15 Sequoia or newer
  • Apple Silicon Mac
  • ~200 MB disk space

v1.1.35

2026-04-17

The "Ready to install and relaunch" alert now tells you your Claude Code sessions are safe — right where the anxiety lives.

What's changed

Session-safety reassurance on the native relaunch alert

When an update is downloaded and Sparkle asks whether to install and relaunch, the alert body now includes:

"Your active Claude Code sessions will resume automatically after the relaunch — no work lost."

Sparkle doesn't expose the alert's body text through any delegate hook, so the copy is patched into Sparkle.framework/Resources/Base.lproj/Sparkle.strings at assembly time — English-only, drift-guarded (the build hard-fails if a future Sparkle upgrade renames the key rather than silently dropping the copy).

Same reassurance already shows in the earlier "Update Available" dialog's "What's New" pane via the release-notes banner; this release extends it to the second, higher-stakes dialog.

Installation

  • Download Push-1.1.35.dmg from releases.pushto.do.
  • Drag Push.app to /Applications, replacing the existing copy.
  • Open it.

Your local data in ~/.push/ is preserved across the update.

System requirements

  • macOS 15 Sequoia or newer
  • Apple Silicon Mac
  • ~200 MB disk space

v1.1.34

2026-04-16

First release since the auto-update flow was fixed end-to-end — this is the one where you'll actually see the native "Update Available" dialog.

What's changed

Nothing functionally — this is a staging release to observe the corrected auto-update UX now that 1.1.33 is the baseline.

If you're running 1.1.33, within ~30 minutes of that version starting up your next scheduled update check will find this 1.1.34 entry, and Sparkle will surface its native "Update Available" dialog — without stealing focus from your active terminal — showing the reassurance banner and release notes in the "What's New" pane next to the Install button. Tick the checkbox if you want silent installs in the future; that preference now sticks across launches.

Installation

  • Download Push-1.1.34.dmg from releases.pushto.do.
  • Drag Push.app to /Applications, replacing the existing copy.
  • Open it.

Your local data in ~/.push/ is preserved across the update.

System requirements

  • macOS 15 Sequoia or newer
  • Apple Silicon Mac
  • ~200 MB disk space

v1.1.33

2026-04-16

Fix to the 1.1.32 fix: the auto-update preference migration now runs exactly once instead of every launch, so ticking "Automatically install updates" actually sticks.

What's changed

Silent-install preference is respected again

1.1.32 cleared SUAutomaticallyUpdate=true from persisted preferences on every launch, which meant: if you saw the native "Update Available" dialog, ticked "Automatically download and install updates in the future" to opt into silent-install mode, and restarted Push — the next launch undid your choice.

Sparkle's own docs warn against re-writing this value on every launch (SPUUpdater.h:220). 1.1.33 gates the reset behind a one-shot marker key: the one-time correction for users who were stuck in silent mode from a past release still happens, but from then on your preference is respected.

If you want silent installs, tick the checkbox once on any future update dialog — it'll stay on across launches.

Info.plist cleanup

Removed SUAutomaticallyDownloadsUpdates from Info.plist. It was never a real Sparkle key — the canonical setting is SUAutomaticallyUpdate, which we already had. No behavior change, just removing dead configuration that made the intent unclear to future readers.

Installation

  • Download Push-1.1.33.dmg from releases.pushto.do.
  • Drag Push.app to /Applications, replacing the existing copy.
  • Open it.

Your local data in ~/.push/ is preserved across the update.

System requirements

  • macOS 15 Sequoia or newer
  • Apple Silicon Mac
  • ~200 MB disk space

v1.1.32

2026-04-16

Auto-update relaunch dialog actually appears now — previously Push was silently installing on quit without any prompt.

What's changed

Auto-update now asks before relaunching

If you installed Push before 1.1.27, there's a good chance you ticked "Install updates automatically in the background" the very first time Sparkle asked — or it was on by default. That persisted preference was overriding every later setting and causing Push to silently download and install updates on quit with no dialog whatsoever. Every release from 1.1.27 through 1.1.31 went onto your machine without you ever seeing a prompt.

On first launch of 1.1.32, Push flips that stale preference off so the intended flow takes effect: silent background download, then the native "A new version is ready to install" dialog surfaces — with the "your Claude Code session will be saved and resumed" reassurance banner attached to every release's notes, right next to the Install and Relaunch button.

You can still re-enable silent installs anytime via the checkbox in Sparkle's own update dialog if you prefer the quieter path.

Appcast no longer double-encodes release notes

The release-notes banner on 1.1.28 through 1.1.30 was rendering as literal text (> **Your existing...**) instead of a markdown blockquote because each release was re-serializing past items and adding a layer of HTML escaping on top of what was already there. Fixed by switching the appcast writer to string-splice — existing items stay byte-for-byte identical, new items ship clean. 1.1.32 and every release after will render the banner correctly.

Installation

  • Download Push-1.1.32.dmg from releases.pushto.do.
  • Drag Push.app to /Applications, replacing the existing copy.
  • Open it.

Your local data in ~/.push/ is preserved across the update.

System requirements

  • macOS 14 Sonoma or newer
  • Apple Silicon Mac
  • ~200 MB disk space

v1.1.31

2026-04-16

Smaller, sharper terminal: live slide-reorder for tabs, the active tab now uses your macOS accent color, and the Claude session-resume race after quit-and-relaunch is fixed.

What's changed

Live slide-reorder for tab drag

Drag a tab and the surrounding tabs slide out of the way in real time under the cursor — no more "drop blindly and the list jumps." Same convention as Safari and Apple Terminal. The dragged tab's chip fades to 30% so the floating drag image reads as the moving tab. Drop, escape, or drag-outside-the-app all clean up correctly.

Active tab uses the system accent color

Selected tab chip now fills with your macOS accent color at a soft 15% opacity instead of a neutral grey. Reads as a clearly "selected" pill against the strip's background and follows your Settings → Appearance → Accent color automatically. Inactive chips remain transparent.

Claude session resume after quit + relaunch is reliable now

Two corner cases were intermittently dropping you into a fresh empty Claude session after a Push quit/relaunch (or Sparkle auto-update relaunch) instead of resuming the conversation you were in:

  • You'd cd into a different folder mid-session. The auto-resume couldn't find the JSONL transcript at the new cwd's project slug and silently started fresh. Fixed by recording the session's home folder in the marker file and resolving the resume against that folder.
  • Marker file got half-written if Claude was killed mid-write. Now written atomically (temp + rename), so the marker is always either complete or absent — never partial.

The "fresh empty Claude session with the right title" symptom should be gone.

New tabs open at the leading edge

Open a new terminal tab and it appears at the start of the strip instead of the end. Most recent work sits where you're already looking — no chasing the rightmost tab.

Toolbar polish

The "Open new terminal in a folder" button in the toolbar now uses Apple's folder.badge.plus icon — reads more clearly as "add a folder context" vs the leading + in the tab strip (which adds a tab in the current folder).

Installation

  • Download Push-1.1.31.dmg from releases.pushto.do.
  • Drag Push.app to /Applications, replacing the existing copy.
  • Open it.

Your local data in ~/.push/ is preserved across the update.

System requirements

  • macOS 14 Sonoma or newer
  • Apple Silicon Mac
  • ~200 MB disk space

v1.1.30

2026-04-16

Terminal tabs now feel like a full Mac-native app — drag to reorder, open any folder with one click, and typing just works the moment you switch tabs.

What's changed

Drag tabs to reorder

Grab any tab in the strip and drag it left or right to reorder — same gesture as Safari, Chrome, or Terminal.app. The new order persists across quit and relaunch.

Open a new terminal in any folder

A new button sits in the toolbar next to the folder chips. Click it, pick any folder from the native macOS file picker, and Push opens a new terminal tab there. The strip auto-filters to that folder so the new tab is grouped with any siblings.

The leading + in the tab strip still opens a new tab in the current folder — the toolbar's is specifically for jumping into a different folder.

Typing works immediately after tab switch

Switching between tabs used to require one extra click inside the terminal before keystrokes would land. Fixed — the moment you switch tabs, the new tab's cursor is live and ready to take input.

Cleaner tab strip

  • Safari-style hairline dividers between tabs, so the strip reads at a glance even with a lot of tabs open.
  • Folder-filter chips now show a small count of how many tabs are open in each folder, plus a total next to "All".
  • "Reopen closed" moved from the toolbar into the right edge of the tab strip, where it's visually scoped to the tabs themselves.

Installation

  • Download Push-1.1.30.dmg from releases.pushto.do.
  • Drag Push.app to /Applications, replacing the existing copy.
  • Open it.

Your local data in ~/.push/ is preserved across the update.

System requirements

  • macOS 14 Sonoma or newer
  • Apple Silicon Mac
  • ~200 MB disk space

v1.1.29

2026-04-16

The terminal now renders in SF Mono — the same typeface as Apple Terminal — out of the box.

What's changed

Native Mac typeface in the terminal

Push's embedded terminal was falling back to a generic monospace font because SF Mono isn't a public macOS font — Apple Terminal uses a private variant that third-party apps can't see.

On first launch, Push now silently installs Apple's official SF Mono into ~/Library/Fonts/ and sets it as the default terminal typeface. The first time you open a terminal, it looks like Apple Terminal. No setup, no prompts, no config file to edit.

If you're offline on first launch the install just skips and Push uses a fallback font — no error, nothing breaks — and retries on the next launch.

Installation

  • Download Push-1.1.29.dmg from releases.pushto.do.
  • Drag Push.app to /Applications, replacing the existing copy.
  • Open it.

Your local data in ~/.push/ is preserved across the update.

System requirements

  • macOS 14 Sonoma or newer
  • Apple Silicon Mac
  • ~200 MB disk space

v1.1.28

2026-04-16

⌘⇧T is now rock-solid — it brings back the tab you just closed, not a random Claude session from three days ago.

What's changed

Cleaner "Reopen Closed Tab" behavior

Reopening a closed tab (⌘⇧T or the toolbar's arrow button) was occasionally dropping users into a Claude session they didn't recognize. Root cause: the closed-tab history survived app restarts, and old entries could carry stale session pointers that no longer matched what the user thought they were undoing.

Now the undo history is scoped to the current launch — same as Cmd+Z in every other macOS app. Close a tab by accident, ⌘⇧T brings it back. Quit Push, and the stack is a clean slate on next launch.

What's unchanged: your live tabs still persist across quit+relaunch with their Claude sessions auto-resumed — that's the workspace-restoration promise and it stays exactly as it was.

Background cleanup of stale session pointers

On launch, Push now sweeps out leftover Claude session marker files from tabs that no longer exist (force-quit leftovers, historical iterations, etc.). Prevents those silent zombies from haunting any future session. Live tabs' markers are always preserved.

Installation

  • Download Push-1.1.28.dmg from releases.pushto.do.
  • Drag Push.app to /Applications, replacing the existing copy.
  • Open it.

Your local data in ~/.push/ is preserved across the update.

System requirements

  • macOS 14 Sonoma or newer
  • Apple Silicon Mac
  • ~200 MB disk space

v1.1.27

2026-04-15

Auto-updates are quieter now, and the install prompt tells you your Claude session will be safe across the relaunch.

What's changed

Gentle background auto-updates

Background update checks now silently download the new version and surface Sparkle's native "Ready to install" alert without stealing focus from whatever terminal you're in. The loud, foreground download window that used to flash up every 30 minutes is gone.

Manual Check for Updates… (app menu) is unchanged — when you ask for it, Sparkle still speaks up loudly.

Peace-of-mind copy on the install prompt

Every release's notes now start with a banner that shows up right next to the Install and Relaunch button:

Your existing Claude Code session will be saved and resumed automatically after the relaunch — no work lost.

Same session-resume mechanism that 1.1.25 and 1.1.26 shipped — this just makes it visible at the moment the user is about to click Install.

Installation

  • Download Push-1.1.27.dmg from releases.pushto.do.
  • Drag Push.app to /Applications, replacing the existing copy.
  • Open it.

Your local data in ~/.push/ is preserved across the update.

System requirements

  • macOS 14 Sonoma or newer
  • Apple Silicon Mac
  • ~200 MB disk space

v1.1.26

2026-04-15

Undo-close now replays the same auto-resume path as quit+relaunch.

What's changed

Reopen-closed-tab reuses the proven resume path

⌘⇧T (reopen last closed tab) used to run a parallel "marker ceremony" to bring back the tab's Claude session. Now it just unhides the tab — the bundled .zshenv sees the still-present session marker and fires claude --resume <sid>, exactly like on quit+relaunch. One code path for "restore tab + auto-resume", not two.

Also folded in:

  • Fixed a PUSH_CLAUDE_AUTO_RESUMED guard leak that could block a legitimate resume when Push-Dev was launched from an already-auto-resumed shell.

Installation

  • Download Push-1.1.26.dmg from releases.pushto.do.
  • Drag Push.app to /Applications, replacing the existing copy.
  • Open it.

Your local data in ~/.push/ is preserved across the update.

System requirements

  • macOS 14 Sonoma or newer
  • Apple Silicon Mac
  • ~200 MB disk space

v1.1.25

2026-04-15

Claude tells you when it's done, and pasting an image from the clipboard works.

What's new

Tab badges for Claude "done" and "needs attention"

Every time Claude Code wants your attention — a turn completes, a permission prompt pops up, it's waiting on input — the tab the request came from lights up with an accent-dot badge, the Terminal row in the sidebar shows the aggregate count across tabs, and the Push Dock icon mirrors it when the app isn't frontmost. Switching to the tab clears all three.

Wired via Claude Code's own Notification / Stop / SubagentStop hooks — same pattern cmux uses, no polling, no ANSI sniffing.

Paste an image from the clipboard

Copy an image in Finder → ⌘V in Push → full path lands at the cursor and Claude renders the attachment inline. Same for screenshot-to-clipboard (⌘⇧5 → "Save to Clipboard") or copies from Preview — those get saved to a temp file and the path gets pasted. Previously, Finder-copied images dropped to the bare filename (no directory), and raw clipboard image data didn't paste at all.

Plain text paste is unchanged.

Installation

  • Download Push-1.1.25.dmg from releases.pushto.do.
  • Drag Push.app to /Applications, replacing the existing copy.
  • Open it.

Your local data in ~/.push/ is preserved across the update.

System requirements

  • macOS 14 Sonoma or newer
  • Apple Silicon Mac
  • ~200 MB disk space

v1.1.24

2026-04-15

Hotfix: typing in plain shell tabs no longer eats the last letter.

What changed

Fixed: terminal typing regression

In v1.1.22 / 1.1.23, typing into a plain zsh tab (not inside Claude Code) caused every keystroke to appear as "selected" and the cursor would stay put — the last letter typed kept being replaced by the next. The tier-2 keyboard refactor had introduced macOS input-context routing for IME support, which leaked marked-text state onto normal typing in plain shells. Claude Code's TUI swallowed the stray escapes silently, which is why it only showed up in bare prompts.

Typing is back to the pre-v1.1.22 behavior (proven stable across 20+ releases). As a known trade-off, dead-key composition (Option+e → é) and IME preedit are disabled for now; they'll come back in a follow-up with a narrower gate.

Installation

  • Download Push-1.1.24.dmg from releases.pushto.do.
  • Drag Push.app to /Applications, replacing the existing copy.
  • Open it.

Your local data in ~/.push/ is preserved across the update.

System requirements

  • macOS 14 Sonoma or newer
  • Apple Silicon Mac
  • ~200 MB disk space

v1.1.23

2026-04-15

Tabs now tell you when they want attention, paste lands where you're looking, and updates check every 30 minutes.

What's new

Tab notification badges

Every terminal tab now surfaces an unread counter when the shell or an embedded program signals attention. Claude Code's "task done" beep, any OSC 9 desktop notification, and nonzero shell-command exits all bump the badge on the tab that produced them — but only if you weren't looking at that tab when it happened.

The badge clears the moment you switch to the tab. A sum across all tabs is also mirrored onto the Dock icon, so when Push isn't frontmost you see a number on the app's Dock icon until you come back.

Paste lands in the active tab (fix)

Previously ⌘V always pasted into the first-created tab regardless of which tab was visible. AppKit's performKeyEquivalent walks the view hierarchy depth-first and the first pane consumed the shortcut. Fixed — only the active pane claims ⌘V / ⌘C / ⌘K now. Menu → Paste, right-click Paste, and drag-drop were already correct; this was exclusively a shortcut-routing bug.

Auto-update check every 30 minutes

The Sparkle check interval dropped from 24 hours to 30 minutes, so new releases reach you much faster. (If you're running an older version and reading this from the release notes page: you'll get the upgrade prompt the next time the check fires.)

Installation

  • Download Push-1.1.23.dmg from releases.pushto.do.
  • Drag Push.app to /Applications, replacing the existing copy.
  • Open it.

Your local data in ~/.push/ is preserved across the update.

System requirements

  • macOS 14 Sonoma or newer
  • Apple Silicon Mac
  • ~200 MB disk space

v1.1.22

2026-04-15

Terminal feels instant. Click Terminal — every saved tab is already warm and resumed in the background. Open a new Claude session — no more 15-second wait.

What's new

Pre-warmed terminal sessions

Previously, the first click on the Terminal sidebar spun up every saved tab simultaneously — visible lag with 5+ tabs as PTYs, shells, and Claude resumes all started at once.

Now Push pre-creates each tab's surface in the background at app launch, staggered ~400 ms apart. By the time you click Terminal, every pane already has a live PTY, a running shell, and a resumed Claude session. The tab strip shows up; clicking a tab is instant.

Instant new Claude sessions

The claude command in a fresh Push tab used to take ~15 seconds before you could type — Push was running claude -p "" behind the scenes just to materialize a session file. That's gone.

Now we pass --session-id <push-tab-id> to Claude, which writes the session file naturally on your first real input. Net effect: same instant feel as a plain claude invocation, with our tab UUID still pinned for auto-resume across app quits.

Cmd+click URLs, right-click menus, drag-and-drop files

Catching up to what every other terminal does:

  • ⌘-hover any URL in terminal output to underline it; ⌘-click opens it in your default browser.
  • Right-click any tab for Copy / Paste / Clear.
  • Drag a file from Finder into a terminal — the path lands at your cursor, shell-escaped (so paths with spaces or apostrophes work without manual quoting).
  • First click on a non-frontmost Push window now lands in the terminal instead of being eaten by window activation.

International input

Dead keys, accent composition (Option+e → é), the emoji picker, and Japanese / Chinese / Korean IME composition all work in the Push terminal now via interpretKeyEvents + ghostty_surface_preedit wiring. ⌘V / ⌘C / ⌘K via performKeyEquivalent also fire reliably across tab switches and sidebar remounts.

Rename a terminal tab

Right-click any tab → Rename… opens a native macOS dialog. Type a name, hit return — that name now sticks even when the underlying shell tries to update its own title via OSC. Use Automatic Title in the same menu reverts to the live title. Renames persist across app restarts.

Toolbar polish

The terminal toolbar simplified to a single Resume last Claude button (the New-Claude and pu-work shortcuts were just shell commands you can already run inside any tab). The folder filter group is now a proper one-of-many selector with an All chip, padded to breathe inside the Tahoe glass capsule.

Installation

  • Download Push-1.1.22.dmg from releases.pushto.do.
  • Drag Push.app to /Applications, replacing the existing copy.
  • Open it.

Your local data in ~/.push/ is preserved across the update.

System requirements

  • macOS 14 Sonoma or newer
  • Apple Silicon Mac
  • ~200 MB disk space

v1.1.21

2026-04-15

Undo-close for terminal tabs (⌘⇧T) and a new folder-filter row in the toolbar that lets you scope the tab strip to one project at a time.

What's new

⌘⇧T reopens the last closed tab

Works exactly like Chrome / Safari: close a tab by mistake, press ⌘⇧T, it comes back. The reopened tab keeps its original id, title, and working directory — and if it was running a Claude Code session, the session auto-resumes in the same transcript.

The undo stack is capped at 10 and persists across app quits, so a tab you closed yesterday can still be rescued today.

Folder filter chips in the toolbar

When you have several tabs spread across multiple projects, the toolbar now surfaces a chip per distinct folder — e.g. All · push-macos · push-website · product. Click a chip to narrow the tab strip to just that folder; click All to show everything again. Chips are derived from live tabs, so they appear and disappear as you cd around.

Cleaner toolbar

The New Claude here and pu work buttons are gone — both duplicated flows you can already trigger from inside any shell tab. What's left is one native Resume last Claude button plus the folder filter group. Less chrome, more room.

Faster, cleaner app quit

Every PTY is torn down explicitly on ⌘Q, so child processes never outlive the Push process and can't hold the server port open past shutdown.

Installation

  • Download Push-1.1.21.dmg from releases.pushto.do.
  • Drag Push.app to /Applications, replacing the existing copy.
  • Open it.

Your local data in ~/.push/ is preserved across the update.

System requirements

  • macOS 14 Sonoma or newer
  • Apple Silicon Mac
  • ~200 MB disk space

v1.1.20

2026-04-15

Terminal tabs and Claude Code sessions now survive ⌘Q. Quit Push, reopen, and your Claude conversations are exactly where you left them — same transcripts, same history, no clicking required.

What's new

Tab persistence + Claude session auto-resume

Every Push terminal tab is saved to disk and restored on app launch. If a tab was running a Claude Code session when you quit, it resumes that exact session — same conversation, same context, same working directory. No clicks, no manual --resume <id> typing.

How it works (roughly):

  • Each Push tab gets a stable UUID injected as PUSH_TAB_ID into its shell.
  • Our bundled zsh integration wraps claude so it always uses that UUID as the session id. Tab ↔ session is 1:1, deterministic.
  • Markers track the live session per tab. ⌘Q leaves the marker behind; on next launch each tab reads its marker and runs claude --resume <sid> automatically.

The architecture preserves any claude() function you already have in your ~/.zshrc (e.g. one that adds --dangerously-skip-permissions) — your flags pass through unchanged.

/resume from inside Claude is also tracked

Type /resume <some-old-uuid> inside Claude's TUI to switch to a different conversation, then quit Push — when you reopen, the tab restores into the switched-to session, not the original. Implemented via Claude Code's SessionStart hook, which fires deterministically on every session-start event including the slash command.

Cleaner shell experience

The shell-integration debug output is now opt-in via PUSH_DEBUG=1. By default, your terminal opens with nothing but a clean prompt. All transitions still get logged to ~/.push/mac/shell-debug.log for postmortem if anything misbehaves.

Smaller cosmetic improvements

  • The claude -p "" bootstrap no longer leaves a "ready / Ready." exchange at the top of every session's transcript. Resumed sessions open as if you'd just typed claude for the first time.

Installation

  • Download Push-1.1.20.dmg from releases.pushto.do.
  • Drag Push.app to /Applications, replacing the existing copy.
  • Open it.

Your local data in ~/.push/ is preserved across the update.

System requirements

  • macOS 14 Sonoma or newer
  • Apple Silicon Mac
  • ~200 MB disk space

Known limitations

  • Auto-resume binds tabs to Claude sessions only when claude is launched through a Push tab's shell. If you run claude from a terminal outside Push (Terminal.app, iTerm2, etc.) and somehow expect Push to pick it up, that won't happen — Push only knows what its own tabs do.
  • claude --continue (Claude picks the most recent session globally) is passed through verbatim. Push doesn't bind that tab, so on restart you'd land in a fresh shell.
  • Bash users get a plain claude (no tab binding). The shell integration is zsh-only for now.

v1.1.19

2026-04-15

Two terminal fixes that should have shipped in 1.1.18: paste works everywhere, and Claude Code's voice input actually asks for microphone access now.

What's new

Paste + text injection

The Terminal section now implements the standard macOS text-input protocols it was missing:

  • Edit → Paste (menu bar) and ⌘V both reliably paste clipboard text. Previously only keybinding-path ⌘V worked, and the Edit menu was greyed out.
  • Wispr Flow and other text-injection tools (dictation, accessibility-based automation) can now deliver text into the terminal. They write through NSTextInputClient.insertText, which we didn't implement — text went nowhere.
  • Accessibility role is now .textArea, so AX-based tools see the terminal as a text target at all.

Claude Code voice input microphone access

Voice-input inside an embedded terminal (e.g. claude --voice) used to fail with "No audio detected from microphone" and macOS never showed a permission prompt.

Root cause: child processes spawned from Push inherit Push's sandbox. For macOS to prompt, the parent app (Push) needs NSMicrophoneUsageDescription in its Info.plist and com.apple.security.device.audio-input in its entitlements. Push had neither, so macOS silently denied with no UI.

1.1.19 adds both. On first use, macOS will prompt for mic access. The same plumbing covers any other TCC-gated capability (camera, etc.) we wire up later.

If you were silently denied before upgrading, macOS remembers that. Run this once in Terminal.app to reset:

tccutil reset Microphone ai.massless.push

Then relaunch Push. New installs don't need this.

Docs

  • New doc/TERMINAL.md — architecture reference for contributors touching the native terminal (surface manager, tab store, shell-integration injection, TCC inheritance, cmux parity gaps).

Installation

  • Download Push-1.1.19.dmg from releases.pushto.do.
  • Drag Push.app to /Applications, replacing the existing copy.
  • Open it.

Your local data in ~/.push/ is preserved across the update.

System requirements

  • macOS 14 Sonoma or newer
  • Apple Silicon Mac (1.1.16+ dropped Intel support)
  • ~200 MB disk space

CLI

No CLI changes. Latest @masslessai/push remains on npm at 0.4.2.

v1.1.18

2026-04-15

Push 1.1.18

v1.1.17

2026-04-15

First run now sets you up completely — no folder picker, no questions. And if you've been using Push since before 1.1.17, a new card in Settings lets you catch up in one click.

What's new

Onboarding: Push sets itself up

Previous versions got you to a working server and a workspace, but stopped short. You still had to figure out which folder your agent should watch, create a coder agent by hand, and invent a first task to test it on. Nobody knew what "pick a folder" meant in practice.

1.1.17 finishes the job. First run now:

  • Detects the project folder you've most recently been using Claude Code in (we read ~/.claude/projects/ and score by recency, git presence, and CLAUDE.md presence).
  • Creates a default agent named coder pointed at that folder, with heartbeat + wake-on-demand enabled and a $50/month budget.
  • Seeds a welcome issue assigned to coder so your first successful agent run is visible, not something you have to invent.
  • Wakes the agent on the welcome issue automatically — the first thing you see in the feed is Push doing work for you.

The step renders live: a checklist ticks off as each of the 10 phases completes. "Show terminal" reveals the raw log if you want to see what Push is doing under the hood.

Paused ≠ failed

If Push can't auto-detect a project folder (no Claude Code history, no ~/workspace), it now pauses cleanly with an amber "Try again" — instead of showing a scary red error. Same for any other step that's waiting on you or a missing prerequisite (~/.claude not installed, etc.). Failures are still red; prerequisites are amber.

Settings → "Coder agent" card

Users who upgraded from 1.1.16 or earlier never saw the new onboarding step because their hasCompletedOnboarding flag was already true. To close that gap, Settings → Integrations has a new Coder agent card with a "Run setup" button. Same script as onboarding, same checklist, in a modal sheet. Idempotent — if you already have a coder agent, it reuses it and skips the welcome seed.

pu setup on the CLI

The terminal equivalent works from any shell:

pu setup --json         # structured NDJSON events
pu setup --dry          # plan only, no mutations
pu setup --cwd <path>   # override folder detection
pu setup --force        # re-seed the welcome issue
pu setup --skip-wake    # skip the final agent wake

pu skill install is also new — standalone skill installer for users who prefer npm i -g @masslessai/push over Push.app.

Release pipeline fix

Fresh release bundles now include ghostty-shell-integration resources that were previously missing — the terminal's shell integration features (prompt markers, cwd reporting) work correctly on fresh installs.

Installation

  • Download Push-1.1.17.dmg from releases.pushto.do.
  • Drag Push.app to /Applications, replacing the existing copy.
  • Open it.

Your local data in ~/.push/ is preserved across the update. Upgraders: open Settings → Coder agent → Run setup to provision the default agent if you don't have one.

System requirements

  • macOS 14 Sonoma or newer
  • Apple Silicon only (M1 or later)
  • ~300 MB disk space
  • Claude Code installed (used by the default coder agent)

v1.1.16

2026-04-15

The DMG is 79% smaller. Same app, same features — we just stopped shipping three things that shouldn't have been in the bundle.

What's new

Apple Silicon only, DMG 513 MB → ~130 MB

Every Push release from 1.0.0 through 1.1.15 shipped a universal binary with ~1.1 GB of dead weight. 1.1.16 cuts three compounding sources of bloat that we discovered while auditing the release pipeline:

  • Apple Silicon only. Every Mac sold since 2020 is arm64. Intel slices (the x86_64 Swift binary, the x86_64 Node runtime, and @embedded-postgres/darwin-x64) added ~440 MB installed with no user benefit — the Intel install base is in single digits and shrinking. Info.plist and the build pipeline now produce an arm64-only binary; Intel Macs will get a standard system "can't be opened on this Mac" dialog instead of a cryptic module-not-found crash at startup.
  • GhosttyKit.xcframework removed from the bundle. libghostty is linked statically into the Push binary at compile time (we confirmed with otool -L and nm — the _ghostty_* symbols live inside the Push executable). The 541 MB xcframework that was also being copied into Contents/Frameworks/ was pure dead weight at runtime, never loaded. We just stopped copying it.
  • Postgres dylib de-duplication. Our build was using rsync --copy-links, which dereferenced every symlink inside @embedded-postgres/darwin-arm64 — including intra-package version aliases like libicudata.dylib → libicudata.77.dylib → libicudata.77.1.dylib. That single 61 MB file was being shipped three times. Switching to --copy-unsafe-links preserves the real symlinks and saves ~170 MB installed without changing what actually loads at runtime.

Concrete numbers, measured end-to-end:

1.1.151.1.16
Installed1.4 GB294 MB
DMG download~513 MB~130 MB

Nothing else changes. Every feature — the native terminal from 1.1.15, embedded Postgres, the server, the UI — still works identically. We've just stopped asking users to download four extra copies of everything.

For Intel Mac users

1.1.15 was the last universal build. 1.1.16 will not install on Intel Macs. If you need Intel support, stay on 1.1.15 — your local data in ~/.push/ will continue to work with any future Intel-compatible release we choose to ship.

Installation

  • Download Push-1.1.16.dmg from releases.pushto.do.
  • Drag Push.app to /Applications, replacing the existing copy.
  • Open it.

Your local data in ~/.push/ is preserved across the update.

System requirements

  • macOS 14 Sonoma or newer
  • Apple Silicon only (M1 or later) — Intel Macs not supported starting with 1.1.16
  • ~300 MB disk space (was ~1.5 GB)

v1.1.15

2026-04-15

The Terminal section is now a real, native Mac terminal — multi-tab, GPU-rendered via libghostty, with cwd-inheritance on new tabs and a translucent macOS material background that adapts to dark/light mode.

What's new

Native multi-tab terminal

  • The Terminal sidebar entry now hosts as many concurrent shells as you want, each its own libghostty surface (the same engine that powers Ghostty.app).
  • Tabs persist across sidebar navigation. Switching to Issues / Agents and back keeps every PTY running — no more lost top, tail -f, or long-running shells.
  • + button at the leading edge (easier to hit than the trailing edge once you have many tabs).
  • Keyboard: ⌘T new tab, ⌘W close, ⌘⇧] / ⌘⇧[ cycle.
  • Tab titles update live from the terminal's own OSC 0/2 stream — Claude Code session renames flow straight onto the tab label.

Smart cwd inheritance on new tabs

  • Click + while you're in ~/code/foo and the new tab opens in ~/code/foo, not $HOME — same behavior as Terminal.app, iTerm2, and Ghostty.app.
  • Implemented by bundling Ghostty's shell-integration scripts into Push.app and injecting ZDOTDIR at surface creation, so spawned shells emit OSC 7 (cwd reports) regardless of your personal zsh setup.

Native macOS look

  • Terminal background is now an NSVisualEffectView with the system content material — adapts to dark/light mode and picks up window translucency, instead of the old hard-coded black slab.
  • Ghostty cells render with background-opacity = 0 so the system material shows through.
  • Terminal sidebar entry moved into the Overview group (after Feed) for faster reach.

Bug fixes

  • Arrow keys (and page-up/down, F-keys, Home/End, all macOS function-key codepoints) now produce the correct CSI escape sequences instead of garbled CJK glyphs. Caused by macOS's private-use-area function-key encoding being forwarded as raw text into the PTY.

Installation

  • Download Push-1.1.15.dmg from releases.pushto.do.
  • Drag Push.app to /Applications, replacing the existing copy.
  • Open it.

Your local data in ~/.push/ is preserved across the update.

System requirements

  • macOS 14 Sonoma or newer
  • Apple Silicon or Intel Mac (universal binary)
  • ~200 MB disk space

CLI

No CLI changes in this release. Latest @masslessai/push remains on npm at 0.4.2.

v1.1.14

2026-04-14

Session tracking rebuilt. Opening Claude Code from anywhere — plain claude, pu work, pu session — now reliably shows up as a session in the Mac app, tied to whatever issue you're on.

What's new

Claude Code session tracking actually works now

Previous versions documented a /pu <issue> lifecycle that depended on an env var Claude Code doesn't set (CLAUDE_SESSION_ID) and hook registrations that get overridden by terminal wrappers like cmux. In practice, interactive sessions were invisible to Push. The 1.1.14 rebuild moves the contract onto the pu CLI itself, which nothing wraps or replaces:

  • New commands: pu session begin --issue <n>, pu session end, pu session current.
  • The CLI auto-injects X-Push-Run-Id on every mutation from a per-session sidecar. You don't manage headers.
  • pu session (the launcher) auto-creates a freestanding run before spawning Claude Code, so every launch is visible in Push from t=0.
  • The /pu skill has been simplified from a 50-line six-step procedure to a two-command contract.
  • Server-side sweeper: interactive runs idle for over an hour are now finalized as detached (was: failed at four hours), with the issue's active-run lock released automatically.

Full design details in doc/SESSION-TRACKING.md.

Graceful UX when Push.app isn't running

  • pu CLI now detects connection failures and prints a clear "Push server isn't running — Start it with: pu server start" instead of a raw TypeError: fetch failed.
  • pu server start on macOS launches Push.app via open -a Push and polls /api/health until ready, instead of spawning a competing headless Node server. One server, one lifecycle.
  • /pu slash command auto-recovers once per session: if Push isn't running, it starts the app, tells you in one line, and retries your command.
  • Push.app now gives the Node server a stdio lifeline so that an app hard-crash can't orphan the server — the Node process sees EOF on stdin and exits cleanly.

UI

  • Inspector replaced with a dedicated sidebar + push-navigation pattern.

Installation

  • Download Push-1.1.14.dmg from releases.pushto.do.
  • Drag Push.app to /Applications, replacing the existing copy.
  • Open it.

Your local data in ~/.push/ is preserved across the update.

System requirements

  • macOS 14 Sonoma or newer
  • Apple Silicon or Intel Mac (universal binary)
  • ~200 MB disk space

CLI

The @masslessai/push CLI ships separately on npm at 0.4.2. Update with npm install -g @masslessai/push to pick up the new pu session command family and the server-down UX improvements.

v1.1.13

2026-04-14

Fixes auto-update. Every prior release, going back to 1.0.0, has been silently invisible to Sparkle.

What's new

  • Sparkle auto-update works. The appcast generator had been writing the marketing version ("1.1.12") into <sparkle:version>, but Sparkle's default comparator uses that element as the build number and splits it on dots. It compared installed CFBundleVersion=10 against feed "1.1.12" as [10] vs [1,1,12], saw 10 > 1 at the first index, and concluded the installed app was newer than the feed — hence "You're up to date!" on every auto-update check since 1.0.0. Fixed: <sparkle:version> now carries CFBundleVersion (a strictly-increasing integer), <sparkle:shortVersionString> carries the marketing version. The preflight monotonicity check was reading the wrong xpath; that's fixed too, so it will actually catch this class of regression going forward.

For existing users — important

If you are running any build between 1.0.0 and 1.1.12, auto-update cannot rescue you. Your Sparkle client will keep saying "You're up to date" forever because the comparison bug is in the feed it's reading. You need to install 1.1.13 once by hand. After that, auto-update will start working and 1.1.14 onwards will arrive silently the way it's supposed to.

Installation

  • Download Push-1.1.13.dmg from releases.pushto.do.
  • Drag Push.app to /Applications, replacing the existing copy.
  • Open it.

Your local data in ~/.push/ is preserved across the update.

System requirements

  • macOS 14 Sonoma or newer
  • Apple Silicon or Intel Mac (universal binary)
  • ~200 MB disk space

v1.1.12

2026-04-14

Release pipeline fully green end-to-end — no user-facing app changes since 1.1.11.

What's new

  • Post-publish CI smoke passes again. Every release since 1.1.8 had been reporting red CI because the smoke script launched Push.app from /tmp, where macOS AppMover shows a blocking "Move to Applications" modal. On a headless CI runner there is no user to click, the modal hangs indefinitely, and the server never starts. Fixed: the smoke now copies Push.app to /Applications before launch, matching AppMover's Bundle.main.isInstalled short-circuit. Confirmed via a disposable debug-smoke workflow against the live 1.1.11 DMG. Side effect: the website-deploy trigger fires again, so pushto.do stays in sync with each release instead of lagging until ISR revalidation.

For users

Nothing to do. If you're on 1.1.10 or 1.1.11 you already have the macOS 26.2 Tahoe launch-crash fix; 1.1.12 is the same app with a green release pipeline.

Installation

  • Download Push-1.1.12.dmg from releases.pushto.do.
  • Drag Push.app to /Applications.
  • Open it.

Existing users will be offered this update automatically via Sparkle.

System requirements

  • macOS 14 Sonoma or newer
  • Apple Silicon or Intel Mac (universal binary)
  • ~200 MB disk space

v1.1.11

2026-04-14

Release pipeline reliability — no app changes since 1.1.10.

What's new

  • CI smoke test now polls the real health endpoint. The post-publish smoke that was added in 1.1.8 had always asked the server for /health — an endpoint the server has never exposed (it lives at /api/health). Every release since 1.1.8 failed CI at that step, which meant the automatic website-deploy trigger was skipped and the pushto.do download button lagged behind the real appcast by up to 10 minutes. Fixed: smoke hits the right URL, finishes in ~3 seconds, CI is green, and the website picks up each new release automatically.

For users

Nothing to do. If you're on 1.1.10 you already have the macOS 26.2 Tahoe launch-crash fix; 1.1.11 is the same app with working release infrastructure.

Installation

  • Download Push-1.1.11.dmg from releases.pushto.do.
  • Drag Push.app to /Applications.
  • Open it.

Existing users will be offered this update automatically via Sparkle.

System requirements

  • macOS 14 Sonoma or newer
  • Apple Silicon or Intel Mac (universal binary)
  • ~200 MB disk space

v1.1.10

2026-04-14

Critical fix for a launch crash on macOS 26.2 Tahoe.

What's new

  • Fixed a fresh-install launch crash on macOS 26.2. On Tahoe 26.2 (SwiftUI 7.2.5), a handful of users hit an NSInternalInconsistencyException from NSToolbar seconds after launching — the window would appear, then the app would die before anything became interactive. Root cause: SwiftUI's new incremental-insert path for toolbar items throws on identifier collisions that older SwiftUI silently deduped. Every toolbar item in Push now carries an explicit, namespaced identifier, so the collision is impossible regardless of SwiftUI version.
  • Release pipeline smoke-test timeout bumped 30s → 120s. Post-publish health check was failing on slower CI runners even when the actual artifact was fine (this is why 1.1.9 showed as "failed" in CI even though the DMG shipped correctly).

Installation

  • Download Push-1.1.10.dmg from releases.pushto.do.
  • Drag Push.app to /Applications.
  • Open it.

Existing users will be offered this update automatically via Sparkle.

System requirements

  • macOS 14 Sonoma or newer
  • Apple Silicon or Intel Mac (universal binary)
  • ~200 MB disk space

v1.1.9

2026-04-14

Every session now tells you which issue it's working on — and /pu inside Claude Code finally shows up on the dashboard.

What's new

Sessions know about their issue

The sessions list and session detail view used to show "interactive · slash_pu" with no hint of which issue the agent was actually working on. Now every session — timer-tick, user-spawned, proactive, or interactive /pu — surfaces its linked issue: the identifier chip (e.g. MAS-3387), the title, and its status. Click through from the detail view straight to the issue page.

No backfill needed. Old sessions already had the issue reference in their context snapshot; Push just wasn't rendering it. The moment this update installs, your entire session history lights up with the right context.

/pu <issue> in Claude Code is first-class

When you run /pu 3387 inside a Claude Code session, Push now:

  • Claims the issue as actively executing (atomic, race-safe — two concurrent /pu invocations can't clobber each other).
  • Captures the Claude Code session UUID on the run for resume-ready metadata.
  • Releases the lock cleanly when the session ends, whether you finalize explicitly or just close the window (in which case the run is marked detached rather than leaking as running forever).

Approval and human-in-the-loop flows are unchanged: closing the heartbeat run doesn't close the issue, so pending approvals still route through the normal path.

Release pipeline reliability

CI post-publish smoke would occasionally reject freshly-published DMGs with "Unnotarized Developer ID" even though the Push.app inside was correctly notarized and stapled. Fixed: the DMG envelope itself now gets notarized + stapled, so verification works offline and doesn't depend on Apple's notary service being responsive at the exact moment CI polls.

Installation

  • Download Push-1.1.9.dmg from releases.pushto.do.
  • Drag Push.app to /Applications.
  • Open it.

Existing users will be offered this update automatically via Sparkle.

System requirements

  • macOS 14 Sonoma or newer
  • Apple Silicon or Intel Mac (universal binary)
  • ~200 MB disk space

v1.1.8

2026-04-14

/pu sessions inside Claude Code now show up as live work on the issue.

What's new

  • Interactive /pu <issue> is first-class. When you run /pu 3387 inside a Claude Code session, Push now claims the issue as actively executing, attaches the Claude Code session UUID to the run, and releases the lock cleanly when the session ends. The issue card surfaces the session while it's active — same visual affordance you get for spawned runs, minus the live log (the subprocess isn't owned by Push, so stdout isn't streamed).
  • No more zombie interactive runs. If you close the Claude Code window without explicitly wrapping up, session-end finalizes the run as detached and frees the issue lock. Previously the run stayed running forever and the issue stayed locked.
  • Resume-ready session metadata. The Claude Code session UUID is now stored on the heartbeat run itself (sessionIdAfter), visible on the session detail page with a copy button. Paves the way for one-click resume into the exact Claude session from Push.

Under the hood

  • Interactive run create atomically claims issues.executionRunId only when the issue is free (race-safe; two concurrent /pu invocations don't clobber each other).
  • New detached run status — distinct from succeeded so dashboards can tell an explicit handoff from a window-closed-mid-work.
  • Approval / HITL flow is unchanged: closing the heartbeat run does not close the issue, so pending approvals still route correctly.

Installation

  • Download Push-1.1.8.dmg from releases.pushto.do.
  • Drag Push.app to /Applications.
  • Open it.

Existing users will be offered this update automatically via Sparkle.

System requirements

  • macOS 14 Sonoma or newer
  • Apple Silicon or Intel Mac (universal binary)
  • ~200 MB disk space

v1.1.7

2026-04-14

Push 1.1.7

v1.1.6

2026-04-14

Push 1.1.6

v1.1.5

2026-04-14

Push 1.1.5

v1.1.4

2026-04-14

Push 1.1.4

v1.1.3

2026-04-14

Push 1.1.3

v1.1.2

2026-04-14

Push 1.1.2

v1.1.1

2026-04-14

Push in your Dock, a complete install, and download links that never go stale.

What's new

  • Push now lives in your Dock. Alongside the menu bar. You can quit Push from the Dock like any other Mac app, it shows up in ⌘-Tab, and Force Quit sees it by name. The menu-bar icon is still there as the server-status heartbeat and quick-access point — it's just no longer the only signal that Push is running.
  • Database ships correctly. Some embedded database binaries were missing from the v1.1.0 DMG, which could cause workspaces to fail to start cleanly on a fresh install. v1.1.1 bundles them properly.
  • Download links never go stale. The website and auto-update now always point at immutable versioned URLs. If a future release ever fails to publish, you'll simply keep using the prior working version automatically — no 404s, no stale bytes, no manual fixes.

Installation

  • Download Push-1.1.1.dmg from releases.pushto.do.
  • Drag Push.app to /Applications.
  • Open it.

Existing users will be offered this update automatically via Sparkle.

System requirements

  • macOS 14 Sonoma or newer
  • Apple Silicon or Intel Mac (universal binary)
  • ~200 MB disk space

v1.1.0

2026-04-13

A friendlier install, a calmer first launch, a cleaner way out.

What's new

  • Redesigned installer. The Push DMG now matches Push for iOS — same warm off-white canvas, the same subtle grid, a single hero arrow guiding you from Push.app to Applications.
  • No more "running from Downloads" confusion. If you launch Push from your Downloads folder or the DMG, it offers to relocate itself to Applications so the CLI, Claude skills, and Login Item all work reliably. (Under the hood: App Translocation handled via AppMover.)
  • Context before every permission prompt. Every install-time ask — your password, notifications, Login Item access — is now preceded by a short explanation of what's about to happen and why.
  • New "You're all set" screen. After onboarding, Push points you at its menu-bar icon with a gentle pulse (it's easy to lose track of a menu-bar-only app) and offers one-click actions to try your first task or install the iPhone companion.
  • Settings → Uninstall Push. One place to cleanly remove everything Push installed — CLI symlink, Launch Item, app data, and optionally the Claude skills. A good-citizen way to leave.

Installation

  • Download Push-1.1.0.dmg from releases.pushto.do.
  • Drag Push.app to /Applications.
  • Open it — the new onboarding walks you through setup.

Existing users will be offered this update automatically via Sparkle.

System requirements

  • macOS 14 Sonoma or newer
  • Apple Silicon or Intel Mac (universal binary)
  • ~200 MB disk space

v1.0.1

2026-04-13

Fix launch crash affecting all v1.0.0 installs.

  • v1.0.0 shipped without the Sparkle auto-update framework embedded in the app bundle. Every launch failed immediately with a dyld library-not-loaded error. v1.0.1 embeds the framework correctly and adds the required runtime rpath so the app starts successfully.
  • No changes to features or data — if you installed v1.0.0 and it bounced on launch, installing v1.0.1 will fix it.