fix(pi): validate user-entry conversation forks

This commit is contained in:
2026-08-25 17:48:31 +08:00
parent 941b015330
commit 8ba2a6055c
10 changed files with 767 additions and 13 deletions

View File

@@ -0,0 +1,191 @@
# Task: Fix Pi user-entry conversation fork
## Identity
- Task ID: 20260825-pi-user-entry-fork-4a7d9c21
- Mode: Feature
- Branch: codex/20260825-pi-user-entry-fork-4a7d9c21-pi-user-entry-fork
- Worktree: D:\Datas\OthersProjects\makelore-pi-user-entry-fork-4a7d9c21
- Base commit: 941b015330206af648f944183846ac2857468847
- Owner: codex-root
- Status: Verifying
## Scope
- Restrict the Renderer fork action to durable user messages on the active
Conversation path.
- Validate the same invariant in `CodingConversationService.fork` before any
target Conversation metadata, worker, session binding, archive, or cleanup
resource can be created.
- Preserve the existing real Pi `0.84.2` fork RPC for a valid user entry and
prove the resulting target binding/hydration through unit, managed runtime,
Windows Electron, and final packaged-product seams.
- Produce and verify a new Windows x64 NSIS without modifying the user's
installed application.
## Intent And Constraints
- Keep the Pi hard cutover and current project/Agent/Conversation ownership.
Do not add an OpenCode fallback, runtime restart control, compatibility
layer, schema migration, feature flag, Pi upgrade, or Provider/worker-pool
redesign.
- A valid source is a message node on the source Snapshot's current active
path with the exact `sourceEntryId`, `role === 'user'`, and non-optimistic
durable state. Missing, assistant, unknown, stale, abandoned-path, or
optimistic sources fail before target creation.
- Invalid input returns existing `400 CODING_CONVERSATION_REQUEST_INVALID`
through the fixed safe Chinese route projection. Pi's raw rejection,
stderr, session paths, prompts, credentials, and provider data must not
reach Renderer or proof reports.
- Keep fork mutation no-replay: a timeout or uncertain result is never
automatically submitted again. A valid fork retains the existing source
session and hydrates a distinct target binding under the same Agent.
- Preserve cumulative base `941b015330206af648f944183846ac2857468847`,
including product parent `45d933732a8fbc0dadb07a4a4d65f0d12000c2ea`
and its Agent-owned Conversation hierarchy. Do not touch dirty `main`, the
diagnosis worktree/record, previous packaging worktrees, or user install.
- Real external Provider remains Explicitly Waived / Accepted Risk with
`realTurnVerified=false`; macOS/native Linux status is unchanged and is not
Pass evidence. Do not create subagents, push, or publish.
## Project Context Loaded
Task context:
- Task ID: `20260825-pi-user-entry-fork-4a7d9c21`
- Mode: feature
- Branch: `codex/20260825-pi-user-entry-fork-4a7d9c21-pi-user-entry-fork`
- Worktree: `D:\Datas\OthersProjects\makelore-pi-user-entry-fork-4a7d9c21`
- Base commit: `941b015330206af648f944183846ac2857468847`
- Other active local tasks: eleven non-ready owner records were inspected.
One AI Design E2E task has a narrow unrelated test-only scope; the occupied
`main` integration task owns historical OpenCode model-switch integration.
The remaining older planning records still contain undefined placeholder
scopes.
- Overlap or semantic-conflict assessment: no known peer owns Pi fork,
`CodingConversationTimeline`, `CodingConversationService`, or packaged Pi
fork proof semantics. Placeholder scopes remain unknown coordination state,
but their stated titles and isolated worktrees expose no semantic conflict
that changes this plan.
Read:
- `.project-docs/05-agent-entry/read-before-planning.md`
- `.project-docs/05-agent-entry/memory-index.md`
- `.project-docs/05-agent-entry/planning-gate.md`
- this active task record
- project positioning, current state, decision index, system overview,
module map, data flow, business rules, success criteria, glossary, evidence,
reflection, commitments, and stale-item indexes
- diagnosis task `20260825-fork-runtime-unavailable-8e7c4a21`
- Pi cutover Spec and the relevant PI-050/060/100/130/150 ticket sections
- PI Conversation contracts, Renderer store, Host cutover, and cumulative
Agent→Conversation hierarchy task records
- every non-ready peer's Scope, Intent And Constraints, and Promotion
Candidates sections
Relevant understanding:
- Project goal: Makelore Code exposes a vendor-neutral, local project-scoped
Conversation product while Electron Main exclusively owns Pi runtime,
sessions, credentials, files, and recovery.
- Current integrated focus: shared canonical memory is OpenCode-stale relative
to the cumulative Pi product. Current source, the Pi Spec/task chain, and the
committed diagnosis are authoritative for this repair.
- Active task scope: correct the supported fork source and error boundary only;
do not reinterpret a deterministic Pi entry rejection as runtime failure.
- Active constraints: target validation precedes metadata/resource creation;
valid user fork semantics and no-replay remain unchanged; the Agent-owned
Conversation hierarchy from `45d9337` must remain intact.
- Decisions affecting this task: Spec route `/api/coding/conversations/:id/fork`
is explicitly from a user entry; hydrated Snapshot nodes contain only the
active path and expose durable `sourceEntryId` without leaking Pi wire.
- Evidence, reflections, or commitments affecting this task: real pinned Pi
accepted the persisted user entry and rejected the assistant entry with
empty stderr. Windows formal packaging must retain artifact/runtime closure;
real Provider and non-Windows platform gates remain out of scope.
- Files or modules likely involved: Timeline and Chat E2E, shared/facade fork
types, `conversation-service.ts`, focused Host/runtime tests, Pi packaged
release proof, packaged smoke runner, and Windows artifact reports.
- Unknowns, stale docs, or conflicts: canonical positioning is a placeholder
and architecture/data-flow are OpenCode-stale. Several peer task scopes are
placeholders, but no known decision conflicts with the precise Pi repair.
Gate result:
- Passed.
## Plan
1. Add failing Renderer and Main regressions for user-only visibility, exact
callback identity, invalid active-path rejection, and zero target-side
effects; verify they fail on the diagnosed behavior.
2. Implement the smallest product-contract correction: user-only durable UI
action and service-owned active-Snapshot validation before target create.
3. Add valid user fork coverage through the real pinned Pi managed opener and
service seam, checking distinct target binding, Pi session identity,
hydrated Snapshot, source isolation, and clean worker shutdown.
4. Extend Windows Electron E2E and final packaged Main/UI proof so assistant
has no action, the user action creates/selects a same-Agent branch, the
original remains unchanged, and no runtime-unavailable state or process
residue appears.
5. Run frozen install, focused tests, typecheck, lint, full unit, production
build, Windows Electron E2E, then commit a clean candidate and run formal
`package:win`, Windows/Pi closure verifiers, packaged proof, signing/hash,
documentation drift, registry completion, and final clean-state checks.
## Outcome
- Renderer now exposes the fork action only on non-optimistic user message
nodes with a durable `sourceEntryId`; assistant and non-persisted nodes do
not expose the action.
- `CodingConversationService.fork` now resolves the source Conversation's
authoritative hydrated Snapshot and rejects missing, assistant, unknown,
stale, or abandoned-path entries with
`400 CODING_CONVERSATION_REQUEST_INVALID` before target metadata creation.
The Host keeps its fixed safe Chinese projection and never exposes Pi's raw
`Invalid entry ID for forking` response.
- A valid user entry still reaches the locked Pi fork RPC. The permanent real
Pi `0.84.2` regression proves source hydration, a same-Agent target with a
distinct Pi session binding, correct before-entry target hydration, source
isolation, and clean shutdown of both workers.
- Windows Electron coverage now proves one user fork action for a history that
also contains an assistant entry, exact `sourceEntryId` submission, target
selection under the same Agent, preserved source history, and absence of
the runtime-unavailable banner.
- The final packaged proxy proof now performs the user-entry fork through the
real packaged Main composition and UI after a settled controlled turn. It
checks distinct bindings, correct target hydrate, source preservation,
zero prompt replay, user/assistant action visibility, process cleanup, and
the existing credential/role-contract boundaries.
- `README.md` now states that only persisted user messages are fork sources.
No Provider, worker-pool, schema, compatibility, or fallback architecture
changed.
## Verification
- `corepack pnpm install --frozen-lockfile` — passed with package-manager
pinned pnpm `10.33.4` and Pi `0.84.2`.
- Red phase — the focused Timeline/Main tests failed as expected: assistant
exposed a second fork action, and invalid entries reached target creation.
- Focused green — `4` files / `29` tests passed, including the real managed Pi
service fork and release-proof wiring.
- `corepack pnpm run typecheck` — passed.
- `corepack pnpm run lint:check` — passed with the existing five warnings and
no errors.
- `corepack pnpm test` — passed: `180` files / `1525` tests plus the isolated
pressure test; `2` pre-existing skips.
- `corepack pnpm run build:vite` — passed for Renderer, Main, Preload, and
release utility worker.
- Focused Windows Electron Playwright — passed: `2` tests, including the
same-Agent user-entry branch flow and source isolation.
- `corepack pnpm run test:electron:windows` — passed: `2` files / `4` tests.
- Formal Windows package, artifact closure, packaged UI/Main fork proof,
signing, fingerprint, and zero-residue evidence remain the active gate.
## Follow-ups
- Real external Provider execution remains explicitly waived and is not proof
for this fix (`realTurnVerified=false`). macOS and native non-WSL Linux
states remain unchanged.
## Promotion Candidates
- None. The current Pi Spec already states that fork is from a user entry; the
product README and this task-owned record now match that existing decision.