Initial ForgeLM import

This commit is contained in:
Evandro Fonseca Junior committed 2026-03-29 19:10:57 -03:00
commit a392b9dc60
4296 files changed
+1212675

No files matched your search

@@ -0,0 +1,159 @@
---
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>
@@ -0,0 +1,106 @@
---
phase: 02-lifecycle-command-safety
plan: 01
subsystem: runtime
tags: [tauri, rust, lifecycle, runtime, desktop]
requires:
- phase: 01-draft-state-ownership
provides: runtime draft/live separation that lifecycle metadata now extends without clobbering edits
provides:
- explicit runtime snapshot lifecycle command metadata
- backend guards for conflicting local runtime start and stop requests
- restart-required save guidance aligned to backend-owned runtime fields
affects: [phase-02-02, phase-02-03, runtime-workspace]
tech-stack:
added: []
patterns: [snapshot lifecycle transport, centralized runtime command guards]
key-files:
created: []
modified:
- tauri-app/src-tauri/src/runtime/types.rs
- tauri-app/src-tauri/src/runtime/state.rs
- tauri-app/src-tauri/src/runtime/mod.rs
key-decisions:
- "Expose active and last lifecycle command data on RuntimeSnapshot so frontend polling can render trustworthy lifecycle feedback without inferring from status alone."
- "Centralize duplicate start/stop blocking in runtime state helpers so save, start, and stop flows share the same command-result contract."
- "Keep restart-required save enforcement limited to connection_mode, model_path, addr, gpu_layers, and ctx_size to stay aligned with the existing backend runtime boundary."
patterns-established:
- "RuntimeSnapshot includes active_command plus last_command_result for stable in-flight and terminal lifecycle messaging."
- "Lifecycle commands update terminal outcomes through helper functions instead of ad-hoc last_error-only handling."
requirements-completed: [CTRL-01, CTRL-03]
duration: 6min
completed: 2026-03-29
---
# Phase 2 Plan 01: Lifecycle Command Safety Summary
**Runtime snapshot lifecycle metadata and guarded local start/stop/save outcomes at the Tauri runtime boundary**
## Performance
- **Duration:** 6 min
- **Started:** 2026-03-29T03:45:48Z
- **Completed:** 2026-03-29T03:51:43Z
- **Tasks:** 2
- **Files modified:** 3
## Accomplishments
- Added serialized lifecycle command metadata to `RuntimeSnapshot` so backend snapshots now report both the active command and the last completed command result.
- Centralized lifecycle result helpers and guard rules in runtime state so duplicate start/stop requests return stable blocked messages instead of silent no-ops.
- Routed restart-required saves and lifecycle success/failure paths through one command-result contract while preserving the existing backend restart-impact field list.
## Task Commits
Each task was committed atomically:
1. **Task 1: Add explicit lifecycle command metadata to the runtime snapshot** - `217e265` (test)
2. **Task 1: Add explicit lifecycle command metadata to the runtime snapshot** - `cf73eb2` (feat)
3. **Task 2: Guard conflicting lifecycle commands and preserve restart-impacting save rules** - `581b81b` (test)
4. **Task 2: Guard conflicting lifecycle commands and preserve restart-impacting save rules** - `67651bf` (feat)
## Files Created/Modified
- `tauri-app/src-tauri/src/runtime/types.rs` - defines lifecycle command and result payloads on runtime snapshots.
- `tauri-app/src-tauri/src/runtime/state.rs` - stores lifecycle metadata, shared guard helpers, and regression tests for blocked command behavior.
- `tauri-app/src-tauri/src/runtime/mod.rs` - applies guard helpers to save/start/stop commands and records consistent lifecycle outcomes.
## Decisions Made
- Used snapshot-level lifecycle transport instead of separate command-specific responses so existing polling consumers can adopt the new contract incrementally.
- Kept blocked lifecycle feedback in `last_command_result` while reserving `last_error` for actual runtime failures such as restart-required, spawn, health, and stop failures.
- Preserved the restart-required field list exactly as planned and covered non-restart fields with regression tests.
## Deviations from Plan
### Auto-fixed Issues
**1. [Rule 1 - Bug] Cleared latched active commands when config persistence fails**
- **Found during:** Task 2 (Guard conflicting lifecycle commands and preserve restart-impacting save rules)
- **Issue:** Beginning a save or save-and-start command before persisting config could leave `active_command` stuck if `save_runtime_config` returned an error.
- **Fix:** Added failure handling that records a failed lifecycle result, stores `save_failed`, and exits without leaving the runtime in an in-flight command state.
- **Files modified:** `tauri-app/src-tauri/src/runtime/mod.rs`
- **Verification:** `cargo test --manifest-path "tauri-app/src-tauri/Cargo.toml" runtime::` and `cargo check --manifest-path "tauri-app/src-tauri/Cargo.toml"`
- **Committed in:** `67651bf` (part of task commit)
---
**Total deviations:** 1 auto-fixed (1 bug)
**Impact on plan:** The auto-fix kept the new lifecycle contract trustworthy under save failures without expanding scope beyond the planned backend boundary.
## Issues Encountered
- The plan's verification command used `cargo test -p tauri-app runtime::`, but the Tauri crate is named `forgelm-chat`; verification succeeded via `cargo test --manifest-path "tauri-app/src-tauri/Cargo.toml" runtime::`.
## User Setup Required
None - no external service configuration required.
## Next Phase Readiness
- Frontend lifecycle manager work can now read `active_command` and `last_command_result` directly from runtime snapshots.
- Restart/apply UI work can rely on the backend restart-required contract staying limited to `connection_mode`, `model_path`, `addr`, `gpu_layers`, and `ctx_size`.
## Self-Check: PASSED
- Summary file exists at `.planning/phases/02-lifecycle-command-safety/02-01-SUMMARY.md`.
- Verified commit hashes `217e265`, `cf73eb2`, `581b81b`, and `67651bf` are present in git history.
---
*Phase: 02-lifecycle-command-safety*
*Completed: 2026-03-29*
@@ -0,0 +1,173 @@
---
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
---
<objective>
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.
</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
@.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
<interfaces>
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;
}
```
</interfaces>
</context>
<tasks>
<task type="auto" tdd="true">
<name>Task 1: Mirror backend lifecycle metadata and define frontend apply-impact contracts</name>
<files>tauri-app/src/features/runtime/types.ts, tauri-app/src/features/runtime/api.ts, tauri-app/src/features/runtime/runtime-utils.ts</files>
<read_first>
- 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
</read_first>
<behavior>
- 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`.
</behavior>
<action>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.</action>
<acceptance_criteria>
- `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.
</acceptance_criteria>
<verify>
<automated>npm test -- RuntimeSurface</automated>
</verify>
<done>The frontend type layer can represent both backend lifecycle results and user-facing apply semantics without ad hoc string handling.</done>
</task>
<task type="auto" tdd="true">
<name>Task 2: Replace generic busy handling with action-specific manager state</name>
<files>tauri-app/src/features/runtime/useRuntimeManager.ts, tauri-app/src/features/runtime/components/RuntimeSurface.test.tsx</files>
<read_first>
- 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
</read_first>
<behavior>
- 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.
</behavior>
<action>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.</action>
<acceptance_criteria>
- `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.
</acceptance_criteria>
<verify>
<automated>npm test -- RuntimeSurface</automated>
</verify>
<done>The runtime manager becomes the single source of truth for lifecycle command progress, completion, and apply semantics.</done>
</task>
</tasks>
<verification>
- `npm test -- RuntimeSurface`
</verification>
<success_criteria>
- 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.
</success_criteria>
<output>
After completion, create `.planning/phases/02-lifecycle-command-safety/02-02-SUMMARY.md`
</output>
@@ -0,0 +1,92 @@
---
phase: 02-lifecycle-command-safety
plan: 02
subsystem: ui
tags: [react, typescript, runtime, lifecycle, tauri]
requires:
- phase: 02-lifecycle-command-safety
provides: backend lifecycle snapshot metadata and conflict guards from plan 02-01
provides:
- frontend lifecycle command and result types
- manager-owned lifecycle progress and apply-impact state
- RuntimeSurface regression coverage for lifecycle manager semantics
affects: [phase-02-03, runtime-status-card, runtime-settings-drawer]
tech-stack:
added: []
patterns: [manager-owned lifecycle state, apply-impact classification from saved-versus-draft config]
key-files:
created: []
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
key-decisions:
- "Mirror backend lifecycle metadata directly into the frontend RuntimeSnapshot so polling and command responses share one contract."
- "Derive applyImpact from saved baseline versus draft config using the backend restart-required field list instead of UI-only heuristics."
- "Expose activeLifecycleCommand, lastLifecycleResult, and lifecycleBusy from the manager so status-card and drawer copy can consume explicit state in plan 02-03."
patterns-established:
- "Runtime manager normalizes lifecycle metadata on every snapshot before reconciling draft-safe state."
- "Blocked duplicate lifecycle requests become manager-visible lifecycle results before UI surfaces render them."
requirements-completed: [CTRL-01, CTRL-02, CTRL-03]
duration: 14min
completed: 2026-03-29
---
# Phase 2 Plan 02: Lifecycle Command Safety Summary
**Frontend runtime state now tracks explicit lifecycle commands, completion messages, and restart/apply impact on top of the Phase 1 draft model**
## Performance
- **Duration:** 14 min
- **Started:** 2026-03-29T03:56:04Z
- **Completed:** 2026-03-29T04:10:22Z
- **Tasks:** 2
- **Files modified:** 5
## Accomplishments
- Added frontend lifecycle command, result, and apply-impact types plus fallback snapshot support in the runtime API layer.
- Refactored the runtime manager to expose `activeLifecycleCommand`, `lastLifecycleResult`, `lifecycleBusy`, and `applyImpact` while keeping dirty-draft polling safety intact.
- Expanded `RuntimeSurface` tests to cover lifecycle metadata mirroring, apply-impact classification, start/stop progress, and restart-required manager state.
## Task Commits
Each task was committed atomically:
1. **Task 1: Mirror backend lifecycle metadata and define frontend apply-impact contracts** - `a72c902` (test)
2. **Task 1: Mirror backend lifecycle metadata and define frontend apply-impact contracts** - `2cb6cad` (feat)
3. **Task 2: Replace generic busy handling with action-specific manager state** - `1d45342` (test)
4. **Task 2: Replace generic busy handling with action-specific manager state** - `1bcd4be` (feat)
## Files Created/Modified
- `tauri-app/src/features/runtime/types.ts` - mirrors backend lifecycle fields and apply-impact unions in the frontend contract.
- `tauri-app/src/features/runtime/api.ts` - preserves lifecycle result metadata in local fallback snapshots.
- `tauri-app/src/features/runtime/runtime-utils.ts` - classifies apply impact from the backend restart-required field list and provides lifecycle result defaults.
- `tauri-app/src/features/runtime/useRuntimeManager.ts` - owns explicit lifecycle progress and completion state alongside the existing draft/live split.
- `tauri-app/src/features/runtime/components/RuntimeSurface.test.tsx` - covers manager lifecycle semantics through the React runtime feature harness.
## Decisions Made
- Normalized snapshots inside the manager so older fallback payloads and fresh backend payloads both produce defined lifecycle fields.
- Kept `busy` available for brownfield consumers while adding explicit lifecycle state that plan 02-03 can render directly.
- Used the backend restart-required field list (`connection_mode`, `model_path`, `addr`, `gpu_layers`, `ctx_size`) as the only source of truth for `restart_required` apply impact.
## Deviations from Plan
None - plan executed as written.
## Issues Encountered
- None.
## User Setup Required
None - no external service configuration required.
## Next Phase Readiness
- `RuntimeStatusCard` and `RuntimeSettingsDrawer` can now render explicit lifecycle progress and apply guidance from manager props instead of inferring from `busy`.
- Phase 2 UI work can show restart-required and save-before-apply messaging without re-implementing backend guard logic.
---
*Phase: 02-lifecycle-command-safety*
*Completed: 2026-03-29*
@@ -0,0 +1,163 @@
---
phase: 02-lifecycle-command-safety
plan: 03
type: execute
wave: 3
depends_on: [02-02]
files_modified:
- tauri-app/src/features/runtime/components/RuntimeStatusCard.tsx
- tauri-app/src/features/runtime/components/RuntimeStatusCard.css
- tauri-app/src/features/runtime/components/RuntimeSettingsDrawer.tsx
- tauri-app/src/features/runtime/components/RuntimeSettingsDrawer.css
- tauri-app/src/features/runtime/components/RuntimeSurface.test.tsx
autonomous: true
requirements: [CTRL-01, CTRL-02, CTRL-03]
must_haves:
truths:
- The runtime workspace tells the user exactly which lifecycle action is happening, what completed, and whether a saved draft is live now or waiting for restart/apply.
- Status-card and drawer controls disable or relabel themselves consistently while a conflicting lifecycle action is in progress.
- Restart-required and save-before-apply guidance is visible before the user commits a high-impact action.
artifacts:
- path: tauri-app/src/features/runtime/components/RuntimeStatusCard.tsx
provides: authoritative status-card action labels, blocked states, and completion messaging for runtime lifecycle actions
- path: tauri-app/src/features/runtime/components/RuntimeSettingsDrawer.tsx
provides: explicit apply-semantics guidance and lifecycle CTA copy inside the draft editor
- path: tauri-app/src/features/runtime/components/RuntimeSurface.test.tsx
provides: integrated UI regression coverage for lifecycle commands and apply feedback
key_links:
- from: tauri-app/src/features/runtime/useRuntimeManager.ts
to: tauri-app/src/features/runtime/components/RuntimeStatusCard.tsx
via: active lifecycle command, lifecycle result, and action guards passed as props
pattern: start|stop|retry|result
- from: tauri-app/src/features/runtime/useRuntimeManager.ts
to: tauri-app/src/features/runtime/components/RuntimeSettingsDrawer.tsx
via: applyImpact and lifecycle status passed into drawer CTA and helper copy
pattern: restart_required|save_before_apply|lifecycleBusy
---
<objective>
Finish Phase 2 by making lifecycle command safety visible and trustworthy in the runtime workspace.
Purpose: expose the backend and manager semantics from plans 02-01 and 02-02 through the status card and settings drawer without regressing Phase 1 draft UX.
Output: clear CTA labels, restart/apply guidance, blocked-state rules, and end-to-end UI regressions for lifecycle safety.
</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
@.planning/phases/02-lifecycle-command-safety/02-01-SUMMARY.md
@.planning/phases/02-lifecycle-command-safety/02-02-SUMMARY.md
@tauri-app/src/features/runtime/components/RuntimeStatusCard.tsx
@tauri-app/src/features/runtime/components/RuntimeStatusCard.css
@tauri-app/src/features/runtime/components/RuntimeSettingsDrawer.tsx
@tauri-app/src/features/runtime/components/RuntimeSettingsDrawer.css
@tauri-app/src/features/runtime/components/RuntimeSurface.test.tsx
<interfaces>
From `tauri-app/src/features/runtime/components/RuntimeStatusCard.tsx`:
```tsx
interface Props {
snapshot: RuntimeSnapshot;
loading: boolean;
busy: boolean;
onOpenSettings: () => void;
onStart: () => void;
onStop: () => void;
}
```
From `tauri-app/src/features/runtime/components/RuntimeSettingsDrawer.tsx`:
```tsx
interface Props {
open: boolean;
snapshot: RuntimeSnapshot;
busy: boolean;
onSave: (patch: Partial<RuntimeConfig>) => Promise<RuntimeSnapshot>;
onStart: () => Promise<unknown>;
onStop: () => Promise<unknown>;
}
```
</interfaces>
</context>
<tasks>
<task type="auto" tdd="true">
<name>Task 1: Update the runtime status card for action-specific lifecycle feedback</name>
<files>tauri-app/src/features/runtime/components/RuntimeStatusCard.tsx, tauri-app/src/features/runtime/components/RuntimeStatusCard.css, tauri-app/src/features/runtime/components/RuntimeSurface.test.tsx</files>
<read_first>
- tauri-app/src/features/runtime/components/RuntimeStatusCard.tsx
- tauri-app/src/features/runtime/components/RuntimeStatusCard.css
- tauri-app/src/features/runtime/components/RuntimeSurface.test.tsx
- tauri-app/src/features/runtime/useRuntimeManager.ts
- .planning/phases/02-lifecycle-command-safety/02-02-SUMMARY.md
</read_first>
<behavior>
- Test 1: while a start command is active, the primary status-card action reads `Starting local API...` and conflicting controls are disabled.
- Test 2: while a stop command is active, the primary status-card action reads `Stopping local API...` and start is not re-enabled until completion.
- Test 3: after a blocked or failed command, the status card surfaces the latest lifecycle result message with retry guidance.
</behavior>
<action>Refactor `tauri-app/src/features/runtime/components/RuntimeStatusCard.tsx` to consume the explicit lifecycle fields from the runtime manager instead of only `busy`. Replace the generic label `Working...` with concrete command labels `Starting local API...` and `Stopping local API...`. Add a visible result banner or note area that renders the latest lifecycle result message from manager state, including blocked/failed copy such as `Local API start already in progress.` or `Local API stopped.`. Update `RuntimeStatusCard.css` so the new note/banner styles match the existing runtime card tone system and remain readable on desktop and mobile layouts.</action>
<acceptance_criteria>
- `tauri-app/src/features/runtime/components/RuntimeStatusCard.tsx` no longer renders the generic text `Working...`.
- `tauri-app/src/features/runtime/components/RuntimeStatusCard.tsx` contains the strings `Starting local API...` and `Stopping local API...`.
- `tauri-app/src/features/runtime/components/RuntimeSurface.test.tsx` asserts lifecycle result messages or banners on the status card.
</acceptance_criteria>
<verify>
<automated>npm test -- RuntimeSurface</automated>
</verify>
<done>The runtime status card becomes a trustworthy summary of what lifecycle command is happening and what just completed.</done>
</task>
<task type="auto" tdd="true">
<name>Task 2: Make drawer CTA copy and apply guidance explicit</name>
<files>tauri-app/src/features/runtime/components/RuntimeSettingsDrawer.tsx, tauri-app/src/features/runtime/components/RuntimeSettingsDrawer.css, tauri-app/src/features/runtime/components/RuntimeSurface.test.tsx</files>
<read_first>
- tauri-app/src/features/runtime/components/RuntimeSettingsDrawer.tsx
- tauri-app/src/features/runtime/components/RuntimeSettingsDrawer.css
- tauri-app/src/features/runtime/components/RuntimeSurface.test.tsx
- tauri-app/src/features/runtime/runtime-utils.ts
- .planning/phases/02-lifecycle-command-safety/02-RESEARCH.md
</read_first>
<behavior>
- Test 1: when a dirty local draft changes only non-restart fields, the drawer continues to offer `Save changes` and `Save and start local API` with immediate-apply helper copy.
- Test 2: when a running local runtime has dirty restart-impact fields, the drawer shows `Restart required to apply saved changes` and disables conflicting lifecycle actions until the user saves or stops.
- Test 3: when the user changes connection mode from remote to local, the drawer keeps the existing `Save mode before applying` safeguard and pairs it with explicit apply guidance.
</behavior>
<action>Update `tauri-app/src/features/runtime/components/RuntimeSettingsDrawer.tsx` so the footer CTA copy is driven by `applyImpact` and active lifecycle command state. Keep `Save changes` for plain saves, keep `Save mode before applying` for pending mode switches, and add explicit helper or warning copy using these exact phrases when relevant: `Applies immediately after save.`, `Restart required to apply saved changes.`, and `Stop the local API or save first before applying this change.`. When the process is already running and restart-impact fields are dirty, disable `Save and start local API` as a conflicting action and render restart guidance instead of pretending the change is live. Update `RuntimeSettingsDrawer.css` so apply-guidance rows, warning banners, and disabled CTA states are visually distinct but consistent with the existing drawer styling.</action>
<acceptance_criteria>
- `tauri-app/src/features/runtime/components/RuntimeSettingsDrawer.tsx` contains the strings `Applies immediately after save.` and `Restart required to apply saved changes.`.
- The drawer still contains `Save mode before applying` for mode-switch safeguards.
- `tauri-app/src/features/runtime/components/RuntimeSurface.test.tsx` covers immediate-apply, restart-required, and conflicting-action-disabled states.
</acceptance_criteria>
<verify>
<automated>npm test -- RuntimeSurface</automated>
</verify>
<done>The draft editor explains exactly when a change goes live, when restart is required, and why conflicting lifecycle actions are disabled.</done>
</task>
</tasks>
<verification>
- `npm test -- RuntimeSurface`
</verification>
<success_criteria>
- Status card and drawer use action-specific lifecycle copy instead of one generic busy state.
- Restart-required and save-before-apply semantics are visible before the user commits a conflicting action.
- End-to-end tests prove lifecycle safety across start, stop, retry/block, and apply-guidance flows.
</success_criteria>
<output>
After completion, create `.planning/phases/02-lifecycle-command-safety/02-03-SUMMARY.md`
</output>
@@ -0,0 +1,108 @@
---
phase: 02-lifecycle-command-safety
plan: 03
subsystem: ui
tags: [react, css, runtime, lifecycle, ux]
requires:
- phase: 02-lifecycle-command-safety
provides: explicit manager lifecycle state and apply-impact semantics from plan 02-02
provides:
- status card lifecycle progress and result messaging
- drawer apply guidance for immediate, restart-required, and save-before-apply states
- UI regression coverage for lifecycle-safe runtime controls
affects: [phase-03, runtime-workspace, lifecycle-ux]
tech-stack:
added: []
patterns: [action-specific CTA copy, lifecycle result banners, pre-action apply guidance]
key-files:
created: []
modified:
- tauri-app/src/features/runtime/components/RuntimeStatusCard.tsx
- tauri-app/src/features/runtime/components/RuntimeStatusCard.css
- tauri-app/src/features/runtime/components/RuntimeSettingsDrawer.tsx
- tauri-app/src/features/runtime/components/RuntimeSettingsDrawer.css
- tauri-app/src/features/runtime/components/RuntimeSurface.test.tsx
- tauri-app/src/components/Sidebar.tsx
- tauri-app/src/features/runtime/runtime-utils.test.ts
key-decisions:
- "Use explicit lifecycle result banners instead of overloading last_error so completed and blocked actions remain visible after polling refreshes."
- "Render apply guidance in the drawer before the CTA row so users see whether a draft applies immediately, needs restart, or must be saved before apply."
- "Keep status-card and drawer props backward-compatible with optional lifecycle fields while Sidebar forwards the new manager contract."
patterns-established:
- "Status card consumes activeLifecycleCommand plus lastLifecycleResult to show action-specific CTA labels and stable completion feedback."
- "Drawer CTA text and disabled states derive from applyImpact and lifecycleBusy instead of one generic busy label."
requirements-completed: [CTRL-01, CTRL-02, CTRL-03]
duration: 13min
completed: 2026-03-29
---
# Phase 2 Plan 03: Lifecycle Command Safety Summary
**The runtime workspace now tells the user what lifecycle action is running, what just completed, and whether saved draft changes apply now or wait for restart**
## Performance
- **Duration:** 13 min
- **Started:** 2026-03-29T04:04:51Z
- **Completed:** 2026-03-29T04:17:00Z
- **Tasks:** 2
- **Files modified:** 7
## Accomplishments
- Replaced generic status-card busy copy with action-specific lifecycle labels and result banners.
- Added explicit drawer guidance for immediate-apply, restart-required, and save-before-apply runtime drafts.
- Extended UI regression coverage and build wiring so the lifecycle manager contract reaches both the sidebar shell and direct test helpers.
## Task Commits
Each task was committed atomically:
1. **Task 1: Update the runtime status card for action-specific lifecycle feedback** - `8c303a9` (test)
2. **Task 2: Make drawer CTA copy and apply guidance explicit** - `06c1a44` (feat)
3. **Build follow-up: wire lifecycle props through build callers** - `93f7fd9` (fix)
## Files Created/Modified
- `tauri-app/src/features/runtime/components/RuntimeStatusCard.tsx` - renders explicit start/stop labels, lifecycle result messages, and disabled conflicting actions.
- `tauri-app/src/features/runtime/components/RuntimeStatusCard.css` - styles lifecycle result banners to match the existing runtime card tone system.
- `tauri-app/src/features/runtime/components/RuntimeSettingsDrawer.tsx` - drives CTA labels and helper guidance from `applyImpact`, `activeLifecycleCommand`, and `lifecycleBusy`.
- `tauri-app/src/features/runtime/components/RuntimeSettingsDrawer.css` - styles apply-guidance rows and lifecycle outcome messaging.
- `tauri-app/src/features/runtime/components/RuntimeSurface.test.tsx` - covers UI lifecycle copy, result banners, and apply-guidance behavior.
- `tauri-app/src/components/Sidebar.tsx` - forwards lifecycle manager props to the status card and drawer.
- `tauri-app/src/features/runtime/runtime-utils.test.ts` - keeps direct snapshot helpers aligned with the expanded lifecycle snapshot shape.
## Decisions Made
- Status-card completion feedback lives in a dedicated lifecycle result banner so success, blocked, and failure messages do not compete with runtime error diagnostics.
- Drawer helper copy uses exact operator-facing phrases from the plan to keep restart and mode-switch behavior predictable.
- Optional lifecycle props on UI components preserve compatibility for direct component renders while the live app passes explicit manager state.
## Deviations from Plan
### Auto-fixed Issues
**1. [Rule 3 - Blocking] Wired expanded lifecycle props through build callers and helper tests**
- **Found during:** Task 2 (Make drawer CTA copy and apply guidance explicit)
- **Issue:** `npm run build` failed because `Sidebar.tsx` and `runtime-utils.test.ts` still used the older component and snapshot contracts.
- **Fix:** Forwarded lifecycle props from `Sidebar.tsx`, made status-card lifecycle props optional for direct renders, and updated `runtime-utils.test.ts` snapshot fixtures to include lifecycle defaults.
- **Files modified:** `tauri-app/src/components/Sidebar.tsx`, `tauri-app/src/features/runtime/components/RuntimeStatusCard.tsx`, `tauri-app/src/features/runtime/runtime-utils.test.ts`
- **Verification:** `npm test -- RuntimeSurface` and `npm run build`
- **Committed in:** `93f7fd9` (follow-up fix commit)
---
**Total deviations:** 1 auto-fixed (1 blocking)
**Impact on plan:** The follow-up kept the new UI contract buildable and testable without changing the plan's scope.
## Issues Encountered
- The first full frontend build surfaced stale callers that still used the pre-Phase-2 component contracts; the fix was contained to the sidebar shell and helper test fixtures.
## User Setup Required
None - no external service configuration required.
## Next Phase Readiness
- Phase 2 lifecycle command safety is ready for workspace consolidation in Phase 3.
- Phase 3 can now compose the runtime workspace around one trustworthy lifecycle/apply state model instead of duplicated control heuristics.
---
*Phase: 02-lifecycle-command-safety*
*Completed: 2026-03-29*
@@ -0,0 +1,72 @@
# 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