Files
Michał Pierzchała 2d2aa6c670 feat: add stale device claim release and dead-end recovery guidance (#2162)
* feat: add stale device claim release and dead-end recovery guidance

Close the #1320 recovery loop for claims no daemon can settle on its own:

- agent-device device release --stale settles a provably dead owner's durable
  resources through the same exact-owner reconciliation open and daemon
  startup use, then clears the claim last — daemonlessly, composing a
  local-only platform gateway in the CLI process. Live, uncertain, PID-reused,
  and corrupt claims always fail closed and are reported with the reason.
- DEVICE_IN_USE conflicts whose recorded owner provably cannot release
  (dead or superseded) now carry the exact release command as their recovery
  instead of a status inspection that dead-ended.
- device status --stale now offers the matching release command when provably
  dead owners are listed.
- daemon stop now warns in text output when a claim was orphaned (previously
  visible only via --json) and names the status/release commands.

Part of #1320.

* test: cover release refusal branches, text rendering, and orphan warnings

Changed-line coverage on the stale-release slice was 69.33% against the 70%
gate: the refusal-reason branches, the text-mode outcome rendering with the
live-owner hint, and the daemon stop orphaned-claim warning had no tests.
Cover them directly; the misnamed-claim-file refusal is also pinned.

* test: prove resources gate stale release and the scan-to-lock race stays closed

Review follow-ups on #2162:

- Two end-to-end CLI regressions run device release --stale through the real
  local gateway against a dead owner whose state dir holds an attributable
  durable screen-recording/app-log record: an owner-mismatched record and
  unreadable resource evidence both retain the claim (app-log-owner-mismatch,
  app-log-descriptor-invalid), proving cleanup must reach a terminal state
  before the claim can be deleted.
- A deterministic race regression holds the per-device claim lock while a
  release is in flight, replaces the claim with a successor before releasing
  the lock, and proves the transaction reports changed without reconciling or
  touching the successor's claim.

* fix: bind stale-claim recovery to the dead owner's state dir

Review P1 on #2162: the CLI composed one gateway from the caller's state
dir, so recovery for a foreign stale claim could clear the caller's live
owned-process record when both used the same session name — Apple recording
cleanup clears by session id through the gateway-composed store.

Recovery is now composed per claim, with the owned-process record store and
session artifact paths bound to the stale claim's recorded state dir, and
disposed after each transaction. The regression writes two dead claims with
one shared session name in different state dirs and proves each recovery is
composed from that claim's own state dir, never the caller's.
2026-08-31 14:32:29 +02:00
..