feat: add coding project schema v2 migration

This commit is contained in:
2026-08-22 20:56:33 +08:00
parent 9a31dacb2a
commit 81d8ad1b6b
8 changed files with 1690 additions and 0 deletions

View File

@@ -0,0 +1,83 @@
# Task: Implement PI-020 project and conversation schema v2
## Identity
- Task ID: 20260822-pi-schema-v2-5e4b7a91
- Mode: Feature
- Branch: codex/20260822-pi-schema-v2-5e4b7a91-pi-schema-v2
- Worktree: D:\Datas\OthersProjects\makelore-pi-schema-v2-5e4b7a91
- Base commit: 9a31dacb2aad92a2deee0cec11fdb162a2916bfc
- Owner: codex
- Status: Completed
## Scope
- Implement `PI-020 — Project and Conversation schema v2 migration` from the merged Pi hard-cutover Spec/ticket graph.
- Add vendor-neutral project configuration, project registry, Conversation metadata store, atomic JSON persistence, and one-time OpenCode cutover migration under `electron/coding-projects`.
- Preserve stable Agent metadata while mapping legacy string models only through an injected unique resolver; unresolved/no-account models remain explicit user-selection requirements.
- Back up schema v1 metadata before replacement, initialize empty schema v2 Conversations, expose a persistent one-time notice state, and clean only proven generated `.opencode/agent` files while moving uncertain content to the migration backup.
- Add focused table, rollback, path-validation, single-flight, local-only creation, and performance tests without wiring the new stores into Renderer, Host routes, Provider Service, or Pi workers yet.
## Intent And Constraints
- Keep project/Agent/Conversation identity product-owned. Conversation IDs are Makelore UUIDs; Pi session IDs and opaque session keys remain optional Main-private binding fields.
- Reuse the vendor-neutral `ProductModelRef` and thinking-level contract from PI-010. Do not duplicate Pi/OpenCode provider identifiers in persisted schema v2.
- A legacy model string maps automatically only when the injected resolver returns one unique account/model reference. Missing, ambiguous, or unavailable account mappings persist `{ model: null, modelResolution: "required" }`; no default model fallback is allowed.
- Treat migration as one explicit v1→v2 branch, not a generic framework: create a timestamped backup first, use atomic per-file replacement, and restore original metadata/Agent files if any later migration operation fails.
- Never recursively delete `userData/opencode` or arbitrary project `.opencode` content. Only exact current generated Agent materializations may be deleted; modified or unknown Agent files move to backup, and other `.opencode` paths remain untouched and unread by new code.
- Project, partner, and empty Conversation creation are local metadata operations and must not import or call child-process/runtime code. Per-Conversation Pi binding creation exposes a single-flight contract but does not start Pi in this ticket.
- Preserve the explicit real-Provider Accepted Risk and deferred macOS decisions; neither affects this local metadata ticket or becomes a Pass here.
## Project Context Loaded
- `maintain-project-docs` created and verified isolated feature ownership at base `9a31dacb2aad92a2deee0cec11fdb162a2916bfc`; Concurrent Task Gate and Planning Gate passed.
- The planner audited PI-030 as Done in commit `fc7b80793c0a8b0a39d8eef31b57ee42895dd9fd`, recomputed the unchanged 18-node/31-edge DAG, and identified PI-020 as the unique Ready Frontier ticket from implementation base `9a31dac`.
- The planner status commit remains read-only on its own branch because the repository Integration Gate is currently held by blocked task `20260821-integrate-model-switch-fix-8f2d6c41`; this does not block isolated PI-020 feature work.
- Repository guidance, shared project memory, PI Spec sections 5/6/15/17/19/20, the PI-020 ticket, PI-010 contracts, existing v1 project/conversation modules, tests, and all planning peer task records were inspected before planning.
- Existing v1 ownership is split between `shared/project-config.ts`, `shared/project-conversations.ts`, and `electron/opencode/*`; those remain the old product path until later cutover tickets. PI-020 introduces the new `electron/coding-projects` boundary rather than incidentally rewriting current OpenCode routes.
- Other planning peers do not declare writes to the PI-020 files. `20260820-partner-create-model-parity-c83d` has an undefined scope and is retained as an unknown coordination warning; no confirmed contradictory decision or file ownership was found.
- `codebase-design` keeps persistence/migration details behind small store interfaces. The repository prohibition on subagents is honored; no parallel agent was created.
## Plan
1. Implement schema v2 types/normalizers and atomic JSON persistence, reusing PI-010 product model contracts without runtime imports beyond types.
2. Implement local project registry/config/Agent creation and Conversation CRUD with UUID identity, opaque session-key validation, serialized writes, and per-Conversation binding single-flight.
3. Implement the explicit v1 cutover: complete backup, injected unique model resolution, v2 atomic writes, one-time notice, generated/modified Agent classification, and rollback on failure.
4. Add migration-table, atomic-failure/retry, Agent-file, path, single-flight, no-spawn, and local performance tests.
5. Run focused tests, typecheck, lint, full tests, build when warranted, documentation gates, commit, and planner handoff.
## Outcome
- Added the vendor-neutral `electron/coding-projects` boundary with atomic JSON replacement, strict project schema v2, product-owned project registry, and Conversation schema v2. Agent and Conversation model state uses the PI-010 `ProductModelRef`; unresolved/no-account state is persisted as `model: null` plus `modelResolution: "required"` with no default fallback.
- Added local-only project, Agent, and unbound Conversation creation. Project path upserts and Conversation writes are serialized; concurrent first prompts share one per-Conversation binding flight and persist one validated Pi session ID/opaque session key pair.
- Added explicit v1→v2 migration with timestamped `.niancode/migration-backups/opencode-cutover-*` backup, injected unique legacy-model resolution, empty v2 Conversation initialization, and persistent `pending`→`acknowledged` legacy notice state.
- Migration preserves stable Agent ID/name/prompt/skills/archive. Exact cleanup proof uses the old runtime's normalized `buildProjectAgentManifest` output; locally modified or unknown `.opencode/agent` files are copied into the migration backup before being removed, while unrelated `.opencode` content remains untouched.
- A missing backup aborts before either v2 write. A failure after writes begin restores project metadata, Conversation metadata, and Agent file contents so the original v1 project remains retryable; a retry matrix test completes successfully after a deliberately corrupt Conversation write.
- The real Provider gate remains explicitly waived: real protocol/account compatibility, Provider concurrency, credential isolation, and Provider-specific behavior are Accepted Risk and remain unverified, not Pass. No real Provider or macOS validation was performed in this ticket.
## Verification
- `pnpm install --frozen-lockfile`: passed with repository-pinned pnpm 10.33.4.
- Focused schema/migration suite: 2 files, 15 tests passed. Coverage includes unique/unresolved/no-account mapping, stable Agent fields, modified/unknown generated files, no-backup refusal, corrupt write rollback/retry, empty v2 Conversations, one-time notice consumption, traversal/absolute session-key rejection, per-Conversation single-flight, atomic replacement/temp cleanup, local metadata performance budgets, and spawn spy = 0.
- `pnpm run typecheck`: passed.
- Scoped ESLint over `electron/coding-projects` and both focused tests: passed with no findings.
- `pnpm run lint:check`: passed with 0 errors and 6 pre-existing warnings outside this task's files.
- `pnpm test`: 184 files, 2133 tests passed.
- `pnpm run build:vite`: passed for Renderer, Main, Preload, and release utility output; existing dynamic-import and chunk-size warnings remain unchanged.
- `git diff --check`: passed.
## Follow-ups
- `PI-040` may consume project/Agent `ProductModelRef` data after integration; it owns Provider catalog, credential projection, and managed prompt/skill resources. The accepted real-Provider risks must remain labeled unverified rather than being inferred from these local metadata tests.
- `PI-050` may connect `ensureSessionBinding` to the Pi worker pool/session registry. Its runtime integration must retain the store's single-flight semantics and keep the opaque key relative/private.
- `PI-100` may expose project/Agent/Conversation create routes. These routes must stay local-only until a prompt explicitly requests runtime preparation.
- `PI-140` owns final cutover wiring and old OpenCode removal. It should consume the one-time legacy notice and must not broaden cleanup beyond the conservative migration behavior delivered here.
## Promotion Candidates
- Target: Pi cutover migration implementation guidance in the architecture/planner task.
Proposal: preserve v1 Agent prompt and selected skill IDs from raw project metadata, but classify deletable generated Agent files only against the old runtime's normalized manifest; any mismatch remains uncertain and is moved into the migration backup.
Evidence: the focused migration matrix preserves a retired legacy skill ID and prompt whitespace while deleting two exact generated files, backing up one modified generated file plus one unknown file, and leaving unrelated `.opencode/skills` content intact.
Future impact: `PI-140` can remove old runtime code without reimplementing or widening the data-cleanup boundary.
Semantic conflicts: none; this is the conservative implementation of PI-020 Required work 6 and Acceptance data-preservation rules.
Human confirmation required: no.