# Runtime performance

## Admission across asynchronous checks

A start reserves its foreground/background capacity before an acquire guard or worktree copy can suspend; the same pool is charged and released. A void guard retains its same-tick path. After a guard settles, both success and failure must still match the captured record, session and generation. A late refusal cannot fail a newer queued resume. An individual queued abort owns its generation's completion callback, including the microtask gap between a rejected guard and startup settlement. Refusal completion drains the queue in `finally`, so a throwing observer cannot strand a sibling. The existing `queued` record status can also cover a start suspended on admission; receipts and stop messages therefore consult actual concurrency-queue membership rather than falsely displaying a queue position. Queued resumes are checked too, without relying on spawn-only `startGate`. These checks add no public report fields or permission overrides.

Prepared RPC and mention starts/reopens await root-store admission before the private spawn handoff. That handoff rechecks index-local reservations without repeating synchronous disk checks; the manager still guards the actual start. RPC preparation also rejects a session/root change during suspension. The genuinely synchronous public registry keeps its fresh synchronous guard and never accepts a caller-supplied definition capability.

A canceled start releases only its captured pending claim, even if its completion callback immediately resumes the same record. Running charges remain owned by finalizers. Parent abort listeners are attached before the first acquire wait, owned by generation and removed on cancellation, refusal, replacement and settlement; a stale signal cannot stop a later signal-less run. Resume callers prepare shared tool-call/output wiring only in the manager's exclusive `onAdmitted` hook, with identity rechecked afterward.

## Workflow journals and detached shutdown

The runtime exposes an accepted-write drain before creating its worker. Both tool and CLI launches retain that handle and their actual run promise; graceful shutdown stops producers and awaits the handles until stable, including final artifact registration. Paused resumes await the prior run's accepted tail before reading replay records, then revalidate session/root/cleanup selection before creating a new run. Initial artifact persistence is another await boundary: both launch paths recheck closing state, epoch, root and cleanup access before publishing a task or worker. A later session start resets the closing fence while changing the epoch, so old admissions remain stale.

The drain covers writes accepted by that runtime, not arbitrary future calls from an active producer. Slow storage can delay graceful shutdown; hard process termination has no durability guarantee. Journal failures retain best-effort behavior. Artifacts already created before a refused late launch are not automatically deleted.

## Durable metadata and async identity

Awaited availability checks revalidate closed state and activation/root identity after I/O. Durable report verification distinguishes proven absence from uncertainty, checks embedded agent/root identities, and lstats the entry and its `agents` directory before/after reading. Stable links/junctions and changed identities are refused; directory size/mtime alone are not identity because unrelated sibling writes legitimately change them. Disk maintenance exemptions and release require this process's PID plus its nonce. These are bounded before/after checks, not an atomic cross-filesystem protocol. An archive-provenance body also requires its final durable entry to remain a verified full row at the captured generation; proven absence is a refusal, not permission. Genuine parent-record and legacy-output recoveries retain their distinct provenance.

## Fresh definition loading

`loadCustomAgentsAsync` enumerates the configured global, workspace and project directories and reads current file contents through `node:fs/promises`. It coalesces concurrent refreshes for the same scope/strictness and reuses YAML parsing only when exact content matches. It does not trust size/mtime as authorization freshness.

The parsed-content cache has a 64-entry / 2 MiB retained-content-and-body accounting budget; a larger file is not cached. Parsed object overhead and the active registry are separate working sets, so this is not a universal heap limit. Each caller receives detached mutable definitions and permission arrays, while immutable prompt strings can share storage. Strict validation, precedence, new/edited/deleted files and warning decisions still run. Synchronous factory/schema/legacy entry points retain a deliberate fresh synchronous fallback.

The five-file/two-refresh operation guard observes **zero synchronous loader operations**, sixteen fresh async directory/content operations, five parses and five parse hits. This is an event-loop/CPU improvement, not zero disk I/O.

## Transcript production and output buffering

Finalized messages are snapshotted as JSONL when the session observes them. Serialization of an individual message is still synchronous. Disk appends are asynchronous and serialized per output path.

On SDKs with awaited core Agent subscriptions (verified with Pi 1.3 and 1.4), a second listener on `session.agent` applies producer backpressure after the session observer has captured the message:

- **1 MiB high-water mark:** count queued plus in-flight serialized UTF-8 bytes for that path. At or above it, finalized-message production waits for pressure to fall.
- **256 KiB / 64-record batches:** each append respects both limits, except one larger indivisible record, which is kept whole to preserve JSON and Unicode.
- **64-message cooperative slices:** even small records below the high-water mark yield a macrotask periodically. An already-resolved Promise alone does not let input/timers run.
- **Turn/run settlement:** drain accepted output before the producer finishes. Closing a stream first removes its observer and producer listeners, then flushes/drains idempotently.
- **Long backlogs:** a head-index queue releases consumed payload references and compacts geometrically; it does not repeatedly shift the remaining array for each append.

The main Pi event loop does not wait synchronously on disk. A child may wait for slow storage before advancing; that is the backpressure, not dropped output. Resumed streams share per-path byte accounting and write ordering.

### Limits

This is not a universal process-memory cap. A single arbitrarily large message can exceed the high-water mark and still has atomic JSON serialization. Multiple producers sharing a path may each have a current-record overshoot. SDK-retained conversation/tool-result data and manually injected/eventless history are separate working sets.

A plain synchronous fake emitter or an older SDK that does not await Agent listeners cannot receive this producer guarantee; bounded async writing and final drains still apply. The tests use a real Agent, not just manually fired session callbacks, to prove the awaited checkpoint.

Append errors remain best-effort: failures release accounting/waiters and do not poison later writes, but failed I/O can lose records. A hard process kill can lose writes not yet flushed. No disk-retention quota, user-file deletion or cleanup-policy change is introduced here; existing startup path/prompt initialization helpers are not part of the streaming-pressure change.

### Verification

`test/output-file-producer.test.ts` covers a blocked append stopping the real producer before the next model request, full records/order, large Unicode records, append failure, abort, listener teardown, resumed streams, distributed macrotask progress and linear queue-reference work. Its teardown observes every prompt result and drains owned streams even when an assertion fails. Scoped strict test TypeScript is also checked because the normal source-only package typecheck does not include test files.
