---
title: Unfinished work card (branches nobody is coming back for)
---

# Unfinished work card (branches nobody is coming back for)

## TL;DR

When a session goes away and leaves commits that never merged, this inbox card is where you find them and decide: pick the work back up, or let the folder go and keep just the branch.

## What it is

Every agent session works on its own branch in its own folder. When a session ends, closes or is archived before its work lands, that work is not lost — the branch still holds it — but nobody is coming back for it. This card lists every such branch, with how many commits it holds that the main branch does not.

The card covers every project you work in, not only the one you are looking at, so its branches are grouped into one fold per project. The heading names the project and says how many branches are inside it, and a project holding enough branches to fill more than a screen arrives folded shut — so one project's long list never buries another project's work behind it. Opening a fold shows the same rows and the same two buttons described below; nothing is hidden or left out, only folded.

A row comes in one of two kinds:

- **The folder is still on disk.** The row shows where the folder lives.
- **"Folder reclaimed — branch kept".** The folder was freed so the disk could be reused (freed folders are routine), but the branch was kept, and it still holds work that never merged. The old location is not shown, because another task may already be using that folder.

Saved Work lists work that was already rescued; this card lists work that has not been.

## Where to find it

It appears in the inbox as **"Unfinished work nobody is coming back for"** whenever at least one such branch exists. Open it to see one fold per project; open a fold to see its rows, and each row has its own buttons.

**On your phone** the alert arrives as usual but the card itself does not: this list and both of its buttons live only in the desktop app, and the phone says so in one line instead of showing you a list it cannot read. Open Omniscio on your computer to see the folders and act on them.

## How it behaves

- **Pick it back up** starts one new agent session on that branch to finish the work. That costs money like any session, so it only ever happens when you click — and two clicks on the same row still start just one agent. On a "Folder reclaimed" row it first makes a fresh folder for the branch, then starts the agent there.
- **Let the folder go** frees the folder's disk space. Any unsaved changes are saved to a recovery copy first, the save is checked, and the branch itself is always kept. This button is not offered on a "Folder reclaimed" row — that folder is already gone.
- **A row leaves the card by itself** once its branch is picked back up (by you or anyone else), lands on the main branch, or is deleted. Opening the card never scans or changes anything.
- **Known limit.** Right after a folder is freed, the folder pool can still hold the branch checked out for a while. Until the pool reuses that folder, "Pick it back up" says so and starts nothing; it works once the folder is reused.
- **How rows get here.** A background check runs every 10 minutes and looks at recently freed folders (newest first, a limited batch each pass), so a freed branch appears within minutes, and older ones fill in over time.
- **Branches nobody ever asked to finish** also appear here. When a session walks away without asking its work to be finished, that branch is **held** — nothing adopts, gates or lands it — and it is yours to **complete or drop**. It is named in your daily summary every day until you answer, and the same two buttons decide it here. Answering is permanent: the branch leaves both this card and the daily summary for good, and the answer is written onto the branch itself so nothing re-asks you. A branch whose folder was already freed shows no path, exactly like the reclaimed rows above, and "Let the folder go" is not offered because there is no folder left to free.
- **If a branch known to hold unlanded work disappears**, you get a separate card — **"A branch holding unlanded work is gone"** — naming the branch, the last commit it was seen at, how many commits had not merged, where its folder used to be, which session owned it, and which repository it was in. Nothing in the app can bring a deleted branch back, so that card tells you what was lost rather than offering a button that pretends otherwise; the commit id it names is what you would search for, in that repository, before the objects are cleaned up for good. It only fires when the app had actually checked that the branch still held unlanded commits, so a branch that had already merged, been picked back up, or been deliberately released stays quiet.

## For agents

- Readout: `listStrandedWork` in src/main/services/worktree/stranded-work-readout.ts. **Three arms:** open rows with a `stranded` event; closed rows whose newest watch event is `stranded-reclaimed`; and the branches held for the owner's complete-or-drop decision. The badge count is backend-computed from the same rows.
- **Arm 3 (the held branches), and why it is a third source.** They are not ledger rows — measured 2026-09-28, of 22 held branches only 2 had a live ledger row and 11 had no ledger row at all — so they cannot arrive as the events arms 1 and 2 are built from. The same hourly inventory that feeds the daily summary writes `~/.amc/held-branches.json` (`writeHeldBranchQueue`, called from `refreshHeldBranchDigest`) and this arm reads it, so opening the card still spawns no git (I90 holds unchanged). A branch already carried by arm 1 or 2 is skipped: one row per branch. A row with no live folder is emitted as the existing `reclaimed` kind, which is what gives it the right reach with no new row kind and no new button.
- **Who is held, and the answer that ends it.** ONE predicate decides it: `isHeldForOwnerDecision` in `src/shared/land-core/rescue-hold.mjs`, shared with the ops rescue that refuses to touch those branches. Both actions write `owner-decided:<complete|drop> | <ISO>` into the branch DESCRIPTION on success (after the spawn is confirmed / after the preserve is proven), and the predicate excludes it — neither action removes the branch, so without a durable mark on the branch the next pass would re-ask. `isAnsweredByOwner` is asked SEPARATELY by the ops rescue's `partitionStranded`, so an answered branch lands in neither its held nor its recoverable list.
- **`worktreeId` may be empty, and two writers respect that.** `writeReclaimedClearedMark` writes no ledger event when the id is empty, and ADOPT's single-flight `creatorFingerprint` falls back to repo+branch — keyed on the id alone, every no-row branch would share one fingerprint and a second row's "Pick it back up" would silently do nothing.
- Background watch: src/main/services/worktree/kept-branch-watch.ts, startup task 1521, pausable from the Resources diagnostics panel; off switch `AMC_DISABLE_KEPT_BRANCH_WATCH=1` (default ON).
- The watch never turns a git fault into an answer: a repo whose branch list cannot be read (the read failed, or came back empty) is skipped for that pass and counted as `repos-unreadable`; a landed proof that fails counts toward the three-strike `repo-unreadable` record. Branch names match regardless of case.
- A record is about the branch it names. If the row's branch label later changes, the watch keeps checking the recorded branch. Known limit: an ops adopt that hands the row to a live session hides it from the card (never seen on the owner's box).
- Actions: src/main/services/worktree/stranded-work-actions.ts (`adoptStrandedBranch`, `dropStrandedFolder`); a reclaimed row's DROP is refused as `no-folder` before any path is touched.
- The loss alarm: src/main/services/worktree/lost-unlanded-branch.ts is the ONLY writer of a `stranded-reclaimed-cleared` event. It carries the record's tip into the clear instead of nulling it, asks git whether that tip is already on the integration branch before believing a `branch-gone` clear means loss, and raises the card. A structural guard fails the build if a new writer appears outside it.
- **Desktop-only, and the phone is told so (I99).** `STRANDED_WORK_LIST` and both actions are in `BLOCKED_CHANNELS` (web-access-ws-channels.ts), so a paired phone's read returns the bridge's `blockedChannelResult` — `success: false`, `code: FORBIDDEN`. That refusal is NOT an empty population: until 2026-09-28 the card drew its empty state on it and told a phone "Nothing is stranded right now." on a box holding 115 stranded folders. `AlertInboxViewer` now mounts `StrandedWorkDesktopOnlyNotice` on a non-Electron client instead of the card — the same answer the mobile-device card gives. The gate is `isElectron` (the thing that actually decides), never a viewport width.
- Contract: .claude/memory/contracts/stranded-work-triage-contract.md (I90–I99).

## Related

The Saved Work view lists work that was already rescued. The orphaned-workspace archive page explains how folders get freed while their branches are kept. The worktree cleanup dashboard shows the background jobs that tidy workspace folders.
