# Phase 2 Research: Lifecycle Command Safety **Date:** 2026-03-29 **Source:** Manual orchestration fallback in current session (subagent spawning unavailable in this environment) ## Goal to Plan For Phase 2 must make runtime lifecycle actions trustworthy after Phase 1's draft ownership work. The user should be able to: - start, stop, and retry the managed local runtime with clear in-progress and completion feedback (`CTRL-01`) - understand whether a draft change applies immediately, requires restart, or should be saved before an apply action (`CTRL-02`) - avoid conflicting lifecycle actions while another lifecycle action is already running (`CTRL-03`) ## Current Implementation Findings ### Frontend runtime manager - `tauri-app/src/features/runtime/useRuntimeManager.ts` owns one coarse `busy` boolean for all save and lifecycle actions. - `startLocal()` and `stopLocal()` optimistically set `status` to `starting` or `stopping`, but there is no explicit command metadata describing which action is in flight, what completed, or what the user should do next. - Polling continues every 3000 ms and already preserves dirty draft values from Phase 1, so Phase 2 should build on that state model instead of bypassing it. ### Runtime status and settings UI - `tauri-app/src/features/runtime/components/RuntimeStatusCard.tsx` exposes a single primary action with generic busy copy (`Working...`). It does not tell the user whether the current action is a start, stop, retry, or blocked transition. - `tauri-app/src/features/runtime/components/RuntimeSettingsDrawer.tsx` already has a combined `Save and start local API` / `Stop local API` CTA and blocks saves for invalid or pending draft fields. - The drawer already recognizes one restart-related backend signal: it exits early when a save returns `last_error.code === "restart_required"`, but there is no explicit impact preview before save and no dedicated restart/apply guidance after save. ### Backend runtime commands - `tauri-app/src-tauri/src/runtime/mod.rs` already prevents restart-impacting config changes while the local process is running by returning `restart_required` from `save_runtime_config_command()` when `connection_mode`, `model_path`, `addr`, `gpu_layers`, or `ctx_size` change. - `start_runtime_inner()` returns the current snapshot if a child process already exists, but it does not record a structured "busy/conflict" result. - `stop_runtime()` sets status to `Stopping`, kills the child, then returns `Stopped`, but it does not distinguish repeated stop requests, remote-mode no-ops, or user-facing completion messages. - `tauri-app/src-tauri/src/runtime/state.rs` already revalidates health and degrades a running process when `/health` fails, which means lifecycle feedback should continue to respect `ready`, `degraded`, and `error` states rather than flattening everything into `busy`. ### Existing tests and patterns - `tauri-app/src/features/runtime/components/RuntimeSurface.test.tsx` already covers start, stop, save-and-start, degraded polling, and draft safety. It is the best end-to-end harness for Phase 2 regressions. - Phase 1 plan files under `.planning/phases/01-hf-model-discovery/` define the house style for executable plans: frontmatter with `requirements` and `must_haves`, then XML tasks with mandatory `read_first` and `acceptance_criteria` blocks. ## Gaps to Close in Phase 2 1. `busy` is too generic. The UI needs action-specific state such as `start`, `stop`, `save`, `save_and_start`, and `retry` so labels and disabled states are predictable. 2. Apply semantics are implicit. The user can trigger save or save-and-start flows, but the UI does not explicitly explain which fields require restart and when a saved change is live versus pending restart. 3. Conflict prevention is mostly optimistic. Frontend controls disable while `busy` is true, but backend commands do not provide a strong structured guard for duplicate or conflicting lifecycle requests. 4. Completion behavior is underspecified. The user sees status changes, but there is no stable command-result summary for success/failure/retry guidance after a lifecycle action finishes. ## Planning Implications Phase 2 should be split into three execution plans: 1. **Backend lifecycle command contract** - add explicit command guard behavior and command-result semantics at the Tauri runtime boundary. 2. **Frontend lifecycle/apply state model** - teach the runtime manager and utilities how to classify draft impact, track the active command, and preserve actionable completion feedback. 3. **User-facing lifecycle surfaces** - update the status card, settings drawer, and regression tests so the user sees the correct action copy, restart/apply guidance, and blocked states. ## Concrete Decisions for the Plans - Treat these fields as restart-impacting because the backend already does: `connection_mode`, `model_path`, `addr`, `gpu_layers`, `ctx_size`. - Keep Phase 1 draft-preservation behavior intact; Phase 2 must layer lifecycle semantics on top of the current draft/baseline/live split rather than replacing it. - Use `RuntimeSurface.test.tsx` for integrated lifecycle behavior and Rust runtime tests for backend guard behavior. - Keep brownfield compatibility: do not remove existing `startRuntime`, `stopRuntime`, `saveRuntimeConfig`, or `RuntimeSnapshot` entry points; extend them with additional metadata instead. ## Risks - If the frontend invents apply semantics that do not match backend restart rules, the workspace will feel inconsistent. - If backend guards remain implicit, racey double-clicks or overlapping drawer/status-card actions can still produce confusing no-op results. - If completion feedback only lives in transient button labels, users will lose the reason for a blocked or completed action after the next poll. ## Recommended Verification Focus - Rust tests around duplicate/conflicting start/stop/save requests - `RuntimeSurface.test.tsx` coverage for action-specific busy labels, blocked conflicting actions, restart-required guidance, and retry/error completion flows - Existing Phase 1 draft safety assertions still passing after lifecycle changes