--- phase: 02-lifecycle-command-safety plan: 02 type: execute wave: 2 depends_on: [02-01] files_modified: - tauri-app/src/features/runtime/types.ts - tauri-app/src/features/runtime/api.ts - tauri-app/src/features/runtime/runtime-utils.ts - tauri-app/src/features/runtime/useRuntimeManager.ts - tauri-app/src/features/runtime/components/RuntimeSurface.test.tsx autonomous: true requirements: [CTRL-01, CTRL-02, CTRL-03] must_haves: truths: - The runtime manager knows which lifecycle command is active and can expose action-specific feedback instead of one generic busy state. - The runtime feature classifies draft changes as immediate, restart-required, or save-before-apply so the UI can explain what happens next. - Frontend command guards stay aligned with the backend lifecycle contract from plan 02-01. artifacts: - path: tauri-app/src/features/runtime/useRuntimeManager.ts provides: lifecycle command state, completion feedback, and apply-impact orchestration on top of the Phase 1 draft model - path: tauri-app/src/features/runtime/runtime-utils.ts provides: reusable lifecycle labels, action guards, and apply-semantics helpers for runtime surfaces - path: tauri-app/src/features/runtime/types.ts provides: frontend types for active lifecycle commands, command results, and apply-impact values - path: tauri-app/src/features/runtime/components/RuntimeSurface.test.tsx provides: integrated regression coverage for lifecycle semantics at the React feature layer key_links: - from: tauri-app/src/features/runtime/api.ts to: tauri-app/src/features/runtime/types.ts via: typed runtime snapshot fields mirrored from the Tauri backend pattern: RuntimeSnapshot|last_error|status - from: tauri-app/src/features/runtime/useRuntimeManager.ts to: tauri-app/src/features/runtime/runtime-utils.ts via: apply-impact classification and lifecycle command label helpers pattern: start|stop|restart|required --- Teach the frontend runtime state layer how to represent lifecycle operations and apply semantics before any surface copy or button behavior changes. Purpose: keep lifecycle trust rules in one manager-owned place, just as Phase 1 centralized draft ownership. Output: typed frontend lifecycle state, reusable apply-impact helpers, and test coverage proving the manager exposes the right action-specific states. @$HOME/.config/opencode/get-shit-done/workflows/execute-plan.md @$HOME/.config/opencode/get-shit-done/templates/summary.md @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/REQUIREMENTS.md @.planning/phases/02-lifecycle-command-safety/02-RESEARCH.md @.planning/phases/02-lifecycle-command-safety/02-01-SUMMARY.md @tauri-app/src/features/runtime/types.ts @tauri-app/src/features/runtime/api.ts @tauri-app/src/features/runtime/runtime-utils.ts @tauri-app/src/features/runtime/useRuntimeManager.ts @tauri-app/src/features/runtime/components/RuntimeSurface.test.tsx From `tauri-app/src/features/runtime/useRuntimeManager.ts`: ```ts export function useRuntimeManager() { return { snapshot, liveSnapshot, savedBaseline, draftConfig, draftDirty, draftOpen, draftFields, busy, refresh, saveDraft, saveConfig, startLocal, stopLocal, }; } ``` From `tauri-app/src/features/runtime/types.ts`: ```ts export interface RuntimeSnapshot { status: RuntimeStatus; ready: boolean; process_running: boolean; resolved_url: string; binary_path: string | null; last_error: RuntimeErrorInfo | null; config: RuntimeConfig; } ``` Task 1: Mirror backend lifecycle metadata and define frontend apply-impact contracts tauri-app/src/features/runtime/types.ts, tauri-app/src/features/runtime/api.ts, tauri-app/src/features/runtime/runtime-utils.ts - tauri-app/src/features/runtime/types.ts - tauri-app/src/features/runtime/api.ts - tauri-app/src/features/runtime/runtime-utils.ts - tauri-app/src-tauri/src/runtime/types.rs - .planning/phases/02-lifecycle-command-safety/02-RESEARCH.md - Test 1: the frontend `RuntimeSnapshot` includes the backend lifecycle command fields added in plan 02-01. - Test 2: a helper can classify a draft as `immediate`, `restart_required`, or `save_before_apply` from `savedBaseline`, `draftConfig`, and `snapshot.process_running`. Update `tauri-app/src/features/runtime/types.ts` to mirror the new backend lifecycle snapshot fields using concrete frontend unions for active command values `idle | save | start | stop | save_and_start`, command outcomes `succeeded | blocked | failed`, and apply-impact values `immediate | restart_required | save_before_apply`. Update `tauri-app/src/features/runtime/api.ts` to preserve these fields in the local fallback snapshot shape instead of dropping them. Add reusable helpers in `tauri-app/src/features/runtime/runtime-utils.ts` that classify draft impact using the backend restart-impact field list (`connection_mode`, `model_path`, `addr`, `gpu_layers`, `ctx_size`) and current runtime state. - `tauri-app/src/features/runtime/types.ts` exports frontend lifecycle command and apply-impact types. - `tauri-app/src/features/runtime/api.ts` fallback snapshots include the new lifecycle metadata fields. - `tauri-app/src/features/runtime/runtime-utils.ts` contains logic that references `connection_mode`, `model_path`, `addr`, `gpu_layers`, and `ctx_size` when determining restart-required impact. npm test -- RuntimeSurface The frontend type layer can represent both backend lifecycle results and user-facing apply semantics without ad hoc string handling. Task 2: Replace generic busy handling with action-specific manager state tauri-app/src/features/runtime/useRuntimeManager.ts, tauri-app/src/features/runtime/components/RuntimeSurface.test.tsx - tauri-app/src/features/runtime/useRuntimeManager.ts - tauri-app/src/features/runtime/components/RuntimeSurface.test.tsx - tauri-app/src/features/runtime/runtime-utils.ts - tauri-app/src/features/runtime/types.ts - .planning/phases/02-lifecycle-command-safety/02-01-SUMMARY.md - Test 1: starting the runtime exposes an active command of `start` and resolves to a completion result with a stable success message. - Test 2: stopping the runtime exposes an active command of `stop` and blocks duplicate lifecycle actions while the command is active. - Test 3: when draft changes hit restart-impact fields while the process is running, the manager exposes `restart_required` instead of pretending the save is immediately applied. Refactor `tauri-app/src/features/runtime/useRuntimeManager.ts` so it exposes explicit manager-owned fields for `activeLifecycleCommand`, `lastLifecycleResult`, `lifecycleBusy`, and `applyImpact` rather than relying only on `busy`. Map `saveDraft`, `saveConfig`, `startLocal`, and `stopLocal` to concrete command names `save`, `save_and_start`, `start`, and `stop`. Preserve Phase 1 draft safety: polling must still avoid overwriting dirty draft values. When a command resolves, store a result message based on the backend snapshot metadata or fallback defaults such as `Local API started.`, `Local API stopped.`, `Save completed.`, or the backend-provided blocked/failed message. Update `RuntimeSurface.test.tsx` so the feature harness asserts action-specific state instead of only the generic `busy` boolean. - `tauri-app/src/features/runtime/useRuntimeManager.ts` exports `activeLifecycleCommand`, `lastLifecycleResult`, or equivalent explicit action-specific fields. - The manager still preserves dirty draft values across polling while lifecycle state changes. - `tauri-app/src/features/runtime/components/RuntimeSurface.test.tsx` contains lifecycle assertions for start, stop, blocked duplicates, and restart-required saves. npm test -- RuntimeSurface The runtime manager becomes the single source of truth for lifecycle command progress, completion, and apply semantics. - `npm test -- RuntimeSurface` - Frontend state exposes action-specific lifecycle progress and completion information. - Apply-impact classification matches backend restart-required rules. - Duplicate/conflicting lifecycle actions are blocked at the manager layer before the UI triggers them. After completion, create `.planning/phases/02-lifecycle-command-safety/02-02-SUMMARY.md`