# Gap: Tests page — Pause / Retry-phase / Skip-phase controls ## Context The Tests wizard (`Plan → Preconditions → Running → Done`) introduces three controls in the Running step's bottom bar: | Control | Purpose | |---|---| | **Pause** | Temporarily halts phase progression — bench coasts to a safe-RPM hold, keep-alive and CAN traffic continue, timers freeze. | | **Retry current phase** | Aborts the current phase (without failing it), zeroes pump outputs, restarts the same phase from conditioning. | | **Skip current phase** | Marks the current phase as "skipped" (neither passed nor failed) and advances to the next enabled phase. | At the time of writing these three buttons exist in `Views/UserControls/TestRunningView.xaml` rendered `IsEnabled="False"` with a `Test.Running.ComingSoon` tooltip. This document captures the full requirements for a follow-up task that wires them to `IBenchService`. ## Required `IBenchService` API additions ```csharp /// /// Pauses the currently running test. The bench holds at idle RPM, oil pump stays on, /// keep-alive continues. Phase timers are frozen. No-op if not running. /// Task PauseAsync(CancellationToken ct = default); /// /// Resumes a paused test. Re-arms phase timers from where they were frozen. /// Throws if not paused. /// Task ResumeAsync(CancellationToken ct = default); /// /// Aborts the current phase (without marking it failed), zeros pump outputs /// (ME/FBKW/PreIn → 0), and restarts the same phase from the beginning of /// its conditioning sub-section. No-op if not running. /// Task RetryCurrentPhaseAsync(CancellationToken ct = default); /// /// Marks the current phase as "skipped" and advances to the next enabled phase. /// Pump outputs are zeroed between phases. No-op if at the last enabled phase /// (in that case the test completes normally). /// Task SkipPhaseAsync(CancellationToken ct = default); ``` All four methods must be callable from a UI thread and must **not** block — they queue a transition request onto the existing `BenchService` worker loop and await completion via a `TaskCompletionSource`. ## Safe-state expectations per operation ### Pause - RPM: ramp to **idle hold** (configurable, default 600 rpm) using the existing ramp logic. - Oil pump relay: stays on. - Pump outputs: unchanged (paused at current values). - Counter / encoder relays: unchanged. - Keep-alive loop: continues. - Phase countdown: frozen in place. Timer resumes on `ResumeAsync`. - Measurement buffer: continues accumulating samples while paused? **Decision needed** — current recommendation is to discard samples received while paused, so tolerance evaluation uses only "active" measurement time. ### Resume - RPM: ramp from idle-hold back to the current phase's target RPM before un-freezing timers. - Un-freeze phase timers only **after** RPM is within ±5% of target for ≥500 ms (same condition the initial conditioning ramp uses). ### Retry - Pump outputs: **zeroed immediately** (ME=0, FBKW=0, PreIn=0). This addresses the HIGH-priority gap in CLAUDE.md ("Pump parameters (ME/FBKW/PreIn) not zeroed between test phases") for the retry path specifically. - Phase: same phase re-entered at start of conditioning. - Results: any partial samples for the retried phase are discarded. `TestResult.Samples` for that phase is cleared. - `PhaseChanged` event fires with the same phase name again so the UI re-highlights the card. ### Skip - Pump outputs: zeroed. - Phase disposition: `TestResult` row is inserted with `Status = Skipped` (new enum value on `TestResult.Status` or existing `Pass/Fail` with a separate `IsSkipped` flag). - Progress: next enabled phase in the same test, or next enabled test if this was the last phase. If no enabled phase follows, the test completes as normal (`IsTestRunning = false`). ## UI state transitions | Button | Enabled when | Label change | |---|---|---| | Pause | `IsTestRunning && !IsPaused` | becomes "Resume" (`Test.Running.Resume`) while paused | | Retry | `IsTestRunning && !IsPaused` | static — no relabel | | Skip | `IsTestRunning && !IsPaused` | static | | Abort | always while running (paused or not) | static | When paused, the Retry and Skip buttons are **disabled** to simplify the state machine (operator must Resume first, then Retry/Skip). This keeps the BenchService state space small (Running ↔ Paused; Retry/Skip only transition inside Running). The **Abort confirm dialog** already used for Abort should be reused for **Skip** but with different title/message (`Test.Skip.ConfirmTitle`, `Test.Skip.ConfirmMessage`). Retry does **not** require confirmation — it's a non-destructive restart of the current phase only. ## Interaction with PID temperature control PID loop (`BenchService.TemperatureLoopAsync`) should **continue** during all four operations — temperature is an environmental condition, not a test output. The oil-pump interlock (`IsOilPumpOn`) must stay satisfied throughout; a Pause that loses the oil pump (e.g. operator toggles the relay via the Bench page) must **abort** the test through the existing safety path, not silently resume with oil off. ## Interaction with the oil-pump safety interlock The "no critical alarms" precondition is evaluated at Preconditions-step entry only. Once Running, the existing `DashboardAlarmsViewModel.HasCritical` observer is the authoritative safety check. Pause/Retry/Skip do **not** bypass it — any of the four operations should fail (throw) if a critical alarm is latched when invoked, surfacing the same warning banner the existing Running state already shows. ## Localization keys Already added to `Resources/Strings.en.xaml` and `Resources/Strings.es.xaml`: - `Test.Running.Pause` - `Test.Running.Resume` - `Test.Running.Retry` - `Test.Running.Skip` - `Test.Running.ComingSoon` (tooltip while buttons remain disabled) - `Test.Running.Abort` To add when Skip confirmation lands: - `Test.Skip.ConfirmTitle` - `Test.Skip.ConfirmMessage` - `Test.Skip.Confirm` - `Test.Skip.Cancel` ## Relation to existing HIGH-priority gap CLAUDE.md ("Pump parameters (ME/FBKW/PreIn) not zeroed between test phases") is the broader gap. This document's Retry path implements the zero-between-phases behaviour for the retry transition specifically. The general phase-to-phase zeroing in `BenchService.RunPhaseAsync` must also be fixed — tracked separately. ## Manual verification checklist 1. Start a multi-phase test. During a measurement phase click **Pause** — RPM drops to idle, countdown freezes, button relabels to "Resume". Flow charts stop advancing, oil pump stays on, no critical alarm. 2. Click **Resume** — RPM ramps back to the phase target, countdown unfreezes from the exact value, label returns to "Pause". 3. During a measurement phase with partial samples, click **Retry** — pump outputs zero, phase restarts at conditioning, card highlight refreshes, previous samples cleared from `TestResult`. 4. Click **Skip** on the second of three enabled phases — confirm dialog opens (`Test.Skip.ConfirmMessage`). Cancel: test continues. Confirm: phase disposition set to Skipped, pump outputs zeroed, next enabled phase starts conditioning. 5. Skip the final enabled phase — test completes, transitions to Done step. 6. Trigger a critical alarm mid-Pause — test aborts via the existing critical-alarm path, Done step shows "Failed — safety stop". 7. Toggle language mid-Pause — button text updates to localized "Resume" / "Pause". 8. Unplug CAN during Pause — CAN-liveness watchdog aborts the test. ## Out of scope (acknowledged) - Skipping / retrying a phase while paused (requires Resume first). - A Skip-all-remaining-phases shortcut. - A "Retry this test" that restarts from the first phase of the current test.