Skip to main content

Extending the shared pipeline

One rule governs almost every architectural decision in Kaizen:

Implement cross-agent behavior once, in the shared pipeline and runtime. Streaming, journal projection, retries, timeout salvage, durability, grounding, lifecycle, policy, and error handling must apply uniformly to every agent. Never patch these concerns into an individual agent.

Agent-specific code may define only genuinely domain-specific persona, capability, tool-grant, or source-selection behavior.

Grounding asks for retrieval when a claim needs evidence beyond the current member message. If the member supplies enough quoted passages for a bounded answer, the agent can answer from those passages directly, attributing their source IDs as member-provided evidence. Those IDs are not Kaizen retrieval citations, even when the member requests retrieval syntax. Their labels use inline code so UUID-shaped IDs remain literal text and cannot become source cards through citation lookup. A UUID supplied in the current member message cannot be rehydrated from the corpus merely because the model repeats it; a tool must retrieve that source during the current turn before it can become a retrieved citation. UUID labels in steering that may have reached the active run receive the same protection; a confirmed rejection adds no new labels. This bounded inventory exists only for that run. Older conversation history remains subject to current ACL revalidation rather than a cross-run exclusion ledger. Questions that require verification, fresh state, or additional facts still use the authorized retrieval tools.

Private-chat steering uses the same durable turn identity as replay and cancellation. The browser submits the exact orchestration and an idempotency key; PostgreSQL accepts it only while that owner/conversation turn holds a live lease. The execution owner claims commands with a short recovery lease before passing them to the runtime. Process-local run maps remain callback capabilities, not cross-replica coordination state.

Shared-chat steering preserves the active input's selected model. Admission locks the room, reads only that input's durable deployment ID, and resolves its runtime details from the steering member's authenticated inventory. It never borrows the original member's credentials or substitutes the current default. When there is no active input, admission first uses the steering member's latest accepted message or steer, including a queued input. If that member has no accepted model history, the room's persisted selection remains the compatibility fallback. Another member's input never supplies this idle target. Missing or inaccessible selections reject steering with an actionable error; send a normal message with an available model to establish a new selection.

An idempotent retry revalidates its own previously accepted target, scoped to the same actor, conversation and key. Accepted steering retains its captured model through queueing and lease recovery even if the room selection changes. Stop and cancel remain available without model discovery.

Uploaded-document scope is resolved at the shared member turn boundary for every agent. Conversation references and explicitly selected document ids are unioned under the authenticated viewer. The model receives a bounded pointer summary; query_document searches the complete ACL-visible set and returns citable chunks. Images and unsupported files are stored with no extracted text, so a selection made only of such files is not offered query_document, and its pointer summary tells the model that no search or document tool can read it. A library image whose pixels are withheld is described as unreachable by any tool. Do not move this selection or text injection into a specific agent. The separate prompt-warming hint still accepts at most 20 document ids; large selections skip optional warming, not the actual turn.

The runtime's conversation_id is the journal UUID, not the attachment key. Resolve document references under the public ID recovered from the server-owned WEB_<id> channel; never query them under the journal UUID or drop the selection from the model's context.

Claimed durable chat inputs also pass through the canonical pre-runtime routing resolver. The selected room agent remains the execution authority; routing may mint only the same bounded one-turn child grant used by direct chat. The grant is attached before the runtime is constructed and the resulting decision is audited against the durable input ID, so queueing, retry, or collaboration cannot silently bypass specialist routing.

Task turns use the same durable conversation input. A repeated key whose input already completed is a successful replay even when the current drain processes zero new turns; failed and salvaged inputs are not treated as completed. Corpus repairs use a new key only after a completed answer fails record validation. Retries after an uncertain worker interruption keep the original key so they cannot repeat an ambiguous tool effect.

Orchestration child runs cross a versioned SubagentInputEnvelope and return a TaskResultProjection carrying answer and result digests plus runtime-observed evidence bindings. Add fields at that common projection boundary; never pass a child transcript or invent a parallel per-workflow handoff shape.

Reviewed pipeline controllers​

Kaizen ships three agent pipelines without duplicating protected runtime code. A controller owns when work continues, yields, or settles. The shared host owns model access, tool execution, authorization, budgets, journals, checkpoints, usage, event projection, and terminal settlement.

Start with the General view, then switch to Tomo, Codex, or OMP for each controller's real sequencing flow. Protected model, tool, journal, budget, authorization, and settlement effects remain shared.

Other views: General · Tomo · Codex · OMP

  • Tomo keeps Kaizen's pure Turn Engine reducer and effect driver.
  • Codex transfers Codex's Session -> Task -> Turn -> Step ownership. One Codex turn contains repeated model sampling and tool observations until that turn finishes. Input already queued when sampling finishes joins that turn before its next request. Input that remains after the turn settles starts a new owned turn.
  • OMP transfers OMP's outer input queue and inner agent loop. Tool continuation remains inside the inner loop. The outer loop polls late input only after the inner loop would otherwise yield. Each provider call and its ordered tool results belong to one inner turn.

Best-fit tasks​

These recommendations follow each controller's sequencing model. They are not benchmark rankings. This release does not bundle valid comparative live benchmark results, so Tomo remains the safe default until reviewed evidence supports a different assignment.

PipelineBest fitWhy it fits
TomoDirect answers, grounded retrieval, and short, predictable tool chains.The pure reducer chooses one bounded effect at a time and preserves the baseline trace.
CodexCode changes, artifact creation, and multi-step jobs where corrections should join active work.Session, task, turn, and step ownership keeps repeated model and tool observations inside one task. Queued steering can join before the next model request.
OMPExploratory diagnosis, research, and tool-heavy work where the current tool loop should stay coherent before late input is polled.The inner loop keeps provider and tool continuation together. The outer loop accepts late input at a safe yield boundary.

The transferred behavior is based on openai/codex commit 0818b6550bacacd06b9f8b175789fab1389163e4 and can1357/oh-my-pi tag v18.1.17, commit 3b3a6dc9bbd85102ce19d0b1c11bf6870915f6ec. Kaizen does not import either upstream runtime. A shared protected-effect runtime retains Kaizen's stronger server trust, finite tool concurrency, mutation ambiguity, durable journal, and terminal-settlement contracts. Adding a controller must not add provider, credential, journal, or tool clients.

Register a controller​

Controller registration is a source change, not a runtime API:

  1. Add one pipeline-<name>.ts controller that implements run, abort, steer, interruptForSteering, inactivityTimedOut, and inspect.

  2. Add one descriptor to PRODUCTION_PIPELINE_DESCRIPTORS in pipeline-registry.ts. Use a unique lowercase ID and positive integer version.

  3. Declare inclusive host-protocol and run-manifest compatibility ranges. The process rejects an incompatible or duplicate descriptor before it can run.

  4. Run the shared conformance test:

    cd sidecar/pi
    npm run build
    NODE_PATH=$PWD/node_modules node --test dist/pipeline-contract.test.js

The registry copies and freezes every descriptor, builds its complete lookup once during process initialization, and exposes no registration or mutation method. Do not add dynamic discovery or a management endpoint.

A controller may own orchestration sequence, controller-local lifecycle state, steering and cancellation response, a bounded diagnostic projection, and one terminal intent. It may request model, tool, compaction, and checkpoint effects only through the shared host.

A controller must not import provider clients, callback clients, journal writers, public-event emitters, tool executors, credentials, or another pipeline variant. The architecture boundary test enforces these restrictions.

Increment the controller version when its observable sequencing or lifecycle contract changes. Keep the admitted ID and version immutable for a running turn. Update compatibility ranges only after the matching host protocol or run manifest contract passes conformance; never widen a range to bypass a failed check.

Register a reviewed lifecycle extension​

Use a shared lifecycle extension only for bounded behavior at an existing host phase. The extension must be statically imported product code. Add its descriptor to the source-owned registry with an exact ID, positive version, unique order, compatible host-protocol range, and explicit hook allowlist. Manifest references are an unordered enabled set; they cannot select code or control hook order.

Activation is atomic. The host loads every enabled factory into temporary Pi state, inspects all handlers and registrations, and publishes one immutable descriptor-ordered chain only after the complete set passes. A failed load, duplicate order, incompatible version, unsupported registration, or undeclared hook leaves the prior chain unchanged.

Hooks receive bounded, credential-free projections. The restricted Pi API does not provide provider routes or headers, model or budget controls, identity, sessions, journals, UI, shell execution, direct messages, terminal publication, or tool executors. Context and provider changes must retain the accepted input and valid message history. Tool-call changes must pass the admitted schema. A tool result cannot hide a failure or reduce usage. Each phase commits only after all handlers complete within their deadlines and the final draft passes.

Shared extensions are trusted code in the sidecar process. Runtime projections reduce accidental authority, but they do not sandbox reviewed source or its dependencies. Static source and supply-chain review must reject ambient Node.js I/O, undeclared resource access, and direct effects. Member-authored Pi packages remain in disposable validation and the secretless package runner; they cannot register these hooks.

Controlled rollout​

AGENT_PIPELINE_SELECTOR_ENABLED defaults to off. Chat's model and run settings still show the public Tomo baseline as one disabled option in the leftmost pipeline pane, but the profile returns no selectable options and protected selection routes answer 404. Enabling the flag grants nothing by itself: live Kaizen Admin authority and the deployment-bundled opaque scope allowlist remain mandatory. The experiment manifest keeps Tomo at 100% weight and both derived pipelines at zero until reviewed evidence permits another rollout stage.

Chat-promoted background jobs, including corpus jobs, pin the originating conversation's persisted pipeline assignment to their private Task thread. Directly created Tasks use their own assignment. Runtime admission still checks the pinned ID and version against the active manifest before model or tool work.

Controlled comparison​

A valid comparison freezes the authenticated model deployment, runtime model, reasoning behavior, Plain-speak setting, member authority, agent capability ceiling, tool descriptors, approval policy, budgets, starting history, host protocol, extension set, and grader versions. All controllers in one case use the same frozen values.

The version-controlled corpus contains 100 content-safe fixture references across direct answers, retrieval, read tools, mutations, artifacts, streaming, compaction, recovery, delegation, and shared durability. Deterministic cases run once. Non-deterministic cases run at least three times and report pass@3, score distributions, p50 and p95 latency, usage, and recovery outcomes.

Live compatibility and protected-corpus UAT use the pinned Promptfoo package under uat/promptfoo/. Promptfoo owns matrix expansion, repetition, serial execution, assertions, and report output. Kaizen's custom provider owns only authenticated API calls, durable terminal observation, exact preference restoration, and conversation cleanup. It returns redacted measurements rather than model answers or Thinking. The Make target disables Promptfoo sharing, cache, telemetry, update checks, environment-template access, and local logs. Deterministic Python tests remain the correct layer for schema, aggregation, redaction, and controller-state invariants; they are not live UAT evidence.

An on-demand browser comparison covers three distinct public cases for each of direct answers, one-tool work, clarification across turns, and failed-tool recovery on one operator-selected healthy chat deployment. Every pipeline receives the same case input. Validity gates are separate from task quality, and durable tool events provide the recovery oracle. A task with any invalid cell is incomparable. A unique task-quality leader may be reported; a quality tie produces no recommendation. Latency and token measurements stay descriptive because this small lane cannot establish an efficiency winner. The command is not part of the release contract, full UAT, targeted UAT, or changed-area UAT.

A result is invalid or incomparable when any frozen input differs or attribution is incomplete. Such a result never enters ranking. Authorization, approval, audit, finite-budget, cancellation, replay, terminal, shadow-write, and extension-boundary checks are hard gates. A safety failure blocks promotion; quality, speed, or cost cannot compensate for it.

Promotion stages​

Promotion is manual and evidence-based:

  1. Contract fixtures prove shared safety, baseline trace equivalence, complete result validation, and bounded no-model dispatch overhead.
  2. A non-mutating shadow stage compares read-only work without publishing member-visible events.
  3. Explicitly eligible administrators validate sticky assignments through the protected product surface.
  4. A named owner may approve a bounded weighted cohort for new conversations.

No stage changes an active run. Rollback changes only future assignments. Restricted reports may contain bounded identifiers, timings, usage, and terminal metadata. They never contain credentials, member identity, prompts, answers, raw Thinking, provider errors, or private tool payloads.

Why the rule is absolute​

Without it, "every agent behaves the same way" becomes a convention, and conventions decay. With it, uniformity is a structural property:

  • a guardrail cannot be quietly weaker in one corner of the product;
  • an agent cannot be accidentally more privileged because its feature landed on a different code path;
  • a fix to durability fixes it everywhere at once;
  • a security review can reason about one pipeline instead of N agents.

Boundary tests walk the source to enforce it. A failing boundary test usually means a design rule was crossed, not that a test needs updating.

The seams​

When you need agent-visible behavior to change, extend one of these rather than adding an agent-specific path.

1. The tool boundary​

handle_tool_exec is where the runtime's request meets the backend's authority. It re-checks the authority bundle, re-resolves permissions, applies the capability ceiling, executes as the member, and returns a bounded projection.

For delegated work, the bundle also carries only trusted in-memory Core authority: workload role/revision and activation epoch, run/claim, immutable authority deadline and fence, plus the exact protected-resource revision and request digest. The shared boundary reserves one Core effect, waits for exact approval where policy requires it, and sends the sender-constrained capability to the registered resource guard. The guard consumes it before protected code; an individual tool never implements this exchange.

Extend it for anything that must happen to every tool result. This is where Tool-result compression lives, after authorization and policy filtering are complete.

Failure handling is part of this seam, not a per-tool concern. Each invocation runs under the tool's declared bound and is cancelled if it overruns or if the caller disconnects, so an abandoned call releases its session, connections, and sandbox lease instead of running for nobody. Each invocation also records exactly one outcome metric. A tool that raises is settled by capability: a read tool's exception becomes an error result the model can recover from, while a write or sensitive tool still closes the turn because the runtime cannot know whether the change landed. Cancellation and turn-lifecycle breaches are never settled that way. Do not re-implement any of this inside an individual tool.

A denied call never runs, but a denial is not by itself the end of the turn. The first denial for a tool name in a turn reads back to the model as an error result carrying the reason: arguments that fail the advertised schema, a tool the captured step did not offer, or a policy refusal from the decision seam. The model can then correct the call or tell the member what could not be done. A repeat denial for the same tool name, or one beyond three distinct names in a turn, is not read back again, and a batch made only of such denials ends the turn, so a model that ignores the reason cannot loop. A member's own denial, an approval that expired or stopped being valid, an expired run, a decision seam that could not be reached, a reused call id or a call that could not enter the execution queue, a batch the turn has already stopped, and a required-tool gate's own block all stay terminal from the first occurrence. Recovery is decided for the whole batch: if the turn was cut short or stopped, a write in the batch is waiting on the member's approval, or the member denied any call in it, every denial in that batch ends the turn. When a denied change is the last thing the turn did, the member's answer reports the original reason, never the recovery instructions the model was given. Before this rule, a denial-only batch ended the turn on its first occurrence, and the member saw only whatever the model wrote before the call.

When a failed write is the last thing a turn did, the shared sidecar grounding replaces the model's closing text with a product-authored account of the failure. A call that declared the files it would create (through its descriptor's run_artifact_targets_field, such as run_code's artifact_targets) is reported as "I couldn't create the PowerPoint file <name>" with a single-line cause. The cause comes from the payload's structured error, then the first sandbox diagnostic, then the sandbox worker's own dependency-install failure (only when its marker opens stderr and no exception line follows), then the exception line that ends stderr. A run_code whose dependency install failed carries a structured dependency_install_failed error naming the package and whether the registry was unreachable, so that is the cause the member reads; terminal escapes, control characters, and host paths are removed and it is capped at 200 characters. Serialized tool output is never quoted to the member — with no readable cause the text says only that the tool reported an error. The host remembers which files earlier calls in the same turn retained, so a failed refinement of a file that already exists is reported as a change that was not applied, not as a file that was never made. A failed run_code payload also carries a repair_instruction that names the exception and asks the model to fix and rerun unless the member forbade retries; the payload, not the summary, is what the model reads.

A batch that the turn's own inactivity budget cuts short is still settled whole. Once that timeout has fired, every call not yet decided is settled as blocked without asking Kaizen for a decision, and a call that was already granted is never dispatched afterwards: a decision or dispatch sent on the spent signal could only fail, and would be misread as another approval wait. The settlement writes that remain — each call's journal request and result, the tool_result extension hook, and the grounded answer telling the member that a parked change still needs approval — run on one fresh 15-second deadline per turn instead of the aborted signal. Only the inactivity timeout earns that deadline. A member Stop ends the turn on the spent signal, so nothing it interrupted is settled afterwards. A failed journal append fails only itself; the session's append chain stays usable for every write after it.

Persistent sandbox code re-enters tools through this same seam. The worker controller holds the run capability, and a kernel names only a read tool and arguments. /internal/sandbox/tool-exec resolves the bearer to the active run, refuses writes and recursive code execution, and then invokes the ordinary snapshot- and descriptor-bound execution path. Do not place a platform token or tool implementation inside a kernel.

read_workspace and edit_workspace share the existing sandbox service, composite workspace lease, and WriteToolGuard. Reads retain normalized full text and seen-line provenance in trusted owner/workspace-scoped storage outside the workspace tar. The model receives only [PATH#TAG] and numbered lines. Edits send the OMP-compatible patch grammar to a pinned MIT helper that operates on bounded in-memory file content. Python remains the filesystem authority: every parsed or recovered path is revalidated, all sections preflight, and the resulting write/delete plan commits with rollback before trusted snapshot and named-register state advances. The helper receives no identity, bearer, object store key, or direct filesystem grant.

Prompt capability attachments join immediately before this boundary. The Prompt parser recognizes only structural Tool and Connector tokens; the runner-scoped resolver validates them against the effective step Agent and the current member/deployment catalog before compiling. The executor then derives an execution-only AgentSpec for that step. Do not implement this as an Agent special case or persist the overlay back to Agent Studio.

Package attachments carry exact active package UUID and Tool-name pairs on that ephemeral spec. packages/runtime_tools.py reloads the active pin, repeats the capability clamp and collision checks, and binds it to the ordinary dispatcher. Platform and Connector attachments extend their existing request-scoped selection fields. All paths still converge on the shared callback, approval, collaboration, audit, and sandbox machinery.

Tool failures may carry a trusted repair directive beside the model-visible payload. The tool owns domain facts: whether retry is possible, whether the same tool is required, which arguments stay locked, and whether substitution is safe. The Turn Engine owns enforcement and the bounded response opportunity. A non-retryable, non-substitutable failure lets the model explain the exact reason once; any later tool attempt is blocked and terminates through the existing mutation-failure projection. A failure the model may substitute for is refused, not terminated, and a refusal can be resent indefinitely — so the Turn Engine host also fails the turn once one tool has failed three times with byte-identical arguments (repeated_tool_failure_exhausted, surfaced to the member as repeated_tool_failure). The count is per input: a changed argument set starts its own, and a success on that input clears it. Do not encode any of this as prompt wording or browser-specific retry logic. The shared host also stops a turn after four executed tool_search calls without a successful non-discovery tool between them, even when each search used different arguments. This prevents an unproductive discovery loop from consuming the entire model step budget. Both dispositions produce a failed terminal and a member-visible non-delivery statement.

Every failed result also carries a closed-vocabulary error_class (invalid_argument, not_found, forbidden, rate_limited, timeout, provider_error, tool_error), derived by the same classifier the repair directives use. It rides the callback result onto the tool end frame and into the journal's tool object on a failed step. A caller-domain failure is always tool_error: its text is the caller's own output and is never read.

An error result also declares whose failure it was. ToolResult.failure_domain is tool by default, meaning the tool could not do its job — that is evidence about the tool's health, and the runtime may stop the model calling it once it has failed enough times across different arguments. Set it to caller when the tool ran correctly and is relaying an error produced by its input, which is what run_code does for a non-zero exit or a wall-time stop; both arm the repeat guard, so a non-terminating program resent unchanged is refused rather than paying the wall-time limit again. A caller-domain failure stays fully model-visible and repairable, but it is excluded from the tool's health tally and from every text-matching classification rule, because the text being matched is the caller's own output rather than the tool's report. The sole rule that still applies is the identical-repeat block.

A write that already committed is not run twice — for the tools that ask to be treated that way. Suppression is opt-in per tool via suppress_identical_repeat (default False), because capability tier does not imply idempotence by arguments: run_code must be able to re-run the same program after the workspace moved, and act_on_browser_page must be able to repeat a click the member confirmed. The tier is still required on top of the declaration, so a read can never be suppressed. create_artifact opts in; the version-bound update_artifact and edit_artifact do not, because a stale repeat already fails on its own expected-version check. The execution seam keeps a per-run ledger of every successful opted-in call, keyed by tool name and the exact argument digest, and answers an identical repeat with the first call's projected payload instead of invoking the tool. The replay reports outcome="success" — the state the model asked for exists — and carries repeat_suppressed plus a non-retryable, non-substitutable repair directive so the model reads what happened rather than believing it wrote something new. It carries no presentation, because that out-of-band member surface was already materialized by the first call. Suppression is deliberately narrow: read tools are untouched, the staged-artifact protocol tools (begin_artifact, append_artifact, finalize_artifact, discard_artifact) are exempt by name because repetition is their normal shape, only the literal outcome="success" counts as a commit, a staged confirmation is not a commit and is never recorded, a failed write leaves nothing behind and may be retried unchanged, the ledger is bounded and evicts oldest-first, and it dies with the run. Implement this once here; never in an individual tool.

Suppression is enforced at two layers against one ledger, because a streamed create opens its durable row before the execution seam ever sees the call. ArtifactGenerationCheckpoints.observe materializes a member_artifact row via checkpoint_generation as soon as the streamed argument JSON completes, keyed by (owner, turn_id, call_id) — so a repeat refused only at execution would leave the row behind, and leave it unfinished. The checkpoint layer therefore asks the same ledger, read-only, at the first moment the identity is computable, and declines to open a row for a create that already committed. The execution seam still suppresses as well: it covers the non-streamed path and is what reports the repeat back to the model. The read-only lookup counts nothing, so only the execution seam claims an attempt and the two layers cannot disagree.

The ledger also records the step_snapshot_id that committed, and a later call carrying that same step identity is treated as a sibling in one tool batch rather than a repeat. Identical calls the model emitted together are a deliberate parallel request; the pathology is a model that reads a success and decides again, which is a new step every time. A call with no step identity cannot prove it was batched and is treated as a later step, so a runtime that supplies none is still bounded. Each turned-away repeat increments tool_run with outcome="repeat_suppressed", because a write that quietly does not happen needs to be as visible to an operator as one that does.

A refused repeat is returned as an error result, never raised. Raising answers the sidecar with a non-2xx, which its tool client throws; the throw escapes dispatch_tool_batch, classifies as ambiguous_write, and fails the whole turn closed. Reserve that for authorization and policy decisions that genuinely must fail closed.

Because a refusal never reaches the tool, nothing about it bounds a model that simply resends. Refusals are counted per tool and argument set, and past a few the directive escalates from "change the input" to "stop calling this and answer with what you have". Escalating does not stop the calls — nothing here does — but a refused call still spends one of the run's tool rounds, so without the escalation a resend loop reaches the member as the same dead turn the refusal exists to prevent.

2. The system-prompt composition chain​

Instructions that every agent should receive join one composition chain, in order: grounding, temporal awareness, member identity, then anything added afterwards.

Member identity states the signed-in member's display name once per run, so every agent addresses the person by name instead of narrating "the user". The name comes only from the forwarded platform envelope, is flattened to one bounded line of plain text, and is labeled as a label — it grants no identity, role, or permission, and an envelope with no usable name composes no block at all. Durable harness sessions are actor-scoped, so a shared room never carries one participant's name into another participant's session.

Prompt trim is the seam's counterpart on the member's own message: the raw message stays the run's logical message, and only the model-visible copy composed beside document, artifact, and sub-agent context is trimmed. Add a member-message transformation there, not in a persona or an agent adapter.

Plain-speak joins here. So does the server-authored pipeline settings projection — appended after member-authored resource context, so resource text cannot override it. A router-issued specialist grant also joins here and is reinforced by the current turn's required-tool policy. It is not copied into the member's message. A resumed durable session can retain its original system prefix, so the Turn Engine's manifest-bound interlock and bounded retry remain the execution authority rather than relying on prompt wording alone.

Extend this for instructions that must apply uniformly. Never inject them into a persona.

3. The guardrail chain​

Budgets, citation discipline, redaction, and loop control run at defined points in the turn. Add a guardrail in guardrails/builtins/.

Guardrails are backend-side by construction. A guardrail inside the runtime would be advice to a system designed to be flexible.

4. The model gateway​

Routing, policy, budgets, effort, retries, and normalization. Extend here for anything about how model calls are made.

The platform proxy receives one server-resolved connection value containing the target, model authority, request-scoped authorization, TLS policy, and timeout. Streaming lifecycle options are separate. Provider error frames are projected through the shared safe SSE adapter before they reach any runtime and finalize the shared LLM audit as failures even when their HTTP response was 200.

The direct gateway client that server-side work uses — manual chat compaction and orchestration planning — holds the same property from the other side. Its chokepoint records one audit row for every failure the provider raises, including a status it does not map to a named condition, so an unexplained server-side model call always leaves an outcome and an error_class behind. Recording the failure never changes it: a caller that has to tell a malformed request apart from a provider refusal still receives the original exception.

Never let a component talk to a provider directly.

A delegated model call enters the same seam. Core's registered model guard attributes it to both the consenting member and acting workload, atomically reserves the member charge and workload concurrency/spend ceilings, consumes the exact model effect before provider I/O, and settles from trusted usage. Production Tasks select Core's closed /api/serving/delegated-models proxy from the authenticated deployment ID and public origin. Its proof binds the exact proxy URL; the existing engine runtime route remains the member path. The Core proxy adopts that engine behind the shared guard and preserves the same correlation and cancellation fence without acquiring its lifecycle.

The Pi sidecar follows the same rule. Kaizen's durable Turn Engine is the served path; its low-level Pi Agent is only the rollback-compatible execution path. Both call models through the run-bound Kaizen proxy, use the same tool decision and execution callbacks, and preserve the same terminal normalization. Pi's higher-level AgentHarness is not an architectural seam, so no Kaizen contract depends on it.

Pi 0.85 replaced its previous V4 session facade with a context-aware repository protocol. Kaizen keeps its immutable, backend-owned V4 entry log behind a narrow local facade. This preserves journal authority and does not create a second persistence protocol inside the sidecar.

Conditional response correction belongs to the Turn Engine. Rule-enabled samples buffer model-visible events until accepted; a match yields a reducer transition that commits no model item, appends a fixed server-authored correction, captures a new snapshot, and resamples within a manifest-bound attempt limit. Adding a correction to one persona is prohibited.

5. The repository layer​

Authorization predicates live in SQL. Extend the repository query — never fetch candidates and filter them in application code.

Generated-file reconciliation resolves the room UUID, the stored web channel, and the channel's public conversation key into one server-authored artifact scope. The same scope admits task-specific orchestration branches and rejects lookalike prefixes. Invitation-time pinning and post-run linking both use this shared seam, so an adapter's internal session-key representation cannot orphan a file when the chat is shared later.

Follow-up use of a retained binary stays at this shared seam too. Model-context projection emits bounded file metadata, while the sandbox projector loads the same currently authorized reference and sends its digest-verified bytes through the read-only worker envelope. All runtime adapters inherit that split; no agent or Office-file format owns a continuation path.

6. The tool registry​

A closed set with a cardinality trip-wire. Adding a tool is a deliberate expansion of the loop's trust surface. See Building a tool or package.

7. The audit funnel​

One append-only funnel with bounded reason codes. Individual agents do not implement their own audit path — which is why enabling a new agent cannot create a gap in the record.

8. Terminal answer normalization​

Every runtime adapter passes its final member-safe answer and durable artifact projection through one response normalizer before persistence and emission. This is where native-file delivery claims are checked. A matching durable file preserves the answer; an unsupported success claim is corrected; a clarification or honest failure remains intact.

Output intent is bound to the action phrase. A source format after from, using, or another source-material boundary is not treated as a second output; for example, “create a PDF from the shared PowerPoint” requires the durable PDF and treats the PowerPoint as input. This keeps cross-format conversions from being rewritten into false failures after successful persistence.

Keep this contract harness-neutral. Local and remote adapters may assemble their artifact projections differently, but neither owns a weaker version of the member-facing delivery rule.

The collaboration runtime stages the normalized answer rather than publishing it immediately. The answer and exactly one terminal event are appended and committed together; only then are durable journal heads announced. A runtime that ends after yielding an answer is salvaged as a usable answer, not surfaced as a contradictory failed turn.

9. Streaming and generative file presentation​

Model-message boundaries are explicit runtime data. The first visible delta of a later assistant message carries segment_start; clients preserve exact token adjacency within a message and add a paragraph only at that typed boundary. They must not infer spacing from punctuation or reset a local cursor after a tool call.

Provider deltas remain off the persistence critical path. Visible model activity continues across structural stream events, Thinking and answer bytes relay immediately, and generated artifact source advances through bounded checkpoints before the exact tool-execution boundary forces its final snapshot. Reasoning checkpoints drain through a bounded ordered journal writer; the next provider delta does not wait for PostgreSQL, while every later non-reasoning or terminal projection flushes that writer as an ordering barrier. The sidecar's bounded reconnect relay appends steady-state deltas in constant time and only rebuilds its replay window when the byte cap requires compaction. Room-local publication lanes preserve order without awaiting one Valkey round trip per provider fragment; a bounded process-wide command pool and one multiplexed subscription keep connection growth independent of browser count. The durable journal-head announcement is the lane's completion barrier, so lower-latency delivery does not weaken replay or terminal ordering. Live token estimates accumulate admitted input across all provider calls in the turn, while the context meter retains the latest request-local prompt.

Provider output is reduced as typed message, Thinking, and tool-call deltas, including interleaved content indexes. Completed assistant items and every ordered tool result are added to the next model context before another model step. A length-truncated response rejects its entire tool batch before policy or execution, then exposes those error results to a same-effort follow-up. One structurally empty length stop may receive a typed semantic continuation; it is bounded to one attempt and is distinct from transport retry.

A transport-successful response carrying neither text nor a tool call is retried as a pre-commit provider failure, per request payload, up to the configured provider retry count. Visible Thinking is progress, not an answer or tool-call commit: a reasoning-only empty response or later transport failure remains eligible for that bounded retry. Thinking already shown stays in the member's trace. With the turn engine enabled, incomplete attempts are never appended to provider conversation history or included in retry requests. The legacy compatibility rollback does not provide this model-history isolation. A retried model may produce new Thinking, which is also shown. Answer text and tool-call commits still close the retry window.

The per-payload retry budget is latched rather than cleared when it runs out: the first exhaustion still hands the empty response to the reducer, but a repeat on the same payload ends the turn instead of re-arming the budget. With the turn engine enabled, content-free assistant responses are omitted from model-history commits, so reducer recovery cannot persist incomplete Thinking or blank assistant items; the terminal response's usage accounting and member-visible progress remain intact. This commit guard does not change accounting for earlier retry attempts.

That latch only fires when the same request recurs, so a second, digest-blind bound sits above it: a turn ends once it has seen twice the per-payload budget's worth of consecutive responses carrying neither text nor a tool call, however much the payload drifts between them. Without it a run whose payload changes each cycle while the responses stay empty falls back to the model-step budget, which empty-response retries are exempt from — a bound measured in tens of provider calls. Both rules are about progress rather than liveness: the inactivity watchdog is re-armed by any provider delta and so cannot bound a run that is busy producing nothing. Any response carrying content resets both counters, so a productive turn can never approach either. A turn stopped this way fails with provider_empty_response_exhausted rather than the generic engine error, so the member is told the request keeps coming back empty instead of being invited to wait.

When the agent has the staged writer, an exact new-artifact request prefers the closed begin/append/finalize/discard protocol to the ordinary single-call writer. The preference is a first-step ranking, not a required tool: the agent's other tools that fit in the bounded direct set stay directly advertised, and tool discovery reaches the rest. When the delivery check reads such a request as asking for a native PDF, Word, PowerPoint, or Excel file and the agent also has run_code, the request prefers run_code instead. Either way, the request's per-agent discovery allowance is raised only to the existing shared turn-step ceiling, never beyond it, so a specialist with a small search allowance can still finish the transaction. Each append commits an ordered digest receipt in a short transaction, while the private execution fence prevents an expired worker from continuing after recovery. The final tool result is the only transition that makes the artifact eligible for terminal answer projection.

Any file-producing tool can declare a descriptor field containing its intended relative output filenames. Tool start projects those names as presentation-only building references; tool completion removes the matching placeholders and projects the retained conversation references. The generic runtime owns this mapping. PDF, PowerPoint, or a particular agent must not add a parallel storage or streaming path.

The transcript projector also preserves resource type. Only an uploaded_document selected on a member message becomes a document chip or Workspace import. A run_artifact remains the originating answer's authoritative output and is opened through its owner or exact-room content route; its room reference ID must never be reinterpreted as an upload ID. This keeps live, reload, owner, and collaborator views on the same retained-file contract. Document scope retains every authorized conversation attachment for retrieval and sandbox tools. If the member attaches a new image on a later turn, the inline image parts on that turn contain the new attachment rather than reintroducing older images as if they were newly sent. A follow-up without a new image can still use the previously attached image.

Model-bound tool results and member-facing tool detail are deliberately different projections. The former includes bounded stdout, stderr, diagnostics, and artifact metadata needed for repair. The latter contains only sanitized status and durable references. Adding a tool must preserve this split.

10. The delegated-workload gate​

Readiness and current-state authority are cross-agent concerns. One shared gate covers member capability projection, chat task tools, scheduler, queue, dispatcher, worker, model gateway, credential broker, and resource effects. It requires the local rollout plus the complete fresh Core v1 graph; dependency loss closes every new boundary together while retained history remains readable. No individual agent may infer readiness or add a local fallback.

11. Goals and bounded background work​

Agent Goals are Task definition metadata, not a second orchestration system. The shared inspect_tasks and manage_tasks tools normalize the outcome, constraints, and verification checks, then bind their digest to the exact Task, accepted input, and Agent version. One-shot background work may carry a Goal; create_loop additionally requires explicit time and occurrence bounds.

Each occurrence still enters the ordinary Task runtime, private Task thread, progress-event stream, approval path, budgets, and side-effect fence. A Goal terminal marker can only report continue, complete, needs-input, or blocked; it cannot grant an effect. Needs-input releases the execution lease and resumes the same run only after answer_goal_input binds the exact accepted member input. steer_goal likewise loads accepted member text server-side and targets only the owner-bound live orchestration. Models never supply steering or answer text as tool arguments.

conversation_input_execution.py owns accepted-input publication and shared runner dispatch. task_chain_execution.py runs a pinned prompt chain through that same path; task_execution.py maps Task targets and applies an exact approved call before resuming. The successful tool summary becomes the waiting step's durable output. Later steps must execute against that receipt, not against their pre-approval outputs.

Loops force missed-occurrence skip, zero backfill, and no overlap. The dispatcher disables the schedule at the first of Goal completion, ends_at, or max_occurrences. Do not implement watches with a detached process, an idle worker, or an Agent-specific polling path.

Worked examples​

"Web Researcher should search differently." → Source selection and tool grants on the agent definition. Not a new retrieval path.

"Task runs need governed external effects." → One Core reservation and resource-guard consumption path carried through the shared gateway, tool registry, guardrails, budgets, sandbox, journal, and terminal projection. Not a task-only agent loop.

"Delegation should be bounded." → invoke_agent is a tool, so it inherits the tool boundary, capability ceilings, budgets, and audit. Not an orchestration-specific authority model.

"A routed specialist must actually run." → The shared prompt composer, tool exposure policy, and Turn Engine bind the exact one-turn invoke_agent grant. Not a special case in Default or Web Researcher.

"Compression should shorten tool results." → One authorized seam at the tool boundary, plus one seam in the prompt chain. Not per-agent implementations.

"Agents should answer questions about their own settings." → A server-authored projection appended to the shared prompt chain, from authoritative runtime values. Not per-agent knowledge.

"A generated file is ready for download." → The shared terminal answer normalizer verifies a matching durable PDF or Office artifact for every runtime adapter. In a collaborative run, the metadata-only tool result and room journal link use that same retained reference. Not an adapter-specific prompt or persona instruction.

The pattern is consistent: find the seam, extend it once, and let every agent inherit it.

When you genuinely need agent-specific behavior​

Ask which of the four permitted categories it falls into:

CategoryExample
PersonaTone, scope, what to do when evidence is thin
CapabilityWhich tools this agent may use
Tool grantWhether run_code is offered to it
Source selectionWhich collections or connectors it retrieves over

If it does not fit one of those, it is cross-agent behavior wearing a disguise, and it belongs in the pipeline.

The escape hatch that is not one​

A recurring temptation: "this agent is special, just this once." It never is — the second special agent arrives within a release, and the third stops inheriting the fix that the first two got.

If a genuine exception exists, it needs a spec change and an updated boundary test, not a conditional branch.