Skip to content

Reclaim is its own coordinator and commits what the driver returns #373

Description

@V3RON

Part of #359.

Scope

After this PR the component that purges a released device no longer decides whether the device stays running. It commits exactly what the driver's reclaim returns and kicks the queue. Until #368 lands, every released iOS device ends shutdown and the next lease for it boots it, and an Android device released over the running limit stays ready. This is the floor the warm pool module (next task) is built on.

In short

A release still works as today for the caller: the lease is gone at once and the purge runs in the background. The device comes back shutdown on iOS (after the erase) or ready on Android (after the snapshot restore), and that is where it stays: the keep-or-shutdown decision and the boot back to warm are deleted here and return, rebuilt, in the next task. Between the two tasks every iOS release costs the next lease a boot (a boot-shutdown plan), and an Android device released over the limit is not shut down.

sequenceDiagram
  participant REL as Lease release coordinator
  participant RC as Reclaim coordinator (renamed)
  participant DRV as Driver
  participant REG as Registry
  participant ACQ as Acquisition
  REL->>RC: reclaim(released), background
  RC->>DRV: reclaim(device)
  DRV-->>RC: shutdown (iOS) / ready (Android)
  rect rgb(243,217,164)
  note over RC: changed: no keep decision, no rewarm boot
  RC->>REG: commit that state, emit device.reclaimed
  end
  RC->>ACQ: kick
Loading

reclaim coordinator: the renamed WarmPoolCoordinator. It purges, hands a failed purge to quarantine, deletes a spent fresh device, and recovers a reclaim a crash interrupted.

If this goes wrong: a released device is left reclaiming, or a waiting request is not woken after a release.

Technical spec

File and line references are to main at 4a52f6e.

Modules touched

flowchart LR
  classDef changed fill:#f3d9a4,stroke:#8a6a2a
  REL[lease-release-coordinator.ts]:::changed --> RC[reclaim-coordinator.ts<br/>was warm-pool-coordinator.ts]:::changed
  RC --> REG[registry.ts]
  RC --> DRV[driver catalog]
  RC --> Q[quarantine-coordinator.ts]
  ENG[lease-engine.ts]:::changed --> RC
  ENG --> SC[startup-converger.ts]:::changed
  PLAN[acquisition-planner.ts]:::changed --> IDLE[idle-order.ts<br/>was warm-pool.ts]:::changed
  SC --> IDLE
Loading
  • src/core/warm-pool-coordinator.ts becomes src/core/reclaim-coordinator.ts, class ReclaimCoordinator. Delete #warmDecision, #mayRemainWarm, #disposition and #tryMakeReady (:259-345). reclaim commits result.state as returned by the driver with the device.reclaimed event, and calls notifyAvailability. Delete the capacity and queueHeadDemand options and the WarmPoolCapacityReader interface. recoverInterrupted, deleteSpent, #retireSpent, #deleteSpent and #recoverPurgeFailure are unchanged.
  • src/core/warm-pool.ts becomes src/core/idle-order.ts: compareLeastRecentlyUsed, selectWarmVictim (renamed selectIdleRunningVictim), selectManagedVictim, WarmVictimScope (renamed IdleVictimScope). No behaviour change. src/core/acquisition-planner.ts:21,134,167 and src/core/startup-converger.ts:13 import from it.
  • src/core/lease-release-coordinator.ts:48,210,232: the option is reclaim: Pick<ReclaimCoordinator, "reclaim">.
  • src/core/lease-engine.ts:134,236-250,258: construct ReclaimCoordinator without capacity and queueHeadDemand; queueHeadSpec on LeaseAcquisitionCoordinator (:229) is deleted with its only caller.
  • src/core/lease-engine.ts:272 and src/core/startup-converger.ts:121-131,170-181: the excess-ready shutdown step (startup-max-running) is deleted here, not moved; the next task's first pass replaces it. Between the two tasks a lowered maxRunning is converged by nothing at startup, which the next task's Done when covers.
  • docs/internal/COMPONENTS.md: the rows for the renamed coordinator, the idle-order helper and the startup converger. docs/internal/ARCHITECTURE.md: "Release hands the purge off", "Lease subsystem boundaries and wiring", the startup convergence paragraph and the capacity sweep paragraph name the new component and no longer claim a keep decision or a startup excess shutdown. docs/internal/KNOWN-PITFALLS.md "Warm-pool purge failures" names the reclaim coordinator.

Contract and event changes

None. device.reclaimed and device.shutdown keep their payloads; the device.shutdown row in both EVENTS.md files names the reclaim coordinator for interrupted-reclaim recovery.

Rules in play

  • architecture.md rules 13 (every doc that names the old coordinator or the startup step) and 15 (the release path holds a port to reclaim only).
  • testing.md rules 1 and 3: the deleted keep decision's tests go with it; the rename keeps every other test.

Tests

  • A reclaim whose driver returns shutdown commits shutdown, emits device.reclaimed, and kicks acquisition, with no capacity question asked.
  • A reclaim whose driver returns ready commits ready even when running capacity is over its limit.
  • A reclaim never calls makeReady.
  • A release-time purge failure still hands the device to quarantine with the attempted strategy.
  • A spent fresh device is still shut down and deleted, never erased.
  • An unleased interrupted reclaim is still recovered through shutdown at startup.
  • The planner's eviction and the startup converger import the LRU order from idle-order.ts, and no file imports warm-pool.js.
  • At startup, ready devices over maxRunning are left running (the step is gone; the next task proves its replacement).

Done when

  • pnpm check is green, no source file under src is named warm-pool-coordinator or warm-pool, and no class is named WarmPoolCoordinator. The event initiator warm-pool-active-demand and the emitting module name warm-pool-coordinator on existing events stay as they are: payload values are never renamed.
  • On the fast lane: release an iOS fake-driver lease and the device is shutdown in simlock list --devices; release an Android fake-driver lease and the device is ready.
  • The lease-engine tests that covered a reclaimed device staying warm are deleted, not skipped.

Out of scope

  • Any keep-or-shutdown decision. Nothing stays warm by policy until the next task.
  • The config key warmPool.quarantine.*, which keeps its name.

Depends on

Approval

  • Approved for delivery

Written by an agent.

Activity

  1. added
    task:draftScope written; technical spec, approval, or deps missing.
    on Oct 5, 2026
  2. added
    task:readyAn agent may implement it.
    and removed
    task:draftScope written; technical spec, approval, or deps missing.
    on Oct 5, 2026
  3. github-actions commented on Oct 5, 2026

    @github-actions

    Now task:ready: approved, technical spec present.

  4. self-assigned this
    on Oct 5, 2026
  5. V3RON commented on Oct 5, 2026

    @V3RON
    ContributorAuthor

    Handoff

    Done: PR #375 (draft) renames the coordinator to ReclaimCoordinator, commits what the driver's reclaim returns, deletes the keep decision and the startup excess shutdown. Three review rounds; 15 blocking findings fixed. pnpm check green, 2 mutants, 0 alive. No hardware lines.

    Not done: one blocking finding confirmed after round 3, so rule 14 stops here.

    Findings: stale docs and one comment. ARCHITECTURE.md:25, :1148, :1192, :1531 and lease-release-coordinator.ts:105 still describe a warm-pool policy or disposition that this PR deletes. CLI.md:678 and ARCHITECTURE.md:1152 say an Android emulator always comes back ready, but the wipe fallback returns shutdown (drivers/android/index.ts:944,955,971). All are text fixes with one obvious answer.

    Blocked on: the maintainer — fix those lines (or have an agent do it) and mark the PR ready, or re-run deliver after the fix.

    Written by an agent.

  6. added
    task:blockedAgent stopped on a blocker in its handoff. Maintainer re-adds the label it came from.
    on Oct 5, 2026
  7. removed their assignment
    on Oct 5, 2026
  8. self-assigned this
    on Oct 5, 2026
  9. added
    task:readyAn agent may implement it.
    and removed
    task:blockedAgent stopped on a blocker in its handoff. Maintainer re-adds the label it came from.
    on Oct 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

task:readyAn agent may implement it.

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions