Files
makelore/.project-docs/30-worklog/tasks/20260825-pi-background-run-lease-6a4e2c91.md
T

295 lines
18 KiB
Markdown

# 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
- Follow up the installed `621ebb1` Windows artifact: treat an RPC
confirmation timeout for an already-written Pi top-level mutation as
uncertain ownership rather than a definite terminal failure. Keep the
target run permit, process ownership, and Main background lease until a
correlated late result/event, worker failure, abort, recover, or other
authoritative cleanup converges it.
- Prevent prompt, follow-up, steer, compact, model reconfiguration, and fork
mutations from overlapping an unresolved uncertain mutation in the same
Conversation, while preserving sibling Conversation independence.
- Give manual compact a long-mutation lifecycle through compaction completion
and authoritative settle, and keep `CODING_REQUEST_UNCERTAIN` distinct from
local-runtime-unavailable UI semantics.
- 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
Follow-up plan from cumulative HEAD `621ebb17810394f6f7b97154cb01217bc9112857`:
1. Add deterministic red tests for delayed Pi prompt preflight confirmation,
delayed compact confirmation, late failure/exit/abort convergence,
same-Conversation mutation exclusion, sibling isolation, and Renderer
uncertainty unlock.
2. Make RPC confirmation timeout preserve authoritative uncertain ownership
without replay, then converge exactly once from late response/events or
explicit cleanup.
3. Extend final-ASAR packaged proof beyond the old ten-second threshold and
re-run the full Windows release gate from a clean follow-up commit.
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
### Installed-package P1 follow-up
- Pi RPC mutation confirmation now has an explicit retained-correlation mode.
Crossing the former 10-second Host confirmation threshold returns the safe
recoverable `CODING_REQUEST_UNCERTAIN` contract but does not retire the RPC
id or release the pool's active run, revision run, process permit, or Main
background lease.
- A late correlated success clears the provisional run error without ending
the run; a late explicit RPC failure terminalizes only the target; worker
invalidation, abort/recover cleanup, or authoritative `agent_settled`
converge the same ownership exactly once. `agent_settled` also confirms a
mutation when it arrives before the RPC success response, avoiding a hung
Host acceptance promise or terminal-state regression.
- Prompt, queued follow-up/steer, compact, model reconfiguration, and fork are
blocked for the same Conversation while uncertainty is unresolved. The
service performs model/fork rejection before metadata persistence or fork
target creation; sibling Conversations retain independent permits.
- Manual compact uses the same retained ownership through
`compaction_start`, `compaction_end`, correlated RPC settlement, and
hydrate. Locked Pi `0.84.2` does not emit `agent_settled` for a manual
compact, so the pool now emits a target-scoped top-level settlement after
the correlated compact success (including a late success) and the runtime
releases its lease exactly once. If `compaction_end(error)` terminalizes the
projection before the RPC rejection arrives, the later failure cleanup also
releases the already-terminal run lease idempotently.
- The final-ASAR proof now arms controlled 12-second Provider and Pi RPC
response delays, materializes proof-only compact thresholds in the isolated
userData, and records running/uncertain followed by completed/idle states.
- Renderer submission/action errors retain their stable backend code. An
uncertain submission keeps the draft and prevents overlap, shows the
actionable delay message instead of a runtime-outage banner, and the Header
derives away a stale compact uncertainty once a newer generation/sequence
reaches a non-uncertain terminal state.
Model, thinking, compact, and fork controls are disabled while the run is
active; abort and recover remain available.
- 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
### Installed-package P1 follow-up candidate
- Red phase — 5 expected failures across RPC correlation, pool ownership,
Main background lease, compact overlap, and Renderer reconciliation: the
old implementation retired the correlation and released active ownership at
10 seconds, returned a raw timeout, and retained the UI uncertainty after a
completed run.
- Focused green gate — passed: 10 files / 121 tests, including delayed prompt
and compact, late success/failure, settled-before-response, same-target
mutation exclusion with zero fork/model persistence, sibling isolation,
safe Host error mapping, Renderer automatic unlock, and final-ASAR wiring.
- `pnpm run typecheck` — passed after the final runtime/Renderer/proof changes.
- `pnpm run lint:check` — passed with zero errors and the same 5 pre-existing
warnings in `src/pages/Home/index.tsx` and `src/pages/Makelore/index.tsx`.
- `pnpm test` — passed: 181 regular files / 1538 tests passed / 2 skipped,
followed by the isolated pressure file / 1 test passed.
- `pnpm run build:vite` — passed for Renderer, Main, Preload, and utility
bundles; only existing dynamic-import and chunk-size warnings were emitted.
- `pnpm run test:electron:windows` — passed: 2 files / 4 tests.
- Final clean-HEAD NSIS, artifact closure, 12-second packaged UI/Main proof,
Authenticode, and zero-residual-process results are run after this task
documentation commit so the returned artifact binds to the docs-bearing
cumulative HEAD.
- The first candidate packaged proof intentionally required
`CODING_REQUEST_UNCERTAIN` but the controlled Provider's message-shape
matcher did not arm the delay; diagnostics showed both runs, both Main
leases, both Pi processes, and both Provider requests still active with no
runtime failure. The proof was corrected to arm prompt and compact delay
explicitly through E2E-only Main seams, removing message-shape dependence;
this was a proof-wiring failure, not a product fallback or relaxed gate.
- The explicit Provider arm then proved the loopback HTTP response remained
open for 12 seconds, but locked Pi acknowledged the local prompt RPC before
the first Provider response on this controlled path. The proof therefore
separates the two facts instead of claiming causality: the Provider response
remains delayed for 12 seconds, and an E2E-only one-shot Pi RPC response hold
independently crosses the 10-second client threshold. The hold is armed only
through the existing Main fault-injection surface, is absent from Renderer
and product configuration, and preserves the real packaged Pi command,
session, Provider request, events, and cleanup path.
- The final-ASAR candidate proof passed after the compact settlement fix:
prompt and compact each crossed the former 10-second confirmation threshold
while retaining target ownership; compact reached a durable completed node,
context/run idle, Provider active count zero, and background lease zero;
stale uncertainty disappeared from the real UI. Hidden idle eviction then
used `background_sleep`, the explicit crash/protocol/dispose matrix retained
sibling isolation and same-session recovery, and all tracked proof PIDs and
resource counters reached zero. This remains controlled loopback/fault
injection evidence with `realTurnVerified=false`.
- `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.