206 lines
13 KiB
Markdown
206 lines
13 KiB
Markdown
# Task: Implement PI-080 child workers and subagent runtime
|
|
|
|
## Identity
|
|
|
|
- Task ID: 20260823-pi-child-workers-5c8e2a71
|
|
- Mode: Feature
|
|
- Branch: codex/20260823-pi-child-workers-5c8e2a71-pi-child-workers
|
|
- Worktree: D:\Datas\OthersProjects\makelore-pi-child-workers-5c8e2a71
|
|
- Base commit: b806c78139aa11e338c680d4c3fa92e076901aa2
|
|
- Owner: codex
|
|
- Status: Ready for integration
|
|
|
|
## Scope
|
|
|
|
- Implement `PI-080 — Subagent scheduler and nested protocol` on top of the
|
|
cumulative PI-070 baseline `b806c78139aa11e338c680d4c3fa92e076901aa2`.
|
|
- Own the Main-private child scheduler/supervisor, managed ephemeral child Pi
|
|
opener, parent-child lifecycle registry, global child concurrency limit, and
|
|
strict `subagent.v1` validation/projection.
|
|
- Extend the single managed Makelore Pi extension and its authenticated Main
|
|
bridge only as needed to dispatch `single`, `parallel`, and `chain` child
|
|
work and stream stable product details back to the parent tool.
|
|
- Add fake scheduler integration coverage, real Pi child process smoke,
|
|
process/semaphore leak coverage, bridge identity/abort coverage, and
|
|
same-project parent/child write-lease coverage. Renderer/Host product routes
|
|
and final nested UI remain owned by later tickets.
|
|
|
|
## Intent And Constraints
|
|
|
|
- Follow planner task `20260822-pi-runtime-spec-b6e2c9a4`, ticket `PI-080`,
|
|
Spec `UX-041` through `UX-044`, `EXT-001` through `EXT-006`, and section 9.4.
|
|
- A dispatch accepts exactly one mode, contains at most eight bounded tasks,
|
|
and resolves only through Makelore-owned `subagent.v1`; unknown schema or raw
|
|
Pi/example details never enter product state or diagnostics.
|
|
- Every child owns an independent ephemeral Pi process/context and uses an
|
|
enabled project Agent's managed prompt, skills, model, credentials, and one
|
|
exact `read-only` or `coding` tool profile. Child sessions are not persisted
|
|
as recoverable user Conversations and children cannot recursively dispatch
|
|
more subagents.
|
|
- Reuse the same application `PiProcessBudget` as parent workers for the locked
|
|
parent/child soft cap of eight and enforce one global FIFO maximum of four
|
|
running children across all parents. Do not add a competing process counter.
|
|
- Reuse PI-070's single project mutation lease. A coding child loads the same
|
|
managed extension lease hooks as its parent; read-only children receive no
|
|
mutation tools. Do not introduce child worktrees or a second mutation lock.
|
|
- Parent abort/crash/recover/dispose, stale generation/run identity, bridge
|
|
disconnect, and host shutdown cancel unfinished children and release every
|
|
child/process permit. Parallel failures preserve sibling results; chain
|
|
failure marks the remaining tasks skipped.
|
|
- Keep Provider credentials in the child environment/redaction set only. Real
|
|
external Provider validation remains **Explicitly Waived / Accepted Risk**
|
|
with `realTurnVerified=false`; macOS x64/arm64 remains deferred to mandatory
|
|
PI-150. Neither is a Pass.
|
|
- Keep the change surgical: no dual runtime, generic plugin/permission system,
|
|
project/user Pi discovery, Renderer wire changes, Host product routes,
|
|
OpenCode refactor, or canonical project-memory edits.
|
|
|
|
## Outcome
|
|
|
|
- Added the Main-private `PiSubagentScheduler` with strict one-to-eight task
|
|
validation, single/parallel/chain execution, one FIFO four-child semaphore,
|
|
a parent/run dispatch registry, shared `PiProcessBudget` acquisition, idle
|
|
parent reclamation when the eight-process budget is full, and deterministic
|
|
abort/error/skipped projection without raw child errors.
|
|
- Planner review of `f1c7cd8` found that a previously queued parent process
|
|
budget waiter could take a reclaimed idle permit before the child. The
|
|
corrected budget keeps normal waiters FIFO, keeps child waiters FIFO, and
|
|
prioritizes child reservations so a running parent cannot deadlock while
|
|
waiting on its own child. The child enters the priority queue before idle
|
|
reclamation releases capacity.
|
|
- Planner re-review of `3de85d6` confirmed the original three findings closed,
|
|
then reproduced two further supported failure paths: a child reservation
|
|
could wait forever when no parent was idle during the one-time reclaim but a
|
|
spawning/running parent became reclaimable later; and reclaim/worker-stop
|
|
failure could leave an unowned process lease. The pool now wakes pending
|
|
reclaimers on `ready`/`idle` state transitions without polling. Scheduler
|
|
reservations race direct capacity against this cancellable event-driven
|
|
reclaim and always settle/release on reclaim rejection, parent abort, or any
|
|
other early exit. Parent `stop()` now releases its process lease in `finally`.
|
|
- Planner re-review of `a10b98e` confirmed both lease-lifecycle findings closed,
|
|
then reproduced the production-capacity shape `4 running + 4 queued`: queued
|
|
parent workers retained all eight process leases, leaving no ready/idle
|
|
worker for four children to reclaim. The pool now prefers ready/idle
|
|
eviction, then suspends the oldest queued parent process while retaining its
|
|
top-level FIFO entry, Conversation state, and session binding. When its turn
|
|
arrives, the same queue entry reopens that session as a new worker generation
|
|
before sending the original prompt. This closes the deadlock without
|
|
dropping queued work, reordering it, adding another queue, or exceeding the
|
|
shared eight-process cap.
|
|
- Planner re-review of `1727b75` confirmed the 4+4 deadlock closed, then
|
|
reproduced a supported stop/dispatch overlap: a queued worker could be
|
|
selected while its suspension `stop()` was still pending, so `ensureFresh`
|
|
saw the not-yet-released old lease and sent the prompt to a stopping
|
|
generation. `ensureFresh` now awaits any existing `processStopFlight`,
|
|
rechecks record ownership/rebuild state, and only then reopens a lease-less
|
|
worker. The old generation never receives the queued prompt.
|
|
- Final planner review of `30c8bfd` passed with zero Standards findings and
|
|
zero Spec findings. Independent reproduction confirmed the exact stop/slot
|
|
overlap and the production-default `maxRunning=4`, `maxIdle=4`,
|
|
`maxProcesses=8` 4+4 path; PI-080 is Done and the ready frontier advances to
|
|
PI-090 and PI-100.
|
|
- Added the managed ephemeral child opener. It resolves only enabled,
|
|
unarchived project Agents from `.niancode/project.json`, materializes their
|
|
exact model/prompt/skills, keeps credentials in the child environment and
|
|
redaction set, selects the exact read-only or coding tools, uses
|
|
`--no-session`, and returns bounded public summary/usage data.
|
|
- Upgraded the single materialized Makelore extension bundle to v2. Parent
|
|
workers expose `ask_user` and `subagent`; child workers expose neither and
|
|
retain only the shared mutation-lease hooks required by coding tools. The
|
|
authenticated loopback bridge streams only NDJSON `subagent.v1` details and
|
|
rejects recursive child dispatch and stale identities.
|
|
- Planner review of `f1c7cd8` also found that Pi 0.84.2 treats `--tools` as a
|
|
strict whitelist across built-in and extension tools. The managed parent
|
|
default now explicitly includes `subagent`; child read-only/coding lists
|
|
remain exact and exclude both `subagent` and `ask_user`.
|
|
- Connected subagent dispatches to PI-070 generation resource cancellation so
|
|
parent abort/crash/recover/dispose and bridge disconnect stop unfinished
|
|
children and release child/process permits. Coding children join the same
|
|
project write lease as their parent.
|
|
- Added vendor-neutral strict `subagent.v1` projection for live events, durable
|
|
session hydration, and reducer validation. Unknown versions render one fixed
|
|
unavailable message and never copy their raw payload into product state.
|
|
- `PiConversationRuntime` now configures the scheduler/bridge against the
|
|
existing pool generation tracker. Final product cutover and nested Renderer
|
|
presentation remain outside this ticket.
|
|
|
|
## Verification
|
|
|
|
- Focused scheduler, managed-child, bridge, bundle, projector, session, process
|
|
and contract tests passed, including: 1/4/8 task coverage and ninth-task
|
|
rejection; two-parent global child concurrency of four; full-budget idle
|
|
parent reclamation; parallel sibling preservation; chain skip/abort rules;
|
|
parent generation cancellation with no orphan and zero leaked permits;
|
|
recursive child rejection; parent/child same-project write-lease queuing;
|
|
unknown schema/version raw-payload suppression; first reclaim finding no
|
|
idle followed by delayed parent readiness and automatic child execution;
|
|
reclaim rejection/abort with zero queued lease; and stop rejection with zero
|
|
active/waiting lease. A production-scale regression also covers four running
|
|
plus four queued parents dispatching four parallel children: all four queued
|
|
workers suspend, all children complete, and the queued parents then resume
|
|
FIFO with unchanged session bindings and generation 2. Their `stop()` calls
|
|
are deliberately held behind a gate while an earlier parent settles; the
|
|
test confirms no prompt reaches the stopping generation before the gate and
|
|
the original prompt reaches only the reopened generation.
|
|
- Locked real Pi 0.84.2 workspace smoke passed for both the parent worker and
|
|
an ephemeral read-only child launched through Electron Node with the child
|
|
extension role and `--no-session`. A probe extension reads Pi's actual
|
|
active-tool list at `session_start`: the parent includes `subagent`, while
|
|
the child is exactly `read,grep,find,ls`.
|
|
- `pnpm run test:pi-subagent:packaged` passed 3/3. The dedicated runner uses
|
|
PI-030's production bundler to create and validate a temporary staged
|
|
production closure/manifest, then starts the staged Pi CLI as an ephemeral
|
|
read-only child, verifies `get_state`, the exact active tools and child
|
|
extension role, and clean stdin shutdown. The normal suite skips only this
|
|
staging case so it does not perform a production `npm ci` on every unit run.
|
|
- The real materialized extension bundle test executes `subagent` through the
|
|
authenticated Main bridge; the active-tool process probes and bridge execute
|
|
test jointly cover visibility and tool invocation without an external
|
|
Provider.
|
|
- All cumulative Pi tests passed: 22 files, 111 passed and 1 staged-only
|
|
skipped; the staged-only command passed separately as described above.
|
|
- `pnpm run typecheck`: passed.
|
|
- `pnpm run lint:check`: passed with 0 errors and 6 pre-existing frontend
|
|
warnings outside this task.
|
|
- `pnpm run build:vite`: passed for Renderer, Main, Preload, and utility
|
|
worker bundles; existing chunk-size/dynamic-import warnings remain.
|
|
- The final full-suite first pass hit the known Windows temporary JSON `rename`
|
|
`EPERM` in the unchanged conversation store while the other 2213 tests
|
|
passed. The failing runtime test passed 1/1 in isolation and the single full
|
|
rerun passed: 202 files, 2214 passed and 1 staged-only skipped.
|
|
- Real external Provider validation remains **Explicitly Waived / Accepted
|
|
Risk** with `realTurnVerified=false`. Provider concurrency, credential
|
|
isolation, protocol compatibility, and image-path risk are accepted rather
|
|
than marked Pass. macOS x64/arm64 validation was not run and remains deferred
|
|
to mandatory PI-150; it is not marked Pass.
|
|
|
|
## Follow-ups
|
|
|
|
- PI-130 owns the final nested Renderer execution graph and user-facing child
|
|
error/abort presentation.
|
|
- PI-150 owns final packaged provider-shaped subagent smoke and mandatory
|
|
macOS x64/arm64 validation.
|
|
|
|
## Promotion Candidates
|
|
|
|
- Target canonical document: Pi runtime process/concurrency architecture.
|
|
Proposal: record that child budget acquisition must reclaim an idle parent
|
|
worker when the shared eight-process budget is full; otherwise the supported
|
|
shape of four running parents plus four warm-idle parents can deadlock while
|
|
the running parents wait on children. Evidence: the focused full-budget
|
|
scheduler regression test and the shared-budget implementation in PI-080.
|
|
Future impact: any final Main composition must pass one `PiProcessBudget` to
|
|
both the parent pool and child scheduler, preserve child-priority budget
|
|
reservations ahead of normal parent-start waiters, and wire the scheduler's
|
|
cancellable capacity reclaimer to `PiWorkerPool.reclaimIdleWorker`. The pool
|
|
must notify an already waiting child when a spawning/running parent becomes
|
|
`ready`/`idle`, and stop/reclaim failure must never retain a lease. If the
|
|
cap consists only of running and queued parents, it must suspend queued
|
|
parent processes in FIFO-safe/LRU order, retain their queue entries and
|
|
session bindings, and reopen them as a new generation when scheduled. A
|
|
queued run selected while suspension is still stopping must await that stop
|
|
flight and recheck ownership before it can reopen or send the prompt.
|
|
Semantic conflicts: none with the accepted PI runtime specification; this
|
|
makes its parent/child cap executable. Human confirmation required: no,
|
|
unless integration changes the accepted process-cap policy.
|