fix: allow replacing unavailable conversation models

Persist the selected model before opening dormant sessions, and reconfigure crashed workers on the same account. Preserve session history and report model removal through the model-unavailable contract instead of a generic runtime failure.
This commit is contained in:
2026-09-21 12:23:04 +08:00
parent d222d17500
commit 0f7093d173
8 changed files with 236 additions and 6 deletions

View File

@@ -0,0 +1,49 @@
# Task: Recover model switching after managed model removal
## Identity
- Task ID: 20260921-disabled-model-switch-0e02d13a
- Mode: Feature
- Branch: codex/20260921-disabled-model-switch-0e02d13a-disabled-model-switch
- Worktree: D:\Datas\OthersProjects\.codex-worktrees\makelore\20260921-disabled-model-switch-0e02d13a
- Base commit: d222d175000008bad5f344577679256d22a636c2
- Owner: codex
- Status: Ready for Integration
## Scope
- Reproduce and correct Code model switching when Operations has removed the model saved by an existing Conversation. Preserve the conversation/history and allow explicit selection of an available model without depending on the invalid old model.
## Intent And Constraints
- Official check/start/status passed with the exact feature task, branch, worktree and main base above. Read all 149 peer Scope/Intent/Promotion records; retained historical placeholders as unknown/read-only. Previous model-list-sync and Windows shell tasks are separate ready branches, not on main; neither has a conflicting recovery policy. Audio/WeChat peers are independent.
- Planning Gate Passed after entry/own record, same-base integrated context, accepted ADR-006/model-capabilities authority, architecture/domain, evidence/reflection/commitment context. Positioning templates are not authoritative over AGENTS/README and accepted decisions. Main owns provider validation, metadata and Pi lifecycle; active/uncertain work must not be silently stopped or replayed.
- Apply diagnosing-bugs with a deterministic failing test at the actual model-change service/runtime seam. No subagents, live credentials, user conversation changes, paid inference, installed-file changes, merges or deployment.
## Outcome
- Confirmed two model-switch failures: a cold Conversation first opened Pi using its removed saved model, so the valid replacement was never persisted; a crashed worker on the same account received set_model despite having no live channel. PiProviderConfigError(MODEL_UNAVAILABLE) also lacked the public contract consumed by the service, producing the misleading CODING_RUNTIME_UNAVAILABLE / 503.
- Model selection now waits for an already pending preparation, then uses the existing persist/dispose/prepare path when there is no worker. The replacement is validated before any write. Cached failed registration is forgotten so the next preparation reads the new durable model and original session binding.
- Existing workers retain the normal mutation/uncertainty guards. A crashed worker uses the existing target reconfiguration path even on the same account. No active worker is forcibly disposed to repair a model choice.
- Model unavailability maps to CODING_MODEL_UNAVAILABLE / 409 and the existing actionable UI text. Renderer required no production changes; its existing unavailable-option and selectable alternatives already work once Main no longer requires the old model.
- README documents explicit replacement, retained history, and no automatic selection/replay. No dependencies, backend/operator configuration, installed application or user data changed.
## Verification
- RED: pnpm exec vitest run tests/unit/coding-core-routes.test.ts -t 'switches a removed saved model' failed at service.ensurePrepared before the replacement could be persisted.
- Exact symptom confirmed against unmodified production files with the real Pi runtime, registry and catalog selector: cold preparation returned CODING_RUNTIME_UNAVAILABLE instead of CODING_MODEL_UNAVAILABLE, and same-account switching on a crashed worker returned CODING_RUNTIME_UNAVAILABLE / 503. Original production contents were restored after that comparison. One incidental test teardown write race was removed by settling and awaiting the test prompt before cleanup.
- Diagnostic phase adjustment: broad ranked hypotheses and extra instrumentation were unnecessary after the deterministic service failure and real selector/runtime comparison identified the precise call and error translation. No debug logging or throwaway code retained.
- GREEN: pnpm exec vitest run tests/unit/coding-core-routes.test.ts tests/unit/pi-conversation-runtime.test.ts tests/unit/pi-provider-config.test.ts tests/unit/pi-worker-pool.test.ts — 75 tests passed. Covers removed targets rejected without writes, cold/idle/crashed managed sessions, unchanged IDs/session keys/history, no automatic prompts, successful subsequent explicit prompt, existing active switching, and uncertain-mutation rejection.
- pnpm exec playwright test tests/e2e/pi-coding-first-chat.spec.ts --grep 'removed saved model' — 1 passed in isolated Electron. Covers initial snapshot error, enabled picker, disabled unavailable option, replacement selection, error clearance, retained messages and no automatic prompt/abort. Initial test expected an assistant process message to be visible while its group was collapsed; fixed the test to expand that existing group. Uses mocked Host responses, not live inference.
- pnpm run typecheck, pnpm run build:vite, targeted ESLint, and git diff --check passed. Build emits existing Browserslist/dynamic-import warnings.
- Additional Main type check: pnpm exec tsc --project tsconfig.node.json --noEmit --composite false --incremental false reports 62 errors. Compared against unchanged base production files in the same dependency environment: 62 baseline errors and zero diagnostic differences (ignoring shifted line numbers). No new Main diagnostics. The first attempt without --composite false was rejected by TypeScript because composite requires incremental compilation.
- Reviewed every changed file against the recorded base. No full suite, production provider calls, installer/package smoke or manual installed-client verification was performed.
## Follow-ups
- Integrate this task commit and ship a client build before the installed application receives the fix. Prior model-list synchronization and Windows shell tasks remain separate branches; this fix does not require merging either to run.
- Existing Main type-check failures remain outside this task's scope.
## Promotion Candidates
- Target: .project-docs/20-architecture/data-flow.md and current-state.md. Proposal: explicit model replacement for a dormant Conversation is validated/persisted before runtime preparation; a crashed target reconfigures with the selected model while retaining its durable session. A removed model yields the actionable model-unavailable contract, not a generic runtime error. Evidence: 75 focused tests and the isolated Electron replacement flow above. Future impact: provider catalog removals must not block changing a saved selection; active/uncertain work and history remain protected. Semantic conflicts: none with accepted ADR-006 or managed model authority. Human confirmation: not required for the bug-fix policy; canonical edits remain reserved for a later Integration Gate.