fix(pi): keep active runs alive during background sleep

This commit is contained in:
2026-08-25 20:56:17 +08:00
parent 92f4c91088
commit 621ebb1781
22 changed files with 1061 additions and 68 deletions

View File

@@ -0,0 +1,184 @@
# Task: Prevent background sleep from terminating active Pi runs
## Identity
- Task ID: 20260825-pi-background-run-lease-6a4e2c91
- Mode: Feature
- Branch: codex/20260825-pi-background-run-lease-6a4e2c91-pi-background-run-lease
- Worktree: D:\Datas\OthersProjects\makelore-pi-background-run-lease-6a4e2c91
- Base commit: 92f4c91088e79252aca76af3279b184da68e1eb7
- Owner: codex-root
- Status: Ready for integration
## Scope
- Add a Main-owned authoritative background lease for every queued or accepted
top-level Pi Conversation run, acquired before mutation queueing and held
through parent/child work until authoritative terminal cleanup.
- Close the `BackgroundLifecycleController.sleepIfIdle()` in-flight race so a
lease acquired while `onSleep` is awaiting prevents the later runtime stop,
while preserving hidden-idle warm-worker eviction after all runs settle.
- Terminalize supported intentional disposal of an active target Conversation
before runtime projection state is removed, without replaying an accepted or
uncertain mutation or affecting sibling Conversations.
- Replace ambiguous Main stop reason `dispose` at supported callers with a
bounded caller-specific reason, and retain content-free lifecycle evidence.
- Extend focused Main/runtime/Renderer tests, Windows Electron E2E, final-ASAR
packaged proof, and formal Windows NSIS/artifact verification.
## Intent And Constraints
- Preserve the Pi hard cutover and exact Pi `0.84.2`; do not add OpenCode
fallback, a Provider watchdog, automatic mutation replay, a global daemon,
schema migration, feature flag, compatibility track, or Pi upgrade.
- The authoritative lease is Main-owned. Renderer visibility or IPC lease
delivery must not own run liveness. Acquire synchronously before a top-level
mutation can be queued/accepted; a parent lease covers its child/subagent.
- Release the lease exactly once only after authoritative settle, definite
pre-accept rejection, terminal crash/protocol failure, completed cancel,
explicit dispose cleanup, or application shutdown cleanup. Generation or
resource replacement for the same logical run must not release it early.
- Preserve target isolation, the existing same-session recover contract, and
no-replay semantics. Intentional termination while the UI remains alive must
project a recoverable terminal state; it must not fabricate success.
- Keep stop reasons finite and content-free. Diagnostics may retain only safe
lifecycle classification, reason, redacted identifiers, generation,
code/signal, stage, and duration; no prompt/tool/header/token/session text or
complete project path may enter logs or Renderer DTOs.
- Do not edit the read-only diagnosis task or overwrite the user's installed
Makelore. Do not push or publish. Real Provider remains Explicitly Waived /
Accepted Risk (`realTurnVerified=false`); macOS/native Linux gates are out.
## Project Context Loaded
- Concurrent Task Gate passed with isolated ownership of this task, branch,
and worktree at base `92f4c91088e79252aca76af3279b184da68e1eb7`.
- Diagnosis commit `dc820b2` is a docs-only sibling based on `941b015`, not an
ancestor of the cumulative product HEAD; its task record was read-only and
is not used as the implementation base.
- Shared canonical memory remains OpenCode-stale relative to the cumulative Pi
product. Current source, the Pi resilience contract, cumulative Pi task
records, and the read-only output-stall diagnosis are authoritative here.
- Eleven non-ready peer records were inspected. One owns an old main/OpenCode
integration and the others are unrelated or placeholder scopes; no known
peer owns Pi background lifecycle, runtime disposal, or packaged proof
semantics. Placeholder scopes remain unknown coordination state but expose
no semantic conflict that changes this isolated plan.
- Relevant constraints are Main-owned run liveness, target-scoped monotonic
terminalization, same-binding recovery, bounded stop reasons, no mutation
replay, and final Windows packaged proof with zero residual resources.
- Planning Gate result: Passed.
## Plan
1. Trace actual background lifecycle, Pi mutation/dispose, composition, and
stop-reason callers; build a deterministic combined red test for the exact
hidden-active-run failure and the in-flight sleep race.
2. Add the narrow Main-owned lease dependency, acquire before top-level
queue/acceptance, and release exactly once across settle/reject/crash/
protocol/abort/dispose/shutdown paths.
3. Reuse the existing Conversation projection chain to terminalize active
intentional disposal before state removal, and pass caller-specific bounded
reasons without changing settled runs or sibling Conversations.
4. Extend Renderer/Host/Windows E2E and final packaged proof for hidden active
completion, later idle stop, explicit-dispose unlock/recover/isolation, no
replay, safe diagnostics, and zero resource/process residue.
5. Run focused tests, typecheck, lint, full unit, build, Windows Electron E2E,
then commit a clean candidate and run formal `package:win`, Windows/Pi
closure verification, packaged proof, artifact fingerprinting, and the Task
Documentation Gate.
## Outcome
- Implemented a Main-owned per-Conversation top-level run lease in the Pi
runtime. Prompt and compact acquire before optimistic/queue acceptance;
steer/follow-up reuse the parent run lease. Settle, definite rejection,
crash, protocol invalidation, abort convergence, explicit recovery,
intentional disposal, and shutdown release through an idempotent owner map.
- Closed both background-stop races: the lifecycle controller rechecks
visibility and leases after asynchronous sleep preparation, and the Pi pool
rechecks the target's authoritative active-work predicate after pending
prepare/rebuild work and immediately before worker cancellation.
- Background sleep now refuses the entire coding-runtime sleep while any run
is queued or active, while settled hidden workers remain eligible for normal
eviction. A parent lease remains held while child/subagent resources finish.
- Active intentional disposal projects the existing safe recoverable terminal
failure on the target Conversation chain before cleanup/removal, preserves
the durable session binding, does not replay the accepted prompt, and leaves
sibling Conversations unchanged.
- Replaced the ambiguous `dispose` worker stop reason with caller-owned finite
reasons for background sleep, project deactivation/removal, Conversation
deletion, auth cleanup, model reconfiguration, fork cleanup, app shutdown,
and existing internal replacement/cleanup paths.
- Extended the final-ASAR resilience proof to use the real Main composition for
hidden active-run survival, settled idle eviction, intentional-dispose UI
unlock/recover/isolation, safe lifecycle reason evidence, background lease
counts, and zero residual resource/process verification.
- The clean implementation candidate completed Windows NSIS creation, both
artifact/closure verifiers, and the final packaged Main/UI/fault proof. The
task record is amended into the cumulative delivery commit before the formal
commands are rerun for the returned artifact.
## Verification
- `pnpm install --frozen-lockfile` — passed with package-manager-pinned pnpm
`10.33.4` and locked Pi `0.84.2`.
- Red phase: the new background lifecycle in-flight test observed one unwanted
`onStopRuntime` call; the real Pi-runtime/lifecycle combination observed zero
Main run leases after prompt acceptance.
- Focused regression gate — passed: 8 files / 106 tests, covering lifecycle,
running+queued Pi work, parent/child lease retention, exactly-once release,
crash/protocol/abort/recover, forced disposal, worker reason diagnostics,
project/auth callers, and packaged proof wiring.
- `pnpm run typecheck` — passed.
- `pnpm run lint:check` — passed with 5 pre-existing warnings and zero errors.
- `pnpm test` — passed: 182 files, 1529 tests passed, 2 skipped.
- `pnpm run build:vite` — passed for Renderer, Main, Preload, and utility
bundles; only existing dynamic-import/chunk-size warnings were emitted.
- `pnpm run test:electron:windows` — passed: 2 files / 4 tests.
- Clean-candidate `pnpm run package:win` — passed; generated the Windows x64
NSIS without overwriting an installed Makelore directory.
- `pnpm run verify:artifact:win` — passed for NSIS/unpacked Electron, app.asar,
bundled Python/uv/npm, and Windows native modules; verifier HEAD matched the
clean implementation candidate.
- `pnpm run verify:artifact:pi` — passed: exact Pi `0.84.2`, 130 production
packages, 6 asset groups, zero missing/relocated package or asset, 5 native
assets outside ASAR, managed Skills, and materialized extension/subagent.
- `pnpm run test:pi-subagent:packaged` — passed from packaged app.asar Main:
two UI/Main runs retained 2 background leases and 2 worker processes while
hidden past grace; both settled, released leases, then warm idle workers
stopped with reason `background_sleep`. Controlled intentional disposal
projected `CODING_RUNTIME_START_FAILED` as recoverable, kept the sibling
running and both bindings intact, and did not change Provider request counts.
Recover reused the binding without mutation replay. Final workers, Pi
processes, process leases, run permits, child permits/dispatches, extension
registrations, write leases, and background leases all reached zero.
- Packaged proof clean exit — passed with zero lingering tracked Electron/Pi
PIDs. Lifecycle evidence distinguished unexpected exit, protocol
invalidation, and intentional stop; every stop/replacement had a reason and
redaction/content-free checks passed.
- Authenticode inspection — `NotSigned`; no signing identity was available.
- The returned installer is rebuilt and reverified from the amended cumulative
clean HEAD; its exact path, size, SHA-256, and signing state are reported in
the delivery response.
## Follow-ups
- PI-R002 soak/field attribution remains a separate follow-up and is not a
blocker for this deterministic failure-convergence hotfix.
- Real external Provider concurrency, credential isolation, and protocol
compatibility remain Explicitly Waived / Accepted Risk with
`realTurnVerified=false`; loopback/fault injection is not a Provider Pass.
- macOS and native non-WSL Linux gate status is unchanged and not promoted.
## Promotion Candidates
- Target canonical document: Pi runtime architecture/resilience documentation.
Proposal: record Main-owned top-level run leases, post-`onSleep` activity
recheck, terminal-before-dispose monotonicity, and finite caller-owned stop
reasons as accepted invariants. Evidence: focused lifecycle/runtime tests,
Windows Electron E2E, final-ASAR hidden-run/idle-eviction/intentional-dispose
proof, and zero-resource shutdown. Future impact: new Pi mutation or stop
callers must join these seams rather than relying on Renderer visibility.
Semantic conflicts: none known with the accepted Pi hard cutover/resilience
plan. Human confirmation required: yes, during Integration Gate.