158 lines
9.1 KiB
Markdown
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>
|