# 15. Replace `DetectorArmLogic` with `DetectorAcquireLogic` and four lifecycle hooks Date: 2026-04-29 ## Status Accepted ## Context `DetectorArmLogic` had three abstract methods: - `arm()` — start acquiring, called from `prepare()`, `kickoff()`, or `trigger()` - `wait_for_idle()` — wait for idle after the final collection - `disarm(on_unstage: bool)` — stop acquiring; the flag distinguished `stage()` (reset before a new scan) from `unstage()` (end-of-scan teardown) The `on_unstage` flag was introduced in ADR 0014 to give implementations a way to distinguish the two call sites, but the single method still forced those two concerns into one place. More importantly, there was no hook at `stage()` time that could perform *different* work from `unstage()`. A detector that should be armed once at `stage()` and then triggered multiple times (e.g. an Eiger in step-scan mode, or a continuously-acquiring detector) had no clean way to express this; the closest workaround was to put the arm call inside `disarm` and branch on `on_unstage`, which was confusing. ## Decision `DetectorArmLogic` is renamed `DetectorAcquireLogic` and its interface is replaced with four named hooks that map directly to the `StandardDetector` lifecycle: | Hook | Called from | Purpose | |---|---|---| | `ensure_ready()` | `stage()` | Put the detector into a known idle state before a scan. | | `start_acquiring()` | `prepare()`, `kickoff()`, `trigger()` | Start the detector acquiring. | | `wait_for_idle()` | after final collection | Wait for the detector to return to idle. | | `ensure_stopped()` | `unstage()` | Stop the detector and perform end-of-scan cleanup. | `ensure_ready` has a concrete default that delegates to `ensure_stopped`. This is correct for the common case where stage-time reset and scan-end teardown are identical (e.g. calling `stop_busy_record`). Subclasses that need different behaviour at stage time (such as arming the detector once and keeping it armed across multiple kickoff/complete cycles) override `ensure_ready` independently. `AreaDetector.__init__` renames its `arm_logic` keyword argument to `acquire_logic` for consistency. ## Consequences All existing `DetectorArmLogic` subclasses must be updated: - rename the base class to `DetectorAcquireLogic` - rename `arm()` to `start_acquiring()` - split `disarm(on_unstage)` into `ensure_stopped()` (and optionally override `ensure_ready()` if stage-time behaviour differs) This is a clean breaking change; the library is in alpha and no compatibility shim is provided.