## Verdict
`VERDICT: DO-NOT-SHIP (2 findings)` - all 7 round-1 findings are CLOSED, but the fixes introduced two MINOR regressions; worst: the whole-project delete drops the stored colours of codex threads the CLI refused to delete - the exact survivor-tint loss fix 3 was built to prevent, surviving in the other delete shape.

## Round-1 closure verdicts

1. **Gemini process identity - CLOSED** (runtime-verified). `owns_pid` default at `core/store.py:333-343`, gemini override at `providers/gemini.py:832-845`, consumed via `core/routes.py:139`. Live probe: a plain `node -e 'setTimeout(...)'` (comm `node-MainThread`) now answers `owns_pid=False`; the write-back chain is dead end-to-end because `terminals.ts:383` only applies colour work to terminals the probe reports `running`. The npm/jlpm scenario cannot fire. Residual: see finding 2 below.
2. **Pin-clear - CLOSED**. `core/routes.py:700-703` clears on `session_id is None and fork_from is None`; `panel.ts:750-757` sends the existing row's `encoded_path` so the clear can key. A `fork_from` launch deliberately keeps the pin, and the discovery watcher (`panel.ts:967-994`) pins the minted fork via `_switchBranch` → server `write_pin` - correct. Both new tests pass in isolation.
3. **Removed-ids honesty - CLOSED for the branches shape**. All four stores return the actually-disposed list (claude `:883-911`, kimi `:479-513`, gemini `:696-733`, codex `:636-658`), each id individually OSError-guarded; `routes.py:338-359` responds `removed_ids`; both panel forget sites follow `removed_ids ?? ids` (`panel.ts:674`, `:1055`). Judged on the pinned semantics (already-gone ids excluded): survivor protection intact. But the fix's new whole-project branch reintroduces the bug for codex - finding 1 below.
4. **Migration ordering - CLOSED**. `src/index.ts:288-291` returns before the POST when `settings` is null; the marker is never set, so the next activation retries.
5. **Trash honesty - CLOSED** (runtime-verified). `dispose_path` (`core/store.py:187-205`) lets `send2trash` raise; tested that `TrashPermissionError` MRO is `PermissionError → OSError`, so every caller's `except OSError` catches it, logs, and leaves the item out of `removed`/returns False. `send2trash>=1.8.0` declared at `pyproject.toml:36`. Codex is unaffected by design (CLI archive/delete, no `dispose_path`).
6. **Claude stat race - CLOSED**. `_mtime` (`claude.py:198-209`) guards all four sites (`:597`, `:628`, `:806`, `:811`); `_tail_records` guards its own stat (`:153-157`) and evicts the cache entry on OSError.
7. **Client inherit gate - CLOSED**. `panel.ts:917-925` returns before `_colours.inherit` for `colourSource: 'native'`; server side, `BranchHandler` only inherits for non-native (`routes.py:552-558`) and `ColoursHandler` POST refuses native (`:478-480`). The round-1 SUSPICION (whether a `--fork-session` transcript carries the parent's `agent-color` record) remains open - it still needs an interactive claude session; the finding as filed is closed.

**Shim removal verified**: `routes.py` at package top level is gone, `__init__.py:10` imports `core.routes`, and a repo-wide grep finds no importer of the old path (tests import `core.routes` directly).

## Findings

1. **[MINOR] Whole-project delete drops colours of threads codex refused to delete** - `core/routes.py:344-358`. The fix reads `known = project_session_ids()` before `remove()`, then on `remove() == True` drops colours for ALL of `known` - but codex's `remove()` (`codex.py:625-634`) returns `bool(removed)`, true on PARTIAL success, unlike the other three stores whose remove is one atomic directory disposal. Failure scenario: user invokes Remove Project on a codex row (`panel.ts:570-606`, a live UI flow); `codex delete --force` refuses one thread (the exact premise round-1 finding 3 established as real) while the others go → `remove()` answers True → `drop_colours(known)` deletes the refused survivor's user-set tint server-side, and `removed_ids: known` makes the panel forget it client-side too; the survivor re-lists on the next poll, untinted. FACT by trace; the per-branch shape this class was fixed for is clean. Fix at diff scale: have `remove()` report the ids that actually went (codex already computes the list at `codex.py:633` and throws it away) and hand exactly those to `drop_colours`, keeping `removed_count: 1` semantics for the atomic stores.

2. **[MINOR] Gemini's argv confirmation is a bare substring - any node process with "gemini" in its argv is still claimed** - `providers/gemini.py:845` (`b"gemini" in cmdline.lower()`). **FACT, runtime-tested**: `node /tmp/gemini-dashboard-XXXX/server.js` answers `owns_pid=True` on this machine while plain `node -e ...` answers False. Failure scenario: this extension's audience builds against AI APIs - a project folder named `my-gemini-app`, registered in gemini's `projects.json`, running `npm run dev` puts the absolute project path into the dev server child's argv → the tree walk (`routes.py:135-142`) claims that pid → `running: true` with a null session id → `sessionForCwds` maps the cwd onto the project's row → the round-1 contamination chain (mis-tint plus colour write-back under a cwd-guessed id, gemini's `colourSource` is not native so the `terminals.ts:315` gate passes) fires again, narrowed but alive. Fix at diff scale: match the bundle, not the substring - require a path argv element whose basename is `gemini` or that ends `/gemini.js`/the installed bundle path, rather than `in` over the whole cmdline.

## Tested and cleared
- **Four new closure tests pass in isolation** (`test_a_new_session_launch_clears_a_pin_without_minting_an_id`, `test_resuming_a_conversation_leaves_the_pin_alone`, `test_gemini_claims_only_a_node_process_that_is_actually_gemini`, `test_a_native_binary_store_claims_a_pid_by_comm_alone`); full suites re-run green - pytest 131/131, Jest 61/61
- **`TrashPermissionError` is an `OSError`** - verified MRO at runtime, so the providers' `except OSError` log-and-skip contract around `dispose_path` holds on trash-hostile filesystems
- **`owns_pid` race on a dying process** - cmdline read failure returns None → `owns_pid` False; degrades to "not claimed", never a 500
- **`modes.ts` enum tokens agree with the server gate** - `approvalMode=<value>` tokens composed client-side all appear in gemini's Python `launch_modes` (`gemini.py:895-896`), values lists identical (`default/auto_edit/yolo/plan`); at-default sends nothing. Note: `test_descriptor_parity.py:_mode_tokens` strips the `=value` half, so a future enum-value drift would pass parity and 400 at `routes.py:643` - test-coverage gap only, no live drift today
- **Colour cache honesty** - `colour.ts` `load()` keeps the old cache on failure, `forget()` filters to present ids before DELETE, `inherit()` no-ops on a null parent; no new failure path
- **`_removeProject` partial-failure UI** - optimistic row removal reconciles on the next poll; cosmetic only, folded into finding 1
- **Interrupt journey unchanged** - the raise-on-trash-failure change propagates into existing per-item `except OSError` handlers; no new torn-state window introduced

Journey re-walk: fresh machine and restart paths trace clean through all seven fixes; the interrupt journey introduced nothing new. The one step still unverifiable at runtime is the round-1 claude-fork `agent-color` suspicion (needs an interactive claude session) - carried, not counted.

Not logging to journal: review-only engagement, no artefacts changed.
