Files
ForgeLM/.planning/phases/01-hf-model-discovery/01-03-PLAN.md
T

158 lines
9.1 KiB
Markdown

---
phase: 01-draft-state-ownership
plan: 03
type: execute
wave: 3
depends_on: [01-02]
files_modified:
- tauri-app/src/features/runtime/components/RuntimeSettingsDrawer.tsx
- tauri-app/src/features/runtime/runtime-settings-form.ts
- tauri-app/src/features/runtime/runtime-settings-form.test.ts
- tauri-app/src/features/runtime/components/RuntimeSurface.test.tsx
autonomous: true
requirements: [CONF-01, CONF-03, CONF-04]
must_haves:
truths:
- Folder, model, and provider/source choices stay stable until the user saves or discards them.
- Older async responses do not overwrite newer field intent.
- Invalid draft values remain visible with inline errors and block save instead of being silently removed.
artifacts:
- path: tauri-app/src/features/runtime/components/RuntimeSettingsDrawer.tsx
provides: field-scoped async handlers and inline invalid-state rendering
- path: tauri-app/src/features/runtime/runtime-settings-form.ts
provides: helper logic for preserving selections and determining invalid draft state
- path: tauri-app/src/features/runtime/components/RuntimeSurface.test.tsx
provides: regression coverage for stale async responses and invalid draft preservation
- path: tauri-app/src/features/runtime/runtime-settings-form.test.ts
provides: unit coverage for selection-preservation helpers and invalid-save blocking rules
key_links:
- from: tauri-app/src/features/runtime/components/RuntimeSettingsDrawer.tsx
to: tauri-app/src/features/runtime/runtime-settings-form.ts
via: helper calls that preserve current selections and compute invalid draft state
pattern: chooseModel|createRuntimeConfigPatch
- from: tauri-app/src/features/runtime/components/RuntimeSettingsDrawer.tsx
to: tauri-app/src/features/runtime/components/RuntimeSurface.test.tsx
via: async picker and rescan regression tests
pattern: scanRuntimeModels|pickRuntimeModelsDir|searchHuggingFaceModels
---
<objective>
Harden the controlled draft flow so field-scoped async work, rescans, and validation failures preserve user intent instead of silently mutating the draft.
Purpose: finish the phase by protecting the highest-risk interactions: folder pickers, model rescans, stale async responses, and save failure recovery.
Output: field-scoped async safety, invalid-draft preservation, save blocking, and regression tests for the full draft-safety contract.
</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/phases/01-hf-model-discovery/01-CONTEXT.md
@.planning/phases/01-hf-model-discovery/01-RESEARCH.md
@.planning/phases/01-hf-model-discovery/01-UI-SPEC.md
@.planning/phases/01-hf-model-discovery/01-02-SUMMARY.md
@tauri-app/src/features/runtime/components/RuntimeSettingsDrawer.tsx
@tauri-app/src/features/runtime/runtime-settings-form.ts
@tauri-app/src/features/runtime/runtime-settings-form.test.ts
@tauri-app/src/features/runtime/components/RuntimeSurface.test.tsx
<interfaces>
From `tauri-app/src/features/runtime/runtime-settings-form.ts`:
```ts
export function chooseModel(current: string, scanned: RuntimeModelFile[], extra: RuntimeModelFile[]): string;
export function createRuntimeConfigPatch(
snapshotConfig: RuntimeConfig,
form: RuntimeSettingsFormState,
maxLayers: number,
): RuntimeConfig;
```
Locked behavior from `01-CONTEXT.md`:
```text
- D-07: picker updates only the launched field
- D-08: rescans preserve current draft selection unless invalid
- D-10: stale responses for the same field are ignored
- D-11: invalid drafted values remain visible, inline, and block save
```
</interfaces>
</context>
<tasks>
<task type="auto" tdd="true">
<name>Task 1: Make async picker and rescan handlers field-scoped and stale-safe</name>
<files>tauri-app/src/features/runtime/components/RuntimeSettingsDrawer.tsx, tauri-app/src/features/runtime/runtime-settings-form.ts, tauri-app/src/features/runtime/runtime-settings-form.test.ts</files>
<read_first>
- tauri-app/src/features/runtime/components/RuntimeSettingsDrawer.tsx
- tauri-app/src/features/runtime/runtime-settings-form.ts
- tauri-app/src/features/runtime/runtime-settings-form.test.ts
- .planning/phases/01-hf-model-discovery/01-CONTEXT.md
</read_first>
<behavior>
- Test 1: folder/file pickers update only their target draft fields and do not rehydrate unrelated inputs (D-07).
- Test 2: model rescans preserve the current draft selection when it is still valid and only mark it invalid when it disappears (D-08, D-11).
- Test 3: only the latest request for a field can update draft-facing UI; older responses are ignored (D-10).
</behavior>
<action>Refactor async handlers in `RuntimeSettingsDrawer.tsx` so each operation has field-scoped state and a latest-request guard. Concretely: `handlePickDir`, `handlePickFiles`, `handleRescan`, and any async validation/search path must update only the field(s) they own rather than resetting the full draft. Add explicit request sequencing per field or operation key so older responses are ignored when a newer request for that same field has started. Update `runtime-settings-form.ts` helpers so rescans preserve the current drafted model path when it still exists, and return an invalid-draft signal when the drafted path no longer exists instead of auto-falling back to another model silently. Add helper-level tests in `runtime-settings-form.test.ts` for preserved selection, invalid-selection detection, and non-silent fallback rules.</action>
<acceptance_criteria>
- `tauri-app/src/features/runtime/components/RuntimeSettingsDrawer.tsx` contains explicit stale-request protection for field-level async handlers.
- `tauri-app/src/features/runtime/runtime-settings-form.ts` exposes helper logic that distinguishes valid preservation from invalid drafted selections.
- `tauri-app/src/features/runtime/runtime-settings-form.test.ts` covers preserved selection and invalid-selection outcomes.
</acceptance_criteria>
<verify>
<automated>npm test -- runtime-settings-form</automated>
</verify>
<done>Async field helpers can no longer silently rewrite unrelated fields or let older responses override the latest user intent.</done>
</task>
<task type="auto" tdd="true">
<name>Task 2: Preserve invalid drafts visibly and block save until resolved</name>
<files>tauri-app/src/features/runtime/components/RuntimeSettingsDrawer.tsx, 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/RuntimeSurface.test.tsx
- .planning/phases/01-hf-model-discovery/01-UI-SPEC.md
- .planning/phases/01-hf-model-discovery/01-CONTEXT.md
</read_first>
<behavior>
- Test 1: an invalid model or folder draft remains visible with inline error text and destructive styling instead of being cleared (CONF-04, D-11).
- Test 2: `Save changes` is disabled while invalid draft metadata exists.
- Test 3: a failed save keeps the current draft values in place and surfaces the failure message instead of resetting the form (CONF-04).
</behavior>
<action>In `RuntimeSettingsDrawer.tsx`, render inline invalid-state messaging for drafted values that fail validation or disappear during rescan. Preserve the user's typed or selected value in the control, add the destructive invalid styling from the Phase 1 color contract, and disable `Save changes` whenever invalid draft metadata exists. Keep save failures drawer-visible without resetting the draft or clearing helper state. Extend `RuntimeSurface.test.tsx` to assert concrete UI outcomes: invalid drafted values stay in the control, inline error copy is rendered near the affected field, `Save changes` is disabled until corrected, and a rejected save preserves the user's current draft state.</action>
<acceptance_criteria>
- `tauri-app/src/features/runtime/components/RuntimeSettingsDrawer.tsx` renders inline invalid/error UI near affected fields and disables `Save changes` when invalid draft state exists.
- `tauri-app/src/features/runtime/components/RuntimeSurface.test.tsx` asserts that invalid draft values remain visible and save failures preserve the draft.
</acceptance_criteria>
<verify>
<automated>npm test -- RuntimeSurface</automated>
</verify>
<done>Phase 1 ends with durable draft safety: invalid values are visible, saves are blocked until fixed, stale async work is ignored, and user intent survives failures.</done>
</task>
</tasks>
<verification>
- `npm test -- runtime-settings-form`
- `npm test -- RuntimeSurface`
- `npm run build`
</verification>
<success_criteria>
- Async field updates never overwrite unrelated draft fields.
- Invalid model/folder drafts remain visible, explain the problem inline, and block save until fixed.
- Save failures and rescans preserve user intent rather than silently replacing or clearing values.
</success_criteria>
<output>
After completion, create `.planning/phases/01-hf-model-discovery/01-03-SUMMARY.md`
</output>