Skip to content

Desktop

The Shipper desktop app is a supported macOS app for running the Shipper issue lifecycle from a visual pipeline board. It uses the same core engine as the CLI and covers the same workflow stages: issue intake, grooming, design, planning, implementation, PR review, and merge readiness.

Desktop does not create a separate project database. Workflow identity, stage, priority, failed state, workflow history, and issue title/body content live in Shipper’s local issue-state store and append-only workflow event log. GitHub labels, issue content, and Shipper workflow comments are mirrors for users and integrations; PRs and adopted-PR marker comments remain GitHub artifacts. You can switch between desktop and CLI work on the same repository because both entry points read and write the same state.

The five desktop columns are Design · Plan · Implement · Review · Ready. They describe the next work, derived immediately from existing records without migration or reprocessing:

Completed stageNext step
shipper:newGroom, in the separate New attention area
shipper:groomedDesign
shipper:designed, simple routeImplement
shipper:designed, complex or undecided routePlan
shipper:plannedImplement
shipper:pr-open, with or without a pull requestReview
shipper:reworkImplement, with a Rework badge
shipper:readyReady for final integration

Ready is not shipped and does not prove that a merge is currently eligible. A rejected simple implementation that escalates to complex appears in Plan. Auto-ship says Review in either PR mode; Rework is a current-state badge in Implement, including failed or held cards retaining that state. Implementation returns the card to Review and removes the badge. Other bounces, including rejected simple implementation, have no Rework badge.

CLI/MCP retain six next-step groups. For the same issue population, the five desktop columns are Design · Plan · Implement · Review · Ready: desktop Implement equals CLI/MCP Implement plus Rework. New stays in a separate attention area. Closure-held and failed issues are excluded; closure hold takes precedence over failure. Blocked, locked, operator-paused, and running issues retain their route-aware column membership. Attention counts overlap those memberships. MCP blocked rows remain in their next-step groups with blocker and ownership details; only stageless or unknown-stage blocked rows use Blocked attention — no next-step group.

Current open responses add nextStep, and status adds nextStepPipeline. Existing completed-stage pipeline JSON fields, workflow labels, status filters, commands, and reset arguments keep their meanings. Closed records have no future next step, and historical records are not rewritten.

Maintained GitHub label descriptions retain completed-work meaning and add next-step guidance. New repositories receive them during setup; existing repositories automatically refresh differing descriptions on their next normal label setup or maintenance, without separate initialization. A successful catalog cache skips repeat maintenance; a changed catalog or force refresh reads again. Existing colors and unrelated labels are preserved. Read-only status, list, and view make no label writes.

Reset choices, previews, successes, and refusals lead with the next step, then the literal completed-stage target and any route clearing. An eligible Design drop targets groomed and clears the route to undecided. An eligible Plan drop targets designed for complex or undecided routes; a simple-route Plan drop is refused with the route unchanged because designed would next Implement. An eligible Implement drop selects preserving rework when offered by the existing reset options. Otherwise it targets designed for simple and planned for complex or undecided. The existing main-process preview validates the package; missing, stale, closed, merged, unreachable, or unverifiable evidence refuses confirmation without falling back to destructive cleanup. To deliberately discard work, choose Start over — in the card Reset menu; the existing cleanup confirmation applies. The Plan column is different from --to planned, whose next step is Implement. A successful --to designed says Implement for simple and Plan for complex or undecided. New (--to new) also clears the route.

These translations do not override backward-only eligibility, failed-stage retry, preserving Rework exceptions, active-work restrictions, confirmation, destructive work-loss warnings, or work-preservation guarantees. Review and Ready are not drag-reset destinations. A non-failed same-column drop does nothing, including Rework already in Implement; repeat Rework menu resets remain real preserving operations. Dragging cannot force a route change beyond Design’s existing route clearing.

Use desktop when you want visual triage, queue monitoring, headless grooming with native question forms, setup terminal sessions, pause/resume controls, issue run controls, logs, and hands-on operation from one board.

Use the CLI when you want scripts, CI or container execution, direct terminal commands, explicit stage invocation, or headless automation around commands such as shipper next, shipper ship, shipper merge, and shipper reset.

Many teams use both: desktop for day-to-day monitoring and control, CLI for repeatable commands and automation.

On macOS, Desktop New Issue preparation, background stages, and Groom sessions use the same idle-sleep protection contract as Terminal and MCP. See Keep the Mac awake during active work.

Download the latest macOS arm64 DMG or zip artifact from the private repo’s GitHub Releases (authorized access). The current desktop artifacts are unsigned and not code-signed, so macOS may require manual confirmation before first launch.

Windows and Linux builds are not currently distributed. Desktop can use a GitHub owner/repo entry or an existing local Git working copy.

The app runs Shipper workflows locally, so the normal local prerequisites still apply:

  • Git and either a GitHub-hosted repository or a local primary working tree.
  • For GitHub-backed repositories, GitHub CLI installed and authenticated with gh auth login.
  • Node.js and the configured coding agent CLI for stages that invoke an agent.
  • Repository Shipper settings that select the agent and command behavior.

Repositories whose effective transport has no remote can be opened without GitHub CLI. A GitHub-backed repository shows its own prerequisite guidance when GitHub CLI is missing or unauthenticated; selecting it again rechecks GitHub availability.

Packaged macOS Apple Silicon builds make one best-effort check for a stable release each time the desktop app process launches. The check uses your existing Shipper sign-in and runs independently of app startup. Only a strictly newer release produces a dismissible, display-only notice, which names both the available version and the version you are running.

The check is silent when you are signed out, no stable release exists, the platform is unsupported, the response is rejected or malformed, the service is unavailable, or the request times out or encounters a network failure. None of these outcomes blocks or delays normal app use. Dismissing an update notice hides it for the current app session; a later launch may show it again if a newer release is still available.

The notice’s single upgrade action opens the gated Shipper downloads page. It does not download or install an update, show release notes, restart Shipper, or poll for another release while the app is running.

  1. Launch Shipper desktop.

  2. Add or select a GitHub repository in owner/repo format, or choose an existing local repository folder from the repo picker.

  3. If the selected repository is GitHub-backed, install GitHub CLI and authenticate it:

    Terminal window
    gh auth login
  4. Resolve any prerequisite warning shown by the app. For a signed-out, expired, or terms-outdated Shipper license, select Sign in in the alert. Desktop opens the browser login flow in its terminal drawer and, after a successful sign-in, automatically rechecks the license and configured repositories, then loads accessible pipeline boards without a relaunch. Missing or unauthenticated GitHub CLI guidance appears only on affected repositories; select an affected repository after resolving the prerequisite to recheck it.

  5. If the repository is not configured, run setup when prompted. This records repository settings and provisions GitHub workflow labels for mirror-on repositories. Store-only repositories skip GitHub label provisioning and keep Shipper workflow state in the local store.

  6. Confirm the pipeline board loads issues from the selected repository.

After the board loads, desktop and CLI commands can operate on the same repository interchangeably.

In the Add repository dialog, choose Choose local folder to register an existing working copy. You can select the repository root or any folder beneath it; Shipper resolves a nested selection upward and shows the exact enclosing repository root that it registered. A primary working tree is accepted whether or not it has a remote, whether its origin points to GitHub, and whether Shipper setup has already run.

The selected folder must be inside a usable, non-bare Git working tree. Shipper rejects folders outside a Git repository, bare repositories, and linked worktrees—including Shipper’s own ephemeral worktrees—with a specific error. Dirty files and Git operations such as a merge, rebase, cherry-pick, revert, or bisect do not prevent registration; desktop reports that state as informational.

A registered local path is your existing working copy, not an app-managed clone. Shipper uses it directly and does not reset, clean, stash, synchronize, switch, copy, or relocate its primary checkout. Workflow operations may fetch repository data or create and remove Shipper-owned branches and ephemeral worktrees, but those operations do not change the primary checkout’s branch, index, tracked files, or untracked files.

Repository duplicates are determined by resolved filesystem location. If you add a symlink or another path that resolves to an already configured working copy, desktop selects the first configured representative instead of adding a second entry. A local path and a GitHub owner/name entry remain separate even when they describe the same project.

Desktop preserves the configured path exactly across restarts. If the folder is moved, unmounted, deleted, or stops being a usable working tree, its entry remains in the repository list and is shown as unavailable. Restore the repository at that exact path, or explicitly remove the entry and add its new location; Shipper never silently removes, rewrites, or replaces it with a managed clone.

Use the toolbar’s Settings action to edit per-repository personal values. The dialog does not change user-global or committed repository settings. Each row shows its current effective value and source after all settings layers have been applied.

The Per-stage agent, model, and effort group provides one unit for each of new, groom, design, plan, implement, pr_review, and setup. Every unit has an agent selector for Claude, Codex, Copilot, Grok Build, or OpenCode, plus free-text model and reasoning effort fields. Model and effort values are unconstrained and are not limited to a predefined list. The group initially expands when this personal scope contains any per-stage agent, model, or reasoning effort override and starts collapsed otherwise. A single expand/collapse control remains available at all times.

Choosing Inherit for an agent—shown with the resolved agent, such as Inherit (Claude)—removes the personal override instead of saving the named agent as a new explicit value. For a stage model or reasoning effort, empty or whitespace-only input means inherit and removes that personal value. Its placeholder names the inherited value when one resolves; otherwise it reads Inherit (coding agent default). The next effective value may come from a broader-scope setting for that stage or, when no stage-specific setting applies, from the corresponding default preference.

The default model and default reasoning effort rows use the same clearing rule: emptying either field, including entering only whitespace, removes its personal override. Clear override remains an equivalent second path for those default rows. The dialog writes every change only to the selected repository’s per-repository personal settings. Newly started workflows use the saved result, including Desktop Groom sessions started from the board or resumed from the groom drawer.

Review diff ceiling (bytes) is an exact-byte numeric personal override for the complete reduced review input. The row shows the effective source, remains available when pull requests are disabled, and affects subsequent review and implementation rework runs. It accepts only finite positive whole byte counts. Clear override removes only the personal value and reveals the next repository, user-global, or built-in default value.

The footer action is Close. Whenever Save is available, attempting to leave with Close, Escape, a click outside the dialog, or Repair issue data first shows Discard unsaved changes? Choose Return to settings to keep every pending edit intact, or Discard changes to drop the edits and continue the originally requested close or repair. Leave requests are immediate when Save is unavailable and unavailable while a save is in flight.

Saving a changed agent, model, or reasoning effort probes the exact resulting tuple for every affected stage. Verification has one 30-second overall deadline across command preparation, probes, and any retry of independent switches. Repository checkout, settings resolution, writing, and refreshing are additional parts of the complete save outside that deadline. If an agent rejects a value, is missing, is unauthenticated or offline, or does not respond in time, Desktop refuses every submitted agent, model, and reasoning-effort edit for that default or stage switch and retains those drafts. Independent switches and unrelated edits may still save. The dialog identifies the stage, agent, value, and diagnostic beside each refused control and summarizes which edits were saved and which were not. A reasoning effort that Claude or OpenCode accepts without confirming is applied with an inline warning that the value may be ignored. Desktop never offers a save-anyway bypass.

Use Check agent settings to probe all currently stored effective combinations, including the default, and see rejection provenance, including the dotted key and settings file that supplied a value. The check runs only when you select that action: opening the dialog does not launch an agent probe.

After the first board load, desktop becomes an operating surface for the same issue lifecycle the CLI reference describes. The sections below cover the background queue, logs, terminal sessions, pause state, card actions, shared locks, automation toggles, and reset flow that power users need during day-to-day work.

When Shipper safely preserves issue-owned work before advancing a rewritten remote branch, the issue card shows a blue, informational Superseded work block. This is not a failed state and does not use failure styling. Each occurrence names origin/<branch>, abbreviates the recorded and incoming tips, summarizes unique or indeterminate work, and includes the exact recovery command:

Terminal window
shipper recover S#<localId> --superseded <eventId>

Each occurrence remains visible across polling, board reloads, scrolling, and issue-detail views. Those passive actions never acknowledge it. Choose Restore superseded work to create or reuse ordinary operator-owned recovery branches and acknowledge the occurrence after restoration succeeds. Choose Dismiss notice to acknowledge it without restoring branches. Multiple live occurrences appear newest first, and a failed issue can show its existing recovery controls beside these separate informational notices.

Shipper retains the backing hidden refs for the active issue with no age-based expiry or TTL. Elapsed time does not remove the notice or invalidate recovery. Completion, not-planned or external closure, unadoption, and reset-to-New release the Shipper-managed hidden refs only after the logical issue change commits. Earlier reset targets retain them. Ordinary branches created by Restore superseded work are operator-owned and survive issue release.

Open an issue from the board to inspect its canonical detail. When locally retained review rounds exist, the dialog shows a Reviews section with every round oldest first. Each compact row includes the round number and stable identity, reviewed revision, disposition, finding count, optional addressed or superseded handling state, and a pull request reference only when that round recorded one. Local packaging continues to use the Pending change vocabulary rather than implying that a pull request exists.

Expand a compact row in place to read that review round in full. Only one round is open at a time, and loaded rounds remain cached while the dialog is open. Full detail includes overall feedback, disposition and revision, every stable finding identity, file path, line, side, optional start range, complete comment and review-rework reply text, optional handled or resolved state, and complete partial coverage. The dialog applies no review or finding truncation; its existing content region scrolls to keep every round and finding reachable.

Issues with no retained rounds omit Reviews. If only the review record is unreadable, Reviews shows the reason while the rest of the issue remains available. The same compact and full views work for active, completed, and not-planned issues. They read local Shipper state only and never change review or handling state. GitHub-sourced inline PR review threads are not shown here; locally retained Shipper review rounds are shown under Reviews.

The New Issue modal accepts a text request for the selected repository. When that repository’s configured agent supports image attachments, you can paste images into the dialog or use the attach image control to choose screenshots from disk.

If the configured agent does not support image attachments, desktop shows the unavailable attachment message in the modal and keeps New Issue text-only for that repository.

Canceling, pressing Escape, or clicking outside the modal with a nonblank request or any attached image shows Discard unsaved work? Choose Continue editing to preserve the exact request, attachments, and repository selection, or Discard to clear the draft before closing. A repository-only change or whitespace-only request with no images closes immediately. Launching a valid issue bypasses this confirmation and submits normally.

Changing to a repository whose agent does not support images—or whose image capability cannot be determined—while one or more images are attached takes effect immediately and then shows Remove attached images? Choose Keep images to restore the previous repository with every image intact, or Remove images to retain the new repository and remove only the attachments. The typed request is preserved either way.

Canceling, pressing Escape, or clicking outside the Block Issue modal shows Discard unsaved work? when either the blockers or reason field contains non-whitespace text. Continue editing returns to both intact fields, while Discard clears them and closes the modal. Blank and whitespace-only fields close without a prompt, and a successful Block submission bypasses the confirmation.

The desktop adoption modal lists open untracked issues and, when pull-request access is enabled, eligible open pull requests from the selected repository. Candidate rows are marked as Issue or PR so you can distinguish work that starts from an issue from work that adopts an existing pull request for review. Pull-request policy controls PR reads independently from GitHub issue sourcing: a repository with a verified GitHub identity can still list issue candidates when PR access is disabled. When the repository has no GitHub identity, the modal explains that adoption candidates are unavailable instead of presenting a normal empty result.

In mirror-on repositories, eligible PR rows are open, non-draft, same-repository pull requests without shipper:* labels. PRs that reference multiple open same-repository closing issues remain visible but disabled with a reason. If a backing issue is already tracked by Shipper, adoption is disabled only when that issue still resolves to a different open PR. Draft PRs, fork or cross-repository PRs, closed or merged PRs, and PRs that already carry a shipper:* label are hidden from the list. Open issues already recorded as same-repository origins of local Shipper records are also hidden, even when the source issue has no Shipper label.

In store-only repositories, desktop uses the local issue-state store as the adoption authority. It imports local records, records the GitHub issue or PR origin as read-only provenance, and does not add labels, comments, PR edits, or GitHub tracking issues. Already-imported issue and PR origins are hidden from the list even though the source GitHub artifacts do not carry Shipper labels.

When desktop adopts a foreign-branch PR in mirror-on mode, Shipper records the PR-to-issue link in a Shipper-owned issue comment instead of editing the pull request body. In store-only mode, the same association is stored locally. The CLI and desktop both read that durable association. If the previously adopted PR is closed or superseded, adopt the current PR again from the modal or with shipper adopt <current-pr-number>; Shipper will not guess a replacement automatically.

Adopt All Issues remains issue-only and never adopts pull requests. In mirror-on mode it skips GitHub issues that already have Shipper labels or whose same-repository origin is already recorded locally. In store-only mode it skips by local GitHub origin. Use a PR row’s individual Adopt action, or the manual field, to adopt a pull request. The manual field continues to accept an issue or PR number or URL.

Every live board card—including blocked, failed, closure-held, locked, and queued cards—offers Un-adopt in its ellipsis menu. Closed records do not. Selecting it first opens a read-only confirmation naming the Shipper issue, the surviving GitHub issue, every owned worktree and local or remote branch to remove, and each dependent blocker conversion. Cancel makes no change. If those facts change before apply, Desktop refreshes the preview and requires confirmation again.

Only issues adopted or imported from an existing same-repository GitHub issue are eligible. Shipper-created issues and work items created while adopting a pull request are refused. A running stage must be stopped or allowed to finish; a queued command must be cancelled from the action queue; and every open linked pull request must be merged or closed. There is no force or override.

Mirror-on removes every shipper: label but preserves the GitHub issue body, comments, assignee, unrelated labels, and its open-or-closed state. Dependents remain blocked on a strict GitHub issue blocker. Store-only makes no GitHub write and instead refuses while dependents exist, naming each one so its blocker can be removed or replaced.

Success removes owned development artifacts, the complete local record and event history, and all origin provenance; it leaves no tombstone. A repeated un-adopt fails as not managed. Refresh and reconciliation do not restore or report the source, while a later explicit or bulk adoption treats it as a fresh candidate with a new Shipper ID and history.

Repository tabs show started-workflow running counts and needs-you markers across open repositories. Counts exclude queued work; an unavailable observation stays unknown rather than showing zero. Select the relevant repository to inspect its board.

Issue cards show compact condition badges. Their details show the selected run’s status, available logs, Retry, Stop, and started-elsewhere information. Run status and origin badges do not appear on cards, including completed history on idle New issues. Current ownership still governs card controls. External ownership is read-only where required; durable read-only evidence does not gain mutation controls. Queued runs have no logs, and started merges remain merge-bound and cannot be stopped. Issue details show local packaged branches as Pending change and expose PR links only when current policy permits.

Repository setup shows diagnostics and Retry setup or Set up again for setup failure recovery. New Issue board placeholder cards show creation progress and available logs, Stop, Retry, and Dismiss before an issue exists. Successful creation hands off to the real issue card. Placeholder retries require retained original input; if the request or images are unavailable, reopen New Issue and reattach any images. Dismiss hides the placeholder without deleting durable run records or transcripts.

Run status includes Queued, Running, Succeeded, Failed, Refused, Paused, Cancelled, and Interrupted, with applicable destination vocabulary:

  • Reached <shipper:label>: a successful Ship or Next run advanced to its recorded destination. The raw shipper: label is intentional.
  • Returned to <shipper:label>: a successful rejection moved the issue backward to its recorded destination. The attention-needed treatment does not make the command a failure or change Retry or auto-ship backoff behavior. The return reason remains on the issue.

A Refused run that needs an operator closure decision is terminal but not a failure, does not increment auto-ship failure/backoff state, and never offers Retry. Resolve the closure hold from the issue card before continuing.

Interruption records a proven dead supervisor, not a workflow failure, retry-exhaustion occurrence, or repository safety event. Refusals and operator cancellation remain neutral outcomes. The renderer hydrates current sessions, activity tasks, and durable run outcomes from the supervision host before applying live events, restoring daemon-hosted work after reload or relaunch. Retained output may become unavailable while saved metadata persists. Agents can still use shipper_get_session_logs for recorded agent transcripts and shipper_get_issue_runtime for current runtime inspection; existing tools and saved run records remain available. Desktop does not provide an aggregate run-history browser.

Operational details show Observation waiting, Observation warning, Observation error, inactive, or unconfirmed, even with one worker running and three spare. Genuine observation problems appear in the board banner; observation alone leaves the toolbar pill’s ordinary posture and slot use intact. Terminal shipper status (including --json) and repository-scoped MCP shipper_status report the same committed facts: repository, continuous episode start and age, latest reason, unfinished phase, and actual next check. When no future check has a scheduled time they say no next observation scheduled. Missing information is unconfirmed, and an absent reason is reason unknown.

A failed local eligibility read immediately makes eligibility unverified and records diagnostics. The same local condition becomes a warning after 30 seconds and an error after 120 seconds, measured from its first failure, even while dispatch is halted or capacity is full. Repeated failures update the count, latest failure time, and cause without restarting its age. The panel also shows the last successful read and separate local and remote opportunities, or their blocking reasons.

Local checks retry automatically at the ordinary cadence, approximately every four seconds. The first successful local check clears the local condition without Resume, acknowledgement, or restart, including an empty result or success followed by a GitHub failure. This local-read condition is distinct from a GitHub outage. Automatic starts wait for verified eligibility; queued reservations and running work remain, and all quota, ownership, safety, halt, pause, and capacity gates still apply.

A failed read retains the original quiet heartbeat deadline. Once due, safely permitted current observation proceeds independently of the local probe. A blocked heartbeat keeps that deadline and shows its cause and next remote opportunity separately from the next local check. Only complete current and remembered-blocker observation settles it. Recovery completes overdue work once, with no catch-up bursts; blocked and completed observation remain distinct.

Unrelated known observation errors appear immediately. Unexplained non-dispatch while enabled, permitted, and below capacity warns after a fixed five minutes (300,000 ms), including when eligibility is unknown. The same default applies with zero or some workers busy; there is no threshold setting. A previously normal heartbeat or explained retry becomes overdue from its original due time and warns five minutes later. Starting late, probing locally, or acknowledging a retry does not reset that deadline. A complete quiet heartbeat interval, full capacity, explicit halt/disable/safety or ownership stops, and explained waits for future policy retries do not warn merely for no launch. Quota restrictions and pauses still apply.

For unrelated general observation conditions, warning age continues across failed checks and changing reasons. A successful current eligibility check (including no eligible work) with a real future schedule, or verified admission of work, clears the warning automatically. Halting or disabling ends general applicability rather than claiming recovery. Halt does not stop local failure escalation. Disabled local health is inactive, with no local retry or outstanding heartbeat promise. Restart or re-enable starts unverified with a fresh episode, count, and age; retained diagnostic history remains available. Desktop’s elapsed clock only updates the display; it never starts checks or infers health.

Retry observation requests an ordinary check through the current repository owner. The runtime also automatically restores a missing observation obligation when safe. Neither path replaces an unfinished side-effecting inspection: repeated requests coalesce one follow-up, whose start time is unknown. A scheduled/already-in-progress acknowledgement does not establish recovery or clear an episode. Settings, worker counts, issue ownership, queued work, and safety controls are preserved.

For an inspection that remains unfinished, Restart daemon if idle is deliberate shared-daemon recovery. It checks the displayed owner and daemon instance, stops normally, and launches a replacement only after confirmed stop. Any genuine running or queued session, workflow admission, issue-linked continuation/check, or pending/retrying run-record write in any repository refuses stop. The refusal identifies the repository, issue/session/operation, phase, and applicable action; let that work finish or use its existing control. Observation-only work does not block normal stop. Normal stop has one 30-second total budget, reports cleanup/ownership failures truthfully, and never falls through to forced termination. A refused or failed stop never launches a replacement.

The terminal equivalents are shipper auto-ship retry-observation, then if needed shipper daemon stop, followed by shipper daemon run only if stop succeeded. Retry connects only to an existing daemon and never enables a repository or starts a daemon. A Terminal-owned repository must recover through its hosting Terminal. A sticky Desktop fallback offers local retry; if inspection remains unfinished, close and reopen that hosting Desktop after protected work drains. It does not restart an unrelated daemon. These actions are separate from safety-stop Resume, Clear halt, and dispatch-ownership clearing.

A replacement daemon retains durable settings but starts with unconfirmed observation health. It serves status promptly while independent repository resolution, control, history, and observation jobs fill in current evidence. One slow repository cannot prevent healthy peers from progressing. Service readiness does not imply that all repositories or their history have finished loading.

Rotating supervision diagnostics retain episode transitions, reason changes, escalation, recovery or ending, and cumulative count/duration summaries across daemon restarts. They support later reconstruction without logging every four-second poll. Active warning age is in-memory: a restart has no durable age-continuity guarantee and must obtain fresh evidence.

A daemon normally remains resident whenever at least one repository has Auto-ship enabled, even when every enabled repository is remotely quiescent. Quiescence is a quiet scheduling mode: an eligibility-bearing local change wakes that repository on the regular control cadence, and its slow, distributed heartbeat observes externally originated changes. It is not the durable Auto-ship safety stop and needs no operator resume gesture. Disabling Auto-ship for every repository restores normal idle retirement.

During recovery, Desktop first reports Reaching the background service and the absolute time that phase began. If the bounded connection ladder does not reach a daemon, Desktop reports Starting local supervision and its service/first-snapshot phase with an absolute start time. Launch recovery and recovery after daemon loss use the same host-selection sequence. Service attachment can finish while repository resolution, history, or eligibility checks remain pending; those independent jobs report their own observation evidence. Commands still validate their repository admission requirements.

The local-startup segment has its own approximately 30-second expectation; time spent in the connection ladder does not consume it. Before that threshold the phase copy is neutral. At and after it, the current phase is reported as taking longer than expected and the copy states that workflow actions remain unavailable until recovery finishes. The overrun persists for as long as startup continues: there is no terminal recovery failure, live ticking duration, retry, abort, relaunch control, or automatic ceiling. No timer, poll, or availability probe drives the renderer; the only timed change is the controller’s one threshold transition. When dispatch-ownership acquisition reads a valid durable mutation-guard record and proves its PID alive, Desktop may add Waiting on Shipper process <pid> as information. It names no blocker when that fact is absent, malformed, dead, or indeterminate.

After local startup succeeds, Desktop uses a sticky in-process fallback for the rest of the app lifetime and shows Desktop is hosting background supervision; Auto-ship remains enabled. A repository-scoped Desktop cannot dispatch right now banner appears only when the active repository lacks automatic dispatch authority. Manual workflow actions remain available through their ordinary reservations and locks, and unrelated repositories are unaffected. Clean daemon retirement and successor takeover are event-driven transitions, not polling, and show no error banner.

Only a reachable version-incompatible daemon blocks app-wide readiness. The Shipper versions are incompatible alert lists the client version, daemon version, and minimum required daemon version, and identifies the older side when their release major versions allow it. View upgrade instructions uses the same destination as the startup update notice. The alert is guidance only: Desktop does not stop, restart, or evict the incompatible daemon.

Daemon-hosted work alone never causes a quit prompt and keeps running after Desktop closes. If a Desktop-owned Setup, Sign in, interactive terminal, or headless groom session is live, the existing confirmation names the sessions that will close and separately states that daemon background work will continue. Disabling Auto-ship remains the way to stop new automatic dispatch and drain work.

Ship and Next share the repository’s ordinary workflow-worker capacity. When every worker for a repository is occupied, another Ship or Next stays Queued and starts automatically when capacity becomes available. Workflows for different repositories are not serialized by this rule. Manual launches can instead use the All workflow workers are busy dialog to queue the command or start it immediately.

Use Logs from an issue card’s overflow menu to open its current-app-run output. The card chooses an active Ship, Next, Unblock, or Merge run first; when none is active, it chooses the most recently started eligible run. A queued run has not started and does not make Logs appear, and neither New nor Setup runs are eligible.

Issue details offer another route to the same selected session in the background log viewer. Running commands stream output into the viewer as it arrives. Running or finished runs that remain accessible during the current app run can retrieve their complete output on demand. Desktop receives transient live chunks and reads retained output pages from the supervision host. The renderer stitches a page’s confirmed byte offset with chunks that arrived during the fetch so each byte appears once. Streamed chunks are not durable run-history events; after the host and its retained output lifetime end, old output is unavailable even though the run record remains.

If a rare transcript failure makes part of the output unavailable, the viewer inserts a notice at the missing position that identifies the unavailable byte range. Complete logs have no notice.

The viewer follows the tail while you are scrolled to the bottom. If you scroll up to inspect earlier output, it stops auto-following and shows Jump to latest; selecting that control returns to the tail and resumes following new output.

Use Copy logs to clipboard to copy the visible log content. Any missing-range notice is included in the copied text so a pasted log cannot be mistaken for a complete record. The copy control is disabled while the initial transcript is loading or when the viewer has no log content, and changes to a success icon after the clipboard write succeeds.

Groom starts a headless session for the selected issue and opens the global groom drawer on the right. The drawer remains available while you switch repositories and shows one issue tab for every live, awaiting-answers, suspended, completed, or failed session. Running tabs show current phase, round, elapsed time, and recent retained output. Awaiting tabs present ordinary authored questions as native controls: analysis disclosure, an optional recommendation, candidate options, and a free-text response for each question. Submit answers is the one explicit action, enabled only once every question in the batch has a response, and it submits the whole batch at once. There are no decision or provenance cards, veto controls, or batch-level action to confirm unvetoed decisions. The same bounded scroller contains every question and the recent retained output; the combined submit, Stop, and discard actions remain fixed and reachable. Drafts persist while you switch tabs or close the drawer, and answers submit without exposing a terminal. Cards show Grooming…, Needs answers, or Resume grooming as appropriate; the closed groom rail shows an amber count of sessions that need answers across all connected repositories.

Desktop-owned saved sessions discovered after an app restart appear suspended and wait for an explicit Start. Their detail view shows the saved transcript and answers, read-only pending question batch, grooming notes, answered rounds, and issue-text drift. Starting resumes that owned conversation.

A saved session that still holds the removed compact-confirmation format is incompatible. Shipper discards that saved session, shows an error toast explaining that grooming must restart for the issue, and returns the issue to its ordinary Start grooming card. The removed content is never transformed or resumed, and one incompatible record never hides the other resumable sessions beside it.

A saved session owned by MCP or terminal appears as Review grooming session instead of Resume; a legacy record with no owner appears as Unowned. The drawer shows only issue identity, owner, last-saved time, answered-round count, and whether a pending batch exists. It shows no question, answer, transcript, note, Start, answer, or discard control. Take over is the one forward action. It permanently transfers the full saved conversation to Desktop without launching grooming; the entry then becomes the ordinary Desktop-owned suspended view, and Start remains a separate gesture. While an issue run is live, Take over is disabled and the drawer states why. Elapsed time, an operator pause, or a queued reservation never changes takeover eligibility.

Stop terminates the live worker, unlocks the issue, and preserves saved progress. The destructive live-stop variant requires confirmation and is the only stop action that discards saved progress. After desktop confirms the discard, the session closes its own tab and a brief notification names the issue and confirms that grooming stopped and its saved progress was discarded. If saved progress cannot be discarded, the same notification surface reports that it was not discarded, and the tab remains as an ordinary resumable suspended session. A session blocked by recovery stays on the failed issue card for Recover change rather than offering recovery in the drawer. Only completed and failed receipt tabs can be dismissed during the current app run; suspended tabs remain available for Start.

Setup and authentication remain separate terminal-drawer workflows. Setup opens an interactive setup session for repository configuration. The Sign in action for a recoverable license alert opens the existing shipper auth login browser flow in an auth terminal tab. Selecting Setup or Sign in again while its matching session is live focuses the existing terminal session instead of launching a duplicate.

When sign-in succeeds, desktop rechecks the license and configured repositories and prepares accessible boards automatically. A cancelled or failed sign-in remains visible in the exited terminal tab and can be retried from the alert without an extra error dialog. A revoked license cannot be restored by signing in again; contact Bare Metal Labs support when the alert directs you to do so.

The terminal drawer shows one tab per Setup or Sign in session. Setup tabs are labeled with the repository, and each tab includes a status dot. The observed terminal session states are:

  • running: the session has recent output or input activity.
  • waiting: no output has arrived for a short period, so the session may need attention.
  • finalizing: desktop has asked the session to finish gracefully.
  • exited: the process has ended and the tab can be dismissed.

Closing a terminal tab depends on that state. An exited session dismisses immediately. A finalizable running session is asked to finish gracefully and moves to finalizing. A finalizing session prompts Force-kill finalizing session?.

A manual renderer reload terminates any live Setup or Sign in terminal session and names the interrupted sessions in a notice. Those terminal sessions are not reattached, and their earlier terminal output is not retained after the reload. Headless Groom sessions are reconciled back into the groom drawer from their current snapshots and saved resumable state.

Pause is scoped to one issue within one repository and is stored as shared operator-pause ownership, so it persists across Desktop restarts and is visible to CLI and MCP. The former pause-state.json file is migration input only; successful legacy entries migrate once and the empty file is removed.

Pause behavior depends on what the issue is doing when you select it:

  • Running ship: desktop asks the active ship to halt at the next safe stage boundary, shows pending pause feedback, and atomically hands its run ownership to operator pause at the safe boundary.
  • Queued ship: Desktop installs operator-pause ownership with the matching reservation before it removes/finalizes the queued session. If the queued item has already raced into Running, desktop follows the running-ship pause path instead.
  • Idle issue: Desktop installs operator-pause ownership immediately. A live run or queued reservation visibly refuses the attempt instead of being displaced.

An operator-held card shows an operator hold badge and a Resume action distinct from active run ownership and repository-wide Auto-ship paused. Resume visibly reports resumed or already-resumed. Resume-and-Ship/Next first resumes, then performs ordinary reservation acquisition; Groom also resumes first, without another confirmation. Another dispatcher can win that intentional race and produce the ordinary busy/run contention result. Force unlock remains a reviewed deliberate override for an operator hold.

If an operator hold appears that you do not remember setting, use Resume to clear it. Existing holds are retained as operator intent and are not expired or migrated automatically. Find affected issues from the operator hold badge on Desktop cards or the per-issue <issue>: operator-paused - skipped lines printed by shipper unlock --stale. shipper status reports only an aggregate operator-paused count; it does not identify the held issues.

Issue cards expose primary buttons and an ellipsis menu. Availability depends on the card’s current labels, shipping state, and repository prerequisites.

Card badges use lowercase text: failed, needs answers, blocked, operator hold, closed on github, owner stopped or invalid lock, launch failed, parked, mirror pending, recoverable, superseded work, supervision unknown, stopping, pausing, queued, simple, and high or low priority. Normal priority has no badge. Stage badges are omitted; if Rework is folded into Implement, rework identifies that saved stage. The recoverable badge appears only for failed issues with enabled recovery evidence, even when Recover change is temporarily disabled. Missing or uninspectable recovery evidence has no badge. Action availability, live run status, launch reservations, and run origin stay out of the badge row.

A card whose stored route is simple carries a simple badge alongside its other state badges. Complex and undecided issues show no route indicator, because complex is the uninformative status quo on a glance surface. The current route also appears as a Route row in the issue detail Lifecycle section when it is simple or complex, and is omitted when undecided. Both reads come from Shipper’s local issue state and add no remote lookup.

Ownership is evaluated per issue. When supervision claims are unavailable or stale, an otherwise eligible idle issue that has no known owner or reservation keeps Groom, Ship, Next, Pause, Reset, and drag available; Merge retains its existing supervision requirement. A reservation-only owner, external run, operator hold, queued workflow, or running workflow applies the state-specific rules below. Mutation controls remain visible but disabled when ownership blocks them, without unavailable tooltips or explanatory text, and the card cannot be dragged. Read-only issue details and Logs remain usable, and Groom continues to focus an already-active Desktop Groom session.

Held issues appear in a Closed on GitHub attention section before ordinary failed issues. Issue details show the observed reason, actor when available, closing pull request or commit evidence, and whether that pull request matches the issue’s stored PR. A dual held/failed issue appears only in the held section; its failed state remains saved and becomes visible again if reopening resumes it.

Held cards cannot be dragged or reset, and do not offer Groom, Ship, Next, merge, change recovery, Pause, Resume, Stop, or Retry. Priority, issue details/comments, block, ordinary unblock, force-unblock, and the new Resolve closure action remain available. The resolution dialog starts with no selection: choose Not planned, Reopen and resume, or Complete. Completion first displays Core’s explicit work-shipped assertion and every affected dependent (or says none), then requires a second confirmation using the preview token. A failure keeps the card held and displays the reason for retry. See the canonical distinction between closure holds and failed state for the different clearing rules.

Both not-planned choices display a reason textarea. Blank or whitespace-only text keeps the apply button disabled, while non-blank text is preserved verbatim. Shipper commits it with the tombstone as an ordinary attributed local note headed Reason for Closing as Not Planned. The note remains visible in closed issue details and can be retracted like any other ordinary note. In mirror-on mode it is projected as a GitHub comment—even when the mirror was already closed—after the local commit. A projection failure leaves the local closure successful and shows a repair-pending notice; run shipper issue reconcile --repair to converge the one existing note, labels, and closure without creating duplicates. Store-only retains the same local note and performs no GitHub write.

ActionWhere it appearsAvailable whenEffect
GroomPrimary button on New attention cards; status action on an active or resumable Groom cardThe card is not durably failed, owned by a conflicting live or abandoned run, blocked by an invalid lock, or actively shipping; fetch prerequisites are satisfied; and a Groom launch is not already pending. An operator-held card remains actionable and resumes automatically before Groom opens.Starts or resumes headless grooming, or focuses that issue’s global groom-drawer tab. Native questions appear in the drawer; an operator-held issue is explicitly resumed first when needed.
ShipPrimary button on non-ready pipeline cardsA repository is active, fetch prerequisites are satisfied, and the card is not blocked, failed, actively shipping, owned by a live or abandoned run, blocked by an invalid lock, or already awaiting a ship launch. An operator-held card remains actionable and opens Resume-and-Ship confirmation.Starts the next eligible background Ship command after explicitly resuming an operator-held issue when needed.
NextFirst item in the ellipsis menu on non-ready pipeline cardsA repository is active, fetch prerequisites are satisfied, and the card is not blocked, failed, running another Ship or Next, owned by a live or abandoned run, blocked by an invalid lock, or already awaiting a workflow launch. An operator-held card remains actionable and opens Resume-and-Next confirmation.Runs exactly one eligible workflow stage without merging after explicitly resuming an operator-held issue when needed.
Stop Ship / Stop NextStop control beside a card with an active workflowA controllable Desktop Ship or Next row for that issue is Queued or Running. A stale or unusable supervision claim does not expose a Stop control that cannot act.Cancels the queued or running background command for that issue.
Recover changePrimary button on a failed card with a saved question session, preserved groom output, or packaged workDesktop inspects the shared recovery union. Unreadable or ambiguous grooming state and ineligible packaged state keep the action visible but disabled without unavailable tooltips or explanatory text; recovery reasons are available in issue details. The button also disables while the issue is locked, actively shipping, already recovering, or busy with another action.Preserves a saved session and returns it to New for the next ordinary headless groom, validates and applies preserved grooming output, or restores packaged work to shipper:pr-open. The success toast uses generic recovery wording and session recovery includes its answered-round count.
PauseEllipsis menuThe card is not already operator-held and no Next workflow is active. Queued Ship keeps Pause and Stop; queued Next keeps Stop without Pause. Reservation-only ownership and external ownership leave Pause visible but disabled without an explanatory tooltip.Installs shared operator-pause ownership for idle/queued Ship issues or asks a running Ship to hand off ownership at a safe stage boundary. Active Next work offers Stop instead.
ResumeEllipsis menuThe card is operator-held.Releases operator-pause ownership and visibly reports whether it was resumed or already resumed.
ResetSecondary button when recovery is present; primary for failures with no preserved artifactBackward reset targets exist and the issue is not currently shipping. Packaged complex and undecided issues offer Rework (recommended), Planned, Designed, Groomed, and New; packaged simple issues omit Planned. Failed cards that still carry workflow stage label X also include X when valid. An active local issue lock makes the preview unavailable.Opens the Reset Issue preview: preserving for Rework and destructive for earlier targets. A failed card with visible enabled or disabled Recover change keeps earlier-stage Reset as the destructive secondary path.
PriorityEllipsis menuThe card menu can be opened.Sets local priority to High, Normal, or Low; priority labels are projected to GitHub only when issue projection is enabled.
Release lockInline on a card whose run owner is no longer runningThe card has a stale run lock and a release handler is available. Ship, Next, Groom, merge, and other state mutations remain unavailable until the lock is released.Removes the abandoned run lock without inferring recovery or resetting workflow state.
UnlockEllipsis menuThe card has an operator hold or invalid lock record; an unlock handler is available; and the issue is not currently shipping. Live runs do not expose Unlock.Clears an invalid record directly. An operator hold discloses its pause time without a PID and requires a deliberate Force unlock confirmation.
UnblockEllipsis menuThe card has shipper:blocked, is not locked, and is not currently shipping.Starts a background shipper unblock command.
Close as not plannedEllipsis menuThe issue is not locked or shipping, and the required reason textarea contains non-blank text.Atomically records the verbatim reason as an ordinary local note and the not-planned tombstone; mirror-on then projects the note comment, closes the GitHub issue, and removes Shipper labels.
Un-adoptEllipsis menu on every live cardPreparation authoritatively checks imported origin, running or queued work, open pull requests, dependents, and owned artifacts. No force path exists.Confirms exact cleanup and conversions, preserves the GitHub issue, then removes the issue from Shipper management.
Resolve closureHeld card actionThe issue has an active external closure hold and is not locked. The not-planned apply choice also requires a non-blank reason; its preview remains reason-free.Opens the unselected three-choice closure dialog. Reopen restores the saved state; not planned records the ordinary reason note with the tombstone; complete requires the assertion/dependent preview and second confirmation.

Desktop, CLI, and MCP clients on the same machine coordinate through local issue lock files under the per-repo Shipper issue-state root. Each lock is keyed by Shipper local issue ID and records either a PID-backed run owner or process-independent operator-pause owner.

If a second local client finds active ownership, it avoids conflicting work. Cards show operator pauses as operator hold, dead run owners as owner stopped, and malformed records as invalid lock. Live run status and origin remain in issue details; the one-line card footer keeps timing and token usage. Live or indeterminate run owners remain active regardless of age; wait for them to finish or use the owning client to stop them. An operator pause remains active regardless of age and is excluded from stale cleanup.

The short reservation-only window before a workflow becomes queued or running is also ownership. The card disables Reset, Pause, Groom, Ship, Next, and drag as applicable without a reservation badge or unavailable explanation. Inspect issue details for ownership guidance. Once a controllable row appears, queued Ship offers Pause and Stop, while queued Next offers Stop. Stop is shown only when Desktop has a real queued or running session it can control.

Release lock removes a dead owner’s run lock; Ship, Next, Groom, merge, and other state mutations stay unavailable until that explicit release. Unlock cleans invalid records and remains the cleanup for an invalid record. Use Resume for an operator hold; Force unlock remains available only as a deliberate, reviewed hold override and releases the hold without performing Resume. For a healthy or indeterminate live run, wait for it to finish or stop it from the owning client—Force unlock is not normal recovery and the card exposes no cleanup action. Locks are single-machine only: two different machines operating on the same repository do not observe each other’s local lock files.

Auto-ship and Auto-merge are per-repository toolbar toggles on the pipeline board.

Auto-merge is saved in desktop app config per repository. Select the toggle to enable it for the active repository; select it again to disable it. Automatic merging requires both Auto-ship and Auto-merge. With Auto-ship disabled, Desktop does not dispatch Ready work automatically and the manual merge action on an eligible Ready card remains available and unchanged.

When both toggles are enabled, Desktop fills a single-occupancy automatic merge slot from Ready issues only, independently of ordinary auto-ship worker capacity. Ready issues are ordered by priority tier (high, normal, then low), then by the earliest Ready stageEnteredAt time within a tier. Desktop refreshes that ordering whenever the slot becomes free and dispatches only the current head. An automatic merge or a Ready issue waiting for the merge slot does not consume an ordinary worker slot, so groom, design, plan, implement, and other non-merge work continue at the full configured concurrency.

Automatic Ready waiters stay on their existing Ready cards with a neutral queued badge, also used for commands queued to start. Coincident queue conditions show one badge. Waiting creates no spinner, toast, failure, retry affordance, or failed run. A manually requested merge also waits instead of failing; its issue run details say Another merge is running for this repository. The local repository merge lock remains the cross-process guard for CLI or other external merge activity. For adopted foreign-branch PRs, Ready merge uses the durable issue-side association read-only. If that PR was confirmed closed without merging, use shipper issue reconcile --repair to preserve the implementation branch and clear the dead association; if it cannot be found, issue details direct you to re-establish the link.

Desktop and CLI use the same core merge path in both mirror-on and store-only repositories. See the command reference for canonical merge method, branch update, dry-run, and remote deletion behavior, and the state machine for the canonical merge failure taxonomy and counter semantics. A successful internal method fallback produces no failed command, Retry action, or retry count movement. A post-merge remote-deletion refusal is only a warning and does not change the successful Desktop outcome.

When pull requests are disabled, a Ready card identifies its pending change by packaged source branch and evaluates the target branch locally. Merge is available when the target branch is not checked out or its checkout is clean. A dirty checkout waits without changing the Ready styling and names the exact target branch and checkout path. If no packaged change exists, the card directs you to reset and re-ship under the current no-PR policy. If Git inspection fails, merge fails closed and shows the inspection reason. Normal pipeline refreshes silently evaluate these states again, so Auto-merge starts as soon as a waiting checkout becomes eligible without adding a toast, failure, or run failure while it waits. Issue detail shows the same packaged branch as Pending change.

When pull requests are enabled, existing GitHub-backed behavior is unchanged for cards with linked pull requests: issue detail retains the GitHub PR #N link, while Ready checks, external-merge reconciliation, adoption, and issue details use the policy-approved PR artifact. Change recovery alone follows the raw stored artifact, so a pinned local change from an earlier posture remains recoverable.

Each normal pipeline refresh includes an externally merged ready-issue reconcile step. It reuses the bulk lookup of open pull requests and performs a targeted lookup only for a ready issue whose stored link is absent from that result. When the linked PR was already merged outside Shipper, desktop completes the issue under the repository merge lock, immediately re-checks dependents, and removes the card in the same refresh without deleting its remote branch. Success and a busy-lock skip are silent, with no toast or notification; a busy lock is retried on the next refresh. If completion fails, the card remains and says that the linked PR was merged externally but Shipper could not complete the issue.

Auto-ship is enabled per repository. Select the toggle to enable it for the active repository; select it again to disable it. After a ship finishes, desktop looks for the next eligible issue in that repository and starts it automatically. Auto-ship skips issues that are already active, paused, blocked, failed, or actively locked. Stale local locks do not block eligibility.

When a daemon-hosted ship or merge session successfully performs a merge, Auto-ship immediately re-checks that issue’s blocked, non-New direct dependents, whether the run was started automatically or manually. Targeting includes native Shipper references and GitHub issue numbers mapped to an active or tombstoned managed issue. It does not wait for repository idleness or worker capacity, does not preempt running work, and does not promote the released issue. Halt, safety, lock, pause, quota, dispatch, and bounded-retry controls still apply. Remaining unresolved blockers stay silently Blocked. Completions observed outside Shipper keep their existing behavior, including the externally merged Ready-card reconcile path above. When the repository is idle, the untargeted automatic-unblock sweep remains the backstop for unmapped external GitHub blockers and for dependents a targeted attempt did not resolve. Targeted activity uses the existing automatic-unblock status.

The enabled state and worker count are machine-global desired state. They survive a Desktop quit and restart; an enabled tracked repository resumes silently at launch without a banner, notification, or starting/armed toast. Another local client can change enabled state, worker count, or record a halt, and Desktop observes the change in approximately five seconds even while its window is minimized or unfocused. Raising capacity makes new slots available; lowering it starts no work beyond the new limit and does not terminate running sessions.

Desktop refreshes local supervision diagnostics every five seconds, including when the daemon is absent or recovering and the repository list is empty. An enabled, unregistered saved repository appears persistently at app level: owner/repo is enabled but unregistered. Add owner/repo in Desktop. The Add repository action opens the existing picker. The sentence survives restart, tab changes, and failed refreshes, and clears after observed registration or disablement. A failed refresh labels retained evidence Last known; current status unconfirmed.

Tabs use short Unregistered, Unavailable, or Pickup unconfirmed badges; operational details explain actual paths, causes, and recovery. Pickup unconfirmed alone is neutral, uses unknown capacity, and asks for a refresh after roughly five seconds. A connection or live owner alone does not prove pickup; supervision does not imply running work. Unreadable registry choices show Auto-merge unknown and refuse mutation while the repository list and appearance stay usable. Enable refusal says Enablement did not complete, preserves prior intent, and supplies the fix.

Unreadable registrations are preserved unchanged: use a compatible build for newer information, restore a damaged file from a valid copy, or correct the reported access/lock problem. Discovery uses existing saved intent and verified checkout mappings, not a new membership catalog. An enabled legacy schema-1 record without a verified identity is shown by path as an unidentified enabled saved record with add-repository guidance; corrupt/future records show a discovery limitation without asserting enabled intent. If all identifying evidence is gone, a name cannot be recovered. When an enabled repository leaves supervision, the daemon reports a named warning and an auto-ship-supervision-lost diagnostic.

The board header keeps Auto-merge, Auto-ship, and worker capacity visible beside one compact operational-status pill. The pill always combines an icon, text, and a health color. It reports Auto-ship off, Halted, Active, or Idle with trusted N/M slots; when capacity cannot be confirmed it says slots unavailable. Color is never the only status signal.

Healthy and automatically recovering operation stays quiet: disabled or inactive Auto-ship, idle or active dispatch, normal or waiting observation, expected or absent ownership, active or quiescent remote watch, healthy or guarded quota, explained automatic waits, automatically retrying supervision delivery, intentional halts, and normally progressing workflow activity show no operational banner. The Halted pill is therefore a quiet repository posture, not a failure.

When operator attention is required, exactly one non-dismissible operational/supervision banner appears beneath the header. Observation errors, warnings, evidence-based unconfirmed observation, and unavailable observation facts appear in this banner and operational details while the pill keeps its ordinary Auto-ship off, Halted, Active, or Idle posture and slot use. Other attention conditions make the pill say Needs attention with current slot use or slots unavailable. The banner’s leading plain sentence follows four severity tiers: safety suppression and control-record compatibility first; unavailable supervision or runtime, a recovering host, unknown quota or ownership, each applicable repository dispatch failure, and each exhausted delivery area second; observation errors, severe or floor quota, and secondary-rate-limit pauses third; and observation warnings or evidence-based unconfirmed observation fourth. An actionable condition leads within its tier, remaining ties use stable identity order, and the banner reports how many other conditions remain. It stays present and advances to the next condition until all attention conditions clear, exposing the union of every currently valid recovery action.

Activate the pill or choose View details to open the same labelled operational details region. It starts closed, never opens automatically when health worsens, remains open or closed while the same repository is selected, and resets closed after a repository switch or a new Desktop session. Details contain all available control, runtime, launch-retry, observation, remote-watch, quota, host, supervision-delivery, dispatch-ownership, repository-failure, compatibility, and recovery facts. Automatically retrying delivery episodes and the informational fact that Desktop is hosting background supervision appear here without creating attention banners. Visible timestamps use the local date and time with timezone context; useful relative elapsed durations remain beside them.

Safety Resume, observation recovery, guarded ownership recovery, host Retry now, and each exhausted-delivery retry appear in both the banner and details when valid, sharing pending, disabled, confirmation, result, and focus behavior. Clear halt is details-only and never appears in the banner. When safety suppression and a halt coexist, the banner offers Resume; after suppression clears, the quiet Halted pill remains until Clear halt is selected in details. These recovery actions are not duplicated beside Auto-ship.

Healthy, waiting, busy, and inactive observation offers no observation recovery actions, including in details. Deliberate quota-pacing waits and completed inspections with no dispatchable work stay quiet; elapsed wait time alone does not require recovery. Details retain the wait reason and actual next observation time. Genuine stalled, failing, overdue, or unexplained observation still raises attention with applicable recovery actions.

Per-command command uncertainty alerts and the per-checkout local working copy alert remain independent. They are not operational/supervision conditions and do not count toward the single banner.

Turning Auto-ship off in Desktop cancels queued automatic work and asks running automatic Ships to stop at their next safe checkpoint. An external not-enabled change has the same effect. Disable is enabled: false: it drains current automatic work and prevents dispatch until Auto-ship is enabled again.

A halt is different. It is a persistent operator-requested hold on the repository that preserves the enabled state and worker count, withdraws queued automatic starts, asks running automatic Ships to stop at their next safe checkpoint, and blocks every later automatic stage and merge. It survives an idle repository, wind-down completion, a Desktop, daemon, or machine restart, and disabling and re-enabling Auto-ship. It ends only when an operator clears it.

While a halt is recorded, the quiet operational-status pill reads Halted; draining and unavailable runtime facts remain available in View details. The Auto-ship toggle continues to communicate only enablement, and the worker selector stays visible and operable throughout.

The details distinguish Halt requested while running work is Draining from the settled Halted posture after that work reaches its checkpoint.

Open details and select Clear halt to release the hold. That action does not enable Auto-ship and does not touch the worker count or the safety stop; Resume remains the separate safety-stop recovery gesture and never clears a halt. While the request is in flight only the Clear halt button is disabled, and a refusal is reported inline beside it. Clearing releases repository-control checkpoint Stops that have not yet been consumed, so if the repository is observed enabled and unhalted before a run consumes its request, Desktop clears that run’s pending-stop presentation within the normal observation window and it continues running. Explicit card Pause or Stop remains intact, and manual advances stay available throughout. Clearing does not relaunch stopped work, recreate a withdrawn queue entry, dispatch replacement work, or add a badge, toast, notification, or run entry.

Auto-merge remains a separate Desktop-local per-repository setting. An external client can enable Auto-ship and advance workflow stages, but nothing merges automatically unless the operator has also enabled Auto-merge for that repository in Desktop.

The active repository’s on-demand operational details include one Dispatch ownership fact. It reports not held, held by this Desktop’s background owner, a brief direction to the panel above for a competing or unverified holder or an unavailable inspection, or Unknown. Unknown is shown whenever Desktop lacks the expected-owner identity, an operational daemon or in-process host, or a matching represented live repository row; missing evidence is never presented as ordinary ownership.

The ordinary held reading requires the exact running daemon or in-process fallback instance serving this Desktop. Merely having a Desktop or daemon surface is not enough: a same-surface holder with a different instance identity is competing ownership. The ordinary reading is a collapsed disclosure. Open it to see the repository, holder surface and PID, acquisition timestamp, liveness, Last refresh (diagnostic context only), and the consequence statement: While dispatch ownership is held, any other scheduler on this machine stands down rather than dispatching for this repository. Last refresh is diagnostic context, not freshness, expiry, or liveness evidence.

The disclosure also keeps Clear dispatch ownership reachable when the expected owner appears healthy but is no longer delivering work. Use it only when that holder can no longer be trusted to release normally. The first selection is a read-only preview. Desktop opens confirmation only while ownership remains held, repeats the repository and five facts, and warns: Discarding dispatch ownership held by a genuinely running holder means two schedulers will dispatch for this repository concurrently. The confirm action sends the preview’s opaque token. If the owner changed, Desktop keeps the dialog open, says nothing was cleared, disables the prior confirmation, shows the newly observed status, and offers Review current ownership for a fresh preview. A no-owner result is a truthful no-op. An unavailable result names the read failure and offers no blind retry.

A terminal or unrecognized holder, indeterminate liveness, a Desktop or daemon instance other than the expected owner, and any other unverified held state contribute an ownership attention condition to the single banner. Full repository and holder facts, consequence, and guarded recovery remain in details. When inspection itself is unavailable, the condition says Dispatch ownership could not be inspected, names no holder, and offers no clear control.

The operation is repository-scoped and does not change Auto-ship desired state. Clearing one repository leaves sibling repositories and every state.json unchanged. The normal supervision observation cadence then reacquires ownership and resumes dispatch for only the affected repository; no Desktop reload or manual refresh is required.

Desktop consumes the same six current repository-safety identifiers as the CLI: terminal-stage-failure, merge-retry-exhausted, pending-checks-exhausted, groom-output-invalid, worktree-setup-contention, and upstream-merge-wait. Every completed background Ship transports either literal null or one of those classifications from the validated command-result sidecar. Missing or invalid sidecars and background process failures become explicit terminal-stage-failure occurrences. The UI’s leftStuck field controls presentation and retry behavior only; it never classifies repository safety. Blocked entry refusal and blocked post-work stop are explicitly neutral. A progressed blocked stop resets an earlier streak before its neutral blocked outcome is handled.

Pending CI checks are a deferral rather than a failed Ship. Desktop keeps every such command as a Deferred issue run status in issue details and updates one stable informational notice per repository and local issue. The notice names the PR, the latest incomplete checks, and the fixed deadline. Deferrals do not move retry counts, permanently skip the issue, contribute to the repository safety stop, or show an error toast. Auto-ship reselects the issue after the normal check cadence while manual merge actions retain the same application-session clock for the next action.

The clock measures total elapsed time for one PR head SHA using merge.pendingChecksTimeoutMinutes (default 60). A new commit starts a full new window, and closing, hiding, reopening, reloading, or quitting Desktop preserves the daemon-lifetime window. Deliberately disabling and re-enabling Auto-ship starts a fresh enabled run. When the same SHA reaches the deadline, Desktop changes that issue run status to Failed, suppresses Retry, and reports CI checks never completed within N minutes for PR #P: CHECKS. After the checks complete, recover with shipper recover S#123 or Recover change. A required check that never reports any status is not visible as pending and is outside this bound.

Only checks-pending creates or retains this timer. Required approval, a temporary Ready lookup, dirty local base, transient publication failure, and other typed run-skip outcomes keep the stage, show neutral waiting feedback, and skip that issue only for the current enabled run. They create no deadline, retry count, or repository safety occurrence.

When a clean GitHub outage consumes the full waiting allowance, the command exits 79. Desktop records the issue run as unsuccessful but non-stuck, keeps the issue at its workflow stage, and skips it for the rest of the current enabled run. Deliberately disabling and re-enabling Auto-ship lets the issue be selected normally. The outcome does not advance the repository safety streak.

Retriable automatic Ship failures use per-issue session backoff. Consecutive failures wait 30, 60, 120, and 240 seconds before that issue can be selected again; the delay is silent, affects only that issue, and lets other eligible issues continue. Only temporary failures participate in this retry count. Determined conditions leave Ready immediately for Rework or normal review and create no separate cycle. A confirmed, non-halted merge clears the retry count. Closing, hiding, reopening, reloading, or quitting Desktop preserves timers and counts while the daemon drain remains alive. Disabling and explicitly re-enabling Auto-ship starts a fresh enabled run with new retry tracking. Attempts one through four do not raise notifications; retry state appears in issue run details.

On the fifth consecutive retriable automatic failure, Desktop stops redispatching that issue for the current enabled run, suppresses Retry, and records one repository-scoped merge-retry-exhausted safety occurrence. It does not persist failed state. A later explicit enable starts a fresh retry sequence and can select the issue without Recover change; attempts one through four remain silent and do not move the safety counter. Three retry-exhaustion occurrences pause Auto-ship for the repository.

For non-Ready work, Desktop uses the same store-backed auto-ship ordering as the CLI: priority tier first, then workflow stage preference, then FIFO by the local issue-state replica’s stageEnteredAt timestamp. Ready issues never participate in that ordinary queue; the independent merge slot uses the Ready-only ordering described above. If the replica is absent or unreadable, desktop reports the shipper issue reconcile --repair recovery message and does not fall back to GitHub timeline selection.

Store-only and mirrored candidates participate in auto-ship and auto-unblock the same way. Desktop dispatches them by Shipper local ID and uses GitHub issue numbers only as optional display and projection metadata. Store-only merge deferral, timeout, and failure reporting remain local and make no GitHub writes.

Required PR approval leaves a card Ready with neutral waiting feedback and consumes no retry; an approved later dispatch can merge. No usable merge method fails immediately with repository setting guidance. Missing Ready package, mirror, link, or PR records also fail durably with their specific reset, reconcile, PR-open, or adopt recovery instead of repeatedly returning to the queue.

The shared merge failure taxonomy routes by structured cause rather than console wording. Merge conflicts atomically return the issue to Rework for implementation. Failed checks, a blocked merge, a still-behind branch, a branch-update failure, and an unknown determined condition return it to PR Open (or Change Packaged) for normal review. Both destinations suppress a direct Retry action because the normal workflow owns the next step, and neither route contributes to repository safety. Temporary causes remain at Ready and use the bounded retry schedule; missing merge capability and divergent base publication fail immediately.

For pending-check expiry and other durable failures, use Recover change when the failed card’s packaged artifact remains recoverable. It preserves the packaged work, clears durable failed state, and clears the current Desktop session guard. Retry exhaustion needs only a later explicit Auto-ship enable after the transient condition clears. Use Reset only when the source branch is missing or no longer contains the pinned revision, the linked pull request is no longer open, or no packaged artifact exists. Store-only exhaustion and recovery reporting remain local and perform no GitHub writes.

Auto-ship uses the canonical failure-outcome and repository-safety contract shared with CLI automatic runs. Three distinct qualifying condition instances suppress Auto-ship for the repository by engaging a durable safety stop. Unchanged repeats—including identical surviving workspace processes—share one strike. The machine-global desired state remains enabled, and Desktop keeps the control pressed while changing it to the warning presentation Auto-ship suppressed. The repository panel retains the three distinct conditions that engaged the stop, the engagement time, and the lifetime engagement count across Desktop and daemon restarts. Automatic dispatch remains suppressed until an operator deliberately clears the stop. Enabled therefore means requested rather than dispatching: a halt, a safety stop, or another closed gate can keep an enabled repository from starting any automatic work.

Pre-start automatic Ship, Merge, and Unblock failures use two separate board states. Launch retry scheduled shows whether the repository or one issue is cooled, its consecutive attempt count, and the effective next-attempt time. After the fifth failure, Launch recovery-only shows the same scope and count plus the automatic wake conditions: a cooled-scope change or the ordinary heartbeat. The pressed Auto-ship control remains enabled in both states. The existing notice and affected issue cards update in place and provide no Resume or clear-retry action and no next-pass promise. A successful automatic or manual start clears the repository episode and the started issue’s episode, an ineligible issue’s episode retires, and disable followed by re-enable clears the enablement ledger. Notice dismissal and Desktop or daemon restart do not clear it.

Select Resume in the operational banner or details to clear only the safety stop in one action. Resume preserves the repository’s enabled state, worker count, and halt value, and is never a halt-clear gesture; use Clear halt for an operator hold. When both coexist, Resume is in the banner and details while Clear halt remains details-only, and the two actions are independent. The pressed Auto-ship control remains a separate Disable action; turning it off and then genuinely enabling it again is the alternate reset gesture. A dedicated resume or disable/re-enable transition from another client updates the board back to its normal enabled presentation automatically, without a Desktop restart, reload, or manual refresh. When multiple repositories are suppressed, each tab has its own warning marker and opens that repository’s retained diagnosis and Resume action.

Disabling Auto-ship prevents new Ready auto-merge dispatches as well as ordinary auto-ship work. A successful workflow advancement advances the repository’s durable progress generation before any later condition from that command is classified. Earlier strikes remain, while the same condition observed after that progress is retained as a recurrence. Closing, hiding, reopening, reloading, or quitting Desktop does not reset the pending ledger, durable safety stop, skipped-issue set, pending-check deadline or retry state. The CLI uses the same failure contract for each invocation, but stops selecting or refilling work, drains already-running work, reports any eligible unattempted issue, and exits 1.

The five desktop columns remain visible even when empty in every repository policy presentation. Resetting eligible packaged work to Rework places the card in Implement with a current-state Rework badge. Rework is the recommended preserving menu choice and drop target on simple, complex, and undecided routes. The main-process preview confirms the package and shows its branch, revision, route and preservation guarantees. Missing or unverifiable evidence disables confirmation with an explanation.

Desktop sends the original previewToken back on confirmation; missing or changed tokens require a fresh preview. Rework uses a normal confirmation, preserves all packaged work and history, and may be repeated. After Reset committed; local recovery pending, confirmation is disabled and the board refreshes; do not repeat the operation. Next and auto-ship run implementation for Rework. Failed Rework recovery clears failure while retaining Rework and its existing trigger.

The cleanup flow below describes destructive earlier targets. Rework is the separate preserving choice; --to planned deletes managed branches under existing safeguards.

For a failed, non-held card with a saved grooming question session, preserved groom output, or a packaged artifact, Recover change is the primary action. Groom recovery validates the preserved tree and, only if needed, runs one unattended correction without restarting discovery or re-asking answered questions. Packaged recovery preserves the exact revision, source branch, artifact, and completed review history. The action remains visible but disabled for unreadable or ambiguous recovery state, ineligible packaged work, locks, or another active action, without unavailable tooltips or explanatory text. Recovery reasons and ownership guidance are available in issue details. Earlier-stage Reset remains the destructive secondary path. Rework is the preserving reset choice.

Session recovery runs no agent and asks no questions. It clears failed state, returns the issue to New, reports the preserved answered-round count, and keeps the conversation with its owner unchanged. Desktop can resume normally only when it owns the session; an MCP- or terminal-owned session must be taken over first, and an unowned legacy session must be claimed through takeover. A session takes precedence over simultaneous groom output while retaining both.

Reset targets are route-aware. Packaged simple-route issues offer Rework, Implement (completed target designed), Design (groomed), and Groom (new). Packaged complex and undecided issues offer Rework, Implement (planned), Plan (designed), Design (groomed), and Groom (new). Only eligible choices are rendered. Resetting to Groomed or New clears the stored route back to undecided because Design will re-run; resetting to Designed, Planned, or Rework preserves it.

Reset is a two-phase flow. It is not a one-click destructive menu action.

Phase 1 is preview. Selecting a valid target opens Reset Issue and scans what will be cleaned up: the target stage, labels to remove, pull requests to close, remote branches to delete, local branches to delete, worktrees to remove, preserved grooming output to discard, and issue comment count. A saved grooming question session appears as its own item with its answered-round count, separate from preserved grooming output. Preview can fail before showing a summary if the issue is closed, the issue has an active local issue lock, the target is the current stage for a non-failed issue, or the target is ahead of the current stage. Preview is advisory and does not acquire or preflight a workflow reservation.

If preview finds a pinned local change, the dialog renders a destructive alert with its full packaged revision and source branch and recommends Recover change for an eligible failed issue. This local warning does not request a pull-request acknowledgement. If preview finds an open pull request, the dialog instead names it and states that reset will close it and clear the stored association. The pull request number shown in preview is sent back as the reset acknowledgement. Reset-to-New also names preserved grooming output and saved question sessions before the confirmation button can be used; Recover change is the preserving alternative.

Phase 2 is execute. Pressing Reset repeats validation, acquires the issue lock, runs cleanup, commits the target stage to the local issue-state store, optionally projects workflow labels and the reset notice to GitHub, and releases the lock. If the execute-time rescan finds an open PR that was not acknowledged in preview, desktop makes no changes, keeps the dialog open, and requires a fresh preview. Preserved grooming output and saved sessions are deleted only after the local Reset-to-New transition commits. Execute’s atomic issue-lock acquisition is authoritative: a reservation or run that wins after preview refuses Reset before cleanup, preventing a second pipeline execution.

Reset normally works backward and only on open issues. If a failed card still carries workflow stage label X, desktop includes X as a same-stage recovery target; the reset clears failed state, preserves X-stage work, removes artifacts after X, and posts the standard reset notice. The Reset action is hidden while the issue is currently shipping, and both preview and execute refuse active local locks.

The desktop app does not currently provide these capabilities:

  • Application-level keyboard shortcuts for issue-card actions.
  • Right-click issue-card context menus.
  • Permanent archival of command output after the supervision host’s retained-output lifetime.
  • Changing paused issues so they hide Ship, Next, or Groom; paused cards keep normal actions as described above.

Shipper stores shared workflow identity in the local issue-state store. When issue projection is enabled, it mirrors these fields and artifacts to GitHub:

  • Workflow labels such as shipper:new, shipper:planned, shipper:pr-open, shipper:rework, and shipper:ready.
  • Control labels such as shipper:blocked and shipper:failed.
  • Local issue lock files used by CLI, desktop, and MCP clients on the same machine.
  • Priority labels used by auto-ship ordering.
  • Issue comments containing grooming, design, plan, implementation, review, and summary artifacts.
  • Pull requests and CI checks used by review, implementation review-rework, and merge.

Desktop also has local state. Legacy pause state is migrated into the shared issue-owner store. The renderer rehydrates current background sessions, activity tasks, durable interrupted outcomes, Auto-ship state, and retained output pages from the daemon or sticky in-process fallback. Live output chunks and setup/auth terminal output remain transient. Local stage output, terminal sessions, settings, and the shared local issue-state store follow the same Shipper conventions the CLI uses. The issue-state store and workflow event log are authoritative for Shipper-managed workflow stage, priority, failed state, workflow history, and issue title/body content, and those fields plus workflow comment projections are projected to GitHub only when mirror-on policy permits issue-side effects. Desktop is a UI and supervision client over that shared model.

  • Desktop distribution is macOS arm64-only today.
  • Release artifacts are unsigned and not code-signed today.
  • Windows and Linux desktop artifacts are not currently provided.
  • GitHub-backed repositories require GitHub CLI authentication; repositories with no remote transport remain usable without it.
  • Release tags, package publishes, and desktop release uploads are maintainer-owned release tasks, not actions performed by the desktop app.

Required-check obstruction and stalled remediation

Section titled “Required-check obstruction and stalled remediation”

New automatic check stops apply only to independently acquired hosted checks. Shipper stops in Failed over Rework when current hosted evidence establishes an external obstruction after useful same-pass fixes, or when a completed, answered check-only review repeats with the same failing evidence on the exact unchanged revision. Both stops retain the package, branch, PR association, unpublished fixes, canonical answers, evidence and outstanding findings. Pending remote answer delivery remains visible separately from handled and resolved findings.

An external obstruction records concrete evidence, causal reasoning, an external remedy and why the failure still applies to final HEAD. For example, a required service explicitly rejects an expired organization credential while local code fixes pass: its owner must rotate the credential. A red check, a matching failure on main, or a missing tool alone does not establish that cause. Uncertainty calls for investigation; sandbox limitations and transient failures retain their normal verification and rerun opportunities.

Stalled remediation makes no external causal claim. For example, a completed V4 review’s only blocker is a required hosted verification failure, its canonical answer is retained, no code changes follow, and a fresh hosted acquisition repeats the identical identity and diagnostic evidence. Detection runs after existing refresh/rerun/flake handling and before another review agent. Nits are neutral. Changed HEAD, a passing check, a new blocker, materially changed evidence/procedure/source, pending or unavailable outcomes, incomplete evidence, legacy baselines, unanswered blockers, or request-changes review bodies prevent this mechanical stop. It does not execute a saved arbitrary local command.

Local verification is reported in existing prose summaries and relevant comments, without a structured local evidence report. Every attempted non-permission local failure, including an apparently unrelated or pre-existing failure, requires an anchored 🔴 must-fix: comment on the most relevant changed line and REQUEST_CHANGES, followed by ordinary Rework fixes or prose replies. Permission-denied and unattempted checks are limitations, never passes or waivers. All previously required checks stay required. Local-only workflows have no local-evidence automatic stop; the ordinary review-round cap still applies.

Historical local observations and failure records remain readable without migration. Outstanding historical local findings receive ordinary prose replies and never authorize a new automatic stop. Upgrade alone does not clear findings, renew allowance, or recover Failed. Publication and hook diagnostics and required hook execution remain unchanged.

Each applicable stop records one terminal safety occurrence. A skipped review creates no held round and consumes no review allowance. All required checks remain required: answers, reports, thread resolution and retry actions cannot authorize shipping or merging red checks.

Applying a remedy or restarting Shipper alone leaves the issue Failed. Inspect the durable diagnosis with shipper issue view S#123 (or --json), then explicitly retry with preserving shipper reset S#123 --to rework, or shipper recover S#123 when recovery is eligible. Existing eligibility, locks, closure holds and confirmation rules apply. Both explicit paths renew the five-round review allowance on unchanged HEAD without a dummy commit, preserve history and useful unpublished work, and start a fresh attempt. Missing or divergent retained source refs refuse retry; restore the source history first. Normal integration, hooks, verification and review must confirm the remedy before advancement. Retained failure evidence becomes historical after renewal or completion and remains available on Desktop, CLI and MCP, including failures with zero rounds.