# 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.