Files

160 lines
8.4 KiB
Markdown

---
phase: 02-lifecycle-command-safety
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
- tauri-app/src-tauri/src/runtime/mod.rs
- tauri-app/src-tauri/src/runtime/state.rs
- tauri-app/src-tauri/src/runtime/types.rs
autonomous: true
requirements: [CTRL-01, CTRL-03]
must_haves:
truths:
- The runtime backend distinguishes active lifecycle operations from idle snapshots so overlapping start and stop requests do not silently race.
- The runtime backend returns explicit conflict or completion metadata that frontend consumers can turn into trustworthy lifecycle feedback.
- Restart-impacting save rules remain aligned with the actual backend fields that require a local runtime restart.
artifacts:
- path: tauri-app/src-tauri/src/runtime/mod.rs
provides: lifecycle command guards, structured command results, and start/stop flow behavior
- path: tauri-app/src-tauri/src/runtime/state.rs
provides: authoritative runtime snapshot state and process-refresh rules for in-flight lifecycle actions
- path: tauri-app/src-tauri/src/runtime/types.rs
provides: serialized lifecycle metadata that the frontend can consume without guessing command outcomes
key_links:
- from: tauri-app/src-tauri/src/runtime/mod.rs
to: tauri-app/src-tauri/src/runtime/types.rs
via: serialized lifecycle command fields added to the runtime snapshot
pattern: RuntimeSnapshot|RuntimeStatus|last_error
- from: tauri-app/src-tauri/src/runtime/mod.rs
to: tauri-app/src-tauri/src/runtime/state.rs
via: shared guard rules for starting, stopping, and process refresh transitions
pattern: start|stop|refresh_process_state
---
<objective>
Create the backend lifecycle contract that Phase 2 can build on before frontend state and UI changes begin.
Purpose: make start, stop, and conflicting lifecycle requests deterministic at the Tauri runtime boundary instead of relying on a single frontend `busy` boolean.
Output: runtime snapshot metadata and guard behavior that explicitly describe in-flight operations, blocked conflicts, and lifecycle completion outcomes.
</objective>
<execution_context>
@$HOME/.config/opencode/get-shit-done/workflows/execute-plan.md
@$HOME/.config/opencode/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/REQUIREMENTS.md
@.planning/phases/02-lifecycle-command-safety/02-RESEARCH.md
@tauri-app/src-tauri/src/runtime/mod.rs
@tauri-app/src-tauri/src/runtime/state.rs
@tauri-app/src-tauri/src/runtime/types.rs
<interfaces>
From `tauri-app/src-tauri/src/runtime/types.rs`:
```rust
pub enum RuntimeStatus {
Stopped,
Starting,
Ready,
Degraded,
Stopping,
Error,
}
pub struct RuntimeSnapshot {
pub status: RuntimeStatus,
pub ready: bool,
pub process_running: bool,
pub resolved_url: String,
pub binary_path: Option<String>,
pub last_error: Option<RuntimeErrorInfo>,
pub config: RuntimeConfig,
}
```
From `tauri-app/src-tauri/src/runtime/mod.rs`:
```rust
pub fn save_runtime_config_command(...) -> Result<RuntimeSnapshot, String>;
pub fn start_runtime(...) -> Result<RuntimeSnapshot, String>;
pub fn stop_runtime(...) -> Result<RuntimeSnapshot, String>;
```
</interfaces>
</context>
<tasks>
<task type="auto" tdd="true">
<name>Task 1: Add explicit lifecycle command metadata to the runtime snapshot</name>
<files>tauri-app/src-tauri/src/runtime/types.rs, tauri-app/src-tauri/src/runtime/state.rs</files>
<read_first>
- tauri-app/src-tauri/src/runtime/types.rs
- tauri-app/src-tauri/src/runtime/state.rs
- tauri-app/src/features/runtime/types.ts
- .planning/phases/02-lifecycle-command-safety/02-RESEARCH.md
</read_first>
<behavior>
- Test 1: the serialized runtime snapshot exposes the currently active lifecycle action and the last completed lifecycle result.
- Test 2: snapshots still report existing status, readiness, process_running, and error fields without breaking current callers.
</behavior>
<action>Extend `tauri-app/src-tauri/src/runtime/types.rs` and `tauri-app/src-tauri/src/runtime/state.rs` with concrete lifecycle metadata. Add a serialized enum or string union for the active command with the values `idle`, `save`, `start`, `stop`, and `save_and_start`. Add a serialized result payload or equivalent snapshot fields that record the last command name, a terminal outcome of `succeeded`, `blocked`, or `failed`, and a human-readable message. Keep the existing `RuntimeSnapshot`, `RuntimeStatus`, and `RuntimeErrorInfo` fields intact; add new fields instead of replacing old ones so brownfield frontend callers continue compiling.</action>
<acceptance_criteria>
- `tauri-app/src-tauri/src/runtime/types.rs` defines serialized lifecycle command metadata alongside `RuntimeSnapshot`.
- `tauri-app/src-tauri/src/runtime/state.rs` stores enough lifecycle metadata to build snapshots with active-command and last-result information.
- Existing snapshot fields `status`, `ready`, `process_running`, `resolved_url`, `binary_path`, `last_error`, and `config` remain present in `RuntimeSnapshot`.
</acceptance_criteria>
<verify>
<automated>cargo test -p tauri-app runtime::</automated>
</verify>
<done>The runtime snapshot becomes an explicit transport for lifecycle state instead of forcing the frontend to infer command outcomes from generic status changes.</done>
</task>
<task type="auto" tdd="true">
<name>Task 2: Guard conflicting lifecycle commands and preserve restart-impacting save rules</name>
<files>tauri-app/src-tauri/src/runtime/mod.rs, tauri-app/src-tauri/src/runtime/state.rs</files>
<read_first>
- tauri-app/src-tauri/src/runtime/mod.rs
- tauri-app/src-tauri/src/runtime/state.rs
- tauri-app/src-tauri/src/runtime/process.rs
- .planning/phases/02-lifecycle-command-safety/02-RESEARCH.md
</read_first>
<behavior>
- Test 1: a second `start_runtime` request while a start or stop command is active returns a blocked lifecycle result instead of mutating process state.
- Test 2: `stop_runtime` while no local process is running returns a blocked or no-op lifecycle result with a stable message.
- Test 3: saving a running local config that changes `connection_mode`, `model_path`, `addr`, `gpu_layers`, or `ctx_size` still returns `restart_required` guidance.
</behavior>
<action>Refactor `tauri-app/src-tauri/src/runtime/mod.rs` to centralize lifecycle command guards before mutating process state. Add explicit conflict handling for these cases: `start_runtime` while `status` is `Starting` or `Stopping`, `start_runtime` while a managed child already exists, `stop_runtime` while `status` is `Starting` or `Stopping`, and `stop_runtime` when no managed child exists. For each blocked case, populate the new lifecycle result metadata with `blocked` and a concrete message such as `Local API start already in progress.`, `Stop the local API before starting it again.`, or `Local API is already stopped.`. Preserve the existing restart-impacting save guard for `connection_mode`, `model_path`, `addr`, `gpu_layers`, and `ctx_size`, and route it through the same structured lifecycle result metadata so the frontend receives one consistent contract.</action>
<acceptance_criteria>
- `tauri-app/src-tauri/src/runtime/mod.rs` contains explicit guard branches for duplicate or conflicting start/stop requests.
- Blocked lifecycle cases return concrete user-facing messages containing `already in progress`, `already running`, or `already stopped`.
- The restart-impacting field list in `requires_runtime_restart` remains limited to `connection_mode`, `model_path`, `addr`, `gpu_layers`, and `ctx_size` unless tests are updated to prove a different list.
</acceptance_criteria>
<verify>
<automated>cargo test -p tauri-app runtime::</automated>
</verify>
<done>The backend becomes the authoritative source for lifecycle conflict prevention and restart-required guidance.</done>
</task>
</tasks>
<verification>
- `cargo test -p tauri-app runtime::`
</verification>
<success_criteria>
- Start and stop requests have explicit blocked-state outcomes instead of silent no-ops.
- Runtime snapshots expose active and completed lifecycle command information.
- Restart-required saves still map to the existing backend-impact field list.
</success_criteria>
<output>
After completion, create `.planning/phases/02-lifecycle-command-safety/02-01-SUMMARY.md`
</output>