Appearance
Saivage v3 System Specification
Initial publication is lifecycle-excluded and creates configured root conversations and the required Analyst index before selecting the root card. The explicitly provisioned opt-in native Git path commissions only a fresh private root ref from clean explicit delivery, without checkout, commit, activation or Run. Completed source facts and truthful bootstrap execution evidence precede the initial head. Retained roots must match the selected ON/OFF policy; partial initialization and private-ref collisions refuse without replay/adoption. Native reset qualifies clean delivery and unused provisioning before deleting any generated state, and never deletes or reuses target Git refs.
Immutable card format6 requires nullable native Git source facts on every ordinary and tombstone payload. OFF cards, newly created children and synthetic repair cards start with null facts; the commissioned ON initial root selects completed source facts. Runtime source publication records completed private/parent selections, original correction history and partial-source snapshots separately; it is not approval, source adoption, a resumable job or a promise of preservation after interruption. Root policy binding and private ownership remain fixed once established. A native entry refusal may have the truthful runtime-owned git-workflow-blocked result, without pretending to be an agent/action outcome. Native ON execution requires explicit provisioning, successful commissioning and eligible compiled configuration.
Workflow5 structurally compiles ON correction as the configured card Executor in the same state table, with git-correction purpose and source_ready/blocked/failed results. Only native ownership may enter it and return to the saved ordinary entry after verification. It has no product DONE route, mandatory diagnostic output or product approval. Named agent sessions are reused/deduplicated, including on root and aggregate cards. OFF same-name authored nodes retain ordinary product meaning. The runtime compiler/binder admits only configurations satisfying native eligibility.
Optional quality tools bind each selected policy agent to operator-provisioned JavaScript ESLint, typed TypeScript ESLint or Python Ruff configuration. Strict empty requests cannot replace code or scope. Card, global and externally authorized Analyst callers share the compiled action interpreter and ordinary process ownership; tool calls create no card execution history or accepted action-output records. Findings are successful analysis evidence, not transport failure or acceptance.
Advisory observed source collections
Optional strict logical definitions expose list_observed_collections({}) and query_observed_collections({collections,query,intent?,limit?}). Shipped Analyst, Planner, Executor and Reviewer grants include both; custom role/node ceilings still apply. Oversight gains no grant. Internal and external Analyst use the same configured operation surface; external-only mode needs no model actor or internal conversation. External receipts remain body-free.
The deployment starts one native XERJ watcher per disjoint adopted root. Native initial/event-driven ingestion elects datasets; Saivage only reads bounded catalog, mapping and query responses. Querying never refreshes or waits for ingestion. List reports available/initializing/unavailable with incomplete/unknown coverage; empty catalog is not proof of completely indexed emptiness. Neural fields may be absent even for useful small code/docs/skills. Missing neural fields are distinct from failed neural queries; lexical success can survive a neural failure. Any usable destination can make a collection available; metadata loss elsewhere remains explicit in collection and query limitations with unknown collection coverage, independent of destination order. All failed/unsupported destinations do not become known-empty query success.
Queries select1–8 unique names,1–4096 UTF-8 query bytes, balanced/conceptual/ identifier intent and1–50 hits (default20). One15s deadline includes metadata and queries. At most32 destinations,200 candidates/destination/channel and1600 total candidates are admitted; native bodies are capped at8MiB. Excerpts are redacted before a2048-byte cap; safe results fit32KiB. Results disclose failed/unavailable channels, candidate limits, postfilter starvation and output truncation, always exhaustive:false. Scores are ordinal fusion inputs only, not public confidence.
Each result admits paths through the caller's ordinary reader authority and current path metadata. Only attributable string excerpts elected in impl→body→text order and permitted same-content aliases survive. This includes native short-line documentation windows, not arbitrary inferred columns. Joined/capped windows are not byte-exact source slices; coordinates are unavailable rather than inferred from a fresh body reread. Returned references are not definition claims, instruction activation or acceptance verdicts. Read originals and use read/grep/glob when useful. Stale or missing observations do not prove original absence and are not agent indexing chores. Native incomplete-walk/skip loss affects derived observations, not sources. Cancellation stops the query only, not deployment-owned engine/watchers.
See configuration and explicit native adoption. Source delivery does not commission any existing instance.
This is the normative functional reference. It specifies current behavior, not release news, installation compatibility or an operations recipe. The architecture explains owners; the runbook owns procedures; the UI needs and presentation contracts own the operator experience. Historical source applicability belongs to the durable format reference.
Product and trust boundary
Saivage's purpose is autonomous software development: after configuration, objective/acceptance preparation and explicit Run, configured agents carry out software work represented by cards in one target project. Reducing routine human supervision is an aim, not a guarantee of completion, correctness or minimal intervention. Configured acceptance and the owner's required decisions remain distinct; the contracts below define both execution authority and its limits. The runtime is the sole dispatcher. Its server provides Analyst interaction, card/session/file/evidence inspection and process-local controls. A notification queue is private delivery state, not an ordinary card-query surface.
Deployment must provide external isolation, normally an LXC container; Saivage neither creates nor verifies it. In-container agents are trusted and can run as root. Workflow/tool admission preserves ordinary correctness, prevents likely accidents and maintains explicit API boundaries; it is not malicious-root containment. External authentication and outbound secret non-disclosure remain mandatory boundaries. Persistence is loss-tolerant, including authoritative state; no UI, log, backup or readiness observation promises losslessness or complete effects.
Default roles are configured responsibilities, not a hard-coded privilege system:
| Default agent | Card tools and scope | Record writes | Child ceiling |
|---|---|---|---|
| Analyst | create_card, reorder_child, reopen_card, cancel_card, delete_card; root start_project; no activate_card/edit_card | write, edit; brief.md | true |
| Planner | create_card, edit_card, cancel_card, activate_card, reopen_card, reorder_child; no delete | write, edit; brief.md, status.md | true |
| Reviewer | No card lifecycle/structure tools | write, edit; review.md, review-*.md | false |
| Executor | No card lifecycle/structure tools | write, edit; status.md | false |
can_create_children intersects create_card and the parent's permitted types; it grants neither reopening nor activation. Reviewer/Executor settle configured outcomes rather than dispatching children. Configured names remain identities.
Find the owning contract
| Subject | Contract |
|---|---|
| Analyst callers and permissions | Analyst operations, caller-aware admission |
| Cards, records and selections | Card identity and lifecycle, authored records, card history |
| Graphs, actions and acceptance | Workflow engine, configuration and shipped policies |
| Sessions and retained context | Conversations and instruction evidence |
| Lifecycle and containment | Startup and Run recovery, notifications, activation, runtime controls |
| Commands, files and images | Command and workspace tools, image observations |
| Model context and transport | Provider context and preparation, compaction and exact admission, outbound MCP |
| Durable state and adoption | Direct file persistence, repair/reset/adoption, fatal publication |
| CLI and public projection | Lifecycle exclusion and CLI, operator API admission, safe projections |
| Independent observation | Project Oversight |
Analyst operations and external clients
During explicit work-GC maintenance, new internal/external Analyst submissions receive a temporary busy refusal; already accepted work finishes naturally before collection. Project Pause still gates the provider frontier rather than freezing filesystem writers. The private maintenance hold also excludes Start/Resume from every caller, waits for actual parking and accepted owners, and conservatively refuses unresolved transitions or MCP activity. Read-only views remain available. Stop/server close/restart cancels maintenance and prevents automatic resume. An ordinary Pause removes GC's resume intent. Successful or ordinarily cancelled GC may restore only its own pause of the same existing run, never create another Run. Background processes of unknown footprint require retaining all affected work.
Natural Analyst settlement includes the actual tracked consumer's final projection and delivery, the original producer result and the exact naturally closed turn tracker. Caller response or an idle snapshot alone is insufficient. GC does not close/join the reusable LLM session's lifetime admission or use shutdown disposal to finish an accepted turn; failures refuse collection rather than becoming success.
Explicit work GC first inventories approved ordinary work while execution continues, then holds/parks and freshly marks complete canonical references before checking each candidate at use. Selected baseline/draft/accepted history, all indexed conversation originals and raw results, execution selections, pending messages, operational dependencies and exact aliases protect work. Strict invalid required evidence blocks collection; GC never repairs/truncates it. Unknown ownership or surviving writer effects cause retention/refusal, not guessed safety. Only eligible unreferenced regular files under work can be removed; directories, publication temporaries and canonical/attic/backup data remain untouched.
Default and explicit dry-run use the same hold, fresh marking and selection as delete, but perform no collector file effects and require no backup. Preview reports hypothetical would-delete effects; delete reports only known removals. Remaining accounted logical bytes are estimates, never whole-root/physical/free-space claims. A separate delete always starts fresh, not from a preview report. First failure ends collection; outcome uncertainty is fatal before reporting, cleanup or resume. This optional operation introduces no public maintenance status, automatic schedule, saved job or durable GC event/report.
Analyst tool composition separates the configured policy identity and effective selected tools from actual authorship. External credential holders are attributed as {kind:'external-agent',principal}; the bounded lowercase principal identifies a credential holder, not a verified person or model. Reserved external:<principal> creator/control/history labels are not AgentName or conversation identities. Record writer globs and all required-tool conjunctions apply to the effective selection: permitting webfetch without write cannot authorize record saving. Preparation does not reserve state; final mutation freshly admits it.
The shared invocation boundary validates arguments before executor entry and preserves known failure classification and publication-fatal precedence. Real LLM execution retains its complete wait/child reservation context. External execution has its own invocation UUID, principal and operation-owned signal, with no fabricated session, source input, child reservation or model wait barrier. Image production returns normalized call-local bytes; only LLM settlement publishes conversation PNG descriptors. External bytes must not enter the durable ToolResult union.
Initial child bootstrap acceptance records its actual creator; root bootstrap and explicit repair remain runtime-authored. App log's external_tool_call row records exactly invocation UUID, principal, project identity digest, canonical tool name (or <unsupported>), settlement time, execution classification and ok/denied/error outcome. It contains no arguments, result body, exception, credentials or client-supplied model identity. Receipts publish only after known ordinary settlement; they are neither complete execution history nor delivery/no-effect evidence. Publication uncertainty permits no subsequent receipt, response, cleanup or retry. Current durable identities and historical source changes are separately listed in the format reference.
Direct external Analyst service
Startup selects --analyst-mode internal|external|both (default internal). External/both require --analyst-mcp-port (1–65535), --analyst-mcp-principal (bounded lowercase identifier), a nonempty unique exact --analyst-mcp-tools comma list selected by the configured Analyst, and an unpadded nonblank SAIVAGE_ANALYST_MCP_TOKEN. Internal mode rejects external settings. MCP and operator REST credentials must differ. These process-owned settings are immutable until restart, not YAML/reconfigure fields.
The same application owns a dedicated 127.0.0.1 listener at /mcp/analyst/<projectIdentityDigest>, published only after canonical startup, recovery and outbound MCP installation. SDK1.32.1 serves stateless JSON Streamable HTTP, revision2025-11-25, with no sessions, batch execution, GET stream or replay. Every method requires its separate bearer; exact Host/path, no query or Origin, both application/json and text/event-stream Accept values, and a matching MCP-Protocol-Version: 2025-11-25 header after initialize are mandatory. Initialize must request that exact revision (and any supplied protocol header must match); subsequent initialized notification, context/tool discovery and calls retain those headers. Requests are single JSON-RPC messages, bounded to 1 MiB, without Mcp-Session-Id or task extension. GET/DELETE return405 after authentication. Initialization returns safe binding metadata/instructions; the read-only saivage://analyst/context resource supplies mode, principal, digest, card types and runtime status. No internal prompt or conversation is fabricated.
External-only omits internal Analyst model binding, not configured historical participants, canonical indexes/evidence/startup settlement or real card-role binding of that same agent. GET /api/chat retains session_id and adds mode, internal_enabled, external_enabled without port/token/allowlist. Disabled submissions fail409 analyst_disabled at the application boundary before actor, provider or ingress. GET /api/external-tool-calls exposes body-free settled receipts separately from conversation rows under ordinary operator authentication.
Clients may overlap each other and internal chat in both mode; existing domain admission, not a global turn mutex, orders effects. Disconnect loses delivery, not admitted work or background-process ownership. One principal-owned process scope survives calls until App containment. App shutdown closes admission synchronously, cancels owned signals and joins work/containment. Project Stop leaves MCP available. No receipt, retry, response or disposal follows publication uncertainty. Navigation results remain client intents; restart_server only requests separately authenticated operator confirmation and cannot restart via MCP alone.
Workflow actions and execution evidence
Actions are non-LLM graph nodes; hooks are postcommit advice. Neither is an agent conversation or an alternative acceptance authority. They share the configured engine's routing and terminal gates, with the exact declared action-output publication below.
Action contract and cancellation
Workflow input format4 selects execution.kind:agent or action per node. Actions name an exact qualified ID with two or more dot-separated components and a contract version; each component matches [a-z][a-z0-9-]{0,47}. The first component is the namespace; a plugin's relative key may contain multiple components. No aliases, case normalization or suffix dispatch exist. Only explicitly compiled operations exist. The builtin shell.run contract1 launches captured bash --noprofile --norc -o pipefail -c <script> saivage-action <args...>. Script and isolated argv remain separate; no implicit set -e is added. Project-relative cwd, explicit nonsecret env and at most16 exact generated artifact paths are configured, never inferred or scanned. Timeout defaults to 120000ms (maximum600000); TERM grace defaults to2000ms (maximum10000).
Its four required outcomes are passed/failed with observed integer exit_code, signalled with actual leader signal, and timed_out with an explicit reason after termination/join. Spawn or evidence failure is an engine error, not nonzero. Cancellation returns the exact control interruption after cleanup, never a configured result. Active-card cancellation retains ownership through finite action cleanup and matching delivery settlement before successful CANCELLED publication and root/child release. A distinct cleanup failure is a containment failure, not successful cancellation. Captures have a5000ms post-group settlement deadline; unavailable evidence remains failure. External effects stay unknown. Pause still parks only autonomous provider admission: admitted action chains may continue until a provider frontier or natural completion. Stop contains them.
Flat required typed values support ordered cases (first match, mandatory otherwise) and selected successor inputs. Private fields cannot route or feed successors. An invalid return after effects fails without retry. Agent result tools and their2000-character summary bound remain unchanged. Actions cannot initialize, clean or close authored records. Except for the exact action output binding below, actions cannot write records; continue/exists requires fresh accepted authored content. Shared terminal/export/notification gates still apply. Workflow results identify the actual agent or action executor; actions create no agent session. Their lazy execution log distinguishes entry, observed spawn and retained completion; absent completion means effects unknown, not live status or permission to replay.
Optional action output record
Three explicitly configured quality actions cover JavaScript ESLint, type-aware TypeScript ESLint and Python Ruff. They share the ordinary finite action/output owner, use preprovisioned exact native pins, and are absent from default workflows. Findings are analysis evidence, not workflow/product acceptance; incomplete coverage or native/configuration failure is never clean. See quality configuration.
A node whose compiled action declares output may set output_record: check-report.md, naming one declared nonbootstrap own-card record required by that node as continue/updated. Agent nodes, no-output actions and hooks cannot select it. Compiled declarations select command-stdout (including shell.run) or returned-text; no caller chooses a capture path or substitutes output for routing values. No wildcard, foreign record, initializer, clean mode or stderr source is supported. This binding alone grants exact action write authority; agent tools, writer globs and later assessment permissions are unchanged. See the configuration example.
Node entry rejects any open destination draft before launch and captures the ordinary head identity/revision baseline. Command-stdout requires actual completion, known group absence, stdout EOF without capture error and settled cleanup, the node consumes the exact runner-selected stdout file once, closes it, then calls the normal record acceptance owner. Returned-text instead uses separately validated copied UTF-8 bytes from the operation's private result after cleanup, with actual nullable process facts. Publication freshly rejects intervening drafts. Absent, empty, baseline0 and closed accepted destinations are allowed without rendering an initializer. Only substantive UTF-8 text up to 1 MiB of raw bytes is accepted; invalid UTF-8, NUL, whitespace-only, missing/nonregular/unreadable or oversized output fails without truncation, placeholder or old-record fallback. BOM, CRLF, Unicode, whitespace, trailing newline and valid terminal escape bytes are retained literally; normal safe outbound text/markdown projection still applies.
Inspecting the physical capture through execution preview/download uses the shared Files bounded UTF-8 rules, including control-density refusal and outbound redaction. That inspection is independent of stdout record acceptance and retention: an accepted report does not guarantee an available or previewable capture, and a missing capture is not reconstructed from the record.
Passed, nonzero failed, signalled and timed_out outcomes publish eligible whole captures and retain their original branch and actual leader/deadline facts. A finished capture does not imply completed program work or semantic approval. Cancellation before publication publishes no report; cancellation after the synchronous publication/acceptance section does not undo factual effects. Spawn, capture or cleanup failure publishes nothing. Publication uncertainty is fatal before follow-up reads, writes, logging, acknowledgement or cleanup.
The existing publisher writes an immutable accepted5 artifact and fresh selecting head, independent of capture retention. Each new invocation may publish equal text as a new positive document version; agent no-op rejection is unchanged. Normal updated-head, export, descendant and promotion gates still run. A later gate failure leaves the factual report available but does not grant DONE. Action acceptance returns ordinary immutable record links, never closes a fake draft.
Accepted author is agent (agent), external-agent (principal), runtime (purpose:bootstrap|repair) or action-output. Action provenance contains the exact operation/contract/provider, activation, invocation, node and ordinal, actual nullable process/leader facts, output source/byte size/SHA256 and outcome. Strict accepted reads compare the descriptor to the literal body and enforce generic fact consistency plus shell-specific contract1/exit/outcome rules without today's operation map. Generic retained contract versions are positive safe integers. Returned text is not native stdout, agent authorship or a verdict. Current/history API and ordinary record read data expose this author; draft and baseline content_author are null, even over shell-authored accepted backing. Typed record observations remain selection/hash/exposure facts without authors; shell publication creates no synthetic conversation/tool observation. Retained opaque old observations are not normalized. See the generalized output cutover for source applicability, unchanged conversation12 and separate adoption consent.
Typed operation inputs and routing
The compiler requires exact qualified IDs, versions, providers and supported bindings from explicit trusted module declarations; it never discovers plugins. shell, git and saivage namespaces are reserved; only shell.run is builtin. Unknown operations, duplicate namespaces/IDs, shadows and unsupported bindings fail. Required flat fields are string, boolean, safe integer or string list; declarations may constrain string enums/integer ranges. Limits: 32 fields, 4096 UTF-8 bytes/string, 256 strings/list, 64 KiB encoded values. Unknown, missing, invalid or oversized fields and undeclared outcomes are engine failures even after real effects; no retry or false no-change outcome is synthesized. Private fields cannot drive cases, successor inputs or retained public fields. Cases have stable IDs, one typed eq|gt|gte|lt|lte test, at most eight per outcome and mandatory otherwise; first matching case wins after strict validation. All routes participate in ordinary graph/export/notification admission. Node/hook input suppliers are explicit literals or card facts; only hooks admit committed status. Selected predecessor outputs bind destination fields, checked on every incoming route and again before effects. There is no prior-log replay, implicit merge/default, arbitrary JSON/expression or dynamic target.
Postcommit hooks and entry context
One optional workflow.after_activation_commit action may filter natural done|blocked|failed settlements. Supervisor invokes it only after known outcome commit/currentness check and before processor join/owner release. It receives the committed immutable selection, not card mutation authority. Ordinary failure is advisory and cannot undo the outcome; containment/storage failure still fails settlement and publication uncertainty is fatal before follow-up. It does not run for recovery, Stop, cancellation, reopen, creation, queue or record writes. No retries, recursive triggers, durable queue or replay occur. Required pre-DONE checks belong in normal action nodes. Halt joins the whole finite raw action/hook cleanup and matching delivery consumer; process exit alone is insufficient. Actions/hooks never receive notifications or fabricate agent/tool/provider rows. Required sessions derive only from actual agent nodes. Existing agent correction, fresh record/parent context, exact reviewer selection, descendant and terminal gates remain unchanged. Agent-selected typed transition context is quoted factual data, not a new instruction or capability. Nonempty selected inputs arrive once per actual entry, including lifecycle entry, STOPPED recovery, urgent re-entry and configured outcome/pending-notification edges, as one ordinary compactable quoted user message. Empty inputs add no message. Outbound projection classifies each original field name and filters each scalar or list element, preserving display shape without changing internal typed inputs or routing. Covered text survives only as ordinary summary material, not guaranteed verbatim memory; a fresh entry resolves and delivers fresh inputs again.
Retained execution evidence
records/executions.jsonl is a lazy card-owned strict version3 card-execution family, not an authored record or workflow state. Each append is one nonempty newline-terminated envelope. Whole-log consumers validate identity/associations and all retained rows, not just a requested page. Entry precedes launch and does not assert effects; process start requires actual observed spawn. Missing finish is valid incomplete evidence. Only a torn final suffix after a valid complete prefix may be truncated; complete corruption fails at actual read/append, not a new startup scan. No reader closes or resumes an invocation. Selected captures are actual runner files; up to 16 exact generated artifact paths are observed after known settlement and remain mutable current-source references, never snapshots. Absent artifacts have no invented link and later missing files are unavailable. Authenticated execution list/detail/exact-reference files share Files privacy/alias/attic refusal and bounded redacted text downloads. Public named inputs and retained values use their original names for secret-name neutralization while preserving declared types and list cardinality. Raw operation values and retained evidence remain unchanged. Private configuration and environment are omitted from execution detail, not published as placeholders.
Native Git evidence uses logical start/finish and sequential command start/finish rows, not action/provider impersonation. It records the actual step, invocation/card/parent/activation, optional node/ordinal, reported known effects and refs/OIDs, source-return result and exact captures/private-index artifact selection. Missing finish means effects unknown; reported source return is not product approval or a resumable phase. ON requires explicit provisioning and successful source commissioning. Each command must naturally settle its entire managed group and captures before another command or source observation. Deadline/Stop termination rejects even an exited-zero leader; known containment is not a successful source return. Optional binary input belongs to the existing runner, not a helper protocol or durable source-body field.
The dependent Supervisor source owner orders root/child entry before provider work and failure/cancellation source return before terminal outcome and lease release. Runtime Stop takes over native command ownership rather than promising Git cleanup. Urgent held-source failures preserve queued notifications and suppress continuation; containment does not certify a restored checkout. Fresh commissioning and eligible bundle/configuration admission are prerequisites to running ON.
Product acceptance prepares post-edit source before claim, record closure and the successful result settlement. Writers are joined without disabling corrective feedback; records, current node ownership, descendants, review freshness and pending notifications are consumed again after the await. Review entry prepares its exact candidate before the Reviewer sees it. Approval and later root reviewed completion verify that same clean tuple, never checkpointing later dirt under an old review. Root assessment retains the promoted review author/summary separately from its own closing record exports. Correction results never supply product approval.
Supervisor delivery verifies the accepted child/root tuple and actual parent, squashes onto the parent's real checkout/index, and publishes the completed parent and child source facts before DONE or lease release. No-change delivery creates no empty commit. Dirt introduced after accepted emission refuses delivery and holds source; containment is not permission to attach a fresh checkpoint to old approval.
Under ON, global source writes require a genuinely stopped runtime and the exact clean commissioned delivery selection. Starting, running, paused, transferring, halting and stopped-but-held private source are denied. The final resolved destination is classified synchronously before mkdir, edit-content reads or writes; new paths resolve their existing ancestor, and aliases grant no additional access. Multi-file patches admit every destination before any effect. Webfetch repeats final admission after the network await. Records and permitted unrelated files keep their own authority; OFF retains mid-run Analyst file behavior. An operator source edit is not card work: later Run refuses unattributed dirt rather than adopting it. Run reserves its existing starting ownership before asynchronous entry, without waiting for the submitting Analyst turn to finish.
Global ON bindings reject unknown command-backed tools/quality and MCP writers, using actual canonical provider bindings. Read-only tools and existing process observation/containment remain available. The MCP owner freezes this restriction from startup workflows and refuses later register/replace/start/restart/catalog refresh/invocation and enabled persisted reconciliation before effects. Later configuration cannot hot-enlarge the frozen global tool surface or MCP eligibility.
The configured correction Executor handles pending native tasks directly on reentry. Its source_ready requests native verification after its command writers settle; invalid source receives ordinary corrective feedback, not product acceptance. Verified correction resumes the saved ordinary entry, including STOPPED after a surviving urgent interruption. A blocked completed rewrite publishes the verified current tip but retains the original task/history and latest partial snapshot. Only an entry refusal with a proven untouched clean parent produces runtime git-workflow-blocked; partial or unknown transfer remains a containment failure.
Parent direction and public notifications
Parent activate_card requires strict {card_id,instructions} with non-whitespace direction preserved byte-for-byte. Trusted parent card/session/input/call provenance comes from its exact existing lease. Direction remains advisory across that actual child activation's nodes and agents, including summaries. Exact joins retain it; different leases cannot overwrite it. Reactivation supplies new direction; root has none and grandchildren do not inherit it. Stop reconstructs no old lease or text. Same-owner urgent re-entry retains direction; root replacement has no parent. Ordinary matched parent results and record/Reviewer acceptance gates remain.
Only exact configured global Analyst/Oversight may select public queue_notification, including renamed roles. All card agents and nonselected globals reject at compilation, even when unused. Internal notifications, card recipients, pending alternatives, normal/urgent admission and append-before-remove delivery remain. Historical Planner tool JSON is retained history, not a current supported public call.
The selected retained conversation and actual compacted genesis are inspectable through the existing operator contracts. Shared source-owned outbound projection redacts summary prose and current/exact-history text and tool rows; protected instructions retain their public coordinates and protection. Selection, cursors and strict consumption scopes are unchanged. The reader has one scroll surface and combines exact tool exchanges at the request position, with a later-result cue and both original anchors/coordinates/times; canonical source order is unchanged. This conversation is a basis for request assembly, not the exact model request: prepared/static additions, tool definitions, protocol encoding and private replay differ. A separate optional current-instructions read composes loaded static configuration, not historical or exact-request snapshots. Activation boundaries separately record historical prepared instruction evidence as described below. See conversation presentation.
Caller-aware operation admission
Creation is not dispatch, deletion is not cancellation, and reopening is not activation. Similar lifecycle sets do not confer another caller's authority. The following matrix is the operation-specific admission contract, not an aggregate permission implied by a role name.
| Operation and caller | Scope/readiness and lifecycle admission | Additional admission | Durable effect |
|---|---|---|---|
| Planner create | Current Planner card through its active session; parent may be backlog, changed, running, blocked, or stopped | Compiled child type/creation ceiling; every dependency must already be an immediate child of that same parent | Backlog child publication followed by the parent link |
| Analyst create | Explicit global target while intervention-ready; parent must be non-running and otherwise creation-admitted | Selected Analyst ceiling/tool, permitted child type; each dependency must be an existing active card, with completion deferred to activation; depth above twelve rejects before parent workflow admission, and depth twelve requires a leaf workflow after ordinary admission | Backlog child publication followed by the parent link |
| Planner edit | Immediate child only; backlog/changed/stopped preserve status, blocked/failed may change, running/done/cancelled reject | At least one supplied editable field and a real title/priority/urgency delta | One metadata update, or for blocked/failed the existing status -> changed row then the update; an equal effective patch publishes nothing and does not reopen |
| Planner reopen | DONE or FAILED immediate child through the exact active parent owner | Parent activation remains active/open and application-admitted; target has no activation owner; no caller-supplied parent, activation, reason, content, or ancestor scope | Exactly one child status version to CHANGED; current result/error/completion clear through the existing status effect, historical versions and accepted records remain; no parent/ancestor version, propagation, notification, dispatch, audit, or automatic activation |
| Analyst reopen | Explicit global target while intervention-ready; blocked/done/failed only | No content delta; eligible resting ancestors are included by changed propagation | Status-only changed propagation and notifications; this is not activation |
| Planner cancel | Immediate child through the exact active parent owner | Owner-first delegation; Planner performs no target/status/subtree preflight | Supervisor claims the active suffix and publishes cancellation deepest-first |
| Analyst cancel | Explicit non-root target; no intervention-readiness requirement | Complete requested subtree preflight rejects the whole request if any member is done or cancelled | The same Supervisor cancellation authority after successful preflight |
| Analyst notification | Explicit target without intervention readiness; ordinary operation-entry admission and cancellation still apply | Exact target must accept notifications and any current activation owner must have an open terminal winner | One audited notification enqueue, even while running, paused, or stopped; normal awaits an ordinary recipient opportunity, urgent may interrupt exact live work without promising delivery |
| Planner reorder | Current parent inferred from the active session, including a running parent | Exact active-child permutation; no extra lifecycle or subtree gate | Real reorder appends one parent-only version; no changed propagation, notification, or control-action audit |
| Analyst reorder | Explicit non-running parent while intervention-ready | Exact active-child permutation and no running member in any requested active child's subtree | Real parent-only reorder followed by changed propagation/notification and ordinary audited invocation settlement |
| Planner activate | Immediate child through the exact active parent owner and invocation lease | Compiled activation type; target entry status; every dependency exactly done; exact Supervisor owner/currentness admission | Running publication and child execution ownership |
| Analyst delete | Explicit global non-root subtree while intervention-ready | No running member; complete survivor-dependency and ordering preflight | Permanent tombstones in dependent-before-dependency and child-before-parent order; this is not cancellation |
| Record mutation | Current active linked target; card-scoped agents only their own card | Matching record_writes; every operation-required tool (write, edit, and prepared Analyst Webfetch requirements as applicable); Analyst lifecycle effect support and no existing workflow draft | Card-agent draft publication, or direct Analyst acceptance followed by record change propagation |
| Analyst non-record write/edit | No intervention-readiness requirement; OFF permits mid-run. ON source destinations additionally require final synchronous stopped/clean delivery admission. Ordinary cancellation, permissions, and path admission apply | Existing project/scoped-file routing, not record authority | One file mutation and one workspace.write/workspace.edit audit append attempt; no ancestor propagation |
For both reorder callers, an identity filtered active order succeeds with changed:0 and appends no parent or child version. It performs no status publication, changed propagation, or notification. Planner has no control-action audit for either outcome. Analyst's successful invocation still settles exactly one ordinary card.reorder_child audit row as ok; that audit is not a card mutation effect. A real reorder changes only the parent's active_child_order; changed counts mismatched active ordinals, not movement of retained tombstones. After an Analyst real reorder, propagateChange() walks from that parent toward root until a running card, changes encountered done/failed/blocked cards, and notifies the reordered parent plus the first running ancestor when one exists; Planner performs none of those follow-on effects.
Analyst create, cancel, and real reorder return success:true for the known applied principal mutation and include propagation:{ok:true} or, on an ordinary best-effort propagation failure, propagation:{ok:false,partial:true,error} in result data. Create preserves its direct card-view shape; cancel preserves its existing fields; reorder adds the report only when changed > 0. A no-op reorder neither propagates nor reports an attempt. partial:true means the overall request is only partially fulfilled and propagation may have applied a prefix; it certifies neither which ancestors changed nor that any did. Record mutation retains its existing propagation reporting; plain file edits have none, and reopen has distinct lifecycle/notification semantics. Audit ok describes the principal effect, not advisory delivery; detail remains in the settled tool result. Publication uncertainty propagates fatally without result construction, audit, follow-up reads/effects, retry, or rollback.
Operator tool presentation distinguishes recorded settlement from domain outcome: successful process observation can report running or nonzero exit, an open record draft is not acceptance, and queue success is not notification delivery. Requested intent and separately anchored recorded result stay in physical transcript order. Semantic inspection exposes existing safe evidence and coverage, never inferred completion, replay permission or fresh enrichment. See the conversation UI contract.
Record tool possession plus a matching record_writes glob is necessary but not complete admission. The current URL must resolve to an active linked card, card-scoped authority cannot cross cards, all operation-required tools must be present, Analyst lifecycle admission must be supported, and an Analyst mutation must find no open workflow draft. Content-specific checks then reject absent edit content, missing or multiple old strings, empty results, and unchanged content.
Deletion is not cancellation, reopening is not activation, and creation authority is not dispatch authority.
Card identity and lifecycle
Card heads/mailbox use format1 and ordinary/tombstone documents format4. Only heads supply current version_seq, updated_at and pending notifications; ordinary history supplies its own immutable revision/time and no historical queue. Durable payloads exclude subtype, assigned_to, metrics, estimate, started_at, duration_ms, status_text_author_session_id, latest_self_report and metadata, even null. There are no tags, generic related-card list, top-level status, stored parent/depth or durable allowedActions. Retained status text/time, lifecycle provenance, immutable type and membership/order remain strict. Hierarchy/detail are separate public projections. These current contracts do not authorize adoption or infer compatibility with former stream layouts.
Card IDs use the source-derived grammar and depth ceiling in the exact block below. Root depth is zero; non-root depth is the hierarchical segment count. CardService domain methods reject malformed card IDs; a well-formed inactive target instead produces that method's specified null, empty, or not-found result. Each parent has a deterministic local spreadsheet sequence. Every child-creation call starts at the first segment, derives the exact candidate namespace, and advances only when exclusive candidate mkdir returns EEXIST; it never enumerates siblings or inspects a collided path. Successful directory creation is the sole claim authority and permanently consumes that segment, including when later publication or linking fails. Parent and depth are derived from identity and absent from durable card snapshots. Derivation never grants membership: exact path and history reads require each next ID in the reached parent's monotonic child_membership. API routes, records, sessions, events, process provenance, and UI links preserve the same value; the mutable one-based sibling-rank label is logical_path, is derived from active indices in filtered active_child_order, and is null for root.
Exact card identity contract
text
identity.card = {"alternatives":[{"kind":"literal","value":"project"},{"kind":"pattern","source":"^card-[a-z]+(?:-[a-z]+){0,11}$"}],"maximumSegments":12,"minimumSegments":1,"pattern":{"anchored":true,"flags":"u","source":"^card-[a-z]+(?:-[a-z]+){0,11}$"},"segment":{"anchored":true,"flags":"u","source":"^[a-z]+$"},"separator":"-","stem":"card"}
constant.maximum-card-depth-segments = {"unit":"segments","value":12}One non-root creation attempt accepts exactly NewChildCardInput. After basic input validation it exact-reads and validates the parent, computes resulting child depth, and applies the exact card identity contract maximum before ordinary parent workflow/lifecycle admission, child-workflow lookup, dependency reads, fresh-parent reads, namespace claim, or any write. The over-limit error reports the attempted depth and that source-derived maximum. In-limit creation next performs ordinary parent admission, resolves the selected child workflow (retaining No workflow for child type '<type>'. precedence), and at the maximum card depth requires that workflow's compiled permitted_child_types be empty; otherwise it rejects with the selected type and source-derived maximum. Only after this complete initial sequence does it admit existing dependencies and directly claim the first candidate whose exclusive namespace mkdir succeeds, progressing within that call only across EEXIST. Every other claim error propagates immediately. publishInitialChildCard() constructs every card property and one closed initial brief; root bootstrap separately accepts only {title,brief} and publishInitialProjectCard() constructs the exact Analyst-owned project defaults. The successful child order is namespace claim, complete initial publication and proof, fresh parent reread/admission, second proof, one parent append adding the child to both child_membership and active_child_order, then fixed runtime/runtime child_link history and effects. Dependencies are immutable after version 1. Because a new child may depend only on already existing cards, creation cannot close a cycle; canonical whole-loaded-graph validation still rejects malformed dependency cycles. A claimed but unlinked namespace remains consumed and invisible, and reported write failure authorizes no follow-up read, retry, or reconciliation.
Cards form a strict tree. Card type is chosen exactly once and selects that type's independently compiled workflow; there is no planning/terminal family. Child capability comes only from the intersection compiled for the active node between its named agent's global ceiling and the parent type's permitted_child_types. Any card type may permit children. Card statuses use the exact lifecycle vocabulary below.
Exact card lifecycle vocabulary
text
vocabulary.lifecycle-status = {"members":["backlog","blocked","cancelled","changed","done","failed","running","stopped"]}backlog is the initial creation state: every planner or Analyst created card publishes backlog with null result, error, and completion. It is inactive, nonterminal, mutable, notification-capable, and incomplete for dependency and descendant-completion checks. The configured BACKLOG entry activates it; generic status moves it to running, and cancellation and deletion retain their separate operation rules. A real immediate-child Planner edit_card delta and an accepted Analyst record mutation modify it without changing its status.
changed is an inactive, nonterminal, mutable, notification-capable state representing a card modified since its last activation, and is incomplete for dependency and descendant-completion checks. It has the configured CHANGED entry. Generic status reopens blocked, done, or failed to changed, while running-to-changed is not an operation. A real immediate-child Planner edit_card delta and an accepted Analyst record mutation modify it without changing its status; successive Analyst corrections require no intervening activation. It moves to running through activation and to cancelled through cancellation.
running is a durable lifecycle value, not proof of a current live activation. Normal execution has an exact activation owner, but successful same-process Stop can clear live owners/current work while leaving durable cards running. Fresh startup or later explicit Run performs the specified recovery; neither retained card status nor a socket connection establishes liveness. It is nonterminal and preserves notifications. Terminal completion is owned only by a non-cancelled activation outcome on a running card, producing done, failed, or blocked; cancellation moves it to cancelled. The Supervisor alone may publish running-to-stopped after full-chain recovery stabilization at startup or explicit Run, or after safely joining exact urgently interrupted live activations, including project before its in-run STOPPED replacement. running-to-changed is not an operation, and an exact parent or sibling activate_card may join its retained activation.
blocked is an inactive, unresolved activation result. It settles the current activation and returns control to the parent, but it is not dependency-complete, cannot support successful ancestor completion, remains notification-capable, and is absent from the running chain. An exact parent planner may later re-enter its blocked immediate child only through activate_card, which selects the configured BLOCKED entry; this is not an operator start or restart action. A blocked planning card may create a backlog child subject to the ordinary caller, type, dependency, runtime, publication, and linking gates. A real immediate-child Planner metadata delta or an accepted Analyst record mutation reopens blocked to changed; an equal Planner patch does not. Cancellation, deletion, and reorder retain their separate rules.
stopped is inactive, non-running, nonterminal, mutable, notification-capable, and incomplete for dependency and descendant-completion checks. It is never an active child or running-chain member and is never dispatched automatically. It is the recovery state: recovery from a dirty shutdown moves any card whose durable status was interrupted or is uncertain to stopped to restore a structurally consistent starting point. Existing intervention-ready Analyst authorized record/card mutations and a real immediate-child Planner edit_card delta may edit it without changing its status. Explicit project Run, an urgent in-run root replacement, or exact parent activate_card reuses it through the configured STOPPED entry; new child creation remains BACKLOG.
done is a terminal success state carrying a DoneResult with null error and a completion timestamp. It is notification-empty and not cancellable. It admits no further activation; an accepted Analyst record mutation, the global Analyst reopen_card, or its exact active parent Planner's owner-bound reopen_card reopens it to changed, while Planner edit_card rejects it, and deletion and reorder retain their separate operation rules.
failed is a terminal failure state carrying a FailedResult with error equal to its summary and a completion timestamp. It is notification-empty. An accepted Analyst record mutation, the global Analyst reopen_card, its exact active parent Planner's owner-bound reopen_card, or a real immediate-child Planner metadata delta reopens it to changed; an equal Planner patch does not. It remains cancellable and subject to deletion's separate operation rules.
cancelled is a terminal cancellation state reached only through the cancellation authority. It is notification-empty, not itself cancellable, and carries null result, error, and completion. It rejects Analyst record mutation and Planner metadata edits and admits no activation or reopening; deletion retains its separate operation rules.
Card read models and freshness
In the Cards operator view, the opaque ID in /cards/:id is the sole selected-card authority. CardStore owns disjoint immediate-child slices, route-selected displayed-only detail, separately loaded compiled record descriptors, exact effective current record content, selected version metadata, selected artifact, and current-relative diff. Each exact resource remains an independent REST and request-owner authority. The work-first Overview reuses those same selected-card descriptor and current-record authorities: after admitted selected detail, it reads the configured bootstrap and every declared current record independently, without an overview endpoint, aggregate cache, or summary read model. Presentation may excerpt and disclose the returned content, but it does not infer progress, completion, acceptance, or a wait reason from arbitrary record names/content, revision counters, timestamps, workflow position, session activity, or child lifecycle. Draft/Accepted presentation comes only from the current record's effective_content_source; lifecycle done remains acceptance rather than independent verification. Wire, lifecycle, freshness, and persistence contracts are unchanged.
The current Card REST contract is granular. GET /api/cards/:id/children proves active root-to-parent linkage and returns exactly one {id,title,type,status,permitted_child_types} parent plus ordered active immediate-child summaries of that same shape. Required permitted_child_types is the card type's ordered startup-compiled child-admission policy; it is not evidence that a current child or descendant exists. The operation consumes each linked child's exact current head/selected snapshot only to classify that child; it never projects or follows the child's raw links, opens a grandchild namespace, or returns children, has_children, descendant counts, or another grandchild fact. A retained tombstoned link is omitted and terminates traversal. GET /api/cards/:id returns the strict {card: ...} envelope defined by CardDetailResponseSchema and CardDetailSchema. Its current detail includes the exact UUID head_id and mutable version_seq: equal revisions alone do not identify the same selected head. It reads the selected card head/document, but no record definition, record head or record document. The owning schemas, not a second exhaustive prose field list, define the response. It contains no dependency, assignment, start, note, notification, child, or operator-summary data and imposes no dependency uniqueness rule.
GET /api/cards/:id/records proves active linkage and returns the selected card type's startup-compiled declarations in declaration order, with exact {name,format,schema,bootstrap,current} descriptors and no write-authority field. It does not discover dynamic names. GET /api/cards/:id/records/:name validates both path grammars before file access, proves active linkage, resolves declared metadata, and reads the exact record head and selected acceptance, returning current effective content. A missing optional record head is exactly card_record_not_found; a missing bootstrap record head, a malformed record head or selected document, and every unreadable reached current authority are server failures. Publication uncertainty is rethrown before every ordinary absence mapping and reaches the existing fatal boundary. Titles, lifecycle summaries/errors, descriptor schemas, and record content are redacted before exact response validation. Current authored-record content, explicit record versions, and record diffs share the authored-record outbound artifact projection. Record-diff view selection, equality, line counts, and hunk construction operate on the redacted projected content, so raw secret-only differences that project identically emit no placeholder-only hunk.
Cards freshness frames are strict exact targets; the broad {t:'invalidate',resource:'cards'} frame is invalid. The complete wire union is:
text
{t:'invalidate',resource:'cards',scope:'children',card_id:<parent-id>}
{t:'invalidate',resource:'cards',scope:'detail',card_id:<card-id>}
{t:'invalidate',resource:'cards',scope:'history',card_id:<card-id>}
{t:'invalidate',resource:'cards',scope:'diff',card_id:<card-id>}
{t:'invalidate',resource:'cards',scope:'record',card_id:<card-id>,record_name:<configured-record-name>}The only unscoped invalidation resource is runtime; timeline, files, and processes are not wire values. A runtime frame is only a lossy trigger for a new combined runtime REST read; it neither authors that response nor supplies age or provenance. RuntimeStore separately owns whether a current-epoch combined response has been accepted, its payload, initial loading/error, retained-state refreshing/refresh error, and the absolute completion instant of the last accepted response. Before acceptance runtime status is unknown; accepted runtime:null means stopped with no live runtime; an accepted non-null payload supplies its exact status. The browser has no runtime callback-provenance or age-derived stale model. Browser-to-server input is limited to strict live-sync subscribe/unsubscribe frames; Analyst input uses REST only. Server event egress contains only the strict status connected envelope, with content exactly {event:'connected',timestamp,clientCount}. It has no activity, error, message, thinking, or open member. Live-sync subscribed/invalidate frames are separate strict observations. Every server envelope and content variant is an exact strict object: an undeclared key is structurally invalid. The server-egress parser returns a validated server value or throws for structurally invalid, unknown, malformed, or wrong-direction input; unknown events are never dispatched through a generic envelope path. Ordinary Cards freshness is carried only by the scoped identity-only invalidations above; there is no card-history activity payload carrying changed fields.
children.card_id names the parent key of GET /api/cards/:id/children; history names ascending card-version metadata; diff names a displayed diff against current; and record names one effective current configured record. The mutation owner publishes targets only after successful canonical publication, according to this effect matrix:
| Successful mutation effect | Exact Cards targets |
|---|---|
Any actual current card-version change to C | detail(C), history(C), diff(C), children(C), and children(P) when C has active parent P |
Child link or reorder owned by parent C | the preceding version targets for C, including children(C) for membership/order and children(P) when P exists because C's containing row changed |
Tombstone of C | preceding card targets plus one record(C,name) for every compiled definition on C |
New closed accepted configured record for C | only record(C,name) |
No-op and reported write failure emit nothing. Creation's pre-link bootstrap record emits no record target; the successful parent link emits its parent-version targets. Authored open, edit, and discard publish durable record versions but emit no Cards target; close emits only its exact record name and never card-version history. Subtree deletion emits each successfully committed tombstone's targets from its preflight-known parent before attempting the next append. Targets do not cascade above the containing parent.
An exact invalidation refreshes only an accepted loaded slice or the currently selected accepted and visible detail/record/history/diff scope. A reconnect takes one synchronous bounded snapshot of accepted hierarchy slices plus the selected accepted record names, mounted accepted history, and visible accepted current-relative diff, then refreshes each member once. It does not discover ancestors, preload branches, refresh hidden or unselected resources, or retry a scope already stale because its previous refresh failed. The initial socket open for each authentication/configuration identity is baseline-only; every reconfigure() resets this one-open suppression before replacing the connection. Bootstrap/token change reconfigures, resets CardStore, and starts exactly one ordinary root load whether that request is still pending or already accepted when the baseline socket opens. Later opens are reconnect-eligible.
Each exact scope has one current request-owner object containing its AbortController and promise. A newer same-scope request immediately aborts and replaces it; success, rejection, and finalization may mutate state only while that exact owner remains current. Selected record reads pass their per-record abort signal through the exact Card content request and additionally require the same selected {cardId,recordName} owner, so a completion after route change cannot affect the new card. Reset aborts and removes every owner before clearing accepted state. Refresh starts by marking only that accepted scope stale; success atomically replaces it and clears stale state. Non-404 failure retains exact accepted data, marks only that scope refresh-failed, displays the error, and waits for exact Retry. A current-owner selected-detail 404 is the exception: it aborts and removes every selected record/history/entry/diff owner, clears all accepted selected-card state, and installs fresh non-retryable typed absence while retaining route identity. Late completions cannot repopulate cleared state, and selected-card invalidation/reconnect admits no read until a new explicit route fetch succeeds. Retry performs one immediate REST request, does not refresh siblings, and schedules no further work. There is no Cards polling, timer, automatic retry, trailing refresh, sequence ledger, acknowledgement, replay, persistent cache, or global refetch.
Displayed “Diff vs current” browser requests follow the exact source-derived request identity and currentness contract.
Current authored-record absence handling accepts only the exact strict {error:'card_record_not_found',cardId,name} response for a non-bootstrap definition as empty. Card-not-found, definition-not-found, any other 404 body, and every other failure remain errors. Bootstrap absence is canonical corruption and never empty.
| Request state and outcome | CardStore result |
|---|---|
| Initial bootstrap record 200 | accept content |
| Initial bootstrap record 404 | initial error, with no accepted value |
| Initial non-bootstrap record 200 | accept content |
| Initial non-bootstrap record 404 | successfully accept empty |
| Any refresh 200 | accept returned content and clear stale/error |
| Refresh 404 after accepted content, for any record | retain exact content stale, expose the refresh error and exact Retry |
| Refresh 404 after accepted empty non-bootstrap record | accept unchanged empty successfully and clear transient stale/error |
| Any non-404 refresh failure after accepted content or empty | retain that exact accepted state stale, expose the refresh error and exact Retry |
An initial non-404 failure is an initial error; the bootstrap record has no accepted-empty state. Accepted content is carried self-contained in every later open or discarded current artifact, so a refresh 404 cannot authorize discarding previously accepted content. A non-bootstrap record already accepted empty still represents the same absence after another 404, so that refresh is successful unchanged-empty. This table governs record responses while active detail exists; clearing records after parent-detail 404 is selected-card teardown, not changed record-404 interpretation. Independently invalidated hierarchy/detail surfaces expose card inaccessibility without inventing authored content.
On a cold store, route reveal loads each required ancestor-parent slice in order and follows an edge only when the containing accepted non-stale slice represents it. A stale or failed required slice stops traversal without consuming obsolete membership or auto-retrying. After a relevant ancestor slice is successfully replaced, CardsView invokes the existing reveal action once; that action may continue through newly represented idle descendants, sharing exact in-flight owners, and is bounded by the exact card identity contract. An irrelevant branch replacement does nothing, and a failed-stale ancestor waits for explicit Retry. Successful detail or detail retained after non-404 refresh failure may remain visible while an absent or stale represented edge leaves the selected tree row and Path unrevealed. Detail 404 instead clears the selected-card resource scope while neither clearing nor certifying hierarchy. A direct route outside the current grammar makes no detail read, is not accepted or normalized, and exposes the same explicit not-found/Back to Cards recovery state. Root/current-invalid routes clear route ownership and supersede reveal without cancelling shared hierarchy work. Route-required reveal takes precedence over collapse intent only along represented edges. Desktop uses independently scrolling tree and detail panes with no combined Cards scroll, while mobile presents the tree or selected detail as one pane with Back returning to the tree.
Authored records and current selections
Record document number, mutable record revision and selecting head UUID are different identities. Current baseline/draft state is not accepted history. Each mutation freshly admits its exact linked card and consumes its exact owner head; earlier reads or displayed permissions grant no later write authority.
Notification selection and lifecycle
Every durable card requires ordered pending_notifications. Backlog, changed, running, blocked, and stopped lifecycle state permits enqueue, while done, failed, and cancelled require an empty list and reject enqueue with terminal_card. For a currently owned activation, lifecycle permission is necessary but not sufficient: after its result or cancellation winner is synchronously claimed, enqueue rejects with activation_closed before a card append even while durable status can still be running. Unrelated mutations preserve the list. Cancellation and done or failed publication clear it; BLOCKED activation settlement also clears it before publishing blocked. Reopening supported statuses to changed uses only the supported setStatus('changed') transition; no actor callback or lifecycle-repair authority exists. No next_role, retry count, reviewer phase, recovery cursor, or autonomous-round counter exists.
Record URLs and mutation admission
Authored records have exact owner heads named injectively from safe record names and immutable accepted predecessor documents. Card-type declarations provide ordered metadata hints and exactly one bootstrap; an undeclared valid name resolves deterministically to Markdown with schema authored-record.v1. Each record owns records/record-<stem>.json. Opening, editing, closing, and discarding advance its mutable revision on actual mutation. The head contains the sole current draft and latest accepted reference; accepted bodies live only in immutable records/accepted/<UUID>.json documents. Public v=N selects exactly an accepted document version, never a draft transition or mutable head revision. Accepted document versions are consecutive from 1, independently of mutable revision; absent selectors are not found, not rounded to a nearby acceptance. Discard clears the draft while preserving the accepted selection. There is no retained open/edit/discard or draft history. The schema string is opaque identity/guidance and never executable validation. Card creation first publishes the bootstrap's accepted document1, then its selecting head. Child acceptance records the actual creating agent or external credential holder; project bootstrap is runtime/bootstrap and synthetic discard is runtime/ repair. Optional heads remain absent until mutation; empty-after-discard is distinct from absence. Startup validates records at their owning consumption scope and creates no missing authority.
The reusable current URL is exactly record:///<name>?card=<card-id> for read, write, and edit; ordered &v=N is explicit read-only history. Extra parameters, historical mutation targets, fragments, noncanonical versions, and old head-token forms are rejected inputs. For write and edit, malformed URL/encoding/identity/version and historical targets return the strict record_mutation_invalid_target failure with only the requested operation and a fixed safe diagnostic, before card or record I/O. A selector is always explicit, including for the root: record:///status.md?card=project; no current-card inference or URL normalization exists. The ordinary failed tool result settles the call and permits correction in the same activation; rejection neither mutates a record, propagates a change, nor marks a record written. Ordinary conversation settlement and Analyst error auditing still publish their own evidence and can themselves fail fatally. After target validation, mutation admission first proves the active card, then applies cross-card scope, writer glob, required-tool, and Analyst lifecycle gates in that order using the reached card. Every denial precedes definition and record-state I/O. Only an authorized request resolves the definition and classifies current/open-conflict state; the reached card is ordinary call-local data, not write authority, and each later public open/edit/close/discard freshly admits the current card. Requirements govern entry and acceptance, not free-name admission. Card-agent mutations resolve or open the current cycle and leave a draft; an admitted first mutation publishes the exact record head only after its owner establishes absence. An existing path object is never absence. Analyst mutations reject an existing workflow draft and directly publish one accepted document, then one head selecting it and clearing draft, advancing revision once. Successful acceptance invokes existing record-specific changed propagation. Successive corrections while CHANGED remain admitted without activation; each success can produce fresh target/ancestor notices, without repeated changed transitions or cross-edit deduplication. Webfetch retains read-only pre-network preflight and fresh final admission. Invalid-target, denial, absent-content, open-conflict, unchanged, old-string missing/multiple, empty-result, and current-state-unavailable outcomes are strict structured failures; there is no recoverable stale result. Existing non-publication card/classification read failures return current_state_unavailable with restart required, not invalid-target. Unexpected mutation errors and publication uncertainty retain their escaping boundaries; no failed result is fabricated after an uncertain effect.
Record entry preparation and acceptance gates
Every agent-node requirement is {mode:'clean'|'continue',gate:'exists'|'updated'} and must match the node agent's record-write glob so framework close has valid attribution. The exact action stdout binding has its separate declared authority. clean + exists, clean + updated, and continue + updated require the direct write tool; continue + exists may omit it unless the node selects initialization. Neither edit nor webfetch substitutes for write, and a record-targeted Webfetch additionally requires both webfetch and write.
Declared non-bootstrap records may define optional/null initialization:{format_version:1,template}. Nodes select an ordered duplicate-free initialize_records list independently of completion requirements. Each selection requires a declared initializer, matching record_writes and direct write. Old {content,at} input is rejected. The narrow template host admits cardType, cardId, cardTitle, nodeId, recordName and existing direct fragment includes; unavailable/malformed macros or nested includes fail compilation. Within record initializer declarations, card_types.<type>.records.<name>.initialization.template is the literal content path; ${NAME} remains literal, including in fragments. The configuration reference owns the complete environment-expansion boundary. Inserted values are never reparsed. {{...}} is template syntax, not literal Markdown.
At selected node entry, initialization precedes ordinary requirement preparation:
| Mode / state | Preparation |
|---|---|
| Selected initializer, absent exact head | Render once and publish current-only nonauthored baseline document 0, head revision 1. |
| Selected initializer, any present head | Do not render or publish, including baseline, draft, accepted and established empty-after-discard heads. |
continue requirement | Preserve current state; opening is not implicit. |
Explicit clean requirement | Discard an open draft; open from an existing baseline as draft0/target1, otherwise empty draft for accepted n+1 (or 1). Preserve accepted backing; never rerender. |
Creation, startup, reads, free writes and configuration replacement never initialize automatically. Complete card-type definitions select their own initializers without per-field overlay; compilation freezes templates and fragment closure for the process. Replacement affects only future absent-head entries. Existing-YAML init does not upgrade definitions. Bundled classic/classic-typed/paad-typed declarations explicitly select brief-relative status starters at responsible work nodes and neutral, unassessed review starters at review nodes, including both typed architecture reviews. Bootstrap briefs are excluded. Starters do not satisfy authored completion or acceptance; review still requires a fresh assessment. There are no mandatory headings or engine-special record behaviors. Configurable record initialization is delivered at selected node entry, not by a card-creation hook.
Record heads use strict format_version:3, including previous slots. A nullable baseline contains version:0,created_at,content,content_sha256 and cannot coexist with a draft or acceptance. Opening consumes it into draft version:0,target_version:1; an ordinary changed nonempty edit advances the draft to its target. Untemplated drafts start directly at 1; clean after accepted n targets n+1. Integrity hashes remain, but no seed fingerprint or unfilled flag exists. Accepted artifacts use format 5 with discriminated author and consecutive document numbering independent of head revisions: first acceptance 1, next 2 regardless of intervening opens, edits or discards. Direct Analyst acceptance of changed baseline content creates accepted1; bootstrap and repair supplied content remain factual accepted1, never v0. A bound completed output-capable action may accept equal baseline or accepted text as new factual output, without relaxing agent no-op rejection.
exists requires non-whitespace positive-version effective authored content; baseline0/draft0 cannot borrow eligibility from accepted backing. updated additionally requires a different head identity and mutable record revision above the baseline captured after initialization and explicit clean/open preparation. Initialization/opening alone satisfies neither gate. Ordinary A→B→A edits retain revision-based eligibility; no text fingerprint certifies authorship or adequacy. The same predicate governs required/free-draft finalization and close. Gate rejection uses ordinary corrective diagnostics; meaningful adequacy remains validation/review work.
Initialization is not an agent write or progress event and is not added to the activation-written set. Known-success baseline publication uses the existing advisory invalidation hint. Current-only baseline/drafts are not immutable history promises. Discard leaves an established empty head, never absence or resurrected v0. Accepted history/URLs remain accepted-only v>=1; current effective reads expose baseline0 without a fabricated history URL. Current projections carry real head UUID/revision; historical accepted projections carry null head/revision (including bootstrap/repair) and factual immutable version/entry/provenance. Current HTTP fields remain nonnullable; history HTTP omits mutation revision. VFS/tools expose historical revision/head as null. Authorized Analyst edits suppress later initialization, while existing open workflow drafts still conflict. Reviewer currentness pairs every selected subtree card's head_id with current version_seq (including queue-only traffic), and every included record's head identity/revision with accepted source version/entry identity. Equal displayed revisions alone are not freshness. Reviewer context stamps name the exact card selection; no old authored review is reclassified as a new verdict.
After all acceptance checks, the framework closes eligible open requirements in declaration order and then other eligible activation-written open records in record-name order. A graceful non-accepted settlement discards activation-written drafts in that same deterministic name-local manner before generic surface cleanup; rejected correction loops retain them. Once accepted close begins, a failure is not compensated: the committed prefix remains and no later record is handled. Publication-unknown permits no follow-up effect. A hard crash loses the activation-local written set: requirement drafts are handled only by a later node holding that exact requirement, while a free draft remains directly readable, conflicts with Analyst mutation, and may be reused and closed only by a later same-name glob-authorized activation; otherwise whole generated-state reset is the sole resolution. No scan, catalog, warning inventory, adoption, or selective repair exists.
webfetch saves preserve destination identity in write:{kind,data} rather than nesting a tool-result envelope. Plain relative, project:///, own-card tmp:///, and admitted system:/// destinations use kind:'workspace_file' and data containing respectively project_relative, project_url, tmp_url, or system_url; a record destination uses kind:'record' and mutation data containing revision, reusable current_url and nullable latest accepted_version_url, never an immutable draft URL. A save rejection is the one top-level failed tool outcome. Audited Analyst record Webfetch runs cancellation/readiness, read-only mutation preflight, exactly one HTTP fetch, cancellation/second readiness, then fresh full mutation admission. An invalid-target error, tool/glob/lifecycle denial, open conflict, or unavailable canonical parent at Analyst preflight performs no HTTP request; invalid-target audits error. Readiness or admission loss after fetch discards fetched bytes and performs no mutation. Card Webfetch retains its scoped write authorization before shared mutation admission; its malformed or historical save targets likewise reject before network, under its existing error boundary.
Card history, membership and publication
Card state uses card-head.json, selecting one immutable card-history/<UUID>.json ordinary snapshot or final non-root tombstone and ordered pending UUIDs in mailbox/<UUID>.json. Its version_seq and updated_at are current mutation facts, advancing on every successful queue mutation as well as ordinary publication. Ordinary payloads omit pending messages and current revision/time; immutable metadata supplies historical revision/time. history_version derives from the selected ordinary reference. History 1 → enqueue/current 2 → removal/current 3 → ordinary 4 retains only [1,4], with predecessor 1; historical 2/3 are not found. Catalog totals count entries, not revision maxima.
Previous selector publication and exact head identity
Every card/record head has required UUID head_id, newly generated for each publication, not derived from revision or immutable artifact identity. Current card detail, current record metadata/content, current-side diffs, Files current card.json, tool observation stamps and record mutation success expose that identity alongside revision. Historical accepted projections have no current head identity; their immutable entry/version provenance is unchanged. Ordinary mutations still freshly consume current state, without a carried write token or new CAS admission gate.
Every card head, record head and conversation index publication maintains exactly one deterministic sibling: card-head.prev.json, record-<stem>.prev.json, or index.prev.json. This includes initial publication, queues, draft edits/discard, tombstones and compaction. With owner-established current authority: unlink exact prev (only this unlink's ENOENT is accepted), hardlink current to prev, fsync the parent directory, then publish current using the existing fresh exclusive UUID temporary/write/fsync/close/rename/parent-fsync sequence. Explicit owner-established absence clears prev and fsyncs the parent without link before initial publication. Missing established current never becomes initial by fallback.
Normal readers and publishers never read previous content, automatically restore it, or discover history to regenerate a head. The slot is reserved for stopped/excluded/backed-up/consented exact-target saivage repair. Unlink/link failures stop directly; slot directory-durability errors and ordinary publication uncertainty are fatal before follow-up, retries or cleanup. Hardlinks on the supported Linux filesystem are required, with no copy alternative. The sequence is nontransactional: interruption can leave no prev, prev equal to current, or old current; no usable prior selection is guaranteed. Shared-inode damage and a previous queue-only head selecting the same damaged artifact limit its usefulness. Strict startup remains strict.
Required head UUID was independently incompatible adoption; its pre-policy outer version1 retention is a historical exception, not permission for reuse. Missing identity fails strictly. No migration/probing/normalization/compatibility path or binary-only downgrade exists. The reset/external-migration decision is separate; source completion authorizes neither deployment nor adoption.
Current reads strictly validate exact heads and selected ordinary/accepted documents, not their entire predecessor chains. Pending bodies are read only when consumed. Historical selection follows committed exact UUID references with strictly decreasing source revisions until the exact selector or established absence; full catalogs traverse to initial card version 1/null or accepted null and reverse to ascending order. Every reached identity, document, link and hash remains strict: missing/malformed referenced data fails, never skips or promotes a predecessor. Live mutations enforce exact current +1 admission from the fresh head, not history length. No persistent index, filename guessing, directory scan, retained admission registry, check-once trust, or startup certification of unconsumed history exists.
Every notification producer supplies one fresh UUID, used unchanged for document identity, stored notification, pending reference, receipt and selected-ID removal; reference/document/card identity must agree. Duplicate pending references fail. Source/content/created time remain unchanged, and repeated CHANGED edits produce distinct notices. Enqueue first-publishes the message then replaces the head selecting it; only known head success permits receipts/hints. Delivery appends selected bodies to the recipient conversation before fresh-read exact-ID pointer removal, preserving later equal-body notices with different IDs. Duplicate delivery after dirty shutdown in that window is accepted; no receipts, deduplication or reconciliation are added. Publication uncertainty forbids follow-up removal or other effects.
Ordinary mutations first-publish their snapshot then replace the head while preserving pending pointers. Terminal DONE/FAILED/BLOCKED settlement and cancellation select lifecycle and empty pending in the same head replacement; ordinary Stop preserves pending. BLOCKED may subsequently receive messages. Tombstones select empty pending and preserve pre-delete mutation revision/time, which can exceed predecessor source revision. Parent membership alone grants child reachability after complete initial publication. Delivered/cleared/unselected documents remain physically present, ignored forever and inaccessible through supported APIs; neither pending selection nor head has retained predecessors. This is pending-only reachability, not physical erasure or queue history.
Each parent snapshot has two required duplicate-free direct-child arrays with the same set and length. child_membership is monotonic link chronology and grants reachability; active_child_order is the complete reorderable carrier. Both retain tombstoned IDs because deletion is child-owned. Immediate hierarchy reads each membership child exactly once, then filter the carrier through live folds; this filtered subsequence is the sole semantic sibling order. CardRecord has no position. Creation appends the newly published child to both arrays in one parent version; child_link changes exactly ['child_membership','active_child_order'] with runtime provenance and no piggyback fields.
A reorder request must be a duplicate-free permutation of the exact current active child IDs. An active-order no-op, including one with interspersed retained tombstones, appends and emits nothing. A real reorder writes no child head or document: it preserves child_membership exactly and publishes one parent version whose complete active_child_order places requested active IDs first and retained tombstones afterward in prior carrier-relative order. Canonical transition validation is the sole direct-publication owner: it requires a nonidentity complete same-membership permutation, changed_fields: ['active_child_order'], exact runtime metadata, and no piggyback field. Generic card patches cannot carry either relationship field. Historical get_card_version section children deliberately pages the selected immutable row's complete stored carrier, which may include retained tombstones; it performs no child-liveness reads and makes no current-liveness claim.
Deletion accepts one non-empty requested-root list and performs one fresh complete active linked-tree preflight. Duplicate and ancestor/descendant roots are unioned; unknown/root/permission-denied requests, surviving external dependents, and conflicting hierarchy/dependency constraints reject before mutation. The call-local graph orders intended dependents before dependencies and children before parents, choosing IDs deterministically among ready vertices. Each confirmed tombstone emits its ordinary deletion history event and card/runtime hints before the next append. A reported append failure stops immediately and emits nothing for the uncertain append; every possible visible prefix remains dependency- and hierarchy-valid.
Current card history uses the exact discriminant vocabulary below. Update and delete provenance records the configured agent name supplied by the admitted caller with runtime surface; every other family is runtime/runtime. Planner edit changes only title, priority, or urgency. CardService.editCard() exact-reads and prunes value-equal fields before any effect; an empty effective patch returns unchanged in every admitted status. A real backlog/changed/stopped delta appends only the update. A real blocked/failed delta calls public setStatus(id, 'changed'), whose nested fresh business read owns transition admission and status publication, then appends the metadata update from the returned changed card. If that second publication fails, the completed changed-status prefix and its ordinary effects remain; the error escapes without reread, retry, rollback, compensation, notification, or artifact inspection. Generic status targets are only running (from backlog/blocked/changed), changed (from blocked/done/failed), and cancelled (from backlog/running/blocked/changed/stopped/failed); running-to-changed is not an operation. Running/changed preserve notifications, cancellation clears them, and the ordered delta contains pending_notifications only when a nonempty list changed. Supervisor running-to-stopped and STOPPED stopped-to-running remain disjoint fixed-reason status families.
Exact card history vocabulary
text
vocabulary.card-version-change-kind = {"members":["child_link","delete","git_source","notification_enqueue","notification_remove","reorder","status","terminal","update"]}Terminal completion is owned only by a non-cancelled activation outcome on a running card. Done/failed/blocked results are respectively DoneResult, FailedResult, and BlockedResult; there is no rework result variant. The runtime-owned subset of BlockedResult is exactly primary content-policy-refusal or {kind:'compaction-summary-blocked',summary:'Internal conversation summarization was blocked by the provider after bounded recovery. No further automatic retry was attempted.',session_id,summary_input_id}. The latter requires a canonical owning conversation session and UUID summary-input identity, has no content-policy marker/evidence URL, and is invalid in DONE or FAILED. The outcome summary must equal its result summary. Publication always assigns status_text = summary, status_text_updated_at = settledAt, and empty notifications. Done has null error and completion equal to that timestamp; failed has error equal to summary and the same completion; blocked has error equal to summary and null completion. Ordered changed fields are lifecycle, then each actually changed status companion, then notification clear when needed; change_summary is exactly those names joined in order plus updated. Every family rejects relabelling, wrong provenance/reason, wrong or reordered deltas, and piggyback fields.
Workflow engine
The engine executes the compiled graph one node at a time. This chapter owns actor failure, graph transitions, corrective admission and record acceptance; configuration and profiles own source selection and shipped responsibility. Graph position is live state, never a durable resume cursor.
Failure ownership
Reading BaseActor state before start throws InternalActorError. An ordinary task rejection delivers its failure callback once without fatal-hook notification; start-entry and actor-main failures retain that notification. CardProcess catches publication uncertainty inside raw node work around the captured successor guard and the entire awaited execution, including preparation and finalization. It reaches the nonreturning fatal port before any ordinary failure callback, projection, transition, cleanup, or later read, even if cancellation already settled the tracker wrapper before the raw rejection. Existing earlier provider, compaction, and Analyst fatal boundaries remain unchanged.
Any failure escaping the BaseActor main loop is terminal. BaseActor latches the exact value once, rejects every current and future lifecycle waiter with it, logs it once, invokes the required generic terminal-failure hook once, and never restarts the pump. A throwing hook is logged separately and cannot replace the primary latch. CardProcess owns one guarded first-winner activation boundary across normal terminal result, runtime interruption, cancellation/disposal, publication fatality, and actor-main failure. A callback failure rejects an unsettled activation exactly; if cancellation, Stop, or application close already settled it, that later settlement loses harmlessly while the required exact-owner Supervisor notification still runs.
Compiled graph and named participants
Configuration requires global agents, analyst_agent, and named models.routes, plus the card-type source form: a complete card_types map or omission for the bundled classic definitions.
In values eligible for environment expansion, missing ${ENV_VAR} references substitute an empty string and produce advisory warnings; literal data is not an expansion host, and interpolation does not impose credential-presence validation. Startup emits the successful environment load's warnings to stderr immediately after loading and before runtime construction/readiness. Offline CLI workflow loads report their own successful load's warnings before returning workflows. The static stderr prefix is Configuration warning: . Warning text contains no environment values and receives outbound text redaction, including for credential-shaped variable names. REST config warnings retain their string-array shape and receive text redaction without the stderr prefix. No warning delivery, deduplication, reload, or readiness guarantee is added; invalid configuration continues to reject normally. Template definitions contain complete card types, workflows, and record declarations only; agents, routes, providers, compaction, server, and MCP remain global and references from the resolved definitions are validated against them. Source resolution produces exactly one complete effective card_types map before compilation, and only that map reaches runtime consumers and outbound effective-config projections. An agent owns its prompt, ordered tools, duplicate-free record_writes patterns, model route, skill capability, global | card session scope, and child-creation ceiling. Each effective card type owns permitted child types, declared record metadata with exactly one bootstrap record, and a graph whose four entries are BACKLOG | CHANGED | BLOCKED | STOPPED. Each workflow designates one existing card-scoped notification recipient. Agent nodes select one card-scoped named agent, node/correction prompts, {mode,gate} requirements, optional descendant context and strict outcome edges. Action nodes instead select an exact compiled operation contract; they create no agent session. Every prompt reference is a strict declaration object. Static agents.<name>.prompt and node prompt use {reference,compactable?}; omitted compactable resolves to true, explicit true/false is preserved, and compaction_key is forbidden. Durable lifecycle-entry, correction_prompt, nonterminal edge, and pending-notification prompt sites additionally allow compaction_key. At those sites omitted/true forbids a key, false without a key protects every occurrence, and false with a nonempty exact key protects only the latest occurrence of that key. Keys are exact and are never trimmed, normalized, interpolated, or synthesized. A consuming site owns this policy independently of the shared referenced prompt text. Static and current-node flags never change their exact prepared delivery or create durable model-context occurrences; once-recorded structural activation evidence is distinct. Every descendant-context record must be declared by every transitively reachable permitted descendant type, though schemas may differ; arbitrary dynamic record names cannot enter descendant context. Terminal edges select DONE | FAILED | BLOCKED, ordered exports required by that source node, and current or reachable latest_node promotion. Every DONE edge from a nonrecipient node declares one pending_notifications alternative to an existing node run by the recipient, with its own process prompt; recipient DONE edges and all other edges forbid that declaration.
Classic project adds objective assessment to the visible plan/review loop; goal retains its direct/review completion paths. The other seven classic types independently preserve one-node execution. classic-typed supplies the typed profile graphs without changing runtime classification; paad-typed uses the identical typed configuration with optional methodology guidance only. These names are configuration values, not runtime classifications. Default Reviewer omits MCP, while default Analyst and Executor list unrestricted configured mcp_tool_call.
Workflow input format4 preserves the exact parsed YAML compaction_key at every durable declaration, including record prompts and conditional cases/defaults and their pending alternatives. Environment variables and body/fragment rendering do not alter that string. Empty fails; whitespace-only remains nonempty. History retains actual stored keys without reconstructing their source spelling. See declaration-owned retention and workflow3 adoption.
Each card type is compiled once at startup into one shared immutable semantic state table. Lifecycle entries, configured nodes, and terminal sinks are actor states; accepted typed outcomes are events and configured edges are transitions. Every transition has exactly one target identity; consumers look up that state to obtain destination node or terminal identity. The same configured-outcome transition carries its optional edge prompt and, for a terminal target, promotion and ordered exports. A declared pending-notifications alternative compiles as a distinct conditional event carrying the same accepted outcome and one target node; it participates in reachability, terminal-path, promotion-path, prompt-closure, and Debug Graph projection validation without creating another graph or durable cursor. The actor starts in its explicit parked ready state, and activation sends the configured BACKLOG | CHANGED | BLOCKED | STOPPED entry event. One activation tracker owns its raw node operation, cancellation signal, and matching completion/failure consumer through containment reporting while sharing the low-level FIFO containment mechanics with the separate provider invocation lifecycle. When the one node task settles, the actor clears its task slot before invoking the matching function; the tracker consumer then stages the accepted result and sends its event. A same-node edge explicitly reenters in this exact order: the old task is settled and cleared, its result is accepted and staged, the event is accepted, the transition runs, and state entry starts one new node task. BaseActor performs no task cancellation; tracker revocation owns it. Ordinary node failure instead stages and sends code-owned execution:failed; an app-log publication failure sends no event and halts the current task state for Supervisor-owned runtime halt. Plain text, malformed results, recipient-node pending notifications, record/evidence failures, reviewer freshness failures, and completion-gate rejection are hidden corrections inside one node task and do not transition or increment the workflow ordinal. They jointly consume that node task's corrective re-arm budget. A nonrecipient accepted DONE candidate with pending context instead follows its configured conditional event and increments like any ordinary cross-node transition. Activation run admits an activation operation; provider invocation begin admits one exact lease, whose later runExternal remains valid after non-aborting admission close. This FIFO ownership matches one operation at each current frontier and does not promise generalized concurrent matching. Promptless BACKLOG/CHANGED/BLOCKED entry contributes no transition message; STOPPED contributes the fixed discarded-position notice and its required configured prompt. Every STOPPED entry targets a node run by the configured notification recipient. Each executing node also has a derived notification:interrupt transition to that node, including self-reentry. Unlike an activation entry it preserves the boundary activation and supplies truthful interrupted-node context, not a discarded-activation marker. Its old consumer sends the event synchronously and the installed successor task waits for Supervisor settlement before preparing any execution effect; BaseActor is neither parked nor restarted. The first node ordinal is 0; the entry bridge does not increment it, and each accepted cross-node or self-reentry advances exactly once (0 -> 1 -> 2). Live runtime status reports node executionOrdinal as a required nonnegative safe integer. No state ID or ordinal is durable.
CardProcessActor executes one compiled node at a time and keeps graph position only in live activation memory. Each node's referenced named agent supplies the exact ordered operational tools. Prompt rendering receives that array without emit_result; AgentExecutor directly creates and appends emit_result exactly once and last. It first requires arguments to be a non-null, non-array JSON object, then directly applies the strict {outcome,summary} schema; failures remain in the same-node repair loop and consume its joint corrective re-arm budget. The schema accepts only a configured edge and a trimmed non-empty summary bounded by the exact terminal-result limit. Immediately before each direct terminal append or settlement, this owner parses exactly one strict settlement variant: accepted {success:true,data:{accepted:true}}, an ordinary nonempty-error failure with no data, or the pending-notification failure with exactly {reason:'pending_notifications'}. Malformed arguments, record violations, stale descendant context, incomplete descendants, pending notifications, and acceptance all pass that boundary without changing claim, record closure, continuation, publication-uncertainty, or cleanup ordering. Generated contractDescription is the sole agent-template authority for this contract. The configured global Analyst uses its ordered operational surface unchanged and has no emit_result.
Bundled shared roles instruct agents to prefer available dedicated file tools for authorized ordinary inspection/search and changes, using only selected capabilities and small, reviewable edits. Executor prefers available apply_patch for deliberate source edits over shell replacements or heredoc source rewrites, retaining appropriate selected write/edit use, including permitted record work. Oversight remains inspection-only. Within role authority, shell remains appropriate for builds/tests, Git, external commands, generators, legitimate pipelines and operations dedicated tools cannot reasonably serve; Analyst's no-delivery and source-shell restrictions still apply. Failure/conflict is not permission to bypass tool contracts, record permissions or confidentiality, and publication uncertainty remains fatal without follow-up or replay. This is role instruction policy, not runtime enforcement or a model-compliance guarantee; absolute read/discovery support grants no additional mutation authority.
For authorized Git work, shared Executor guidance prefers ordinary high-level Git operations: preserve unrelated files and staged work, avoid destructive cleanup or history rewriting without authorization, and avoid ad-hoc advancement of a checked-out branch ref that leaves its actual checkout and index out of sync. This is instruction guidance, not runtime enforcement, new Git authority or a universal commit requirement.
Bundled Planner/Executor/Reviewer methods favor the simplest coherent design meeting actual requirements, separating warranted findings and concrete impact from proposed remedies. Optional availability, diagnostic precision and rare-failure recovery are not automatically obligations: explicit safe-boundary failure, capacity rejection before work admission or omission of optional diagnostics may suffice, without losing actual failure/effect facts, owned-resource identity/cleanup, independent control, authentication/authorization or confidentiality. Ordinary contention grants no cross-session destruction, and unknown effects grant no automatic replay or false success. Removing unnecessary machinery is a valid correction; strengthening guarantees or weakening explicit requirements needs the requirement owner's decision, not a simplicity slogan. Every assigned/exhaustive criterion, original-owner correction, supported early return and bounded-throughput rule remains binding. Oversight may advise reconsidering machinery within evidenced non-progress diagnosis, not patrol complexity or acquire strategy authority. This shipped-role instruction policy adds no runtime gate, outcome, feeding/retention change or durable contract; prepared text and scripted feasibility do not prove model compliance or quality improvement. Materialized/custom roles require authorized stopped reconciliation and fresh compilation, not existing-config init or Run.
Shared Analyst/Planner authoring guidance leads briefs, activation directions and advisory notices with useful outcomes, necessary scope, evidence and meaningful acceptance or next action. Prohibitions need an actual requirement, authority boundary or evidenced proportionate risk, with clear reason and scope; ordinary implementation choices stay open. Historical failures/refusals remain facts, not permanent bans on related work or permission to evade provider safeguards. Fresh direction replaces superseded agent-added contradictions within the author's authority; genuine accepted constraints and independent assessment remain binding, and requirement changes still need their authorized owner. This creates no new authority, notification guarantee, retry obligation or runtime gate.
Executor's versioned-work guidance favors meaningful source, small faithful fixtures/goldens, pinned deterministic generators and concise reproducible research and provenance. Credentials, secret/runtime configuration, generated runtime state, caches/build outputs, full logs, exhaustive exploratory captures and duplicated large results stay out of ordinary commits by default. Authored non-secret configuration and genuinely required full test data remain eligible. Compact representations must preserve every applicable workload/assertion; raw evidence may remain at existing locations with useful references, without promised permanence or mandatory hashes, storage mechanisms or approvals. This authorizes no evidence deletion or Git history rewrite and changes no retention or acceptance contract.
Shipped convergence guidance prioritizes useful coherent delivery against accepted requirements over activity volume. Planner delegates bounded outcomes while retaining parent-owned remainder and integration; child milestones do not waive larger briefs, and activation direction cannot edit child requirements or dependencies. Executor continues actionable permitted current-node work; incompleteness alone is not conclusive failure. Research, no-change, typed handoffs and exhausted or unavailable-input returns retain their actual evidence and node-owned criteria. Reviewer assesses the requested combined input, not isolated successes or historical verdicts. Correction addresses the attempted result and diagnostics, not future task selection or proof of fulfillment/impossibility. This is instruction policy, not a runtime convergence guarantee, new outcome or permission; installed adoption remains separately authorized.
Exact terminal-result limit
Generated emit_result advertises the owning summary bound via maxLength; runtime trim, nonempty and length validation remains authoritative. This does not promise identical Unicode/trim acceptance or provider compliance.
text
constant.emit-result-summary-max-chars = {"unit":"characters","value":2000}Exact node corrective budget
text
constant.node-corrective-rearm-limit = {"unit":"logical invocations","value":16}One node task permits at most 16 corrective re-arms counted jointly across plain-text results and every rejected emit_result: malformed object or schema, pending notifications, record violations, stale reviewer context, and completion-gate rejection. Each corrective notice states the attempts remaining after that re-arm. Exhaustion costs no additional provider exchange. If emit_result is pending, the owner first settles it definitively with the ordinary nonempty-error failure emit_result was not accepted: the node corrective budget is exhausted. and no data; plain-text exhaustion has no pending call to settle and retains the complete assistant text row. The resulting NodeCorrectiveBudgetExceededError follows the ordinary code-owned execution:failed path to the FAILED terminal lifecycle and the waiting parent's failed activation result. Exhaustion can also occur without model fault through repeated externally timed notification arrivals at emit_result boundaries. Each admitted interception drains—delivers and removes—the entire selected pending notification batch for one re-arm. From a fresh budget, the first 16 such interceptions can re-arm; the next emit_result request exhausts the budget before delivering or removing its selected batch. Notification-only exhaustion therefore requires about 17 separate arrivals, each interleaved between recipient correction turns—for example, sustained paced arrivals from a looping configured global producer—not a burst or 17 notifications accumulated in one pending batch. The counter belongs only to one live node task, is never persisted, and starts fresh for each later node task or card re-activation.
Configured correction text and runtime diagnostics are separate durable rows. The configured text is the sole model_repair row and carries its declaration's compactability; validation errors and remaining-attempt diagnostics are ordinary compactable user context. A rejected tool call's original failed result, selected notification or refreshed reviewer context, and correction rows are built before publication and appended together in one known continuation batch under the fresh continuation input identity, while the result keeps the original call/input identity. Plain-text repair likewise appends configured repair, diagnostics, and ordinary continuation context in one batch. Call rows remain in their earlier envelope because execution lies between call and result. Callbacks and notification/reviewer updates run only after known batch success; publication uncertainty preserves its exact cause and permits no reread, retry, cleanup, or callback.
Explicit skill selection
The optional on-demand skill catalog is the exact .saivage/skills/index.json JSON array. Each entry is one strict three-field object:
json
{
"name": "typescript-testing",
"file": "typescript-testing.md",
"target_agents": ["executor", "reviewer"]
}name and file are nonempty. target_agents is a nonempty, duplicate-free array of configured agent names whose global contracts enable skills and list skill; the default Planner therefore cannot be targeted. Entry names are unique. file is a normalized relative path beneath .saivage/skills, with no absolute, empty, ., or .. segment. Unknown entry fields fail the complete index read. Index order is listing order.
Reviewer, Executor, and Analyst invoke skill explicitly; Saivage performs no automatic matching, ranking, selection, preload, or prompt injection. Omitted name lists only entries targeted to the caller and returns the complete tool result {success:true,data:{skills:[{name}]}}. Supplied name loads only an entry targeted to the caller and returns {success:true,data:{skill_name,skill_content}}, where skill_content is the exact UTF-8 file text. The result contains no target metadata, delimiter, or loaded flag. A missing exact index is an optional-capability case: listing succeeds with {success:true,data:{skills:[]}}, named loading returns the generic {success:false,error:string} unavailable result, and absence is not cached or replaced by a created file. A present malformed index, old-schema entry, duplicate name, invalid path, missing selected skill file, or other read failure returns that same generic failed ToolResult shape with its actionable error; there is no fallback, normalization, or skill-specific failure projection. A name absent from the caller's filtered catalog and a name targeted only to another role are both unavailable.
Node entry and acceptance
At agent-node entry the owner appends the activation marker, designated-recipient notification context or reviewer context when present, and immediate lifecycle/edge transition context when present. Nonrecipient nodes never receive or remove notification context at entry or continuation. It does not append the current node prompt: before any record, notification, or ingress effect, the actual compiled process/node selects that prompt's unaltered full text for the activation-local prepared node block. Nodes referencing the same agent on one card reuse its stable session; a transition to another agent selects that agent's card session. Immediate edge context contains only source node, accepted outcome and summary, accepted record URLs, and its optional edge prompt. That handoff is delivered once rather than repeated as prepared context, but accepted facts, record evidence, and still-applicable instructions remain applicable after delivery. A later node prepares its own node text and generated outcome/tool contract; neither old rows nor summary prose select graph position. No accumulated graph state is persisted.
One hidden corrective loop owns plain text, malformed or unknown results, record violations, pending notifications, planning completion, and reviewer currentness. Its single per-node-task budget permits at most 16 corrective re-arms across all of those causes; ordinary tool invocations and blocked or provider-error outcomes do not consume it. Terminal completion is enforced by strict actor verification and these acceptance gates, not by a provider terminal phase. Corrections remain in the same logical node, session, baseline, and role. After all read-only gates, recipient terminal candidates with pending context remain in their same-node correction path. A nonrecipient DONE candidate validates records, descendant freshness, completion, and promotion, then synchronously tests only whether context is pending and either claims the ordinary terminal result when none exists or selects its compiled conditional event without claiming when context exists. Queue entries are not exposed to that nonrecipient. This decision occurs before record close or any asynchronous boundary. The conditional route closes the same accepted records, settles emit_result successfully, retains the real accepted result for transition context, and leaves queue selection/removal to recipient entry. Notifications admitted during that record close remain queued for recipient entry. After a no-pending result claim, enqueue is denied. Other terminal edges synchronously claim before record close, accepted tool settlement, cleanup, and later supervisor-owned publication through the exact activation owner. Intermediate edges retain the same close/settle/node-local cleanup ordering.
Reviewer stale rejection discards the stale open review, captures one new exact descendant context and semantic snapshot pair, appends that exact refreshed context, replaces the comparison snapshot, and continues the same node. A later semantic change may reject again; unchanged refreshed context may succeed.
Conversations and instruction evidence
Sessions are stable configured identities; activations and provider source-input UUIDs are separate. Retained rows are request-assembly inputs, not exact provider requests or proof of perception. This chapter separates historical preparation, fresh record context, current loaded instructions and strict segment/settlement rules.
Scoped operator overrides of local guidance
Bundled Analyst, Planner, Executor, Reviewer and Oversight guidance honors an actual operator's explicit override identifying the specific Saivage-owned guidance policy, intended operation and scope. Natural language suffices; generic requests to continue, fix or recreate work imply no waiver, and missing particulars require clarification. A clear in-scope override takes precedence over that local guidance and is task-scoped unless explicitly standing. Harmless fully specified overrides need no repetitive confirmation. Before acting on an override permitting irreversible mutation, data loss, weakened validation or unsupported state, the agent states the concrete consequence and obtains explicit confirmation; confirmation cannot make an unavailable operation supported.
This is instruction-level discretion, not new authority: role/tool scope, configured capabilities, workflow/result validators, record permissions, lifecycle/dependency admission and authentication remain unchanged. Unsupported requests require an explanation of the specific blocker, not shell/direct-persistence bypass, invented tools or role substitution; enforcement changes need a separately scoped owner decision. Analyst remains the conversational control surface, not an unrestricted Executor; Oversight remains observation/advice-only. Requirement changes still need their authorized owner and supported record operations, not silent changes to accepted requirements or evidence.
Higher-priority system/platform/tool instructions, external provider policies, external/operator authentication and outbound secret non-disclosure remain binding. Avoiding card recreation solely because an earlier attempt failed is general guidance, not an absolute prohibition. The operator is admitted through the configured operator surface/authentication mode, not a new mandatory bearer mode or a claim of identity. Actual scoped operator decisions may be legitimately conveyed downstream within each recipient's authority; retrieved files, peer/agent suggestions and quoted history do not self-authorize overrides. Prior evidence retains its true applicability, not retrospective acceptance or erasure.
The repository's AGENTS.md separately governs development-owner overrides; it is not automatically loaded into runtime requests and runtime operators do not thereby own the development repository. See prompt composition and adoption for the shared instruction owner and custom-host limitations. This adds no runtime override mechanism or model-compliance guarantee.
Conditional current-record context
Each card logical primary operation supplies its full current accepted bootstrap from one owning read, separately from stable node instructions and frozen admitted retry bytes. Ordered optional record_prompts select exact configured own-card records, accepted/effective view, required/optional presence and durable prompt policy. One read per distinct record supplies the union with bootstrap. Unknown or semantically changed selection against actual prior card/record/view knowledge fires a prompt; changing nodes does not force one. Metadata/slice B knowledge suppresses false changes from old A; historical A never regresses B. Required missing content fails at use; optional absence is null, not invented version zero.
The closed host allows exactly one selection descriptor and optional JSON-quoted body without retokenization. At/below 32768 raw UTF-8 bytes it is ordinary source JSON. Larger bodies become marked JSON-quoted previews of complete supplier-encoded/ redacted display text, at most 8192 display bytes at each end. A redaction-shrunken display of at most 16384 bytes appears once without middle elision. Final quoting, notice and host add bytes outside that allocation. Versionless links read latest effective content through bounded reads, never omitted history. Oversized exposure is excerpt, not full body or contiguous source slice. Full accepted bootstrap, stored records/history, accepted authority, permissions, exhaustive requirements and Reviewer gates are unchanged; omitted context never waives them.
Rendered prompt bytes/provenance and needed preparation descriptors publish in one ordered envelope before provider work, without a later record-currentness read. Latest descriptor-only source/exposure facts reach primary and summary independently of retained text; they establish neither recall, delivery, acceptance nor write authority. Public current/history/protected record_prompt uses one shared server projection through the supplier's JSON-string encoding. Quoted/template-looking data stays text. The oversized prepass records actual transformed text for primary, summary and protection; source hashes identify source, not display-byte equality. Inline masking consumes complete escaped-double-quoted secret values without wider non-secret advancement. This is pattern-based defense, not arbitrary-encoding secret detection. See prompt handling.
Historical instruction evidence
Format-12 activation_open.instructions is strict. Prepared evidence contains role_text, nullable node:{node_id,text}, activation_id, caller, card and parent_direction. Card ingress records full frozen node text, actual activation and {cardId,cardType,title}; global preparation has null card/node/activation/ parent and caller global. Root caller has no parent direction. Parent caller records exact parent card/session/source-input/call/activate_card provenance plus nonblank rendered direction; identity/parent associations are validated. unavailable/not_recorded states absence, never a current-template fallback; global confirmation-only ingress is not_applicable/non_model_ingress.
New model-backed ingress records this once per real activation, not continuation, correction, retry or compaction. Preparation/publication, admission and turn-start prove neither delivery nor interpretation. Besides those explicit orientation/ caller fields, the snapshot contains authored role/node strings, not arbitrary dynamic records/focus, tools, prefix containers, routing/auth, private replay or diagnostic bodies. Structural evidence stays excluded from primary/summarizer context, accounting, protected prompts and required facts. Compaction retains it in exact indexed source history, not a duplicate active block in genesis.
Lifecycle-entry, configured edge/pending and correction owners positively mark existing card user text/model_repair only with instruction_origin:'workflow_prompt'. Unknown values and other roles/kinds/scopes fail. Provenance survives protection/public projection without changing row content, identity/order, append counts, delivery or policy. Unmarked context is not inferred human-authored; summaries gain no invented provenance. No category parsing or template matching is permitted.
Current/incremental/tail/exact-history share positive public projection and separate role/node text filtering. UI distinguishes privacy-filtered recorded instructions, unavailable recording, start outside segment and non-model ingress; current loaded configuration remains a different inspector. Old/mixed contracts block; source completion authorizes no state adoption.
Stable session layout
Stable session IDs use the source-derived grammar in the exact block below for global-scoped and card-scoped configured agents. One parser owns this grammar across messages, persistence, Agent APIs, chat, live sync, and web contracts. Each deterministic configured session owns conversations/<agent-name>/index.json and immutable versions/<version>-<uuid>.jsonl segments; the global Analyst uses the corresponding .saivage/agents/conversations/<agent-name>/ namespace. First publication establishes an index and its directories even while the catalog is empty for every distinct card-scoped node agent in the published card's compiled workflow; see Established conversation and card-owner directories. Successful initial runtime publication also establishes the selected global Analyst conversation after publishing the project card. Later startup strictly consumes those exact indexes for reached active cards and the selected Analyst without enumerating session/version directories or creating replacements. An interrupted initial runtime publication can therefore leave strict incomplete state requiring the authorized reset remedy. Oversight is different: its selected global conversation is established lazily only when an actual check first uses it. Direct known tombstoned-card sessions remain readable. Conversation formats and the distinct card/record JSON and growing JSONL representations are specified under direct file persistence. Segment ordinals are sequential generations, not format numbers. Earlier formats and mixed shapes are rejected.
Exact conversation-session identity contract
text
identity.conversation-session = {"agentParser":"agentNameSchema","captures":[{"index":1,"meaning":"agentName"},{"index":2,"meaning":"cardId"}],"constructors":[{"name":"globalAgentSessionId","template":"`agent:${agentName}:global`"},{"name":"cardAgentSessionId","template":"`agent:${agentName}:${cardId}`"}],"grouping":"match !== null && agentNameSchema.safeParse(match[1]).success && (match[2] === 'global' || cardIdSchema.safeParse(match[2]).success)","identityParser":"conversationSessionIdentity","inputGuard":"string","nullTest":"match !== null","operators":["&&","!==","||","==="],"pattern":{"anchored":true,"flags":"u","source":"^agent:([a-z][a-z0-9-]{0,63}):(.+)$"},"scopeAlternatives":["global","cardIdSchema"]}The Analyst runtime owns at most one lazily created actor and accepts no caller-selected session identity. GET /api/chat returns the configured global Analyst session_id with startup mode, internal_enabled and external_enabled; it is the sole REST chat identity/availability response and includes no transcript or transport secrets. POST /api/chat accepts exactly a strict object with required non-empty content and optional strict workspace context; success returns only tool-invocation and restart results, with no session identity field. REST content is additionally capped by schema at 1_048_576 UTF-16 code units (JavaScript string.length). REST chats.send (POST /api/chat) is the sole internal Analyst conversation submission transport, available only in internal/both mode. Direct MCP operations do not enter that conversation. The first synchronous AnalystSession.submit() admission wins. Overlap rejects immediately, never queues: REST maps the typed busy error to409 under the exact shared error contract. Failed, disposed, closed-admission, canonical-state, provider, persistence, invariant, and publication-uncertain failures are not busy. WebSocket is observation-only: inbound frames are strict live-sync subscribe/unsubscribe objects. Unsupported frames (including Analyst messages) and malformed JSON close with code 1008 and fixed reason Invalid live-sync frame, before any subscription effect or acknowledgement. Connected status contains exactly {event:'connected',timestamp,clientCount}; chat identity comes only from REST chats.get. No Analyst activity, busy/error, or turn-acknowledgement envelope is sent over WS. Generic Agent detail, conversation, current-instructions, and LLM-exchange parameters use the full shared session grammar. Invalid raw frames are rejected before subscription mutation or acknowledgement, and malformed Vue Agent route input mounts no detail, REST, or live-sync work. The single WebSocket transport caps the entire reassembled inbound message at 1 MiB (1_048_576 bytes); an oversized message closes with code 1009 before JSON parsing or subscription admission. This transport bound is separate from the REST content schema limit and explicit global REST body limit.
Agent summaries require id, agent_name, session_scope, card_id, started_at, required nullable compaction, and exactly one valid liveness pair: status:'active' with activity:'busy', or status:'inactive' with activity:'idle'. compaction is null unless that exact executing session owns current ephemeral progress; otherwise it is exactly {strategy,started_at,folds_done,fold_in_flight} with a nonnegative logical-success count. GET /api/agents derives the configured global Analyst and every active linked card's distinct compiled workflow agents. GET /api/cards/:id/agent-sessions derives only that active card's candidates. Each summary-producing operation captures one request-local set of canonical session IDs from installed autonomous owners plus an already-instantiated Analyst runtime, then decorates each durable summary by exact ID membership. Candidate admission and started_at remain canonical conversation-index facts: a live ID without a current conversation version synthesizes no inventory, and neither card lifecycle nor transcript history implies liveness. Exact retained-tombstone detail remains readable and is inactive/idle. Runtime-status payloads contain no Agent telemetry array.
Existing card/global Agent-membership freshness targets prompt authoritative rereads after process-local projection changes; the no-argument actor callback is coarse transport and does not define public transition semantics.
The conversation LLM actor separately owns nullable process-local compaction progress {strategy,startedAt,foldsDone,foldInFlight}. It begins at zero/false for each preventive, local-exact-admission, or authoritative-recovery compaction, including zero-call structural work. foldInFlight becomes true only after a fold is admitted immediately before logical invocation; validated success increments foldsDone and clears it, while a known failed logical fold clears it without increment before an eligible correction. Transient provider attempts do not increment the count. Ordinary completion, failure, cancellation, or disposal clears only current ownership. Publication-outcome uncertainty is delivered to the fatal owner first and permits no progress clear or later notification. Successful Supervisor halt first validates and clears the frozen authoritative owner graph and retains runtime invalidation, then emits one existing card-membership target for every removed owner while their absence is observable. A failed halt retains its owner graph and emits no successful-removal freshness.
Currently configured static instructions
Operator-session GET /api/agents/:id/current-instructions (agents.currentInstructions) uses the strict shared session parameter and an empty strict query: no segment, node, version or input selection. Existing operator authentication applies (401 unauthorized). The strict success DTO is:
text
{session_id, basis:'server_loaded_configuration',
scope:{kind:'global'} | {kind:'card',card_id,card_type,ownership:'active'|'retained_tombstone'},
bindings:([{kind:'global',instructions}] | [{kind:'workflow_node',node_id,instructions}, ...])}Scope matches the session identity. A global scope has exactly one global binding; a card scope has a nonempty unique-node binding list in workflow declaration order. Backend membership includes every configured matching card-session node, never a guessed historical/current node or a deduplicated first match. Exact current card selection (or retained tombstone final_card) supplies card type; retained-card scope is current template orientation, not historical instructions. Configured globals need no conversation index/segment: a lazy absent conversation remains absent. No catalog, provider evidence, app log, invocation preparation, provider/tool call or state publication is performed.
Composition reuses the loaded compiled prompt registry: global Analyst/Oversight substitute the actual formatVocabularySnippet; workflow agents substitute the actual describeNodeResultContract, including configured outcomes and record gates. Fragments and placeholders resolve before each complete string passes redactTextForOutbound. Loaded image guidance is ordinary instruction prose, not pixel materialization. No disk recompile, default-template fallback, dynamic card/tree/focus context, tool definitions, private replay, wire bodies, provider/auth/environment data, source paths, admission hashes, prompt snapshot/revision or unredacted alternative is exposed. Safe configured instruction prose is not a promise that redaction can classify arbitrary prose as confidential.
Invalid params/query return 400; absent/unconfigured scope returns 404 {error:'Agent session not found'}; an exact required card-read failure returns 503 {error:'current_state_unavailable',resource,owner_id,restart_required:true}. Unexpected render/invariant failures return ordinary safe 500, without prompt content; publication uncertainty propagates unchanged. Failure identity is {kind:'session',parameter:'id'}. Conversation/current-history DTOs, provider execution and durable contracts are unchanged. The inline disclosure reads lazily on open/reopen, refreshes explicitly, and aborts/discards on close/departure/session change. Errors do not replace the retained transcript. Held-open text is last fetched loaded configuration, not live tracking or unactivated disk edits; historical selection labels it current configuration, not that segment's instructions.
Retained conversation contracts
Current record knowledge is owner-produced, not inferred from tool JSON or prose. Successful current single-record reads record exact selection and metadata, slice or full-body exposure; accepted backing metadata can accompany an effective draft read from that same projection. Known mutation projections can record metadata without a follow-up read. Immutable historical reads never advance current knowledge. The strict latestRecordObservations required-fact array starts explicitly empty for new history and derives newest actual occurrences in source order per conversation/card/record/view, including inherited facts and retained suffix. The surviving latest occurrences determine array order: A1, B1, A2 yields B1, A2, including when A1 and B1 are inherited and A2 is in the suffix. Primary and summarizer descriptor facts preserve that order, not revision or timestamp order. The primary request places its nonempty descriptor snapshot last, after retained history and the uncovered suffix; prepared context remains first. The summarizer's separate source keeps its existing facts-before-history placement. This projection order changes no retained contract or observation meaning: the final system facts neither supersede current instructions nor confer present freshness, acceptance or write authority. Descriptors retain provenance/coordinates/time, not bodies, templates, delivery or recall claims. Actual record_prompt text uses existing declaration-based keyed or anonymous protection independently; structural record_observation descriptors cannot protect or reconstruct a prepared body. Draft selection descriptors require explicit baseline/draft version and target, including nested observations and compacted latest facts; accepted backing stays separate. Historical rendering uses recorded facts, not today's template. Missing current-format facts and unsupported old/mixed contracts fail.
One canonical durable conversation state machine owns exact session and message identity, source classification, tool call/result settlement and ordering, source rounds/segments, provider bundles, atomic compaction-prefix validation, protected prompts, required model facts, and the zero-or-one final-source unmatched-call rule. Its in-memory adapter returns immutable ValidatedConversation physical/source rows and derived durable facts. Append admission, readConversation, complete Agent transcripts, bounded read_agent_session, compaction source selection, ordinary provider source selection, Supervisor card-chain recovery, and exact selected card-session activation settlement consume this grammar or its facts. GET /api/agents/:id/conversation returns exactly {session_id,segment_id,segment_version,segment_context,entries,cursor:{segment_id,segment_version,message_id}} only after complete exact validation. segment_id is the selected immutable segment's entry_id UUID, not a generated read token. Selected conversation history returns exactly {session_id,version,entry_id,published_at,segment_context,entries}. Its ordinal selects from the present catalog; it is not an immutable identity handle. An ordinary format-12 segment has segment_context:null; each compacted version has exactly {kind,source_version,covered_through_message_id,summary_text,protected_prompts,required_model_facts,continuation} as strict separately projected context, also shared by read_agent_session. No checksum-only lineage, group counts, disposition accounting, or coverage objects are exposed. Segment identity, cursors, and navigation are unchanged. Incremental reads require segment_id, segment_version, and opaque equality token since together; omission of all three selects a complete current read. Identity or version mismatch returns 409 conversation_segment_changed with requested/current IDs and versions before any cursor lookup, even if both incarnations retain that row. A missing token within the matching segment is 400 conversation_cursor_not_found, carrying the segment ID/version. Later outward rows are selected only after complete validation, and the cursor advances over filtered provider-private rows. An empty established catalog still has no current segment or segment identity; no placeholder UUID is invented. A sole final unmatched call has no active, waiting, pending, or snapshot meaning. When a later ordinary card workflow selects that exact configured session for imminent activation, or a later explicit submission is newly admitted to the configured global Analyst session in an already-running server, the consuming owner may pair it with one permanent synthetic failed result stating exactly Prior activation ended without a recorded tool result. External or domain effects may or may not have happened. The prior call will not be replayed. The result preserves the old source input, call, tool, and persisted result-policy template bytes/hash, carries exactly {outcome_unknown:true} and evidence none, and is appended immediately before the fresh activation marker. It neither continues nor replays the old invocation and appends no recovery notice. Fresh startup recovery uses the same permanent uncertainty-only mate for configured Analyst and established Oversight before readiness. It creates no marker, ingress, submission, check or recovery notice.
Successful GET /api/agents/:id/llm-exchange returns exactly {session_id,exchange}. session_id is the selected Agent-session identity, and exchange is the strict provider-exchange projection.
Current providers and executors return only constructor-created nominal action outcomes; they do not construct wire ToolResults. One settlement authority validates the nominal token, outbound-projects/redacts the fields, creates the strict success/failure ToolResult, and returns that exact result together with its canonical JSON bytes. All failed outcomes receive no evidence. The shared LLM invocation boundary records unsupported names as unsupported_tool and malformed/schema-invalid or already-cancelled calls as rejected_before_execution without executor entry. A typed argument-validation or response-packing failure thrown after executor entry is instead an executed failed result with no evidence. A returned execution result remains executed even if cancellation arrived meanwhile; only an entered throw identical to the signal's exact cancellation reason becomes synthetic execution_failed. Publication uncertainty, missing MCP installation, unrelated I/O, canonical corruption, and every other unclassified rejection propagate unchanged.
Known external MCP discovery rejection (explicit JSON-RPC rejection, unsuccessful HTTP discovery/startup status, or premature stdio response closure) becomes an executed failed mcp_server_control(start) result with evidence none only after successful local containment and the runtime stop join. Diagnostics contain fixed stage/reason text and numeric status or finite JSON-RPC code, not remote messages. Unknown errors and failed containment remain unclassified failures; failed containment blocks replacement. Correction requires a separately authored new start call, never automatic retry, replay or rewriting the prior mate. HTTP containment does not terminate the remote server or undo its effects.
Current inspection admits get_card.id, get_tree.rootId, and optional list_cards.parent only under the exact card identity contract: project or a valid hierarchical non-root ID. Omitted parent means unrestricted discovery. Malformed IDs reject before executor entry without trimming, aliasing or normalization; a syntactically valid absent card instead enters execution and returns an ordinary failed not-found result. Successful result publication and subsequent admission permit correction in the same activation, not guaranteed continuation or retry. Strict failures in consumed canonical card heads, records or conversation indexes, unrelated I/O and publication uncertainty retain their escaping boundaries; they are not argument failures.
Planner create/edit titles reject empty or whitespace-only input in the argument schema without trimming meaningful title bytes; optional card urgency is exactly low | normal | high | critical, defaulting to normal for create. These invalid arguments settle before executor entry and do not fail the activation. Analyst create's missing dependency and depth denials, and delete's missing/root/running-subtree denials, are executed failed results with one denied audit; multi-root running admission precedes all deletions, so a blocked later root leaves earlier roots intact. The model may continue after those denials. Strict store corruption, survivor-dependency failures, and other unclassified domain errors are not generalized into denials. Successful list_agent_sessions and list_processes_tool data contain byte-packed sessions and processes pages. Successful read_runtime_events and read_runtime_errors data contain byte-packed events and errors pages whose page total is the selected newest tail and whose separate total_lines is the full matching retained line count. read_agent_session selects one byte-packed section: omitted or section:'messages' returns {session,ownership,segment_id,segment_version,section:'messages',has_segment_context,total_visible_entries,messages}, where messages.total is the selected last-N tail count; section:'context' returns the same metadata with section:'context' and a zero-or-one-item context page containing the complete current projected segment-context object, using ordinary JSON slices when necessary. Context accepts no last_n. All pages are fresh stateless observations, redact before packing, and measure the complete settled success envelope against response_bytes. Continuation positions do not confer a snapshot; consumers compare the returned exact segment identity across pages rather than trusting equal numeric versions. A failure is the one top-level {success:false,error,data?} ToolResult. Persisted result data remains opaque unknown JSON.
Each Analyst submission starts one strict source round with an exact system/activity marker payload {agent_name:<configured-analyst>,event:'activation_open',input_id,timestamp,instructions} and no card_id. Card-agent markers use the same exact fields plus card_id; both forms require the marker timestamp to equal the row timestamp and input_id to be a canonical UUID. Ordinary model-backed submissions append that marker and one operator user-text row as one ordered batch; confirmed restart appends only its marker and user confirmation row. Workspace focus is prepared context, never an ingress or compaction-source row. Previously valid marker-first histories with ordinary system-text rows remain strict-valid (including after compaction); no retained note is removed or reinterpreted. Global-agent conversations permit no preamble. Unmarked, malformed, wrong-agent/card, or mixed history fails strict ordinary reads rather than receiving an inferred boundary.
AnalystSession is a plain serial coordinator with idle | conversing | failed | disposed ownership.
An error escaping the exact invoked-tool await after its durable call and before its result rejects that submission with the original error. When the error is not publication uncertainty, the parked provider turn is successfully abandoned, and application disposal has not won, the session returns to idle with restart confirmation cleared so a later explicit submission is a fresh activation. Strict preparation/project-context failures, result/continuation publication errors, failed abandonment, and cleanup errors still poison the session; the narrow capacity exceptions below do not weaken those boundaries. Publication uncertainty remains fatal. The later submission strict-reads only its selected global session and applies the final-unmatched rule above before fresh ingress. It never automatically retries or continues the old tool. One accepted operation owns its caller deferred, tracker, abort controller, current step, and immutable acceptedOperationId; its registered tracker consumer is the only caller-delivery authority. That accepted identity marks ingress and confirmed-restart publication. Initial, tool-continuation, and plain-text-repair provider invocations each allocate a distinct fresh source ID and carry the exact canonical input in the direct LLM phase. Deferred startup rechecks exact operation authority before ingress and nested admission. Positively typed orientation mandatory-budget and prepared static-surface capacity failures are reusable pre-ingress preparation rejections, alongside the existing returned focus-budget rejection. They preserve prior restart confirmation and publish no ingress or substitute context. Invalid topology, numerical/configuration invariants and strict consuming failures remain poisoning. An initial or continuation LocalExactAdmissionError restores reusable Analyst admission only with both source:primary_local and reason:capacity, after the exact inner invocation lease, owned progress/observers and internal settlement have completed normally. The original rejection is delivered only then; restart confirmation is cleared. Already accepted ingress, tool effects, the one canonical tool result and published compaction remain durable; the rejected pass creates no fabricated primary error/evidence row and replays nothing. Each later explicit submission prepares fresh tree/focus/static context on the same retained conversation, with no automatic resend or guarantee of fit. Internal-summary admission (even typed capacity), configuration-only primary admission, wrapped summary construction, persistence, observer, cleanup, invariant, outcome-unknown and unclassified rejection retain poisoning/fatal precedence. There is no Analyst-turn cancellation authority, backend method or callback, response flag, transcript notice, fabricated tool result, or hidden generic terminal/cancellation winner. Closing an observation socket does not cancel an accepted global turn. Application teardown instead closes admission, cancels and settles an accepted Analyst operation that owns an initial or continuation provider handoff, and joins owned completion before final Conversation LLM disposal. Every returned tool call is matched without executor entry or model continuation before the submission rejects with the exact disposal reason. If disposal re-enters after ordinary ingress publication has entered, the writer finishes exactly the one two-row activation/user batch; the submission then rejects with the exact disposal reason before conversation reread or provider admission, and the outer tracker and Conversation LLM join successfully.
Server composition derives one immutable discriminated restart capability from the selected authentication policy and boot-owned restart port. Authenticated composition without that port fails; disabled authentication produces only {available:false}. That exact capability reaches runtime status, the Analyst tool/session, and REST routes. A scheduled chat restart is acknowledged only on REST response finish (reply.raw); no socket acknowledgement exists. The sole public status projector maps its discriminator to restart_server_available; no downstream consumer re-derives availability from authentication or combines a boolean with an optional port.
Restart confirmation is one move-only in-memory capability. Admission transfers it from idle to the accepted operation, reusable non-scheduling settlement restores it before delivery and returns confirmation_required, and only successful RestartPort.schedule() consumes it. Exact RESTART SERVER first publishes the accepted-operation activation and user rows in one two-row batch, rechecks authority after the whole writer/observer boundary, and then enters scheduling. If application disposal re-enters after that publication starts but before the writer returns, the two-row batch remains the sole second-submission effect, the submission rejects with the exact disposal reason, and scheduling is suppressed. If disposal instead re-enters synchronously from an already-entered RestartPort.schedule(), schedule entry wins: the port is called once, the confirmation is consumed, and the submission returns scheduled; disposal still closes later admission. Neither outcome appends fallback or cancellation rows, duplicates publication or scheduling, or implies process restart acknowledgement.
Conversation tool settlement has only caller-supplied result settlement, optional ordinary continuation, and direct owner disposal. If disposal re-enters after an ordinary tool-result writer starts, that writer appends exactly one tool_result row with ID <sourceInputId>:tool-result:<toolCallId>, role tool, the original tool name and call ID, and content equal to the JSON encoding of the caller-supplied result. The result settlement and outer Analyst submission then reject with the exact disposal reason, while internal settlement, the outer tracker, and Conversation LLM joins succeed; later admission is closed and no continuation context, conversation reread for continuation, nested provider call, fabricated failure, or cancellation text is admitted. Successful restart_server settlement without continuation has the same entered-writer ownership and a distinct public outcome: its one row has that exact identity/shape and the caller-supplied successful restart_server result, the no-continuation settlement and outer submission reject with the exact disposal reason, internal settlement and joins succeed, later admission is closed, and no restart confirmation is installed. In both cases Analyst application cleanup succeeds when its process-containment branches succeed. When cancellation suppresses a terminal handoff during terminal-error publication, the persisted terminal error and provider-exchange facts remain authoritative, no Analyst notice is appended, and the outer operation consumes the returned terminal outcome before rejecting with the exact disposal reason. When synchronous terminal handoff actually entered first, its existing terminal completion/notice policy remains authoritative. Neither path reads history or fabricates a tool settlement.
The references above to caller-supplied results describe the existing entered-writer lifecycle only; the current caller supplies a nominal action settlement, not a wire envelope. The append owner writes the settlement authority's exact canonical bytes and returns the same settled ToolResult and bytes through the LLM actor. Analyst stores that returned result only after durable append and passes it unchanged to immediate REST results, whose live projection outbound-projects arguments only. Conversation invalidations supply live observation to other clients without activity fan-out. Successful restart no-continuation settlement likewise returns the authority-created facts before confirmation handling.
Every bounded tool response is admitted against canonical bytes of the final post-outbound settled ToolResult, not an inner pre-redaction payload estimate. Current and immutable card summaries use one shared summary projector and therefore apply the same truncation and byte-fit decisions. Neither surface has a notification section.
Provider completion owns canonical conversation publication and all writer observers, mandatory provider-exchange evidence publication, synchronous enclosing-owner handoff carrying the exact terminal input, and only then direct promise delivery. Analyst handoff installs outer settlement before promise callbacks: message completion needs no second row, while provider/model issue preserves ${terminalSource}:error and evidence before the Analyst writes required ${terminalSource}:message. Disposal cannot replace entered completion or evidence, an already-entered synchronous handoff, its required notice, or their exact failure; cancellation that becomes owned during terminal publication suppresses only the not-yet-entered handoff as specified above. Canonical writer failure authorizes no read, retry, fallback, compensation, or second append. Direct LLM disposal/join retains the complete frozen tool context and concrete ChildInvocationLease, and shutdown starts all exact joins before propagating a stable failure. Card result claim still pairs the existing CardActivationOwner winner with direct continuation_closed before record publication, so Stop joins the no-continuation settlement without adding another owner.
Every autonomous, Analyst, repair, continuation, and refine-summary invocation receives a fresh opaque UUID source input generated by its owner. That per-call input identity and the invocation owner's session identity are distinct from the stable source-session identity carried by the validated provider conversation projection. Ordinary persisted actor turns require the invocation and source-session identities to be equal. For original source UUID S and provider tool-call ID T, settlement identities are ${S}:tool-call:${T} and ${S}:tool-result:${T}. Provider call IDs need not be unique across source inputs. Shared UI exchange matching uses the existing compound session/source-input/call identity, not call ID alone. Success and failure both use tool_result; the next invocation UUID is unrelated. Attempt index orders transport attempts only within one invocation.
Live sync has independent acknowledged leases for global Agents, one card's Agent sessions, one conversation, and one LLM exchange. Conversation invalidations carry exact current segment_id and displayed segment_version plus nullable final operator-visible message ID. Pending hints coalesce in accepted arrival order, including lower numeric versions. Numeric versions are not monotonic freshness: rollback followed by recompaction can publish the same ordinal with a different UUID. Message IDs have no ordering authority. Reconnect creates fresh lease generations and authoritative REST reloads.
Startup and Run recovery
After successful strict startup recovery from a dirty shutdown—a process error, kill, crash, or host failure—the stopped card chain is structurally consistent for a later explicit Run; recovery neither executes work nor guarantees semantic completeness. Malformed canonical input or failed publication may prevent server availability, and file persistence offers no guarantee against data loss. For example, the recovered state may legitimately be stale (a child completed but its parent still reflects waiting), redundant (a done card is reactivated because its completion trace was lost or its parent was not yet updated), or lossy (an invocation whose evidence never reached disk is indistinguishable from one that never happened); a duplicate activation is harmless and a missed activation is recovered by ordinary runtime flow and the named agents. The system avoids these situations but does not hard-guarantee against them.
Startup first consumes the canonical linked current-card projection and requires a nonempty project-rooted authority. Every reached head/selected ordinary document is strict; card/record documents have no torn-suffix repair. Reached tombstones are strictly consumed and terminate traversal without descendant access. Admission does not open old card predecessors: missing or malformed unconsumed card history does not block startup, but any historical request that reaches that data fails strictly. Broken current selections still block startup; successful startup is not historical certification. Traversal admits only active cards through workflow and parent/type admission: a reached retained tombstone terminates startup traversal before workflow lookup, record validation, conversation initialization, or conversation truncation for that card, and its descendants are not read. It validates active dependency existence/cycles and checks each non-root active type against its reached active parent's compiled admission. It then strictly reads the exact selected global Analyst conversation index and every distinct node-agent index derived from each admitted card's compiled workflow. Valid empty indexes are sufficient; startup creates none of these required indexes. Only after all required indexes are admitted does startup consume the event/control app log, then each required session's optional provider-exchange stream in deterministic Analyst/card-session order. It reads the selected global Oversight catalog once: if present, it strictly validates that catalog and consumes its optional evidence stream even when scheduling is disabled; if absent, it does not read Oversight evidence. Only after these reads does startup consume each required session's current conversation segment, require each declared bootstrap record head/selected acceptance, and consume each present configured record head/selected acceptance. Optional heads are validated only when present, never created. Remaining exact growing-file owners share the unchanged validated torn-suffix rule; heads/documents do not. No repair sweep, eager predecessor certification or smaller startup certificate is introduced. The selected global Analyst index is required even before its first segment or operator message. The selected Oversight index is not required or initialized at startup; actual check use retains its owner-local lazy initialization. The common generated-state gate above remains unchanged for server startup and CLI init, including all admitted card-session current bodies and evidence, even on stopped cards. It completes before Fastify transport services, Supervisor startup settlement, MCP reconciliation, or listening. Server composition additionally strictly consumes the established selected Oversight current conversation, even when scheduling is disabled. Evidence admission covers attempts across all compaction segments of the currently selected required sessions and established selected Oversight, not every historical provider writer: retained tombstoned cards, descendants below tombstones, removed card-agent assignments, and formerly selected global sessions are excluded. Exact retained-tombstone routes still validate their exact head and selected document when admitted. Startup neither discovers those excluded streams nor certifies cross-file logical-ID uniqueness. Valid settled, final-assistant-text, and other text-ended history remains byte-identical; no text position proves interruption. After lifecycle exclusion, required config/canonical admission and workflow binding, composition strictly consumes configured Analyst current history, then configured Oversight current history only if its exact index exists. Each sole valid final unmatched call receives the permanent uncertainty-only mate. Missing required Analyst state, missing indexed Oversight segment and malformed catalogs fail. Only exact optional Oversight-index absence permits a no-op; it stays absent. Complete malformed or invalid history also fails unchanged. The exact current-conversation owner may truncate an unterminated final physical suffix only after full retained-prefix validation against index, genesis, session, source metadata, and conversation semantics, at startup or ordinary use, including consumed stopped-card streams. Bounded global startup settlement appends no recovery notice, new ingress, provider evidence, or follow-up work; it neither replays nor asserts prior effects. CLI init retains its existing reads and gains no global corrective settlement. After these bounded global operations, Supervisor startup strictly selects the canonical linked running chain and settles it before usable stopped runtime, MCP reconciliation, listener/readiness, or operator admission. Startup creates no activation owner, structural wait, provider request, tool replay, or Run. Empty running-chain selection does not skip the preceding global operations. This ordering is not all-or-nothing validation: a permitted tail truncation or global mate may commit before a later topology failure. The first failure stops progression; publication uncertainty reaches the fatal boundary before any next subject or cleanup.
Startup and explicit Run use the same Supervisor selection across all canonical linked membership, proving that every linked running card belongs to one unique project-rooted chain; missing links, malformed parent identity, branching, and a running card below a non-running ancestor fail before card recovery correction writes. Earlier boot admission may already have consumed the app log or truncated a permitted tail, and fresh-startup global mates may already have committed. Every selected card is then processed as one leaf-to-root unit. Its distinct configured agent sessions stabilize in graph order, skipping action nodes; immediately after all of that card's sessions stabilize, that card is published stopped before recovery advances to its ancestor. Execution logs are not recovery inputs. Planner/Reviewer/Executor are the default named agents, not runtime roles. The one Supervisor operation is the only broad card-chain corrective-recovery owner, invoked during startup and during explicit Run after same-process Stop; it accepts only configured card-scoped sessions and consumes canonical ValidatedConversation call facts before applying its card policy. Separately, the exact consuming card-session activation owner and, in an already-running server, the configured global Analyst owner for a newly admitted explicit submission may settle only the sole strict-valid final unmatched call of the exact session selected for imminent actual use. The bounded configured-global fresh-startup operation above has the same limited call-settlement policy. Each bounded operation performs no chain selection, state classification, notice append, old continuation, or session scan. Recovery makes an explicit local visibility decision for every current message kind before classification and rejects an unsupported runtime kind before filtering or state derivation. activity and provider_private are ignored when deriving implicit state; every other current kind is recovery-visible. For an OpenAI Responses bundle, the marked visible projection alone controls text, tool, and terminal state, while the private row remains persisted for provider replay. Empty, system-prompt-only, settled-terminal, and exact settled-recovery sessions are read-only. Tool-pending sessions receive the ordinary interrupted outcome_unknown:true failed tool result and then a recovery notice; provider/text-pending sessions receive only the notice. Multiple or nonfinal unmatched calls and malformed canonical data fail directly at card-activation or Analyst-submission use, before its activation marker or provider request. Lifecycle admission is unchanged. Settlement publication uncertainty, or uncertainty while publishing the following activation, is fatal and authorizes no read, retry, second result, context append, provider request, cleanup, or reconciliation. An unfinished activate_card is always ordinary interruption: a running child is independently in the reset set, while a terminal or other non-running child is unchanged. No child result is read, formatted, reconstructed, or replayed. Ordinary read-only projections never enter bounded settlement; global fresh startup does not enter the card interruption-classification/notice path.
Every non-clean classification strictly validates the latest activation_open marker's role, card, session, and input_id. The recovery notice uses ${inputId}:model-recovered and deterministicRoundId('pre', inputId) and counts as settled only when that exact notice is the final canonical source row after the marker. A malformed association, collision, or older notice followed by newer same-activation work fails rather than reusing the deterministic ID. A fresh STOPPED activation has a fresh marker and can later receive its own distinct notice.
After all configured sessions for one selected card stabilize, stopRunning immediately publishes that card stopped, regardless of whether stabilization appended model_recovered or recognized an exact final existing notice as read-only clean conversation state. The notice never waives lifecycle settlement. The source must be running. Supervisor full-chain recovery and exact joined live-descendant interruption share this singular domain operation and its sole durable reason recovery stopped lifecycle; other literals are invalid and are not normalized. The Supervisor's startup/Run operation remains the only conversation-corrective chain orchestration owner, while urgent interruption does not invoke recovery; exact selected-session settlement at later activation use is owner-local and is not orchestration. The first stabilization or publication error ends that correction attempt without later card/session read, write, retry, rollback, or reconciliation. Ordinary startup failure may dispose already registered non-persistence resources; publication uncertainty instead reaches the non-returning fatal boundary without halt or cleanup. Stopped descendants below the remaining unique running ancestor prefix are a valid committed prefix. After known partial failure, only a genuinely new process may freshly select and settle it on startup; same-process Stop leaves its running chain for a later explicit Run. Neither path retries an uncertain result. If all cards are already stopped, startup writes no card corrections and a later Run directly selects project STOPPED; bounded configured-global settlement still applies.
On explicit Run after full settlement, activateStopped changes only project from stopped to running after the supervisor has installed its prepared owner; launch starts the configured STOPPED entry. Startup does not activate project. Descendants remain stopped until their exact live parent delegates ordinary activation. Generic mutation cannot perform running-to-stopped or stopped-to-running. There is no transaction, recovery generation, graph cursor, old-node inference, or atomicity guarantee.
Notifications and reviewer arbitration
The durable notification target is one card_id, and queue_notification is the only public agent-facing notification tool. It requires card_id, kind, body, and exact lowercase urgency:'normal'|'urgent'; roles and session IDs are not targets and urgency is not stored in CardNotification. The card type's configured designated recipient receives context through its card-scoped session regardless of which agent runs the current node.
The exact denial outcomes are missing {queued:false,reason:'missing_card',card_id}, persisted terminal {queued:false,reason:'terminal_card',card_id,status:'done'|'failed'|'cancelled'}, closed current activation {queued:false,reason:'activation_closed',card_id}, and Oversight planning-ineligible {queued:false,reason:'planning_ineligible',card_id}. Confirmed success is {queued:true,card_id,notification_id,body,interruption}; body is the submitting call's own outbound-redacted text, not a queue read. Interruption is not_requested, not_applicable, interrupted with exact completed stopped_card_ids, or suppressed for cancellation/runtime ineligibility/stale ownership with exact completed-prefix stopped_card_ids. The closed result carries no winner discriminator because durable status may still be running. Saivage neither retries nor redirects that invocation. Analyst notification and non-record write/edit use runtime cancellation admission rather than intervention readiness; cancel also has no intervention-readiness requirement. Create, reorder, reopen, delete, reconfigure, and record mutation retain their operation-specific intervention readiness and audited denials.
Successful queueing acknowledges durable enqueue, not delivery. Only a node run by the designated recipient uses the append-before-remove delivery path at entry, ordinary tool continuation, applicable plain-text correction, and same-node emit_result arbitration. Recipient entry appends selected bodies before exact selected-ID removal. At an otherwise accepted recipient terminal candidate, a non-empty ordered pending set defeats the candidate without claim: append the paired failed result with reason pending_notifications, append exactly those bodies in order, append the resolved correction and reconsider instruction, then remove exactly the selected IDs only after all appends succeed. Append failure removes nothing; a crash after append and before removal may duplicate visible context.
Expected owned cancellation during urgent interruption, including an internal summary, waits for actual provider evidence publication and actor joins before returning the existing queue receipt and interruption outcome. It produces the matching Analyst tool result and normal audit outcome, not a second enqueue. A distinct provider, recorder or evidence failure remains a genuine failure even when cancellation races it: a failed request can follow a committed enqueue and is not proof of no queue effect. There is no universal post-enqueue partial-result protocol or repair of old unmatched calls. Publication uncertainty remains fatal before result/audit construction, stopped publication or any follow-up.
Urgent submission selects exact committed ancestors and captures the nearest eligible real running owner, node ordinal, current child leases and installed active suffix before enqueue. It appends the target first and eligible path notices bottom-up, skipping DONE, FAILED, CANCELLED and BLOCKED only for synthesized ancestor notices; each notice names the original descendant via the immediate child. After known appends, while runtime admission is running, the Supervisor revalidates and claims the exact complete scope before cancelling. Conflicts suppress without stealing another winner. The surviving boundary's old node has no continuation; its synchronous consumer emits the compiled interruption event and installs a guarded recipient task, which cannot prepare LLM/session/records or dispatch before the Supervisor joins the old node and descendants. The boundary stays running and retains its original parent direction. Stopped descendants publish leaf-to-root; each released child lease delivers {status:'stopped',summary}, exposed by activate_card as a matched failed result with outcome:'stopped'. When project itself is targeted the joined old root also publishes stopped and is replaced inside the same run through a fresh STOPPED activation; there is no parent lease or second Run. Public notification callers are global, not members of that card-owner chain. Stop/application halt owns takeover and complete containment; publication uncertainty is fatal before follow-up. Without an eligible active owner only selected enqueues occur; no activation or Run is created.
A nonrecipient nonterminal edge accepts without inspecting or delivering the queue. At a nonrecipient DONE edge, all ordinary acceptance gates run first; the final synchronous queue decision either claims the ordinary terminal route when empty or follows the required conditional edge while retaining accepted evidence when nonempty. The recipient handler owns later append-before-remove delivery and must traverse its configured path to a terminal. Shipped goal workflows route accepted review with pending context through Planner handle-notifications and then review again; project review always proceeds nonterminally to Planner assess-completion, whose recipient admission handles pending context. Classic-typed architecture routes accepted system review to Executor draft and requires component and system review again. There is no every-later-arrival guarantee: configured BLOCKED/FAILED, runtime refusal/failure, and cancellation may clear admitted notifications without recipient delivery. After the result or cancellation winner claim, later attempts receive activation_closed and create no enqueue version.
Public queue operations belong only to configured global Analyst/Oversight. Internal queue evidence remains private; historical Planner calls remain unchanged recorded facts, not supported current invocations.
Ordinary card query surfaces provide no explicit queue collection, count, membership, IDs/bodies, availability field, direction discriminator, or delivery receipt. Generic version/time/diff/invalidation observations may signal or support inference of hidden queue activity and are not a supported queue query. Explicit enqueue attempts/results, delivered context, and opaque retained conversation evidence remain visible through their existing contracts.
Pause does not interrupt these synchronous terminal sections; continuation parks only if it later reaches the single provider-admission frontier.
Explicit image observations
Image metadata, a selected recorded PNG and a fresh source file are different evidence. Only the positive typed result occurrence selects recorded pixels; recording never proves provider delivery or perception.
view_image({path,max_dimension?}) inspects one explicit PNG/JPEG in an ordinary project-relative path, project:///, work:///, or the caller's own-card tmp:/// namespace. Record/system URLs, directories, outside-resolved paths, secret/hidden/internal/attic paths remain refused by workspace admission. read remains text/directory-only; commands mentioning image paths attach nothing. Custom named agents opt in rather than inheriting capabilities from their names.
Omission selects longest-side 1600; the sole override is integer 1..16384 or 'original'. EXIF rotation/reflection precedes aspect-preserving Lanczos3 inside resize without enlargement or JPEG shrink-on-load. Conversion is sRGB full-colour PNG, preserves alpha and strips metadata; no crop, padding, flattening, palette quantization, source modification or conversion retry. Original bypasses only local spatial reduction, not normalization, resource bounds or provider processing. Source admission is at most 32 MiB, one frame, and 40,000,000 decoded pixels; selected PNG is at most 16 MiB. Animation (including APNG), fake/truncated input and hard-limit excess fail once with bounded guidance. A tall image is not refused solely for exceeding 8K/16K on one side. Illegible text needs a focused screenshot or larger local override, not invented interpretation. Screenshots must be non-secret: text redaction cannot certify or remove pixel secrets.
The common successful tool result has optional nonempty ordered content of strict {type:'text',text:string} and {type:'image',image:<descriptor>} blocks. The removed singular image field is rejected. Each explicit image selects an immutable conversation-owned PNG through a strict {id,mime_type:'image/png',width,height,byte_length,sha256} descriptor. The common typed envelope is producer-neutral: only successful executed results can carry explicit images, with all canonical exchange/policy/evidence/hash commitments intact. Every image occurrence retains its content order and exact source session; ordinary data has no attachment authority. Failed and non-executed results cannot select images. view_image emits one image block and native MCP supplies ordered native images through the same descriptor contract. No arbitrary data.image/image_url promotion exists. view_image strict safe data records source_path, raw source_dimensions, upright oriented_dimensions, selected sent_dimensions, orientation_applied, spatial-only resized, actual sent/upright scale:{x,y} and selected max_dimension. Descriptor dimensions equal sent dimensions. Snapshot publication precedes the ordinary single result append. Unselected artifacts remain ignored forever. There is no image index, deduplication, GC or lossless guarantee. Reopen/continuation uses selected bytes even after source overwrite/deletion. Actual binary use validates exact length/hash/decoded dimensions; missing/corrupt selected bytes fail strictly without original-path recovery. Metadata readers consume only descriptors, not an image inventory. Cancellation after asynchronous conversion prevents publication; committed-but-unselected snapshots are not deleted. Publication uncertainty retains the fatal boundary, and tool effects are never replayed.
Responses and Codex emit one native function_call_output with the originating call_id: ordinary settled metadata as input_text, followed by every text and selected PNG input_image part in content order, without an extra user utterance. PNG data URLs appear only in image parts, without detail (provider default auto). Public Responses remains JSON/stream:false; Codex remains SSE/stream:true. Chat image candidates reject before dispatch without downgrading pixels, while text-only Chat remains supported. Both exact current Sol/Astra identities support images on compatible transports; their Chat tool routes are incompatible. Image-enabled surfaces and fixed summary routes require image capability; retained observations require it at use even after tool removal.
For each emitted image occurrence local reservation is 2 * ceil(sentWidth/32) * ceil(sentHeight/32). Final admission adds those reservations to ceil(UTF-8 bytes of complete serialized body / 4), including base64. A separate 32 MiB image-bearing serialized-wire cap is a size obstruction eligible for ordinary bounded local compaction. Text-only estimates are unchanged. This deterministic best-effort heuristic is neither a billing formula nor a proven fit bound; Sol's official formula is unverified. These models document 1,050,000 context, 922,000 maximum input and 128,000 maximum output; configure existing budgets conservatively, as there is no independent maximum-input axis. Hash/admission/dispatch bind exact retained bytes; no subsequent file read or pixel injection occurs after admission.
Call/result/all content blocks form one atomic summary source component. Synthetic summary/refine material keeps its distinct ordered multimodal user-message path. Summary folds consume actual source-session snapshots, never the internal summary session's paths, and cannot split/duplicate an image to fit. Covered images become lossy summary prose; later compactions consume inherited summary/current tail, never predecessor images. Protected instructions remain text and images acquire no permanent protection. Compaction source/token estimates add image wire weight plus raster reservation. Local-exact qualification/ranking uses textual tuple bytes plus, per image, encodedPartBytes + 4*rasterReservation, where encodedPartBytes is JSON image-part framing plus data-URL prefix plus 4*ceil(byte_length/3). It requires strict accounting- byte reduction and ranks smallest, tying toward the deeper cutoff. Preventive requires strict estimated-token reduction/hard ceiling and ranks smallest estimated tokens; authoritative takes the first strict token reduction. Descriptor-only fingerprints exclude transient data URLs/buffers, so materialization does not alter freshness.
Public conversation JSON/websocket/copy/evidence expose bounded metadata only, not payloads or internal physical image paths. Provider HTTP errors are classified from structured markers but image-bearing response-body previews are suppressed to prevent echoed pixels. Tool success means image snapshot recorded, not that a model saw it.
Authenticated GET /api/agents/:id/conversation/images admits the owning configured session and exact indexed segment using segment_version, segment_id, message_id, zero-based content_index and image_id. Only a successful executed public result's typed image occurrence selects pixels. Source attribution comes from canonical conversation state. Exact selected PNG length/hash/full decode/dimensions are checked; the byte-identical PNG is returned with no-store/nosniff. Invalid locators are 400, absent selections 404, segment UUID conflicts 409 and broken consumed referenced state bounded unavailable. No other images, originals, predecessors or private replay are fallback authority. This capability is producer-neutral, including view_image and ordered native MCP images; producer metadata only enriches labels.
The common caller-local Dialog defaults to no-upscale Fit and offers 1:1 CSS-pixel inspection, same-result Previous/Next, keyboard/focus return, exact-locator Refresh and explicit loading/error states. It preserves conversation history/scroll/follow without visit storage and labels pixels Image shown to model, with the recorded model-input/not-delivery qualifier. Files uses the same viewer, Current source: files.content discriminates decoded static PNG/JPEG/WebP metadata, while authenticated GET /api/files/image?path=... freshly admits/decodes current ordinary bytes by content and returns them unmodified. Displayed source dimensions describe fetched bytes. Limits are 32 MiB/40M source pixels, with unsupported/animated/corrupt input refused. Both Files operations preserve pre-read lexical/resolved-path/card boundaries; config/auth/private/conversation/attic aliases never become raw binary access. Text/JSON/Markdown redaction/copy remain unchanged. Bearer headers, bounded JSON failures, abort/late-response handling and viewer-local revoked object URLs apply; no token URL, cache, persisted derivative or durable-format change is introduced.
Activation admission, outcomes and cancellation
Activation owner model
SupervisorRuntimeApi owns one private activationOwners map and one status field. It alone coordinates structure, current card, run identity/status, status-derived intervention readiness, Pause, terminal winners and halt. Status is internal uninitialized until startup succeeds; there is no second readiness flag. Each plain CardActivationOwner has phase prepared_root|child_admission|active|settling and winner open|result|cancel. One nullable halt freezes exact owners, one shared RuntimeStoppedInterruption and promise. CardProcess remains a micro-actor; direct Conversation LLM is a provider/tool state machine without BaseActor/event queue/lifecycle settlement. Concrete CardService remains the singular card reader and strict schema owner.
Live LLM calls carry the complete frozen toolInvocationContext: exact session/ source/call/tool identity, external/process waits and mandatory child reservation. invokeToolForLlm requires it before the optional signal; lower-level non-LLM invokeTool may omit it. Analyst parses arguments first and carries the same context but has neither activate capability nor Planner child-control port.
Planner activation/cancel/reopen use a port bound to the exact parent owner. The provider does schema/immediate-child checks; Supervisor checks owner before target I/O. Fresh child admission installs owner, relationship and lease before running publication; currentness stays parent until success. Exact same-parent/ same-lease active or settling joins require no I/O. Natural result claims before publication, joins work, removes structure/restores currentness/releases lease, invalidates, then delivers. Publication uncertainty exits before all of these follow-ups, not an ordinary halt or operational failure.
Run preparation installs prepared root/run/current root/one-shot launch token and starting status before recovery or running publication. Launch consumes that authority and opens execution. Application admission closes permanently only at application shutdown. Natural release from running/pausing/paused completes the gate while clearing owner/run/currentness and publishing stopped. Stop, App close and ordinary actor-main failure share the one halt: success stopped admits intervention; failure error rejects it. No publication-unknown path enters halt.
Controlled interruption
Controlled interruption recognizes only the supplied aborted signal's exact reason, including owned internal-summary cancellation. Settled attempts remain evidence even between attempts or during retry wait; cancellation creates no attempt and no retry/recovery handoff. Summary evidence must publish before cancellation reaches settlement/join. Distinct concurrent failure is not relabeled; recorder/projection failure retains precedence. Publication uncertainty is fatal before cleanup/stopped publication. Aborting alone never establishes successful join.
Dependency and owner admission
Planner activate_card admission reads the target's current declared depends_on list and requires every dependency's current durable card status to be exactly done; every other status rejects in declared dependency order. It then admits status before child actor lookup: running may join its retained activation, and a non-running status is activatable only when cardProcessEntryForStatus selects its lifecycle entry. A missing target retains the ordinary not-found result, while malformed canonical state or an impossible missing declared dependency fails strictly. An empty dependency list is vacuously admitted. These are request-time admission rules only: they do not schedule dependencies, propagate status, cancel work, or continuously enforce completion after admission.
For a dependency-rejected or non-activatable request, the reserved lease is rejected before owner or processor construction, running publication, relationship/currentness change, or execution work. A durably running child without its exact owner is an invariant failure and is never reconstructed.
The supervisor coordinates every non-cancellation activation publication through the exact owner. Terminal lifecycle publication still requires running source and one complete strict done, blocked, or failed target. Processors return one final outcome only after required record/tool work and cleanup.
For a non-cancellation result, winner/settling selection precedes publication. Publication and local joins precede one transition that removes the exact owner and relationship, restores parent currentness, and releases the lease. Invalidation precedes deferred outcome delivery and caller continuation.
If terminal publication becomes outcome-unknown at its direct primitive, a later genuinely new process may observe either the prior version or the newly canonical terminal version. The failing process does not retry, reread, reconcile, write again, report the terminal result, naturally release ownership, or freeze/halt the graph. It emits the fixed stderr diagnostic and exits. After dead-owner verification and manual lock repair, a genuinely new server startup accepts either valid durable prefix through ordinary full-chain settlement before availability; a later explicit Run begins execution.
Cancellation and reopening authority
The installed runtime's exact live-card map is the only resolver for running cancellation. If a target or cancellable descendant is live, the request routes to those exact actors. A running card with missing or duplicate live ownership is impossible and fails fast; no direct write fallback exists.
The supervisor synchronously claims cancellation versus result on the exact owner suffix before outward work. Cancellation remains a distinct publication authority, publishes deepest-first, preserves done descendants, settles once, and removes ownership only through the checked one-map transition.
Cancellation has its own status rule: every status except done and cancelled is cancellable. This rule is separate from activation settlement, dependency completion, and descendant completion. Planner cancellation checks immediate-child identity and delegates owner-first through the exact parent port without target/status/subtree reads. Analyst cancellation rejects the root and preflights the complete requested subtree; any done or cancelled member rejects the entire request before cancellation, and the operation may reach the runtime port while running/pausing/paused. Supervisor remains the lifecycle owner: it claims active suffixes, publishes deepest-first, skips and preserves noncancellable stored descendants, rejects a noncancellable requested stored target, and fails on durable running state without an owner. Direct domain cancellation is for verified nonrunning/stopped work under lifecycle-lock and permission admission.
Analyst record mutation has a separate exhaustive analystRecordEditEffect: backlog, changed, running, and stopped preserve their status; blocked, done, and failed reopen to changed; cancelled rejects. For changed, stopped, and reopening targets, record-specific propagation shares one target-inclusive path through the first running card (inclusive), or through root if none is running. Only encountered blocked, done, or failed cards transition to changed; already-changed targets and ancestors receive no repeated changed transition. It attempts an ordinary change notification to the edited target and each ancestor whose compiled permittedChildTypes is nonempty, including the first running ancestor when child-capable, then stops there. Cancelled ancestors are not reopened; the runtime notification owner retains authority to refuse notification. Each genuinely accepted edit or whole-record write may add its own fresh advisory notice, even when earlier notices remain queued. Per-call recipients remain unique, with no cross-call deduplication, retry, delivery acknowledgement, or guaranteed delivery/action. Preserving lifecycle does not suppress record versions or notification card versions. Propagation creates no activation and retains ordinary refusal and partial-report semantics. Backlog still notifies only child-capable ancestors, not the edited target; running still notifies only the target. Analyst mutation owners supply a direct edit summary to generic or record-specific changed propagation. After lifecycle effects, propagation redacts and bounds that summary to 1000 characters and constructs Card changed: … notices with fresh UUIDs, timestamps, and source: 'card_changed'. Stored notification source strings remain opaque historical provenance; earlier source values remain readable and are not reinterpreted as current instructions or a separate correction API. Changed does not relax conjunctive record admission: stopped or settled-paused runtime intervention readiness, exact linked target, writer glob, every operation-required tool, applicable card-agent scope, and no existing workflow draft for Analyst mutations remain required. Cancelled targets and open workflow drafts remain denied without stealing or discarding the draft. The first-class Analyst reopen_card mutation is admitted only while intervention-ready for an existing target whose current status is exactly blocked, done, or failed. It edits no content and delegates target-to-ancestor reopening and notification to normal changed propagation. The card-scoped Planner reopen_card instead accepts {card_id} and delegates through the exact captured PlannerChildControlPort. Before target I/O, the Supervisor requires that captured parent activation to remain its active/open owner with no halt, stop, cancellation, result, settling, or application-close fence. It then requires an ownerless exact immediate child whose status is exactly done or failed. Parent, root, grandchild, other-branch, missing, blocked, changed, backlog, running, stopped, and cancelled targets reject before effects. After complete admission, the Supervisor uses that parent owner as the sole authority for one existing publish(parent, () => CardService.setStatus(childCardId, 'changed')) boundary. Success returns only {card_id,status:'changed'} and appends exactly one child status version; it clears only the current lifecycle result, error, and completion through the existing changed effect, preserves pending notifications and all other card fields, and leaves immutable historical versions and accepted records readable. It creates no child owner, parent or ancestor version, changed propagation, notification, dispatch, control-action audit, record rewrite, retry, or automatic activation. A repeated call sees changed and rejects without another append; a later genuinely new done or failed completion may be reopened again. Expected admission denials are ordinary tool failures and do not initiate a runtime halt. Publication outcome unknown follows the existing immediate fatal boundary and is never converted to an operational result. Any other publication failure begins the existing publication_failure halt and the Planner receives the retained stopped interruption rather than success or a recoverable tool failure; a halt raised during publication behaves the same. No path rereads the outcome, retries, compensates, cleans up, or fabricates history. Immediate-child Planner edit_card is independent: an effective backlog/changed/stopped delta edits in place; an effective blocked/failed delta publishes changed then metadata; running/done/cancelled reject; and an empty effective patch publishes nothing and never reopens.
Runtime controls and application containment
Run owns project execution; Pause gates future autonomous provider admission; Resume continues that same Run. Project Stop contains execution without disposing the server or MCP listeners. Application close/restart closes all application admissions and joins registered cleanup. None replays prior tools.
Run and cooperative Pause
Runtime lifecycle is process-local and is held in one Supervisor status field. It begins at internal uninitialized; successful Supervisor startup establishes the initial empty ownership/gate state, strictly selects the linked running chain, settles configured card sessions and stopped lifecycles leaf-to-root, and only then publishes stopped. A failed startup attempt leaves the field uninitialized, public status reads fail fast, and Analyst intervention rejects. uninitialized is not a public runtime-status or response value. After startup, the field contains exactly stopped | starting | running | pausing | paused | closing | error: only stopped and settled paused admit Analyst intervention, while every other status rejects. This process-local mutation-admission rule is distinct from the public server readiness probe at GET /health/ready.
The supervisor owns the complete Run sequence. Preparation completely validates the current linked running set as one project-rooted chain, installs the project owner, run identity, current root, and opaque one-shot launch authority as starting, then uses the same Supervisor settlement loop for each selected card leaf-to-root with per-operation prepared-owner authority checks. This remains necessary after same-process Stop, which contains execution but leaves durable running lifecycles. Only after the full reset does it activate project through STOPPED. Launch consumes that exact authority, rechecks owner and application admissibility, publishes running, opens the gate, and activates configured-node execution; only then may Run return the same authoritative runtime-state projection as /api/state. A preparation or launch failure returns no successful Run result and never manufactures a second launch authority.
The non-null runtime state is built on demand and contains exactly the public projection of Supervisor status, fixed project identity, lifecycle-lock PID/start time, current_card_id, and the read observation updated_at. The lifecycle identity is injected from the acquired lock handle and remains stable for the server process; only updated_at changes with observation. The supervisor owns no runtime-state or intervention-readiness cache: it directly mutates lifecycle status and gate state for Run, Pause, Resume, and Stop, and its read-only intervention facet checks that same current status at invocation. Pause sets one request flag. Only autonomous-card provider entry observes RuntimeGate.waitUntilOpen() at the single cooperative Pause frontier immediately before provider transport. Global Analyst and Oversight entry has no card gate; invocation currentness and cancellation still apply. Pause does not interrupt already-admitted external, tool, or process work; that work may finish, and its continuation parks only when it reaches the next provider admission. At most one frontier parks, and Resume clears and schedules it once. The Analyst pause_runtime tool admits only running, and resume_runtime admits only settled paused; every wrong public status is an ordinary failed tool result with runtime_status, including guidance to use start_project when stopped. The model may therefore choose another action in the same turn. These tool-boundary checks do not weaken the Supervisor's synchronous Run-identity/currentness authority or convert its invariant/publication failures. Analyst stop_project has no status precheck: it always delegates to the Supervisor so stopped/no-live remains {status:'stopped',contained:false}, live or closing callers join the one halt, and a retained halt failure remains the same rejection. Runtime state and compact status contain neither internal uninitialized nor synthetic counters, tick values, active-work classifier, actor diagnostics, phase, caller, or session telemetry.
The supervisor's inline current leaf is the sole current-card authority. Child entry publishes that child only after owner/relationship installation and successful running publication; child settlement restores the immediate parent before continuation. Pause and closing retain currentness. Natural root fulfillment first proves no process-local owned child remains, then before any terminal root publication invokes the complete durable selector exactly once and requires the result to be exactly [project]. Missing, branching, discontinuous, empty, or additional-running-card topology rejects the outcome while root bytes, version, and running status remain unchanged. Successful publication and join then clear owner/run/currentness and the old Pause callback while publishing process-local stopped; there is no post-publication chain reread or separate readiness value.
REST Pause, Resume, and stop_project are bodyless operations. Absence is their only accepted request shape; every supplied payload, including {} and null, receives that route's local 400 validation response before runtime mutation. restart_server is the only operation in this group with a request body and accepts exactly the strict JSON object {confirmation:'RESTART SERVER'}.
Startup correction starts no work. Every subsequent Run starts only project through the configured STOPPED entry when recovery has settled interrupted cards. It installs no descendant owners or old ancestor waits. Any later legitimate activation constructs a fresh plain CardActivationOwner and ready CardProcessActor rather than reusing settled process-local ownership.
Project Stop and the singular halt
stop_project is a non-domain, restartable project-runtime halt. Starting/running/pausing/paused synchronously installs the single intervention-rejecting closing status, closes the gate, revokes prepared launch and parent admission, freezes the exact owner map, and creates one shared RuntimeStoppedInterruption. Stop never initiates domain cancellation and never itself writes a card, root result, child ToolResult, provider round, or Stop-caused durable event. Before any await, every admitted or settling child lease becomes terminally interrupted, every owner settlement rejects, and every owner is aborted and disposed. This intentionally abandons a near-terminal result or cancellation that had not completed before the freeze; its durable publication may already exist or may be absent. Exact authority fences prevent every late continuation from publishing, activating, or naturally releasing after freeze.
Only after synchronous settlement does the halt start every memoized processor join and one runtime-process-root termination. Actor-main invariant failure synchronously notifies its exact current owner and starts or joins this same halt; it creates no ordinary card result and no second halt or App path. Supervisor processor activation observes both activation rejection and either path's subsequent result-settlement rejection. A current-owner settlement-phase invariant rejection starts or joins this same singular runtime_failure halt and retains the original invariant error as that owner's halt and settlement failure evidence. A stale observer with a remaining live run or retained halt still starts or joins containment as applicable without attributing its obsolete error to a current owner; a stale observer finding neither run nor halt has nothing left to contain and returns without effects. Publication-outcome-unknown activation rejection retains its distinct immediate fatal boundary. Containment failure selection is fixed structural order, independent of completion timing, while every lane is observed: CardProcess pre-join cleanup, frozen LLM joins, tracker join, lifecycle failure; then Supervisor synchronous owner/action containment, frozen processor joins, process-root termination, and final halt-state transition. The first failing slot wins without wrapping, so a cleanup error may be retained even though the exact actor error was logged and rejected locally. Complete success verifies the identical frozen graph, completes the gate, clears ownership/run/currentness, publishes stopped, and maps every joining Stop to {status:'stopped',contained:true}; the status-derived facet then admits intervention. Any interruption/join/termination or actor-main failure retains the halt and graph, publishes error, and rejects both intervention and every later Stop with the same settled failure; Run/Pause/Resume remain unavailable and service restart is required. With no live run or halt Stop returns {status:'stopped',contained:false}. A later Run after successful halt resets any canonical running chain and activates project through STOPPED. Outcome-unknown publication remains the distinct policy specified above.
Application close and component process ownership
The App terminal coordinator is separate and deliberately best effort. App.stop(), SIGINT/SIGTERM, startup failure, and acknowledged authenticated restart_server converge on the sole coordinator in src/boot/app.ts; no entry or cleanup leaf calls stopProject or cancellation. Its runtime admission closer permanently closes application admission and synchronously starts or joins the same supervisor halt before the coordinator's first await; the runtime cleanup leaf awaits that exact operation. It individually catches every closer, then attempts cleanup leaves sequentially in reverse registration order, continuing after rejection or timeout. Registered runtime, Analyst, external Analyst, Oversight, MCP, process and transport owners coexist with LiveSync, Fastify, subscription and lifecycle-lock cleanup. The coordinator bounds each registered leaf, not just a fixed three-component list.
Server composition creates the one shared process registry, allocates exact disjoint runtime, Analyst, and MCP root scopes beneath its registry root, and creates the one shared runner before composing component mechanics. The registry alone owns group/scope truth and exact scope/category authorization. The runner owns no component roots; its declared public API exposes no registry/root object reference or undeclared broad access and intentionally exposes only narrow registry-mediated launch, lifecycle, and scope operations. Exact scope/category admission constrains supported ordinary calls and is not containment of the trusted root-capable agent. Each exact root is injected only into its owner. Composition registers runtime, Analyst, Oversight, process and transport cleanup, including LiveSync disposal, before fallible startup settlement. After generated-state admission and bound-workflow construction, composition performs bounded configured-global settlement immediately before Supervisor startup. Supervisor settles the interrupted card chain while uninitialized, then composition reconciles the exact startup-selected persisted MCP configuration to convergence and installs that manager exactly once behind the required narrow invocation port before routes and listening. Rejected or non-converged MCP reconciliation does not install MCP or expose transport; it may occur after known committed card correction. Ordinary startup failures use the App terminal coordinator and registered reverse-order component cleanup without retry or desired-config rollback; publication uncertainty reaches the fatal boundary before cleanup.
Application shutdown cleanup terminates and joins current component-owned work without becoming card recovery or graph-position authority.
Executor nodes, Analyst sessions, and MCP revisions allocate one exact direct scope under their injected component root. Their lifecycle uses exact direct close-and-contain, not close followed by tree selection. Closing an empty direct scope retires it synchronously. Closing a nonempty scope blocks new launch and retains it only until the last exact member has positive absence. The first positive absence marks the captured record confirmed, removes its registry and direct-scope memberships, retires an empty closed direct scope, calls its absence effect once, and resolves settlement once. A concurrent observer of that same captured record idempotently uses its already-confirmed absence; it does not repeat membership removal or absence delivery. Unverifiable groups remain retained. Unverifiable groups and their closed scopes remain retained. Lost group authority (including EPERM or failed signal dispatch) is sticky. The registry notifies the exact launch once with its first diagnostic; later exit, apparent absence or repeated cleanup never authorizes another numeric-PGID probe or signal. The runner exposes unavailable, not running or completed, and promptly rejects outstanding and new waits without joining output drains. The presentation stays inspectable until server disposal; retirement consumes only exited/failed/killed entries. No ended time or successful terminal outcome is fabricated. Failed cleanup still blocks Run and owner replacement.
Current process evidence independently records group tracked/absent/unverifiable (tracked is not certified liveness), its first diagnostic, actual leader exit code/signal/time, and first leader error/time. Child error is not exit -1. Each stdout/stderr records open, EOF, or close without EOF; interactive streams are not_captured by the runner. First capture-error diagnostics are independent: EOF or an open stream can coexist with capture failure. Later actual events enrich copied snapshots without restoring authority or successful completion. Known capture errors retain their original rejection precedence; later errors remain visible to new waits. No uncertainty-driven stream destruction or drain timeout is added.
Unavailable command tools return the existing bounded redacted failed result without successful data, identifying the process and known facts and directing operator service-level intervention, never ownership takeover. Unavailable wait rejection does not trigger automatic kill. Strict successful command status/payload remains unchanged. API and successful list_processes_tool queries share one redacted evidence projection; query success is not process success. Optional status filtering admits unavailable, complete/sliced pages remain byte bounded, and get_status.runningProcesses counts only running. Retained list data remains a generic recorded observation: omitted old evidence stays unrecorded; canonical readers/current-primary composition never reinterpret it through today's required API schema. There is no scope/group scan, tombstone, historical-ID set, retry, or repair path.
Process presentation normally has one owner-visible lifetime: launch, live or unconsumed result, final owner consumption, then retirement. Foreground run_command, terminal wait_process, and confirmed kill_process copy the terminal record and form their result before retiring it; terminal capture failure is likewise consumed and then reported unchanged. wait:false, a running timeout_ms:0 inspection, and a timed-out running wait do not retire the presentation: that outstanding result remains available for a later wait or kill. Closing the exact direct scope is the final consumer for all residual terminal presentations and joins every eligible settlement even when another rejects, retires eligible entries, and then propagates the first settlement rejection. Scope-tree containment joins confirmed group absence and both readable drains for exactly its successfully stopped selections, retires those presentations, then propagates the first ordinary terminal-settlement rejection. Failed or unconfirmed groups, unrelated scopes/categories, and previously naturally completed presentations outside that selection remain present. Already-entered waits and kills retain their captured terminal record and output despite retirement; publication uncertainty is fatal without retirement or follow-up.
Stdio MCP consumes each normally settled launch's terminal record or capture error in its terminal observer, retains the corresponding stopped/error MCP projection, clears the handle and caches, and retires that launch without waiting for revision-scope teardown. Unavailable rejection instead closes open admission through existing transport closure, clears readiness/catalogs and retains the failed handle/launch. A genuine capture error plus unavailable evidence takes the same retention branch, not successful containment. Intentional closure or failed direct containment may win the observer race; failed-containment precedence and retained ownership remain. The observer never awaits its own public stop join. Permanent runtime-admission/handle/caller fences, connection-owned roots lifetime, classified discovery settlement and correlated native-response telemetry remain unchanged. Publication uncertainty escapes before inspection, closure or cleanup. Process lists are current-lifetime snapshots, not execution history; after final consumption the process ID is unknown to wait/kill and absent from lists. There is no fixed bound on outstanding jobs or unavailable entries. Returned canonical work:/// output URLs remain independently readable subject to ordinary Files admission and file availability; retirement neither deletes output nor promises retention.
Each process-owning component leaf closes admission/revokes callbacks and starts exact root termination before any await, then awaits it with its actor/session/operation joins. A fulfilled ProcessStopReport is successful only when failed is empty. Runtime project Stop and App runtime cleanup share the supervisor halt's one runtime-root termination rather than overlapping it. MCP retains its unrelated direct containment mechanics. Analyst application cleanup synchronously starts its retained session's exact direct containment, then exact Analyst-root containment, then joins the session; root rejection has precedence, otherwise any failed root report or rejected direct/session settlement fails Analyst cleanup. A timed-out component may continue only in its root while later disjoint-root leaves run.
MCP manager admission closure is the manager-level invocation fence. Its synchronous prefix rejects every later manager invocation before runtime or transport delegation, closes every retained MCP runtime, and starts or retrieves each runtime's one exact direct-containment promise during the App admission phase before cleanup leaves. MCP cleanup reuses those same runtime-owned settlements, starts exact MCP-root containment only after obtaining them, and joins all retained runtime stops (including admitted inner operations), root containment, and current reconciliation. A rejected root containment is rethrown unchanged; otherwise a failed root report or any rejected direct/reconciliation settlement fails MCP cleanup. The runtime collection clears only after complete success.
Command and workspace tools
Commands, workspace reads/writes and Webfetch have distinct admission and evidence contracts. Shell capability does not broaden supported Files, record or image APIs.
Command environment
The shared positive-allowlist command environment inherits exactly PATH, HOME, USER, LANG, TERM, every LC_* locale name, and the four optional Git identity names GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, GIT_COMMITTER_NAME, and GIT_COMMITTER_EMAIL from the Saivage process. Supplied values pass through verbatim, including empty strings; absent names remain absent. Saivage supplies no Git identity, performs no identity precheck, and leaves validation to Git at use. Every other inherited name is excluded, including undeclared Saivage, provider, and deployment variables, arbitrary unknown names, GIT_CONFIG and related configuration overrides, GIT_SSH, and GIT_SSH_COMMAND; there is no GIT_* prefix admission.
Ordinary run_command children start from that shared environment, then receive the runner's explicit project locators and any explicit per-command environment overlay. Stdio MCP children also start from the same shared environment, followed by that server entry's exact configured env overlay. Explicit configured overlays are distinct from inherited environment policy, are not filtered by secret-like key names, and win on duplicate names. Streamable HTTP MCP does not launch a child process.
Operator YAML and the exact lazy .saivage/config/mcp-servers.json connection-definition file are the two MCP server-entry authorities. Duplicate names fail, without precedence. Dynamic definitions are configuration preserved by reset and complete backup, not generated runtime state; they never autostart. show_config remains the YAML effective-config projection. Every strict stdio entry requires a nonempty command, may contain only args, env, disabled, and autostart in addition to its discriminator, and remains fully validated when disabled. Every strict streamable-http entry requires one absolute http: or https: URL and may contain only disabled and autostart in addition to its discriminator. Missing transport fields, cross-transport fields, unknown fields, relative or malformed URLs, and non-HTTP(S) protocols fail configuration validation before MCP lifecycle or transport work.
run_command executes its command as one argument to bash -c. timeout_ms is its sole wait timeout; there is no inactivity-timeout input. Every successful run_command, wait_process, and kill_process has data exactly {process_id,exit_code,status,stdout,stderr,stdout_complete,stderr_complete,stdout_url,stderr_url,stdout_bytes,stderr_bytes}. process_id is exactly proc- plus twelve lowercase hexadecimal digits. Each URL is a canonical query/fragment-free work:///processes/<process_id>/{stdout,stderr}.log or work:///cards/<card-id>/processes/<process_id>/{stdout,stderr}.log; card IDs satisfy the current card grammar, the decoded process segment equals process_id, and both URLs name the same card/non-card directory. stdout_bytes and stderr_bytes count the exact raw captured buffers.
Each stream is read once as one buffer. Streaming UTF-8 decoding preserves an initial BOM, omits an incomplete final code point, and otherwise uses ordinary replacement decoding. Complete decoded text is redacted before selecting the largest shared-certified code-point endpoint under both 2,048 UTF-8 bytes and 30 lines; newline terminates a line, a nonempty final segment is a line, the thirtieth newline is included, and CR in CRLF remains text. The two heads and flags are independent. A flag is true only when no raw suffix was buffered and the emitted prefix is the complete redacted decoded capture; redaction itself does not make a complete capture partial.
validateProcessToolResult() in src/tools/process-tool-result.ts is the singular producer/current-primary semantic acceptance owner. It strictly parses the data, requires both heads to be already stable complete outbound projections, and measures exactly one settledSuccessBytes(data) canonical successful provider envelope with both URLs; that complete pre-omission envelope must not exceed 32,768 UTF-8 bytes and its settled data must canonically equal the accepted data. A canonical identity or URL that makes fixed metadata too large fails rather than being shortened. This measurement is not a byte claim about the originally retained row serialization: the independent tool-row result hash still binds the original content, while harmless key order, escaping, and whitespace differences remain acceptable when current semantics pass. A terminal result is consumed after result formation and its ID leaves current process observation; background and timed-out running results remain outstanding for later consumption or scope closure. A missing or empty cwd and canonical project:/// select the project root; plain relative cwd values are project-relative and contained; native absolute cwd values resolve directly with the same execution authority as a system URL naming that destination; project:///... selects a contained project descendant; and system:/// or system:///... selects the system root or descendant. Scoped URL classification precedes native filesystem resolution. Malformed scoped URLs, scoped URL query/fragment cwd values, unknown scoped schemes, and the record, tmp, and work schemes fail before launch. Colon, percent, query and fragment characters in native absolute filenames are filesystem characters, not URL syntax. Relative and explicit project paths retain parent-segment and escaping-symlink rejection; native absolute paths use ordinary native normalization. OS accessibility failures retain ordinary runner behavior. The one shared card/global tool definition states the scratch contract independently of the selected agent prompt: normally keep source edits, builds, and tests in the project workspace. Card-scoped invocations receive SAIVAGE_CARD_WORK_ROOT equal to the exact .saivage/work/cards/<cardId> root; purpose-named children there are the default for disposable copies, extraction areas, caches, and intermediate command work. When the assigned task needs external work, such as clean installation, relocation, or dependency isolation, an appropriate purpose-named directory outside the checkout is permitted within already-authorized task/deployment scope, without granting unrelated machine changes, global installs, or destructive cleanup. Preserve unrelated files, manage only task-owned work/resources, and keep needed evidence and its location clear before disposing of owned temporary artifacts. External files gain no work:/// locators or automatic gc-work coverage. Keep .saivage durable state project-local; processes/ and tmp/ beneath the card work root remain reserved, and project-root .card-*-work siblings must not be invented. Global/non-card invocations do not receive SAIVAGE_CARD_WORK_ROOT and must not use it; task-justified external work remains available within their own authority. Cwd choice changes neither process scope nor project-owned captures. Supported agent-prompt replacement does not replace configured tool definitions. This command environment locator does not grant workspace-tool writes into .saivage and does not change tmp:/// or work:/// behavior. The managed-process registry consumes child error events immediately from spawn return, so a handled OS launch failure cannot independently crash the runtime, and it never registers a process group without a positive leader PID. Raw output-readable error events and ordinary output append-open failures are recorded by the launch's capture owner rather than thrown from stream handlers. After registry-confirmed absence and both readable drains, they force the final presentation to failed while retaining any known leader exit code/signal, and the original error reaches terminal-settlement awaiters including command tools and the stdio MCP observer.
Workspace read.read_mode supports only auto and text; it controls the workspace tool's scoped text reads and is not the web fetch option. Separately, webfetch.read_mode also supports only auto and text. Ordinary web content fetches retain the 500,000-byte default and 1,000,000-byte hard max_bytes ceiling, existing text inference, binary metadata, and save behavior. Every path except a successful no-save text response fails immediately with the existing max-bytes error when a delivered byte beyond the ceiling is observed, before HTTP status or body classification can supersede it. A successful no-save text response instead retains at most the raw ceiling and sets fetch_truncated:true only after positively reading an additional body byte; equality, Content-Length, decoding, redaction, or head omission never imply fetch truncation. metadata_only performs the same guarded GET, redirect validation, final-status, final-URL, and selected-header handling but never acquires or reads a response-body reader; it awaits cancellation of every discarded redirect body and the final body before returning metadata. Every invocation URL removes userinfo, query values, and fragments through the typed URL projector. Every successful metadata-only, binary, no-save text, save, redirect-final, and Analyst brief-write result contains only redacted_url, never a raw url; durable call rows follow that same redaction.
A successful no-save text result has exactly {kind:'text',redacted_url,status,headers,head,head_utf8_bytes,redacted_text_utf8_bytes,fetched_text_utf8_bytes,head_complete,fetch_truncated,content_url?}. fetched_text_utf8_bytes counts the complete normalized unredacted text retained by the fetch, redacted_text_utf8_bytes counts that text after one complete outbound text-redaction pass, and head_utf8_bytes counts the returned already-redacted head. All are safe nonnegative integers; the head count equals its actual UTF-8 bytes and cannot exceed the redacted total. head_complete is true exactly when those two counts are equal, in which case content_url is forbidden; an incomplete head has a strict smaller count and requires the exact canonical work:///tmp/stash/webfetch-<positive-decimal-timestamp>-<16-lowercase-hex>.txt URL. That file contains the complete normalized unredacted retained text and has exactly fetched_text_utf8_bytes; later work reads apply ordinary outbound text defense.
Normalization uses UTF-8 replacement decoding and preserves an initial BOM. At natural EOF, malformed or incomplete terminal input produces U+FFFD. At a positively observed artificial raw ceiling, streaming decode is deliberately not flushed, so an incomplete valid code point bisected by that ceiling is omitted rather than fabricated as U+FFFD. If replacement decoding expands normalized UTF-8 beyond effective max_bytes, the operation fails before publication. Complete normalized text is redacted once before slicing. The redaction owner certifies final-text prefix endpoints: recognized JSON, YAML, escaped-JSON, credential-literal, bearer, assignment, and URL-query matches are indivisible, a changing final-text match lowers the maximum stable endpoint to its start, and surrogate pairs are never bisected. The complete branch additionally requires a certified full endpoint; no repeated redaction or fixed-point interpretation exists. Consequently the longest permitted head can be empty while honestly incomplete.
max_inline_bytes defaults to 100,000 and clamps to 1..effective max_bytes; it limits redacted-head UTF-8 bytes and has no line-count limit. The actual head may be smaller for certification or envelope fit. The complete canonical settled success ToolResult is at most 1,000,000 UTF-8 bytes. Packing measures actual generic settlement, chooses the longest certified prefix fitting both limits, and fails before publication when fixed metadata cannot fit. A stash is published only for the admitted incomplete result. Status, selected headers, binary/save/write identities, generic result settlement, and opaque historical-result interpretation remain unchanged.
Filesystem write, edit, and preauthorization share one resolved write gate. Plain, project:///, and system:/// spellings are rejected when their normalized absolute destination equals the project .saivage directory or lies beneath it. Only a valid lexical tmp:/// path carries the narrow scoped-write capability into its resolver-owned .saivage/work/cards/<cardId>/tmp backing namespace; a project or system spelling of that same backing destination remains rejected. Component-safe destination comparison does not block siblings such as .saivage-other, and existing role, scoped-path, blocked-path, secret-path, and symlink rules remain in force.
Every leaf uses the coordinator's referenced cleanup timer, above the managed-process TERM grace and post-KILL verification periods in the exact cleanup timing contract. Fast settlement clears the timer; timeout does not cancel or retain the leaf. App.stop() resolves an immutable ShutdownReport containing only ordered allowlisted {component, code} warnings, where code is closer_failed, cleanup_failed, or cleanup_timeout; caught messages, paths, payloads, stacks, and causes are never inspected or exposed. It never logs or changes exit status. Process-owning signal/restart/startup adapters log only fixed component and code fields and preserve ordinary behavior. Public terminal access remains confirmed restart_server only with operator authentication; Stop remains available in both auth modes.
Exact cleanup timing contract
text
constant.app-cleanup-leaf-timeout-ms = {"unit":"milliseconds","value":10000}
constant.managed-process-term-grace-ms = {"unit":"milliseconds","value":5000}
constant.managed-process-post-kill-verification-ms = {"unit":"milliseconds","value":2000}These limits do not impose an output-drain deadline or repair unavailable evidence. First group-authority loss rejects process waits immediately; cleanup reports failure without joining that entry's drains, while joining its successfully contained siblings. The retained failure still blocks successful runtime containment. No wait-timeout ownership change or automatic retry follows unavailable settlement.
Run, Pause, Resume, and Stop produce no lifecycle-control audit entries. Unrelated audits remain.
Lifecycle exclusion and CLI
The lock is process exclusion, not application persistence coordination. A control client delegates only through a positively verified live record, never through guessed configuration or offline runtime-state mutation.
Lock observation outcomes are missing, verified live, positively verified dead, indeterminate, and malformed. Indeterminate includes permission denial, unavailable process-start identity, and observation races. No outcome authorizes automatic lock deletion or takeover. After identity admission and successful acquisition of the current process-start identity, lock acquisition creates exactly .saivage and then .saivage/locks with non-recursive default directory creation. At either level only EEXIST permits an exact lstat, and only a real directory is admitted; files, symlinks, and other objects fail. Bound acquisition rejects a missing project identity before either directory can be created.
Initial project publication permits first publication only after direct proof that all four generated roots are absent. Existing generated state enters initialization only through required current-format project card head/selected snapshot and bootstrap record head/selected acceptance. Partial required publication and old or mixed layouts fail reset-required; no stream is probed or accepted as current. The decision derives only exact canonical authorities and never enumerates siblings or descendants.
saivage init performs pre-acquisition identity selection, exclusive lifecycle-lock publication, missing-only configuration publication, effective configuration/workflow validation, and identity creation/binding before generated-state classification. Four absent roots permit the singular initial-runtime publisher to create card authority, publish the project card and its card-scoped workflow-agent indexes, and then create the selected global Analyst index. Existing required current-format heads and selected documents enter strict startup admission. Both paths require one nonempty canonical linked-card projection, active dependency validation, and compiled workflow/parent-type admission before the exact required selected-Analyst and admitted-card node-agent indexes are strictly read. Existing-state admission never creates a missing required index. After that final validation succeeds, init reports the two outcomes independently: Project layout initialized at <canonical root> or Project layout already exists at <canonical root>, followed by Configuration materialized from template <selected name> or Existing configuration preserved. No completion line is emitted before final success.
The first identity read is non-mutating. A known-unsuccessful exclusive open publishes no new lock; a failure after that open is outcome-unknown and may retain the lock. After successful acquisition, ordinary failure releases the exact current lock record, whether bootstrap-unbound or bound, but does not roll back completed config, identity, or generated durable effects. Identity therefore remains if its creation completes but lock binding fails. Card and authored-record heads and every selected document are strict at consumption; required missing, unreadable, malformed, schema-invalid or identity-mismatched state fails directly without repair. Historical predecessors fail when reached by historical consumption, not by unrelated current reads. Growing JSONL families (conversation, app log, provider evidence and lazy card execution evidence) permit exact consuming owners to truncate only a torn final physical suffix after validating the nonempty complete retained prefix, including ordinary runtime/API/tool reads. Execution history adds no startup scan. Card/record heads and immutable JSON documents gain no such permission. If truncation completion cannot be confirmed, the outcome is fatal/unknown and authorizes no follow-up read, retry, repair, or recovery. Publication uncertainty is fatal, may retain its target and lock, and authorizes no inspection, repair, or rollback; generated-publication failure may retain partial state. saivage reset is the separate explicit stopped-service operation over the four generated roots. init --force does not exist.
The CLI performs exactly one strict token-producing option parse using command-specific definitions. init accepts only --profile. start accepts --host, --port, --config, --project-root, --create-runtime, --failed-provider-diagnostics <UUID>, --analyst-mode, --analyst-mcp-port, --analyst-mcp-principal and --analyst-mcp-tools. repair requires --target, --backup, --report, with optional --discard-card and --card-type; discard/type require a card target, and type requires discard. Controls, reset and help accept no options. Unknown, inapplicable, positional, missing-value, and repeated singleton inputs fail before lock or write effects, including equal repetitions and mixed separated/equals spellings. Server startup receives typed parsed inputs rather than argv. Those same inputs select both the canonical pre-lock project root and --create-runtime intent and the complete environment inside terminal cleanup. Selection is project root --project-root > SAIVAGE_PROJECT_ROOT > current working directory; config path --config > SAIVAGE_CONFIG > <resolved-project-root>/.saivage/saivage.yaml; host --host > SAIVAGE_HOST > selected config server.host > 0.0.0.0; and port --port > SAIVAGE_PORT > selected config server.port > 8080. Each chain selects its highest-precedence raw value before validating only that selected value. This specification is the exact owner of those four startup precedence chains; deployment procedures reference rather than redefine them. Port 0 requests an OS-selected ephemeral port. After successful startup, Saivage server listening on http://<host>:<port> reports the actual bound address and port, not the configured spelling or requested port; IPv6 addresses are bracketed. Observed 0.0.0.0 and :: binds append exactly (wildcard bind; use a reachable host address to connect). This identifies binding without promising reachability or dual-stack coverage; the wildcard URL is not a recommended browser destination. The announcement is separate from the published lifecycle-control endpoint authority. NODE_ENV, LOG_LEVEL, and SAIVAGE_API_TOKEN are independent environment-only inputs. A set-but-blank SAIVAGE_API_TOKEN (empty or whitespace-only) and a token with leading or trailing whitespace each fail startup with a typed environment-load error. Unset is the only authentication-disabled selector, and an accepted token is used verbatim rather than trimmed. Startup requires the existing project identity needed for bound lock acquisition. Only after environment validation succeeds may --create-runtime invoke the shared initial-runtime publisher after the classifier returns null; a returned existing card skips publication. Both cases then use the same strict startup admission. Ordinary start never bootstraps and therefore fails immediately when the linked-card projection is empty. Server start and control-endpoint publication follow. A configuration or pre-mutation admission failure releases the acquired lock through ordinary cleanup. saivage reset likewise remains identity-bound.
saivage restart_server is a delegating client process. After exact confirmation it submits the request, prints the acknowledged REST result, and returns normally. The separate live server/service process exits with code 75 only after acknowledgement drives terminal cleanup. Exit 75 means accepted handoff and completed shutdown; it is not evidence that a process supervisor launched a replacement or that a replacement booted successfully.
CLI status, pause, resume, stop, and restart use only a verified live record's published non-null control endpoint and auth mode. Null fails exactly active lifecycle owner; runtime control unavailable; it never implies a lifecycle phase. The shared client never reads current YAML, host/port flags, host/port environment, defaults, runtime files, or current process config to select authority.
- missing/dead status succeeds with stopped/no-current-card;
- missing/dead stop succeeds with
contained:false; - missing/dead pause/resume fails because no live runtime exists;
- dead additionally reports abandoned-lock manual repair;
- indeterminate/malformed fails closed;
- delegation, authentication, response, and schema failure never falls back.
Published disabled auth omits Authorization. Published bearer requires a non-blank SAIVAGE_API_TOKEN and sends it only as a header; the client rejects empty and whitespace-only values before making a request. CLI stop delegates to REST operation stop_project; there is no stop_project CLI alias.
Operator API admission and response contracts
The exact public registry-owned operator REST contracts are GET /health and GET /health/ready, and the complete registry uses only GET | POST. The public built web SPA, documentation site, and their static assets are HTTP resources outside that registry and its operator-session admission. These builds belong to the Saivage package, not the managed project's working directory. GET /docs redirects exactly once with status 302 and Location: /docs/; GET /docs/ serves the built documentation landing page. When the documentation build is absent, documentation paths return 404 rather than web SPA content. Once the server is booted, readiness unconditionally returns 200 {status:'ready',serverAvailability}. Readiness, runtime get-state, and the shared runtime status/Pause/Resume success response require concrete serverAvailability; omission is a response-contract failure. Runtime-status observation failure produces the degraded member of the exact availability vocabulary rather than an unavailable server. The complete state and component-source lists are declaration-level contracts in that block. Null runtime state and stopped runtime status remain valid. Every /api/* operator contract requires operator-session admission through the shared policy and explicitly declares the shared unauthorized response in the exact shared operator error contracts. Ordinary deployments require bearer authentication. With SAIVAGE_API_TOKEN configured, only the exact bearer header admits the request; rejection occurs before request validation or handler work. Set-but-blank and leading- or trailing-whitespace values fail startup rather than silently disabling authentication or starting a server whose configured token cannot be presented unchanged by the bearer protocol. Authentication-disabled mode, in which the same operator-session contracts admit headerless requests, is supported only when deployment-owned external isolation limits exposure to trusted origins. Saivage neither establishes nor detects that isolation. Wildcard CORS remains enabled and is not authentication. API bearer credentials are accepted only in the Authorization header and never in URLs.
Exact availability vocabulary
text
vocabulary.availability-state = {"members":["available","degraded","idle","unknown"]}
vocabulary.availability-component-source = {"members":["health-check","mcp-manager","runtime-application"]}Each registry-backed operator operation has one required status-to-schema response map. Exact lookup in that map is the sole runtime authority for both status and body. The browser likewise parses status 200 and every declared non-200 response through the exact operation-and-status schema before use. A declared non-200 response becomes validated operator error data; an undeclared status, invalid JSON, or a body that fails its declared schema is a contract/protocol failure and never a generic API error. On server output, an undeclared status or malformed declared body follows the fixed actionable-evidence/opaque-500 path without sending the candidate body or consulting another schema. Fixed params, query, body, success, nested-row, and error objects are strict; records and explicitly opaque leaves alone remain open. Runtime validation errors require {error:'ValidationError',message,issues} with strict issue rows, unauthorized responses require numeric statusCode, and handler-owned variants remain exact status-local schemas rather than a generic API error. Agent detail/conversation absence is exactly {error:'Agent session not found'}, absent LLM exchange is exactly {error:'llm_exchange_not_found'}, and the conversation cursor failure is its own exact one-issue 400 variant. Process observations use running | exited | failed | killed | unavailable; unavailable is evidence/authority loss, not a successful terminal process result.
Each request computes one response descriptor inside one complete pre-send ContractRuntime boundary. The ordered phases are authentication evaluation, request-schema execution and transformation, handler preparation/execution, exact declared response-schema execution, and contract-violation publication. Ordinary authentication denial and request rejection remain declared 401 and 400 outcomes. A throw in a pre-send phase instead selects the unexpected-internal variant in the exact shared operator error contracts and logs only the fixed operation plus auth_evaluation_failed, request_validation_failed, handler_failed, or response_validation_failed. Only after complete params, query, and body admission may the declared canonical card or conversation-session identity be copied from parsed params into that log. This scalar is captured before handler invocation without another grammar check; incomplete admission has no failure identity. Exception objects, text, request data, response data, and schema issues are excluded. Fastify request logs retain request ID, method, host, remote address/port, and the other standard request metadata, but serialize the URL only through the path before the first ?; routing and query parsing still receive the complete URL. WebSocket tickets and all other query contents therefore never enter request logs.
Contract violation publication uses the required app-log boundary. A response mismatch publishes fixed durable runtime_actionable_error evidence and emits no event-list WebSocket hint. Contract handlers receive only pre-send header and raw-finish-hook capabilities, not status/send authority. Primitive-classified PublicationOutcomeUnknownError reaches immediate fatal delivery before Fastify logging or a response. Ordinary handler failures retain the generic 500 contract. The separate Analyst mutation runner remains the sole current control-action audit producer.
Doctor is the normal authenticated registry operation debug.doctor. ContractRuntime owns its authentication, request/response validation, outer failures, and final response boundary. Its strict 200 union has exactly one cards check: ok contains the passing cards_loadable check with Cards loaded successfully. and no issues; issues_found contains the failing check with Cards failed to load. and exactly one error issue with that message. issues_found is reserved for an ordinary card-read failure. The bounded handler catch first rethrows PublicationOutcomeUnknownError; ContractRuntime delivers that identical value to the application fatal port before an ordinary diagnostic or response, so publication uncertainty produces no Doctor response. Content supervision, its scanner configuration, routes, storage lane, and UI are absent.
Provider observations and diagnostics
Availability, transport observations, canonical exchange metadata and private request captures have different scopes. None is a complete execution ledger or provider-delivery receipt.
Outbound HTTP lifecycle observations
The process operational logger emits continuous info metadata at each owned external fetch dispatch, returned response headers and terminal response handling, plus the invocation scheduler's actual retry/availability wait boundary. This is local transport observation, not a logical-turn completion, server-receipt proof, tool-effect receipt or canonical event. Provider Chat/Responses/Codex, Codex/Copilot auth refresh, remote HTTP MCP and Websearch/Webfetch are covered. Each retry and manually dispatched web redirect has a fresh request_id; native automatic redirect hops within provider/auth/MCP fetches are not individually observed.
Closed metadata includes service/purpose/endpoint labels, method, request identity, monotonic elapsed milliseconds, numeric status, typed outcome/error code and body disposition. Headers alone are not completion: an HTTP-200 stream can terminate with failed. consumed, not_consumed, cancelled_by_policy and incomplete distinguish complete reading, header-only handling, intentional bounded/metadata cancellation and unfinished consumption. Policy cancellation is not caller abort. Terminal completion describes the transport owner's existing handling, not later evidence, credential or record publication.
Available source session/card/input identity is call-local on provider and agent tool paths, including agent-requested MCP start/discovery and Analyst record-fetch preparation. Configured agent names and model identities use SHA-256 references; canonical card IDs and valid input UUIDs remain identifiable. invocation_session keeps internal-summary execution distinct from its canonical source session. Legitimate ownerless startup/autostart/reconciliation omit source identity. URLs/hosts/paths, headers, credentials, payloads, tool names/arguments/results and raw errors are excluded, not sanitized into log text.
Wait metadata describes the actual selected delay and, where finite, availability deadline; waiting_candidate_ref identifies the wait's selected candidate, not a guaranteed next dispatch. Eligibility is recomputed after waking, and an available alternative can run without waiting. A shared availability wait need not have a preceding request ID. No dispatch is fabricated for admission rejection, reused tokens, pre-cancelled work or sleeping retries.
These observations are independent of opt-in finite content diagnostics and absent from app-log/provider evidence, API and UI Debug. There is no new durable family, replay, heartbeat, request registry, delivery/retention guarantee or crash-terminal reconstruction. Ordinary sink failure does not change transport results or retry policy and has no fallback sink. Publication uncertainty remains fatal before any further observation or cleanup. CLI control HTTP to Saivage, browser API clients, stdio/subprocess/package traffic and remote peers' own requests are excluded. See the operational interpretation.
Private failed-provider request diagnostics
saivage start --failed-provider-diagnostics <UUID> explicitly enables one finite, process-local observation; absence disables it. Invalid/duplicate options and use on another command fail admission. There is no YAML, environment, REST or UI toggle. After lifecycle exclusion and canonical startup admission, the shared InvocationService exclusively claims .saivage/diagnostics/failed-provider-requests/<UUID>/ and publishes a self-ignoring .gitignore (* followed by newline) before dumps. An existing activation disables capture without reading it; a claimed UUID remains consumed even if setup fails. Automatic restart cannot rearm it.
Capture is failure-only at the shared model transport boundary: non-2xx, terminal HTTP-200 SSE errors, malformed/truncated responses, network/inactivity failures and explicit parsed content_filter refusals. Auth refresh, pretransport setup/admission, ordinary success and cancellation produce no dump. Summary business-validation failures such as empty prose or length do not qualify. Existing refusal handling, summary recovery, retries, routing and canonical evidence outcomes are unchanged. An explicit finish-reason refusal can therefore have a diagnostic while canonical exchange evidence still says ok.
All invocations share 16 qualifying publication-attempt slots within one monotonic hour of activation, checked at failure completion without a timer. Known failures and metadata-only records consume slots without refund/retry; success/cancellation does not. Each JSON publication is at most 8 MiB; raw-body or stored-envelope overflow produces metadata only (at most 16 KiB), with an explicit reason and no body prefix.
Diagnostics derive only from the exact captured serialized string passed to model transport, never reconstructed history. They distinguish canonical source session, actual invocation session (including internal summary), input UUID, purpose and final zero-based canonical attempt index. Resumed attempts retain their offset; a pinned attempt uses the actor's actual preceding-attempt count, not pass-local zero. Actual HTTP status is null until a response is observed; any embedded status is separate. Structured provider codes are ephemeral, not inferred from error prose.
The private diagnostic family is format3 with privacy_policy:failed-provider-request-privacy-2 and strict overload failure kind. It has no runtime reader/index; retained captures remain unconsumed. Its historical source change is recorded in the format reference, not a current conversation or shared-envelope version claim. Each document discloses raw/stored UTF-8 byte counts and SHA-256 hashes, exact|redacted|omitted body fidelity, reencoding and counted omissions. The stored hash covers the decoded stored_body string, not JSON escaping or the whole file. Projection explicitly selects Chat or Responses/Codex protocol positions. Native messages retain role/status and string content or ordered visible content blocks; text/refusal and function call/result fields retain prose, name and call correlation (call_id, or Chat call ID/tool_call_id). Native item IDs, annotations (including citations), unknown items/content and every other native extension are omitted. Unknown extension keys are not echoed: _diagnostic_omitted_extensions is a fixed aggregate marker. Omitted array subtrees keep their original positions. Native images become one fixed image placeholder without traversing pixels/references. Root Responses conversation/previous-response references are omitted explicitly. Ordinary instructions, tool schemas and application JSON retain their shape/order; type, id, file_id and replay-shaped keys there have no native authority. Structured auth/header/cookie/environment/config and image-payload fields remain private; text receives data-URL and outbound redaction, including the active credential. Only protocol-known serialized tool arguments are decoded once, projected as ordinary JSON and reencoded once; invalid/non-string arguments are wholly omitted without raw fallback. JSON-looking prose and result strings are never decoded. private_replay counts each wholly omitted native subtree or extension/reference field once (an annotations array is one field); images counts each omitted native image or ordinary image-payload field once. Hidden descendants are not traversed or counted. Successful argument projection counts reencoding separately from invalid omissions; text/structured redaction counts actual changes without reprocessing leaves. Retained format-1 files remain untouched and do not satisfy policy-2 privacy; there is no automatic conversion, scan or disposal. Nonprivate order, instructions and labeled material remain. Redaction is not exhaustive secret certification: sensitive project/prompt text and unusual encoded secrets may remain. Neither hashes nor transport submission prove provider receipt or a replayable request.
Known diagnostic preparation/prepublication failures emit only fixed local notices and preserve the original provider outcome. Publication uncertainty remains fatal before warnings, settlement, retry or cleanup, even under racing cancellation. No diagnostic body, snippet, hash or dynamic error enters logs/API/UI/evidence. Files omits/refuses the diagnostic namespace and aliases before directory/content/ image inspection. There is no inspector/export/replay, retention scan or cleanup; explicit activations accumulate and diagnostics remain outside reset's four roots.
Direct file persistence
Durable authority is the exact selected file graph, not a directory inventory. Current operations validate what they consume; historical traversal validates each reached predecessor. Required publication/layout and strict consumption scopes are separate from backup, repair or adoption procedures.
Saivage durable state uses direct synchronous named file functions. Card heads select immutable ordinary/tombstone predecessor documents and current-only mailbox pointers; record heads select accepted predecessors and the current draft. Card history and publication owns exact paths/order. Only head selection grants current authority. Current consumption certifies no unconsumed historical predecessor. Conversation indexes and indexed segments remain their separate durable authority. Conversation append strictly consumes current history and admits incoming row schemas, session identity, and batch IDs, without a second current-plus-candidate full semantic fold. Record mutations likewise retain current consumption and explicit operation admission, not a second prospective history fold. Full semantics are enforced at consumption. Every exact consuming owner of app-log, provider-evidence and current-conversation JSONL validates the complete retained prefix before discarding an identifiable unterminated final physical suffix. A present empty file or a file without a complete prefix fails unchanged. The suffix may include interrupted UTF-8 or parseable JSON without its terminating newline; every complete malformed envelope, blank line, unsupported format, invalid row, identity/transition/duplicate-ID error, or independent row-policy hash mismatch prevents truncation and remains present. The owner truncates the exact file to the validated byte offset, fsyncs, closes once, and returns that already validated projection without rereading. Open failure is ordinary; failure from truncation onward is publication-unknown and fatal before any follow-up effect. This applies at ordinary API/tool use as well as existing selected startup consumption, not through a sweep or repair command. Card/record heads and immutable JSON documents gain no truncation permission. The shared conversation loader applies this rule to the indexed current version even when selected through a historical route. Older indexed segments are immutable strict reads: an unterminated historical segment is unavailable without mutation. Indexes, identity, configuration, process text logs, unlinked namespaces, and temporary files gain no truncation permission. First-segment and compaction publication use the shared first-envelope publisher: one lstat of the exact selected segment refuses every present object, including a dangling symlink, without inspecting it; only ENOENT admits publication. One fresh UUID same-directory temporary is exclusively opened, written/fsynced/closed, renamed to the exact segment, then its parent is opened/fsynced/closed. Successful segment directory synchronization precedes a separate cumulative-index replacement with its own fresh temporary; only successful index publication grants discoverability and permits freshness hints. Target admission, temporary allocation/open/write/zero-progress/fsync/close failures retain their ordinary exact errors. The segment rename attempt and every subsequent parent-sync failure are publication-outcome-unknown and reach the existing fatal boundary unchanged; index replacement retains the same phase distinction. Every failure ends that publication attempt immediately, without follow-up read, close-after-failure, retry, cleanup, or reconciliation. An unindexed allocation remains ignored forever. These ordered publications are not a transaction and provide no crash-loss guarantee. The publication alignment alone changes neither the current format nor adoption requirements for independently incompatible cutovers. Conversation index, ordinary/compacted genesis, and segment envelope all use strict format 12. Earlier formats and removed fields are rejected, not migrated. Compaction has no source/group/summary/protected-list/continuation/retained-row digests, prior-history attestations, serialized group inventory, or accumulated disposition counters; the entire original retained_rows metadata is absent. Structurally and semantically valid edits to summary or retained content are not detected by a compaction checksum. This introduces no replacement integrity guarantee. Canonical JSON still recursively orders object keys by JavaScript string/code-unit comparison, preserves array order and ordinary JSON.stringify behavior including numeric integer-index enumeration, and performs no Unicode normalization or locale collation. Shared synchronous browser-safe SHA-256 and canonical-value hashing remain for independent contracts: tool-row template_sha256, call_policy_sha256, result_content_sha256 and settled evidence; per-block replacement/evidence and orientation hashes; admitted request bytes/hash and capability/recovery bindings; identity generation, card/record artifacts, tool observations, MCP revisions, and external backup fingerprints. Their exact inputs and validation remain unchanged. Complete malformed or invalid canonical data remains unchanged and fails. There are no repositories, caches, queues, registries, transactions, scans, orphan adoption, or compatibility readers. Only explicit exact-canonical-path repair/wholesale own-root attic moves described above are permitted; normal orphan handling remains forbidden.
The application log .saivage/logs/app.jsonl contains event, control-action and body-free external-tool-call rows under the exact app-log vocabularies. Identity and time exist only in each lane payload. Error membership is explicitly runtime_diagnostic, runtime_actionable_error, and failed mcp_tool_invocation; informational operator_runtime_control rows, including rejections, are not Errors. The writer's sole boundary accepts the project root, lane, and synchronous preparation closure. It first performs identity/time construction, centralized outbound redaction or audit projection, authoritative validation, and serialization. It then reads exact bytes for tail admission: on a clean newline-terminated stream only the final envelope is validated, without a whole-history uniqueness preflight. A torn tail instead requires the owner's full retained-prefix semantic validation and permitted truncation before append; present empty or no-prefix files fail unchanged. A missing target is admissible. Explicit reads validate the complete app-log sequence, including external receipts, before optional lane filtering. Duplicate logical IDs across all row types fail complete reads/startup; append has no full duplicate preflight. There is no cross-file or global logical-ID certification. Missing app-log state yields empty results. A present empty/no-prefix file or invalid complete consumed data fails the whole explicit read (HTTP 500 on the operator HTTP surface), returns no salvaged valid prefix, and leaves bytes unchanged. The validated torn-tail exception described above applies: only an identifiable unterminated final physical suffix after a fully validated complete retained prefix is truncated, and the read returns that already validated projection without rereading. After admission the JSONL owner directly opens the exact target with ordinary O_WRONLY | O_APPEND, without descriptor or physical-ancestor proofs. Append-open ENOENT alone selects one first-publication attempt. App-log-local mkdir establishes .saivage and logs, tolerating only EEXIST; subsequent exact use decides usability without a post-EEXIST directory proof. Provider evidence requires an established session directory. Lazy Oversight conversation establishment retains owner-local mkdir with the same EEXIST rule. First publication refuses every existing exact target, rather than overwriting it. All other errors propagate; previously consumed current-segment append targets must still exist.
Established conversation and card-owner directories
An established conversation consists of its deterministic session root, index.json, and versions/. Card-session roots are <card namespace>/conversations/<agent-name>/; global-session roots are .saivage/agents/conversations/<agent-name>/. Establishment creates the root and versions/ before publishing the index. An existing index implies that both directories belong to established state even when versions: [], current_version: null, and current_filename: null: an empty catalog means no current segment, not absence of its publication parent.
Initial card publication establishes sessions for the distinct card-scoped node agents in that card type's compiled workflow, not a fixed list of role names. Initial runtime publication additionally establishes the selected global Analyst conversation. The same layout requirement applies to selected global Oversight once an actual check establishes it; a never-established Oversight conversation remains legitimately absent. Required Analyst/card indexes remain required, and directories alone do not establish session identity or authorize discovering sessions.
Each card's fixed initial owner layout includes conversations/, card-history/, mailbox/, records/, and records/accepted/ beneath its exact namespace, together with those configured session roots and their versions/ directories. The mailbox is initially empty, and not-yet-used sessions have empty versions/ directories; bootstrap history and accepted records populate their own parents. Optional record heads and provider-evidence files remain absent until their respective producers publish them. children/ is created on child allocation, not unconditionally at card bootstrap. Never-established Oversight, logs/, process-output/stash trees, and arbitrary work descendants are likewise not fixed bootstrap requirements. A complete four-root reconstruction boundary is a replacement scope, not a requirement to precreate all four roots or every possible descendant in a fresh runtime.
This is a producer layout requirement, not a runtime admission guarantee. Empty-index reads and startup can succeed without accessing versions/; file-content validation and readiness do not certify every future publication parent. First-segment and compacted-successor publication rely on the established directory and fail at actual use if it is missing. No directory sweep, append-time mkdir, automatic repair, stronger startup rejection, remembered admission, or storage coordination follows from this requirement.
Provider-reported token usage
Successful parsed attempts may retain strict token_usage with five optional nonnegative integer provider facts: prompt_tokens, completion_tokens, total_tokens, cached_input_tokens, and reasoning_output_tokens. Chat maps prompt/completion/total directly, plus prompt_tokens_details.cached_tokens and completion_tokens_details.reasoning_tokens. Responses and Codex map input_tokens to prompt, output_tokens to completion, total directly, plus input_tokens_details.cached_tokens and output_tokens_details.reasoning_tokens. Codex consumes only the first accepted terminal response.completed.response.usage, never stream deltas. Unknown external members are ignored; malformed consumed containers/counts fail with safe path-only parse errors. Absent/null supported wire values are unknown and omitted; explicit zero is retained. No total is derived. Standard cached input is included in input, and reasoning output in output: these subsets are not additive counters, and no cross-field accounting equality is enforced.
Usage belongs to each successfully parsed HTTP/provider attempt, including tool calls, later local model issues, and internal summaries. Retries/failovers retain separate existing rows, not a turn roll-up. Failed HTTP, noncompleted Responses, truncated Codex, pre-provider failures, and abandoned/stale actor completions gain no usage retention. This loss-tolerant observation is not billing, estimation, complete accounting, or proof that every charged operation was retained; usage extraction itself changes neither publication timing nor request/cache identity. Codex owner-session affinity is a separate request contract below.
Exact app-log vocabularies
text
vocabulary.app-log-type = {"members":["control_action","event","external_tool_call"]}
vocabulary.logged-event-kind = {"members":["mcp_tool_invocation","operator_runtime_control","runtime_actionable_error","runtime_diagnostic"]}Provider evidence instead belongs to the existing canonical conversation owner: a card session uses <card namespace>/conversations/<agent name>/provider-exchange.jsonl; a global session uses .saivage/agents/conversations/<agent name>/provider-exchange.jsonl. The path is derived from the parsed canonical session ID, not discovered from siblings or cataloged in the conversation index. The optional file uses shared strict version-2 growing envelopes with provider_exchange rows, retaining ordinary exchanges and internal-summary attempts across every segment of that source session. Exact missing evidence means no exchanges; a present empty/no-prefix file, invalid retained UTF-8, malformed or unsupported complete envelope, invalid row, wrong owner/lane, or repeated derived logical ID fails unchanged. A torn final suffix is discarded only after the complete retained prefix passes those checks. A row must belong to the owner or to its one derived internal summary ID. Duplicate checks are per physical provider stream and separately across all app-log row types; app-log IDs need not be disjoint from evidence IDs in another file. Evidence publication uses exact-byte owner-specific tail admission and one first publication in the already existing conversation root if missing; it creates no session/index, retries no uncertain write, and adds no conversation/evidence atomicity or completeness guarantee. Neither startup admission nor an earlier read authorizes later writes.
The exact Agent latest-exchange request first admits the selected card/global session and its conversation catalog, then strictly reads only that session's full evidence file; it neither reads the app log nor builds an all-session map. Only primary rows compete: greatest timestamp, then greatest attempt index, with the earlier physical row winning an exact tie. Internal-summary rows are validated but never selected as the ordinary latest exchange. Missing or summary-only evidence yields no exchange under the existing route behavior. Event/error/control queries continue to strictly read the entire event/control app stream before filtering; their cost may grow independently. A different session's corruption no longer breaks this exact evidence request, but the selected stream must always validate completely.
Append-open failures occur before any descriptor exists and remain exact ordinary errors; only exact ENOENT yields the growing-file owner's missing selection result. After the first canonical write begins, any write, zero-progress, fsync, or close failure becomes PublicationOutcomeUnknownError; no second close or operation follows. Preparation, validation, serialization, tail admission, and directory failures remain ordinary. Once the shared signal exists, no catch may convert it into a failed tool/model/card result or perform a read, retry, append, fallback, later provider attempt, diagnostic, settlement, or hint.
Inner catches preserve the identical publication-unknown object before local classification. The first CardProcess, Analyst, server or boot boundary delivers it to the nonreturning fatal port before Supervisor halt, status mutation, child settlement or cleanup. Fastify emits no replacement evidence or HTTP cause. The fatal stderr line may include the captured direct-syscall cause as specified below. Ordinary non-publication failures retain their separate Supervisor halt and exact structural-child interruption/join policy. Observation-only sockets invoke no durable owner.
Successful owners call narrow SyncHub effects directly. Conversation publication emits conversationChanged({session_id,segment_id,segment_version,visible_message_id}) only after confirmed append/index commit. Browser tails append only within the same session and exact segment ID/version; a different identity replaces/refetches the baseline, including when the visible cursor did not change. Existing request/connection epochs still fence superseded completions. Pending conversation hints coalesce in accepted arrival order; numeric versions are not monotonic freshness. Lower versions and new UUIDs at reused ordinals still replace/refetch the exact baseline. Every known-successful first card conversation also emits a membership reconciliation hint; first global publication emits a global-session hint. Failed or publication-unknown writes emit no hint. Runtime, card, membership, conversation, and exchange effects remain lossy, non-throwing, debounced, and socket-only.
One stateless EventQueryService owns event/error queries for the authenticated operator API and Analyst tools. Every operation performs exactly one complete strict read of the event lane before filtering and slicing. GET /api/events is the singular operator event collection, consumed by both Analyst tools and the operator UI: it accepts exact event kind, optional goal/card, nonnegative safe-integer offset, positive safe-integer limit through 1000, and selection:'oldest_page'|'newest_tail'; defaults are oldest page, offset 0, limit 50, while newest-tail forbids nonzero offset and preserves chronological physical order. total is the filtered count before slicing. GET /api/debug/errors returns the complete event-derived error projection and is the System Errors section's only event-derived input; the UI's broader event reading goes through GET /api/events. There is no Debug Timeline, /api/debug/timeline, dedicated error lane, ErrorLog, session filter, or since filter. The ordinary exact-session conversation reader has a separate browser-local Activation entries — this segment index over already returned public system/activity activation_open rows. A marker records entry publication and prepared evidence, not a successful provider call, current liveness, completion, duration, or delivery of its named node instructions. The index preserves physical source order and recorded session, marker, input, configured agent, optional card, and time identities. Activity-only marker rounds remain rendered transcript anchors. Coverage is only the accepted current segment or one explicitly selected indexed segment; an empty index means No activation markers retained in this segment, not never activated. Compacted inherited-open-round context is not another entry and supplies no fabricated timestamp or location. No predecessor search, all-history enumeration, cross-session merge, durable index, or extra activation API exists. The identity-resolved Analyst inspector exception is unchanged and is outside this ordinary-reader coverage.
/agents/:id accepts optional segment=<positive safe integer> and entry=<opaque row ID>. Segment query syntax is a scalar decimal string matching [1-9][0-9]* whose numeric value is safe; absence selects current, while malformed selection is explicitly invalid and makes no version-content request. An explicit version selects that ordinal in the present catalog even when equal to current; Card Evidence segment links retain that number. Catalog and content responses preserve immutable entry_id; ordinal navigation alone does not promise an old incarnation after rollback/recompaction. An entry target is any nonempty scalar router-decoded string, preserved verbatim without trimming, normalization, prefix parsing, or extra decoding; missing, empty, or nonscalar values supply no target. Router-built links preserve composite marker IDs and ordinary UUID rows under this one rule. Unknown entries are missing only in the successfully accepted selected/current segment. Missing segment, failed read, and missing entry are distinct; reload, same-session changes and Back preserve ordinal selection. Historical content stays separate from current updates in one exclusive reader.
The activation index is initially closed with Activation entries · N summary and coverage inside; malformed-marker warnings remain visible outside. Marker existence remains visible in the transcript as Activation entry recorded; raw identities, JSON and time are secondary details. Provider Technical details is also initially closed/lazy and remains latest recorded session exchange, not selected historical evidence. model_repair presents an instruction, not achieved repair; model_recovered presents interrupted activation/effects uncertainty, not success. These separate source-position diagnostics reveal full safe bodies on exact targeting and change no recovery effects or persistence.
Direct runtime-control evidence
Direct operator transport handlers append one operator_runtime_control event at each known-return or explicit handler-rejection branch, with ordinary event ID/time, actor:'operator', surface:'operator_api', and one strict operation-discriminated result:
result.operation | Bounded recorded result |
|---|---|
pause_runtime, resume_runtime | outcome:'returned', runtime_status from the already built status response |
stop_project | outcome:'returned', status:'stopped', exact returned contained boolean |
restart_server | outcome:'restart_scheduled' after scheduling returns |
pause_runtime, resume_runtime, stop_project body rejection | outcome:'rejected', reason:'body_not_allowed' |
restart_server capability denial | outcome:'rejected', reason:'restart_unavailable' |
These global events contain no synthetic card/session association, request body, credentials, headers, arbitrary error, or runtime snapshot. Delegated CLI requests reaching these handlers are included without distinct client attribution. There is no direct Run handler. Status reads, authentication/routing/schema rejection before handler entry, thrown runtime failures, and transport loss have no promised row. Analyst tool controls, Analyst restart confirmation, Planner operations, startup recovery, and internal transitions create no duplicate events here; their respective conversation/lifecycle evidence remains separate.
Publication is one post-effect append attempt immediately before returning the existing response, not a transaction. Its timestamp is observation/publication time, not command duration. A returned pausing is not paused; contained:false is not newly contained. Known append failure propagates without another event, reread, retry, rollback, or undo: the principal effect may already have occurred. Publication uncertainty reaches the fatal boundary unchanged before any follow-up. Missing evidence or HTTP failure never authorizes repeating a command. Restart ordering is schedule, event append, finish-acknowledgement installation, then response; failed append installs no acknowledgement. Restart scheduled proves neither acknowledged shutdown, exit 75, replacement startup, nor readiness.
The event discriminator has its historical incompatible-adoption record in the format reference. Runtime remains strict: no converter, mixed versions or binary-only rollback. Source implementation authorizes no instance deployment, reset or external migration.
Authenticated GET /api/workflows/:cardType/presentation (workflows.presentation) supplies compact structural facts for one installed card type from the immutable startup artifact, with no file reads or all-types projection. Its strict response is {card_type,nodes:[{node_id,execution,initialize_records,output_record}],entries:[{entry,node_id}],edges:[{source_node_id,outcome,route_id,test,condition,target}],terminals:[{terminal}],records:[{name,bootstrap,initialization}]}. Execution is {kind:'agent',agent_name} or {kind:'action',action,contract_version,provider:{kind,name}}; output_record is the exact bound name or null, tests are nullable typed route tests, initializer declarations nullable format1/template objects. Entry, condition, terminal enums and targets match Debug Graphs, including runtime-owned edges. Unknown installed type returns HTTP 404 {error:'workflow_type_not_found',card_type}. Prompt declarations/bodies/paths, globals, agent model/provider bindings, tools, capabilities, secrets and runtime position are absent. Action provenance identifies the compiled operation, not an agent route. This is presentation evidence, not another workflow authority; System retains richer Debug Graphs.
Authenticated GET /api/debug/graphs returns one strict graph projection for each canonical card type in canonical order. Its sole source is the immutable runtime workflow artifact compiled and bound during startup: the handler reads no configuration, prompt file, or runtime-state file. The response includes selected global agents separately, with exact session identity, safe static declaration/source, model route/candidates, skills and tools. Card graphs expose notification recipient, children, record/initializer definitions, lifecycle entries, discriminated agent/action nodes, requirements, descendant context, outcome/case routes, cycles, terminal exports/promotion and all three terminals. Only agent nodes expose safe prompts, model routes/candidates (no accounts), tools and record capabilities. Actions expose exact operation/version/provider, never fake agent bindings or private configuration. Configured routes retain semantic outcome, route ID, nullable typed test, runtime_owned:false and condition:default|pending_notifications. Runtime terminal edges use execution:failed|execution:blocked, runtime_owned:true, default condition and terminal targets. Runtime notification:interrupt edges target STOPPED's configured agent node, not a terminal; they expose its safe entry declaration, empty exports and null promotion. Runtime terminal edges have null prompt, empty exports and null promotion. Prompt declarations expose reference/compactable/legal optional compaction_key, never bodies/paths. Projection comes from startup compilation, not runtime history. Prompt bodies and paths, credentials, auth profiles, provider accounts/secrets, MCP schemas/descriptions, and mutable workflow position are not part of the contract. The complete response is validated before egress. Restart-only reconfigure may change next-start configuration, but this projection remains unchanged until a fresh server startup compiles and binds a replacement artifact.
Every invocation admitted to the audited Analyst mutation runner makes exactly one control-action append attempt. Operation-owned current-admission or application/domain denial records denied; a returned failure or thrown preparation/readiness/application failure records error; and a returned success records ok. Safety class is audit classification metadata rather than an admission rule. The active application/operation owner's abort signal is checked before the owner mutation, not after its return. A pre-owner-call disposal abort prevents mutation; cancellation arriving during an entered operation cannot discard its returned known effect, and returned success audits ok. There is no post-return cancellation/readiness assertion, replay, or undo. Non-record Analyst write and edit use this runner with ordinary runtime cancellation and no intervention readiness, including while running. Record paths retain their separate record authority/readiness and action names. Write content and old/new edit text are excluded from audit parameter summaries; path and replace-all metadata remain. This bounded coverage does not audit every filesystem effect: shell commands, apply_patch, and non-record Webfetch saves remain outside it. An audit publication failure after a file effect never retries, rereads, reverses, or re-executes the write. Control-action append failure propagates directly without read, retry, repair, or a second append attempt. This Analyst settlement runner is the only current production control-action producer. /api/control-actions strictly validates the complete stream before optional card_id and since narrowing and newest-first sorting; it applies no actor or validity filtering. Valid historical rows, including rows whose actor is planner, remain readable and are returned unchanged apart from the shared outbound redaction projection.
Agent identity is configuration-defined. Each agents.<name>.model_route references one models.routes entry; temperature and maximum output tokens belong to that route. Routes select either direct candidates or one profile. Skill targets are exact configured names and require the target agent's declared skill capability. No role enum or role-indexed routing remains.
Replacement and first publication use one fresh random UUID same-directory temp opened exactly once with O_CREAT | O_EXCL | O_WRONLY, then write/fsync/rename/parent-fsync. Before first publication, the owner calls lstat on the exact target exactly once and ignores a successful result: every existing path object is already published. Only ENOENT from that call permits exactly one replacement attempt; every other error propagates. Temporary-path collisions and failures propagate without retry or cleanup. Child candidate claiming is the narrow exception: only candidate-namespace mkdir EEXIST advances to the next deterministic segment, without inspecting that path. Crash-left noncanonical files and incomplete card namespaces are silently ignored forever. Canonical readers derive exact paths from committed identities and references, validate each consumed head, document, or remaining stream, and never discover card, tombstone, record version, session, or temporary artifacts from directory entries. Complete malformed canonical state fails.
All creation uses ordinary Node defaults under process umask with no mode arguments, chmod, mode probing, or repair. The shared growing-JSONL byte reader performs a plain exact read; only initial read ENOENT classifies missing. Format owners parse and fully validate retained semantics outside that missing catch, then may truncate only a proven torn final suffix under the uniform consumption rule. Complete corruption and empty/no-prefix files fail unchanged. App-log initialization delegates to its ordinary consuming read and admits exact missing app.jsonl as absent without creation. Agent inventory, card-session inventory, and exact summary detail instead use canonical conversation index metadata, while complete transcript and bounded tool reads validate the complete current segment through the conversation owner.
Public OpenAI Responses and OpenAI Codex are separate provider contracts: public OpenAI uses its configured API-key credential, while Codex uses an openai-codex-backend OAuth auth profile; neither credential form aliases the other. Auth profile refresh uses strict direct reads and optimistic whole-file replacement. The original invocation signal is checked after response/body awaits and immediately before the synchronous latest-file reread and replacement. Each OpenAI Codex and GitHub Copilot refresh HTTP request has its own fixed 120,000-ms inactivity window, armed before fetch and renewed by nonempty successful-response body byte chunks, not headers. It ends before auth-file reread/replacement and before any subsequent provider request starts its fresh window. Refresh fetch rejection or inactivity timeout, including silence during successful-status body consumption, is server_transient with status 0; owner cancellation retains its exact reason instead. Refresh HTTP 5xx is server_transient with the actual status. Refresh HTTP 429 is instead a typed rate_limit with actual status 429, using the unchanged shared HTTP Retry-After parser for seconds/date timing; no response body or other OAuth rate-limit hints are consumed. These thrown failures occur before wire derivation, recorder creation, and provider request: no stale-token provider request or synthetic provider exchange/401 is created, and the failed refresh performs no auth replacement. With no actual provider exchanges, the invocation failure is pre_provider. All non-OK refresh decisions finish at headers without consuming the body or retaining an inactivity timer into recovery waits. HTTP 4xx other than 429 and malformed complete successful responses return null, retaining the previous token; ordinary non-timeout body-read failures retain that fallback, but inactivity timeout and owner abort cannot become malformed-response fallback. The subsequent real provider response remains authoritative for ordinary final-auth handling. The unchanged admitted-attempt budget and standard cooling apply to a refresh transient. Refresh 429 uses the existing candidate-local BLOCKED_UNTIL rate-limit policy: positive parsed Retry-After timing determines the deadline, while absent, declined, or zero timing retains the fallback of at least 60 seconds. Healthy admitted alternatives remain immediately eligible; otherwise existing finite rate-limit waits, unavailability deadlines, and failure ceilings bound retry/failover. Refresh owns no wait, success or immediate retry is not guaranteed, and pinned refresh failures retain no recovery. OAuth refresh does not inspect overload bodies or grant overload authority. An exhausted invocation record is excluded from the final candidate-availability wait scan and cannot reopen when its process-local cooling entry expires; with no viable alternative, the fourth failed admission terminates immediately as a pre-provider failure with no provider exchanges. An available untried admitted alternative is selected before waiting for a standard-cooling retry. Without pending overload, existing ready/rate-limit priorities and standard waits remain; overload eligibility follows the candidate-local selector below. Route membership, mandatory context-failed retry and finite failure/deadline policies remain unchanged. Cooling remains process-local advice for other or future invocations. Concurrent refresh is last-completed-write-wins with accepted credential-loss risk and no revision, CAS, lock, or merge. A failed 429 refresh writes nothing, but an independently successful concurrent refresh may still replace the auth file.
Provider context and preparation
Preparation freezes one invocation's instruction/tool/context authority before provider dispatch. Exact local admission, provider-reported context recovery and content-policy handling are separate decisions. Compaction may lose narrative but cannot waive requirements or replay already settled effects.
Ordinary and corrective replacement-summary instructions share one historical-scope direction: status, grants and restrictions describe only inherited history and the currently supplied labeled source byte ranges. Preserve attribution, scope, subsequent changes and uncertainty; later supplied source may supersede earlier decisions without expiring unsuperseded obligations. Neither unseen split-source portions nor the retained tail have been consumed by a fold. Prepared orientation is not historical evidence and implies no later completion or permission.
Compaction is mandatory for a bootable server. Configuration requires literal enabled: true, finite context_utilization_fraction U with 0 < U <= 1 (default 0.80), trigger_fraction T (default 0.90), tail_fraction F (default 0.25) with 0 <= F <= T, and a strict structured summarizer_candidate with exact non-empty provider and model plus required nullable account. Removed absolute-budget and completion-reserve keys are invalid. Omission, false, incomplete or malformed fields, and a candidate not emitted by the configured Provider Registry fail startup. account: null is the implicit provider account; explicit _implicit and _ are exact distinct names, and slash-bearing model IDs are preserved without parsing. Registry membership is exact and has no role routing, equivalent, failover, credential probe, or network fallback. /api/providers exposes a strict Registry-backed summary with ordered models and candidates, effective capability axes for every model, and exact HEALTHY, BLOCKED_UNTIL, and COOLING availability variants; each capabilitiesByModel value is the provider/model default plus the implicit-account override from getEffectiveCapabilities(model, null), not an aggregate or any explicit account's effective projection. Explicit account identities remain separate candidate availability entries. The projection never uses or leaks an internal implicit-account sentinel or transport baseUrl. Blocked and cooling records carry positive finite untilMs deadlines, while healthy records carry no deadline. These are recorded states: expiry changes current isAvailable eligibility and availableCandidateCount without rewriting the recorded variant, and all availability resets on process restart. Numeric Retry-After metadata participates only when unit conversion and rounding yield finite nonnegative milliseconds; declined metadata leaves existing failure classification and rate-limit fallback behavior unchanged.
Each workflow-node activation or global-agent operation (Analyst submission or Oversight check) builds immutable stable preparation before any durable ingress effect: a StaticInvocationPrefix, one ordered CompiledInvocationToolContract array, ordered stableBlocks, plus prepared compaction. Card logical operations then construct a new immutable PreparedInvocationContext with separate recordBlocks. The ordered accessor combines stable then record blocks without storing a duplicate array. Prepared compaction validates configuration and numerical invariants first. Finite static prompt/tool exhaustion of the positive trigger or hard ceiling is PreparedCompactionCapacityError; nonfinite/invalid estimates and impossible relationships remain invariant errors. This distinction permits only the Analyst's narrow pre-ingress capacity reuse, not invalid configuration or disabled compaction. The prefix retains the rendered configured agent instructionText and frozen ordered terminalToolNames. The separate compiled tool array retains configured operational tools followed by exactly one workflow-generated emit_result for card nodes, or the terminal-free operational array for the Analyst. Continuations retain the same prefix, compiled tools and stable blocks by ownership, without aggregate hash attestations or a serialized logical-prefix blob. Record blocks are separate per-logical-operation snapshots: before each initial, tool or corrective provider operation, exact deferred invocation authority precedes synchronous current accepted-bootstrap selection. The full accepted body is mandatory even when its descriptor is already recorded; missing required acceptance fails rather than reusing an earlier body. Preparation records exact selection/full exposure only when needed relative to the conversation's latest actual descriptor. This is observation, not delivery, memory, acceptance or write authority. The same operation retains its record blocks through compaction/admission/recovery/pinned retries; mutations await the next logical operation. The composition projector is the singular primary selection and ordering owner. composeContextProjection receives the prepared dynamic blocks explicitly (there is no empty default) and selects them once in prepared order with the accumulated historical summary when present, the existing newest-only recovery/refusal facts, and the uncovered canonical suffix in canonical order. Its request-only provider conversion inserts one concise synthetic system boundary before the first non-dynamic item. The accumulated-summary header uses transiently transported genesis source version and exact covered-through message ID to delimit the accumulated prefix. Summary status and authorization claims concern that prefix, not necessarily the current head; later continuation may supersede them without blanket expiry of still-applicable requirements. Coverage is not a completeness guarantee for prose. No cutoff date is inferred from genesis creation time or message identity. The existing self-contained genesis/source contract is unchanged. The boundary states that actual prepared current-node instruction selects the present workflow step, while historical placement alone establishes no new delivery, transition, execution, or approval and does not revoke still-applicable requirements. The resulting request-only provider-item union contains either an intact canonical row or synthetic context text with role, exact content, origin, and block identity. Synthetic context has no fabricated durable round, timestamp, message, or session-row identity and is never appended to conversation history. Transport wire encoding may differ per protocol, but provider adapters place the rendered static instruction exactly once at the protocol instruction position, map the ordered provider items directly, and project tools only from the compiled contracts. Chat uses ordinary ordered messages; Responses and Codex use equivalent ordered input messages rather than concatenating dynamic or historical system context into the static instruction.
Codex owner-session affinity.
Every invocation carries a transient providerSessionId: SHA-256 hex of JSON.stringify(['saivage-provider-session', projectRoot, sessionId]), using the application-owned absolute project root as supplied and the actual invocation owner's session, not a fresh input UUID or the source conversation's session. Internal summaries therefore have distinct affinity from their primary owner even when consuming that owner's history. The label stays stable for the same root/owner across ordinary turns, retries, activations, compaction and process reconstruction; model/account routing remains independent. It grants no authentication or Responses private-account replay permission.
Codex includes this label as body prompt_cache_key before canonical serialization, hashing, measurement and admission; its session-id header comes from that admitted body's nonempty string key. The exact measured/admitted body bytes are sent unchanged, including for summary probing and final dispatch. Chat and public Responses emit neither affinity field nor header. Instructions, tool/input order, context selection, compaction and private replay semantics are unchanged; affinity is not permission to retain obsolete context or enlarge budgets.
The digest is an opaque routing label, not encryption or a secret; it emits no raw path/prompt/credential/account/tool content and adds no durable field, registry, discovery or realpath resolution. Moving the root changes affinity; equal root/ session names are not globally unique. Stable labels guarantee neither cache hits, retention nor cost/quota savings; source tests prove no live efficacy. See commissioning limits.
Specialized process prompts remain startup-compiled dynamic process context and never enter instructionText. Current-node text is prepared request context with separate structural activation evidence; lifecycle-entry, edge, correction, and recovery text remains ordinary durable context delivered at its existing producer position. Each Analyst submission and Oversight check prepares its selected compiled global role with generated status, reopenable/target status, configured card-type, and urgency vocabulary alongside its selected tool contracts. This vocabulary supplies names, not additional tool authority. Recorded role text remains historical evidence, not regenerated current instructions. Each CompiledInvocationToolContract pairs the provider-neutral wire ToolDefinition—function type, name, description, JSON-schema parameters only—with one frozen ToolResultPolicyTemplate and its exact canonical bytes/hash. Provider definitions never carry internal policy, and adapters never receive or strip it. Tool-local policy bytes/hash validate settlement and reconstruct the policy for unmatched-call recovery. One fixed result/evidence template is declared per tool surface before execution and is never argument-dependent. Exact admission accounting measures the adapter-built canonical serialization of the complete provider request—adapted static instruction, every selected full dynamic block, synthetic historical context, canonical messages, provider tool definitions, wrappers, and requested completion—excluding internal policy bytes that are never sent; admitted bytes and their hash are retained unchanged for send and ordinary transport retries. Oversized prepared context fails capacity admission and is never previewed, truncated, reread, or omitted.
All non-prefix LLM context is one typed ContextBlock with four orthogonal axes: storage (durable canonical conversation state versus one activation_local prepared value), replacement (retain or latest_snapshot keyed by contentSha256, the generic recomputable represented-content revision), audience (primary_and_summarizer, summarizer_only, or evidence_only), and evidence (none, canonical_locator with locator plus SHA-256, or observational_query with tool, arguments, and observed SHA-256). The axes never encode one another and there is no visibility flag. Latest-snapshot selection is by last composition order per key. The Analyst project orientation is exactly one activation_local latest_snapshot block keyed analyst.project_tree: a deterministic bounded compact tree rendered under the orientation and title-preview byte limits in the exact context and compaction limits as compact JSON serialization with UTF-8-safe title previews, running-branch expansion, the exact active path, deterministic wide-parent aggregates, an explicit fresh-observation query marker, and a mandatory root/active-path skeleton whose overflow rejects preparation with AnalystOrientationBudgetError; its constants are sized so the mandatory skeleton of the maximal structural running chain — the project root plus the full depth in the exact card identity contract — fits at allocation-derived identifiers, schema-bounded type-name length, and ordinary config-defined type cardinality. Valid wide type aggregates may still exceed that budget. This typed overflow permits a later explicit fresh submission, not more omission or truncation; topology/current-card inconsistencies and a final rendered-budget invariant failure remain strict poisoning failures. The builder applies existing outbound text redaction to each complete title before its UTF-8-safe preview. title_bytes counts the displayed redacted prefix's UTF-8 bytes, and title_truncated compares that prefix with the full redacted title. Title previews remain limited to 128 bytes and the complete rendered JSON to 8192 bytes; optional-child admission measures the actual redacted rendering, so redaction can change which children fit. Canonical card titles remain unchanged. Its builder owns fullObservationSha256, the canonical SHA-256 over ordered {card_id,version_seq} pairs for the complete strict observation, including omitted cards, plus runtime {status,current_card_id}. This unchanged identity/version/runtime commitment contains no title bytes and remains version-sensitive. Separately, the composer-verifiable contentSha256 hashes the actual final redacted JSON bytes; callers supply neither hash.
Canonical conversation rows carry one exhaustive, mutually exclusive row policy. Every content policy has required explicit compactable; only non-compactable content may carry an optional exact compaction_key. text and model_repair are direct primary-and-summarizer content; a non-compactable occurrence is valid only when it is independent visible configured user content with durable storage, retain replacement, no evidence, and no provider-private mate. content_policy_retry is code-owned retry text; activity is an activation boundary omitted from primary and summarizer prose; model_issue is structural provider failure whose following recovery notice owns semantics; model_recovered is a model-facing synthetic system notice, never evidence-only; content_policy_refusal renders fixed synthetic refusal text and never replays the raw provider response; provider_private is selectable only with its one marked visible mate; tool_call and tool_result form one bundle projecting the pre-execution call template and the settled pair. No role, filename, content match, or adjacency infers protection. An uncovered summarizer_only or evidence_only bundle remains primary-visible; only successful compaction over a validated atomic source prefix may omit it, and an unmatched call is never coverable. OpenAI Responses private/visible rows and tool call/result pairs remain atomic units in canonical composition and compaction. Protocol-owned candidate replay may omit encrypted reasoning items within a selected bundle; it never splits its canonical pairing or duplicates its marked visible mate.
Each new Responses private row records the successful candidate's required producer_account_id: lowercase 64-hex SHA-256 of JSON.stringify(['openai-responses-account', candidate.provider, candidate.account]). This is an opaque local configuration comparison identity, not encryption, anonymization, organization discovery, or proof of credential sameness. Provider namespaces and explicit account aliases differ; null denotes the existing implicit provider account. Model is excluded. Changed credentials under the same alias still compare equal; two aliases sharing credentials compare different. Operators intending a distinct boundary must use distinct local account identities.
Before canonical request serialization, hash, measurement and admission, each Responses candidate compares every private row's producer independently. Equal identity replays all original native items unchanged. Different identity omits only a whole reasoning object with its own encrypted_content property, including that item's summary; nonencrypted reasoning and all other native items remain unchanged and ordered. Native assistant text coexisting with a function call, function name/arguments/call ID, and the later matching result survive exactly once. Generic and synthetic history remain unchanged. Normal routing/failover remains available; successful alternate output records the alternate's identity. Requests and retries do not mutate durable rows or reuse another candidate's projection.
Retained private tail rows preserve exact content/producer, including mixed producers. Covered bundles follow ordinary summary/declared omission; private bytes/producer metadata enter no summary source/prose. The identifier is absent from public transcript/context/tools, request metadata/headers, exchange evidence and app logs. Before public hiding, strict consumption rejects absent/non-string/invalid producer identity even in otherwise format12 data. This independent incompatible payload needs the external-adoption decision, never runtime inference/backfill. External conversion must establish provenance or block.
An equal-account HTTP 400 invalid_encrypted_content without higher-priority failure evidence remains terminal provider_protocol_error. It does not trigger stripping, resending, alternate attempts, compatibility learning or history edits; alias equality cannot guarantee remote decryption success.
Tool-result policy and byte-bounded pages
Tool settlement derives one settled bundle policy from the exact composite call/result identity pair. Mutable current discovery surfaces—list_cards, get_tree, current-section get_card, every general read branch, glob, grep, list_card_versions, and diff_card_versions—are fixed observational_query surfaces whose observational SHA-256 equals the hash of the exact canonical settled result bytes persisted for the provider. Exact historical evidence is exposed only by the dedicated immutable readers get_card_version and read_record_version, whose canonical_locator evidence comes solely from their successful dedicated-reader execution, never from visible-result parsing or a second lookup. Domain failures, unsupported calls, malformed/schema rejection, recovery settlement, and synthetic settlement carry none. Unsupported and synthetic settlement construct their policy template directly and never fabricate locator or observational evidence.
The explicitly cut-over discovery/read/version surfaces pack every provider-visible ToolResult inside the default byte envelope in the exact context and compaction limits. Collection-bearing surfaces and sections use shared deterministic stateless collection positions and UTF-8-safe TextSlice/JsonSlice continuation; bounded scalar get_card sections do not accept collection positions. Immutable get_card_version summary is likewise scalar: omit position, since every supplied position, including all-zero, is rejected; after that error remove position rather than retrying it. Its dependencies and children are collections: omit position initially and copy a non-null next unchanged for the same section over stable input. There are no unbounded arrays, no line-based paging, no duplicate collections, no inline unbounded record previews, and no count-only limits; a page observes fresh state and is never a stable snapshot cursor. Plaintext TextSlice is {content,utf8_bytes,offset_bytes,next_offset_bytes}. Collection-only JsonSlice is {content_hex,utf8_bytes,offset_bytes,next_offset_bytes,total_bytes}: content_hex is lowercase hexadecimal for a decoded-byte interval of the complete outbound-projected canonical JSON item. Consumers hex-decode, concatenate decoded bytes in item/offset order, UTF-8 decode, and JSON-parse the complete item; offsets and lengths count decoded bytes, while the hex payload itself costs two wire characters per decoded byte. Final sizing measures the complete settled success envelope after ordinary outbound projection, and failure to fit one positive UTF-8 progress unit is a bounded tool failure rather than an empty nonterminal success.
Outbound MCP connections and tools
This is Saivage calling configured servers, not an external client calling Analyst. Connection definitions, retained runtime bindings, discovered catalogs and role grants are distinct. Registering or discovering a server grants no tool authority.
mcp_tool_call validates the complete live native CallToolResult envelope at protocol ingress, then carries that schema-inferred result through its internal invocation port to conversion without a second envelope parse. Conversion retains its projection, image, size and publication checks. Successful native text and images retain their original positions in ordered content; non-text/non-image protocol blocks are projected JSON serialized as text at their positions, never fetched or promoted into pixels. Envelope extensions, structuredContent and _meta remain ordinary data.result; indexed native metadata and captures are data.native_content and data.images. Native errors retain safe diagnostics but select no images. Malformed/unknown native blocks fail explicitly. JSON-looking text and image-like structured data stay ordinary data.
For an eligible invocation returning a validated native envelope, isError:true increments the MCP failure counter and publishes one mcp_tool_invocation with success:false; explicit false or absent isError increments success and publishes success:true. Native failure events omit optional error text; safe diagnostics remain in the failed tool result, not copied into the app log. This is the known native invocation outcome, not a guarantee of later projection, image conversion, settlement or model delivery. Later failures do not revise it or replay the call.
Native static PNG/JPEG/WebP require canonical base64 and declared MIME matching decoded format. Each source is at most 32 MiB/40 million pixels; aggregate source images are at most 32 MiB. Orientation and aspect-preserving reduction precede sRGB PNG selection, at most 16 MiB per image. Local optional max_dimension uses the same 1..16384 or original schema as view_image, default 1600, and is not forwarded to the server. Captures preserve actual source/oriented/sent dimensions, applied orientation, resize/scale and requested bound at their content index; no source path is invented. Workspace view_image independently remains PNG/JPEG-only with its actual path.
Stdio JSON frames and HTTP JSON/SSE assembly are bounded at 48 MiB UTF-8 wire bytes. Complete projected non-image text/JSON is bounded at 1 MiB, never truncated or paged. Effects may have occurred before oversize failure. All asynchronous conversion and validation finish before the exact invoking signal's immediate fence ahead of synchronous exact-session image publication. Cancellation during normalization publishes no image and propagates the identical reason, including recognized conversion failures; unrelated errors/publication uncertainty propagate unchanged without later effects. Ordinary projected transport/input error fields remain at most 512 UTF-8 bytes. MCP remains outside response paging and participates normally in compaction/admission, through the shared single originating-call Responses/Codex output and distinct atomic multimodal summary/refine user path.
Deadlines and cancellation
MCP tool execution defaults to 900,000 ms (15 minutes) from dispatch through response consumption. Stdio queue wait and the total agent turn are not part of that budget. Startup retains its separate 180-second budget and catalog refresh its 30-second queue/pagination-inclusive budget. Exact caller cancellation, explicit MCP Stop or application shutdown can interrupt invocation earlier. Timers are event-loop scheduled; containment can extend completion beyond the deadline. No public timeout override is introduced.
An active stdio invocation's runtime-owned deadline or exact caller cancellation synchronously closes that server's admission and discovery, aborts queued work, and contains the existing owner before returning. All sessions on that same server, including idle unrelated sessions and the shared browser context, may be lost; independent server scopes remain unaffected. Queued-only cancellation writes nothing and does not stop another active call; a completely consumed response ends the external-request deadline. Successful containment preserves the first abort reason: deadline failures are executed MCP TIMEOUT failures, while the exact caller reason reaches shared synthetic execution_failed settlement. Failed containment and unclassified errors propagate unchanged; no replay or denial of possible remote effects is implied. HTTP call cancellation remains request-local and does not prove remote execution stopped.
Discovery and retained connection lifecycle
MCP lifecycle uses mcp_server_control({serverName,action:'start'|'stop'|'restart'}) and read-only mcp_tools({serverName,toolName?}), strict nonempty names and no extra fields. Discovery returns current names/descriptions and exact input schemas through outbound redaction and runtime connection bindingId/catalog catalogId together from one snapshot; optional exact tool selection avoids catalog overflow. If redaction would alter an input schema, discovery fails rather than advertising altered validation. More than 1 MiB fails explicitly, including a single oversize schema; no schema truncation. Discovery grants neither invocation nor control. Unknown owners/config entries fail explicitly; stopped/not-ready discovery exposes no phantom capabilities.
Start freshly reads only the exact selected effective MCP config entry; unknown or disabled entries reject. Equivalent ready owners return running, overlapping starts reject busy and changed non-contained owners require explicit stop first. Successfully contained owners can be replaced; failed containment owners are never replaced. YAML startup autostart remains unchanged; dynamic definitions remain stopped on host restart. Runtime config edits alone change no live MCP: explicit stop/start or restart reloads only that selected entry; unrelated routing/workflows still require application restart. Stop immediately fences the exact retained owner and joins startup/calls/containment, never waiting behind global reconciliation. One 180-second start budget includes package fetch, matched browser installation, launch, initialize and every discovery page. No per-phase ten-second timeout remains. Streamable HTTP admission uses initialize, the initialized notification and complete tools/list discovery, with no separate HEAD prerequisite. Cancellation/deadline closes admission, awaits direct containment within inner startup and joins stop only from untracked outer completion, avoiding self-join. Failed start and containment are errors, not installed/ready stubs; no retry or implicit restart. Application closure synchronously fences new starts/calls, aborts starts and closes all retained owners before joins and clearing. Project Stop leaves MCP alive.
Restart requires an exact retained owner, even if stopped; a never-started definition alone returns 404 and requires start. It fences and joins that captured owner's existing stop before freshly starting the selected current definition. Failed containment retains the closed owner and permits no new launch. After the join, manager admission, exact caller cancellation and captured-owner identity are checked before replacement; a changed owner conflicts (409), rather than being adopted. The old runtime is never reopened. Simultaneous restarts of one captured owner cannot both install replacements. Independent explicit actions remain non-atomic: a concurrent stop may complete before the restart's subsequent start; no exclusive restart or stop-wins guarantee is offered.
Pre-entry cancellation has no effects. Cancellation during the stop join does not abandon containment, but prevents the following start. After successful stop, definition rejection, startup failure or cancellation may leave stopped/failed ownership; there is no restoration, compensation or automatic retry. Startup keeps its existing containment/error semantics. A returned lifecycle success remains a known result despite later cancellation. Restart success is {serverName,status:'running',toolCount}: discovery-complete readiness at completion, not continuing readiness or target continuity. Discover again for fresh binding, catalog and schemas before calling. Stale/queued calls never dispatch to the successor; interrupted dispatched work may have unknown effects and is never replayed. Known completed responses retain their original schema, source and invocation attribution. Stdio containment owns the local runner scope; HTTP stop only aborts local requests, not the remote service. Neither remote restart, target reset nor target survival is promised. Publication uncertainty permits no follow-up start, read, logging or cleanup; unclassified failures remain unclassified.
Authenticated POST /api/mcp/servers/:name/restart (mcp.restart) accepts only {} and delegates to the same operation as the selected control tool, with existing lifecycle auth/error boundaries. No new permission or default role grant is added.
Independently selectable mcp_server_register({serverName,connection}) persists a strict stdio command/args/env or Streamable HTTP URL without launching. mcp_server_unregister({serverName}) removes only a dynamic definition, then synchronously fences its captured owner before awaiting containment. Admission/collision errors have no effects. Removal followed by containment failure leaves the definition absent and the old closed owner retained; register/start cannot replace it. Publication uncertainty permits no follow-up, result, logging or containment. No enable/disable tools or card-tree lifetime rules are added.
Dynamic definitions and catalog identity
Independently selectable mcp_server_replace({serverName,connection}) wholly replaces an existing exclusively agent-owned definition, including a transport switch. Omitted args/env disappear; dynamic values remain literal, without YAML interpolation or auth-profile lookup. Operator entries, including source collisions, and missing dynamic entries reject. The operation requires no runtime, a pristine idle/unstarted runtime, or completed successful containment. Starting/running, queued/active work, closure/stop join and failed containment reject before publication, regardless of displayed stopped status. Explicitly stop/join a used server first. One synchronous map publication precedes retirement of its old runtime; success is only {serverName,replaced:true,status:'stopped'}, with null binding/process and zero tools in subsequent observation. Replacement does not stop, launch, reconcile, refresh or replay. Start/discover separately afterward; even identical replacement retires the old binding, and stale binding/catalog calls fail before dispatch. In-flight known responses retain their original attribution; HTTP containment remains local only. Publication uncertainty permits no retirement, result, observation, logging, retry or cleanup. This adds no default grant, REST topology endpoint, editor, autostart or durable-format change.
mcp_servers({serverName?}) independently observes definitions and retained owners, bounded at 1 MiB (select an exact name on overflow). Common observations expose source operator|agent|null, starting|running|stopped|error, containment failure, binding and safe process association. Running means discovery complete; starting exposes no ready tools. Null source means an unregistered retained owner. mcp_tool_call strictly requires both discovered identities and validates against that exact catalog at dispatch, inside the stdio exchange queue or immediately before HTTP dispatch. Stale identities fail without sending the call, retry or replay. Possession grants no tool permission. Prepared generic gateways stay frozen; configured card/global participants may independently select management, refresh, observation or invocation. Oversight remains observation-only and admits no MCP tools.
Independently selectable mcp_catalog_refresh({serverName,bindingId}) re-lists only an already-ready exact configured connection, without initialization, restart, installation or configuration reconciliation. No default role gains this permission. One 30-second budget includes queue wait and every page; overlapping refresh fails busy. Complete validated catalogs replace the runtime snapshot with a fresh opaque UUID, even if unchanged, while keeping the connection binding. Partial pages never publish. Discovery observes the last successful catalog, not guaranteed remote continuity. Classified read rejection retains that snapshot where the connection remains usable; interrupted active stdio exchanges close admission and join exact containment, while queued cancellation leaves the preceding call alone and HTTP cancellation is request-local. Stop prevents late publication. Successful publication is a known result, not rewritten by later cancellation. Already-dispatched calls retain their original schema/response ownership, including HTTP results arriving after refresh. Remote schemas remain discovery data, not native prepared tools; the model must discover and author a new call after a stale rejection. Notifications, native projection and atomic remote-catalog guarantees remain out of scope.
Stdio processId correlates with the shared runner's mcp:<name> runtime-owned service_infrastructure launch, not an OS PID or transferable capability. Protocol streams are not_captured; ordinary activation/session-owned wait_process and kill_process remain denied. MCP stop/unregister and application cleanup own settlement and termination. Natural terminal consumption retires the ID. HTTP has no local process ID; disconnect contains local requests only, not the remote target. Arbitrary already-running process attachment is unsupported. Env values are structurally suppressed and URL/command secrets redacted in supported observations; Files refuses the connection-definition file and resolved aliases.
Authenticated POST /api/mcp/servers/:name/start and /stop have strict empty object bodies, no commands/args/env, and successful {serverName,status:'running'|'stopped',toolCount}. Unknown is 404, disabled/changed/busy conflict 409, invalid body 400; failed start or containment is an internal error. They use existing operator authentication and shared route registration; no browser-control UI is added.
The runbook pins official Playwright MCP 0.0.83, its matched alpha engine and Chromium 1247, not a core dependency or unrelated system browser. Child cwd and negotiated MCP root are the project workspace; automatic outputs use a dedicated project temp directory. Negotiated stdio roots/list is available throughout that connection, independent of frame grouping, discovery pages, idle intervals and successive calls. This changes neither deadlines/cancellation nor the no-replay contract. Installer output goes to stderr. Headless isolated context is shared across admitted agents, not per role/card or durable session continuation. Establish the page/tab needed and expect context loss after stop/restart/active stdio cancellation. Native screenshots omit filename and supply discovered scale:'css'; explicit filename is text-only. No persistent profile, CDP/extension attachment, unrestricted file access or experimental WebMCP. Browse only non-secret pages, never credentials/provider configs/authenticated operator pages; pixels have no automatic secret-removal promise.
Discovered schema dialect
MCP argument validation uses the complete unchanged discovered JSON Schema object at actual invocation, with root type:'object'. Absent $schema means 2020-12; https://json-schema.org/draft/2020-12/schema explicitly selects that dialect. Explicit http://json-schema.org/draft-07/schema selects draft-07. Both declared URIs also accept an empty fragment (#). Other declarations, including non-string values, fail with bounded schema_unsupported diagnostics before tools/call. Malformed schemas and unresolved references fail compilation; validation never coerces types, inserts defaults, removes properties, fetches remote schemas, guesses a dialect or tries another compiler. Dialect-less draft-07-only schemas must declare draft-07 at their external producer. Fingerprints cover the whole received schema, including declarations and extensions; discovery does not prune it. The negotiated protocol remains 2025-06-18; this boundary adopts the explicit schema-language rule clarified in MCP 2025-11-25, not a claim that June specified it.
Native MCP production reuses the strict format-12 ordered-content contract without changing descriptor/layout or retained-data interpretation. Historical ordinary MCP data stays opaque, never normalized into native content. Configured runtime lifecycle/discovery uses the same retained owners; the pinned browser recipe is in the runbook. Neither native production nor recorded descriptors prove model perception.
Workspace discovery and provider-facing tool evidence
Process results at actual primary use
At the direct canonical branch of primary provider conversion, successful current process rows are strictly parsed and pass through validateProcessToolResult() before copying. For a done process (status !== 'running'), each URL is omitted independently only when its corresponding stream is complete; running and partial streams retain their references. Every successful process copy is canonically serialized even when neither URL is omitted, and only that copy's result_content_sha256 is recomputed before agentMessageSchema acceptance. Failed results and other tools pass unchanged. Durable history, live/operator activity, and summarizer source retain both URLs and the original exact row bytes; prepared blocks, retained instructions, and protected-prompt order are untouched. Chat, Responses, and Codex build and measure their actual candidate requests only after this copy. Structurally current hash-consistent retained data can therefore fail at actual primary use for invalid identity, URL, head stability, or canonical-envelope size, but formatting differences alone do not fail and no startup audit or durable normalization occurs.
Agent read, glob.directory and grep.path accept native absolute filesystem paths, including ordinary process-accessible files outside the project. Native relative paths default to the project root and still reject parent segments; explicitly named ordinary symlinks may resolve outside it. Native filenames retain colon, percent, question-mark and hash punctuation; scoped URLs retain their strict existing grammar. Native and system:/// spellings apply identical secret/private-source admission to lexical and resolved identities before content access. Known credential sources and generic .saivage namespaces remain refused; explicit contained project:///, owning tmp:///, read-only work:/// and logical record:/// retain their resource contracts. Physical paths never manufacture record observations. Mutation, Webfetch-save, image selection and operator Files admission are unchanged.
Direct ordinary build/dependency files are readable. Recursive discovery and directory listings exclude fixed dependency/build/cache and .git entries independently of secret-path refusal. Recursive traversal does not follow encountered directory symlinks; an explicitly selected symlink root resolves normally. External native search results are reusable absolute paths; project-native results remain project-relative and explicit URL scopes keep their display forms. Glob matches relative to its selected root or displayed path; grep retains include filtering. Missing/permission errors, metadata/directory behavior, binary refusal, inline size bounds and UTF-8 continuation remain the owning tool outcomes. Work text is projected before packing; settlement projects data/errors before both provider delivery and durable tool-result publication.
glob and grep accept strict optional position:{item_index,item_byte_offset}, response_bytes from 512 through 32,768 (default 32,768), and max_results from 1 through 1,000 (default 200). Each call performs one complete fresh read-only traversal of its eligible discovery domain, counts the exact total, retains only the contiguous candidate window beginning at item_index, and returns matches:{total,position,returned,next,items}; it writes no stash or other result artifact. Copy matches.next unchanged for the same query and stable input. A consumed nonzero item offset must name an existing item, be strictly below its complete projected canonical-JSON byte length, and lie on a UTF-8 boundary; terminal zero-offset positions return a measured empty page. Filesystem traversal is depth-first with sibling names ordered by explicit JavaScript string comparison, and declared record filenames use that same order. Record scope reads only effective current declared records; work scope reads supported process/stash/work paths. Glob data is {matches}. Grep data is {matches,content_truncated,max_line_chars:2000}; content_truncated concerns only eligible line suffixes beyond the searched 2,000-character prefix, previews remain at most 500 characters, and it does not describe collection completion. Stable-input continuation is not a snapshot guarantee, and current search bounding does not promise that compaction will never occur.
The optional exact project-root file .saivage-search-ignore defines additional project-directory exclusions for recursive native glob/grep discovery resolving inside the project (including absolute and aliased roots) and project:/// discovery only. External native searches do not discover or load another project's policy; explicit system:/// retains its policy exemption, not a confidentiality exemption. Saivage direct-reads that file once per project-directory search. Absence and an empty/comment-only file add no exclusions. The file is fatal-decoded as BOM-free UTF-8: malformed UTF-8 and any leading UTF-8 BOM fail the consuming search through the ordinary bounded expected-failure result. Lines accept LF or CRLF, trim surrounding whitespace, and ignore empty lines and full-line comments whose first non-whitespace character is #. Every other line is one literal project-root-relative slash-separated directory path. It has nonempty segments and no leading/trailing slash; ., .., repeated separators, backslashes, colon, control characters, glob characters * ? [ ] { }, and leading ! are invalid. There are no inline comments, escapes, negation, URL/absolute forms, existence checks, or Git/.gitignore inference. Invalid syntax reports the policy filename, fixed reason, and line number without policy content; non-absence read errors and decoding errors likewise fail rather than enabling unrestricted traversal. The file is never repaired or rewritten by a read.
For normalized project-relative directory path p, entry e excludes p exactly when p === e or p starts with e + '/'; matching is literal, case-sensitive, and always anchored at the project root. The walker tests the requested starting directory and each descendant directory before its first enumeration. An excluded starting directory returns an ordinary empty successful page. A configured path that currently names a regular file does not exclude that file. Existing fixed/security exclusions remain independent and cannot be relaxed. Explicit regular-file glob/grep, all read operations including exact files beneath excluded roots and .saivage-search-ignore itself, nonrecursive directory/Files browsing, and record:///, tmp:///, work:///, and system:/// search do not load or apply this policy. Required tracked or untracked fixtures remain discoverable only when projects keep them outside excluded roots; mixed artifacts directories must not be excluded wholesale merely to shorten search or conceal failed evidence. Policy content is part of stable input for continuation, and a later invocation observes edits without restart, cache, generation, retry, or cross-call stabilization. read_record_version(card_id, record_name, version) follows committed accepted predecessors to exactly the accepted document version from the immutable record URL's v=N. Accepted document versions are consecutive from 1, independent of mutable head/draft revisions; absent accepted selectors are not found. It returns the selected acceptance's immutable identity/content/hash and <version_url>#entry=<entry_id> locator unchanged across pages. Current mutation results instead expose revision, reusable current_url, and nullable accepted_version_url, never an immutable draft URL or head_entry_id. Diagnostic, control-action, and agent-session tool result bodies remain arbitrary and are bounded only when exact compaction materialization and request admission admit a subsequent provider request. Process results instead use the strict bounded contract and primary-only copy above.
Compaction construction and exact provider admission
Capacity arithmetic and frozen preparation
For a candidate declaring context window C, output limit M, and exact positive configured output request O <= M, the shared arithmetic is I = floor(U * C) - O. Binding excludes missing limits, unsupported output requests, and nonpositive I, then freezes the maximum eligible I for each participating route. Preparation validates positive static capacity and derives floor(T * I) - S as the message trigger, I - S as the message hard ceiling, and floor(F * I) as the tail budget for static estimate S. Requested output is exact and is never clamped. Prepared stable card context contains canonical {cardId,cardType,title} orientation, the complete selected current-node text and actual parent direction where present. It contains no node-entry bootstrap body. Each newly armed initial/tool/corrective logical operation consumes current accepted bootstrap and declared records once per distinct name, after exact deferred invocation admission. Its immutable record blocks then remain fixed through preventive/local/authoritative compaction, admission and pinned/transport retries. A subsequent logical operation may select B in place of A without changing the role/tools/node/parent. Globals retain bounded ordinary preparation without automatic card-record feeding; confirmed restart is model-free. Tool effects, canonical results and broadcasts remain outside retry seams. Current-node text creates no new durable row. Previously written node-looking text remains unchanged, unrecognized ordinary history and can still appear in suffix or accumulated summary; exact textual duplication does not make it current. The complete prepared node block participates in ordinary request measurement, so an oversized node fails admission rather than being truncated or compacted away. Tool-result and repair continuations retain the activation's exact prefix, tools and stable blocks while refreshing source input ID, canonical projection and the new logical operation's record blocks. Application composition retains candidate identity only in the summarizer's fixed one-candidate invocation route; runtime/Analyst policy, prepared policy, compactor arguments, and prompts are identity-free. The singular ConversationLLMActor requires prepared input for every persisted named-agent provider turn and rejects an unprepared call before transition or persistence.
Structural workflow compilation remains provider-independent. After canonical admission, startup binding derives the distinct participant set: selected global Oversight even when scheduling is disabled, selected internal Analyst unless startup mode is external-only, and every actual agent node in every selected graph. Action nodes acquire no model. Each participant needs at least one capability-compatible configured candidate with positive usable input. Missing limits and output-ineligible candidates do not inflate the maximum. Unused catalog agents are exempt only when they are neither selected globals nor selected graph participants. External-only mode omits internal Analyst model binding, not its structural declaration, required index/history or startup settlement. Binding uses the same immutable Registry snapshot as candidate admission and performs no credential, availability, auth or network probe.
Self-contained successor and protected context
Compaction replaces one validated current conversation projection with a strictly smaller known-published successor and never rewrites an append-only body. The successor segment's compacted genesis is self-contained. Its accumulated history is exactly nonempty summaryText, ordered protectedPrompts with original exact message and first-extraction {segmentVersion,rowIndex} coordinates, and requiredModelFacts with two nullable bounded recovery/refusal slots plus required descriptor-only latestRecordObservations. Observations retain original source coordinates/time, not body/template copies or new occurrences. Their size grows with actually observed record/view keys; no eviction or fixed-memory guarantee exists. Genesis retains identity/session/version/time, source:{version,filename,covered_through_message_id}, and explicit continuation: between_rounds or inherited_open_round carrying only the validated marker ID, canonical input UUID, and active initial/repair segment kind. The index compacted entry retains exactly kind, source_version, source_filename, and covered_through_message_id; exact index/genesis agreement and predecessor-catalog checks remain strict. Physical suffix rows and continuation, not original-tail metadata, govern current messages and round continuation. The protected list size is variable and may grow without a configured bound. The successor identity is preallocated once; the validated candidate and the published genesis share that exact identity and timestamp, and the returned in-memory projection is exactly the published one. Publication-unknown escapes unwrapped before any append-error conversion.
Compacted genesis is the sole self-contained prior-history input to the next compaction: generation N+1 consumes inherited summary, protected prompts, and required facts before newly selected source components and never opens a predecessor body for current projection. The owning read validates strict row policy, session, unique/disjoint IDs, ordered coordinates, and source-version bounds. Publication's existing fresh source read checks current selection and cutoff, validates a nonempty contiguous source prefix with complete tool/private bundles in exact source order, compares the exact derived protected records including content/coordinates, derives latest required facts, and builds retained rows directly from that source suffix. No serialized group proof or checksum lineage remains. One call-local sequential refine accumulator owns a genuinely nullable accumulated summary: it is initially the inherited summary or null, and every successful content-bearing call returns its complete replacement. EMPTY_COVERAGE_SUMMARY is candidate-facing text for structural-only coverage with no inherited or current-segment summary material; it is never stored as the accumulator, labeled as prior history, reduced, or sent to the provider. A structural-only increment advances exact call-local coverage without provider I/O, carries genuine nullable accumulated semantic history unchanged, and uses EMPTY_COVERAGE_SUMMARY only when no accumulator exists; the resulting candidate remains subject to ordinary prospective validation and strategy acceptance. Recovery/refusal slots are derived newest-only from inherited slots plus newly covered rows. Ordinary refine folds a superseded slot's historical meaning into the accumulated summary before replacement; local declared omission does not preserve that superseded narration. Unchanged slots are carried without re-summarization, and each recovery/refusal slot is bounded so neither code-owned text nor marker list grows across generations. Separately, record observations fold inherited and actual current observations by source order per card/record/view, with original coordinates/time and no body/template text. Their retention is independent of prompt protection and can grow with observed records. The exact code-owned recovery notice text and refusal replanning text each occur at most once in any materialized request; a covered refusal locator remains resolvable to the synthetic compacted fact only while it is the latest covered one, and a superseded older marker is no longer a current-transcript locator. Removed raw provider-response bytes are unavailable and never reconstructed.
Source endpoints and candidate selection
Successful compaction over a validated atomic source prefix is the sole omission authority; no durable coverage attestation is required. A summarizer_only or evidence_only bundle below the trigger remains primary-visible even after its round closes—closure alone changes only eligibility—and a closed round stays fully visible until a later successful compaction covers it. The logically open round is never treated as complete because its physical rows are all observed. Explicit open-round inheritance may publish an empty physical tail after strict structural selection of every currently safe settled row through ordinary semantic refine or declared local omission. An unmatched call cannot be covered and remains in the exact tail. Before construction, the preferred endpoint accounts for all canonical agent-session conversation rounds, including the newest open/inherited round, against the source-tail target floor(I * tail_fraction). Rounds are defined by exact activation_open markers: the next marker closes its predecessor; they are not provider calls, tool continuations, or card lifecycle activations. When the newest round's estimate is at most the target, selection walks backward retaining wholly fitting newest rounds. At the first older straddler, keep_straddler_verbatim retains that round whole and compact_straddler covers it whole, then selection stops. No older round is partially split. The desired covered prefix includes the preamble and earlier rounds; this branch uses that exact boundary only if safe, otherwise zero. Only when the newest round exceeds the target, selection walks its rows backward, retaining the newest suffix until its existing token estimates reach or cross the target. The crossing row is retained whole; exact equality stops without adding another older row. At zero target the desired suffix is empty. The desired prefix includes all earlier rounds/preamble and the older part of the newest round, then snaps backward to the greatest validator-owned safeSourcePrefixEnds count at or below it, or zero. Both snap settings use this same conservative interior policy: tool call/result and private/visible bundles are never split, so a large atomic group may retain more than the target or the entire newest round. Existing zero-token structural/private estimates remain unchanged. Compaction selects at most two distinct positive increasing safe endpoints: this preferred endpoint and the unchanged furthest legal closed/open atomic fallback. Empty inherited rounds contribute no artificial endpoint. Equal, absent, and zero endpoints are omitted; no endpoint ladder, all-cutoff array, intermediate-summary search, or candidate memo exists. Both endpoints advance the same accumulator over one contiguous prefix, consuming only newly covered rows with the genuine accumulated summary; repeated compaction consumes only current segment and inherited genesis. The source-tail target is a preference, not a capacity or retention guarantee: summary output can force fallback covering the whole settled tail, or construction can fail. No API, configuration, schema, or durable-format change or reset is required by this selection policy. Preventive compaction accepts a strictly estimated-token-shrinking hard-budget-fitting preferred candidate immediately when it reaches the trigger; otherwise it retains that complete candidate while trying furthest, then chooses the smallest qualifying estimated-token projection even above the trigger. Authoritative recovery retains its first strict estimated-token reduction. Local exact admission retains the smallest complete image-aware accounting-byte projection strictly below rejected, tying toward furthest; eligible later output/size/capacity failure does not destroy it. Measures include encoded image-part weight and raster reservation, while text-only values remain unchanged. With no qualifying complete candidate, authoritative and local successful construction return no_smaller_projection; expected failed construction is distinct. Infrastructure, protocol, cancellation, invariant, and evidence failures never use a retained candidate.
Every ordinary refine request contains, in order, the dedicated replacement-summary instruction, every selected frozen prepared current block labeled prepared_context, every currently selected protected instruction labeled as full read-only orientation, the genuine accumulated history when present, and labeled new source. Static agent instruction is not duplicated. Protected rows remain in atomic source-prefix validation but are excluded from summary source. A later false occurrence of an exact key releases only the older inherited occurrence; ordinary refine admits the released exact text once on the first demanded successful positive advance, while the new occurrence remains orientation whether covered or still in the tail. Declared omission may discard the released older occurrence's compactable narration instead. Anonymous false occurrences never replace one another. Without a positive legal endpoint, release alone publishes nothing. The ordinary instruction preserves attribution, actual work and decisions, unresolved uncertainty, evidence references, important unrecorded information, and still-applicable requirements; distinguishes proposed from executed and draft from accepted or approved; and states that a final success from sequential newline-separated commands does not prove earlier commands passed (pipefail concerns pipelines). Preservation-first condensation prioritizes final applicable decisions, settled effects, uncertainty, original ownership and useful evidence anchors: superseded operational chronology may collapse only if the next authorized action remains unchanged, retaining distinct failure classes, failed-check qualifications and immutable release/source identities where needed. This guidance adds no completion-validity condition or fidelity guarantee. Tool arguments and results are separate ordered source components; evidence-only content uses its existing evidence projection, and private result bytes never enter summary source. The actual-use packer labels each immutable projected component with identity/kind, exact UTF-8 byte ranges at code-point boundaries, total bytes, and omitted_source_bytes=0, without a source-component checksum; empty components carry an explicit empty range. It exact-admits whole-component prefixes at geometrically growing widths, clamped to the remaining count, until EOF fits or an endpoint rejects, then bisects component widths to the greatest fitting whole prefix. A cursor already inside text starts with that component's remainder. Only the next rejected nonempty text component is refined: test its next code point appended to the fitting prefix, then double and bisect code-point endpoints to the greatest admitted range, reusing the known rejected whole endpoint. A rejected minimum, empty component, or indivisible image-bearing component flushes the fitting prefix; without one, it fails capacity. Images and their accompanying ordered text/metadata are never split or skipped. Sent ranges reconstruct every projected component exactly once without gaps or overlaps. The exact winning input and admitted serialized body/hash are retained, without accumulator-side regeneration; existing service re-admission verifies the hash before send. Every probe includes actual wrappers, escaping, range-label digits, prepared orientation, genuine accumulator, and requested output through the real serializer. The fixed summarizer independently uses I_s = floor(U * C_s) - 2000; the primary route ceiling never constrains it. The successful packed admission carries that same configured U with its serialization/hash through normal, split, and corrective folds, production composition, and service admission. Send checks the one candidate's retained request hash against that admission and uses unchanged bytes, without another margin or repacking. Prepared orientation and inherited summary are never split, trimmed, or hypothetically reserved. A representative production-composition test covers the 1,050,000-token Astra preparation and 120,000-token Sol summarizer case at U=.80, including full-window source, both endpoints, correction, strict coverage, and the unchanged 16-call ceiling; this is mechanical feasibility evidence, not a universal compactability, provider-token, quality, or latency guarantee.
The retained offline fact/action evaluation separately reviews one frozen summary against bounded independent source evidence. Its observed judgments, authored C01 continuation controls and mechanical regression evidence are distinct; none establishes newly generated or changed-model summary fidelity or strengthens runtime quality guarantees.
Summary cancellation, refusal and declared omission
Cancellation is observed with its exact reason before compact() reads or prepares source, before each accumulator advance and send, at component-range construction boundaries, and before and after each whole/fine serializer probe. These synchronous fences do not make serialization interruptible by event-loop cancellation or promise a responsiveness deadline. One compact() permits at most 16 logical refine invocations across normal folds, both endpoints, and the single optional corrective regeneration. Immediately before each invocation it checks abort, admission, and the ceiling, increments, then sends; a needed seventeenth call is not made. Ordinary Invocation Service transient attempts remain internal to one logical invocation. Every strictly increasing advance computes its next summary, inherited-fact fold markers, and cutoff transactionally and commits that call-local state only after the entire advance succeeds. The accumulator retains only the latest genuine fold's pre-fold inherited summary and exact source ranges so one correction can regenerate that fold with a fresh UUID, stronger 6,000-byte concision target, and fresh exact admission; it never summarizes rejected output or drops source. A corrected inherited fold retries a blocked next-minimum-range probe from the same unconsumed cursor. After each successfully validated fold is recorded complete, cancellation is observed again before packing resumes. Failure leaves the preceding committed advance state unchanged. Eligible output/model/size/capacity exhaustion may select an already completed qualifying candidate; all other failure classes terminate exactly. Invocation and progress accounting is not rolled back: admitted invocations and started folds remain counted, while only successfully validated folds are recorded complete. Summary exchanges retain the internal evidence ID internal:compaction-summary:<sha256-of-source-session> and internal internal-compaction-summary label, but are physically stored in the known canonical source session's provider-exchange stream, not in an internal conversation or the app log. They are not configured agents, never enter Agent inventories, and remain separate from primary provider-attempt state. Refine calls execute sequentially, with at most one summarizer provider call in flight. One correction is eligible only for empty/tool output, Chat length, actual typed provider output-token capacity, a completed fold blocking the next minimum range, or a final no-reduction/residual-capacity obstruction. Actual provider input capacity gains no correction. Exhausted owned result validation, construction capacity/fold limit, actual typed provider output/input capacity, and completed nonreducing/over-hard-ceiling candidates permit a qualifying completed candidate or local omission. The same narrow decision applies at endpoint and final-correction boundaries. Cancellation, transport/auth/rate-limit exhaustion, unknown/parse/protocol, configuration/capability/invariant, strict source/image/projection/freshness, internal-summary admission-integrity and evidence errors permit neither retained success nor omission; capacity-like prose or nested causes grant no eligibility.
The first actual typed summary content_policy (including cyber_policy), exact isPromptPolicyRejection flag, or typed completion content_filter ends summary attempts immediately. There is no identical policy retry, correction, prompt change, range search or model/route failover after refusal. SummaryPolicyRefusalError carries the exact summary request UUID and original cause; only actual provider-attempt failures and typed completion validation produce it, not prose or recursive cause searches. The summarizer captures the provider outcome separately, publishes its real exchanges exactly once outside refusal/construction catches, then converts eligible refusal. Successful filtered completion retains its actual successful-exchange metadata. SummaryEvidencePublicationError retains the exact publisher exception as cause so even a misleadingly typed exception cannot become fallback eligibility; unknown publication escapes unchanged before any follow-up or progress cleanup. Non-overload recoverable failures retain their four-failure budget. Confirmed overload repeats the same packed summary bytes/hash/UUID indefinitely inside one logical call. The shared bound remains 16 logical calls and one correction, not 64 wire attempts. Overload adds no completed fold, correction or omission eligibility. Overload followed by actual refusal permits only existing completed-candidate/local omission; auth, protocol or exhausted finite failure permits no omission. Primary triggering-context exchanges remain separate.
When no qualifying fully completed ordinary candidate remains after an eligible outcome, local omission evaluates only the already selected positive preferred endpoint, then the existing distinct furthest endpoint if needed. It accepts the first qualifying local candidate even if furthest would be smaller, performs no provider call, source repacking, image materialization or historical read, and adds zero completed LLM folds. Its fixed code-owned nonempty notice declares that covered compactable narrative and all inherited compactable summary prose were omitted without semantic preservation, establishes no tool effect/completion/approval/authorization, and recommends only ordinary authorized source reads when context is missing. EMPTY_COVERAGE_SUMMARY remains distinct. Exact active protected occurrences/extraction coordinates, newest canonical primary refusal/recovery facts, activation input/marker/initial-or-repair continuation, atomic bundles, unmatched call and physical suffix, frozen complete accepted brief/full actual node/static instruction/tools, fresh source and one successor identity remain mandatory. No permanent omission mode or maintenance-refusal slot is added. Prior indexed segment bytes remain unchanged and available only through existing strict historical reads, not automatic active recall.
Preventive local omission must strictly reduce complete prepared estimated tokens and fit the message hard ceiling; local exact admission must strictly reduce complete image-aware accounting bytes; authoritative recovery must strictly reduce estimated tokens. Ordinary candidate preferences remain unchanged. Only known successor and index publication returns ordinary compacted and permits existing exact primary admission or the existing single authoritative retry. A successful result carries nullable call-local {summaryInputId} refusal metadata: its existing invocation operation passes that to any later maintenance strategy in the same primary invocation, which skips summary construction entirely and uses only local omission. It expires with that invocation, not a new synthetic primary turn, durable latch or registry. A genuinely later new turn starts fresh. Maintenance is not task-safety classification or provider approval, changes no primary-refusal policy, and guarantees neither fit, semantic fidelity nor useful remaining context.
Only when neither a retained completed candidate nor local omission qualifies does the consuming actor handle the unified refusal before generic provider/construction wrapping. No positive safe endpoint publishes nothing; remembered refusal retains safe settlement, owned construction failures retain CompactionSummaryConstructionError, other provider failures retain their provider identity, and merely nonreducing successful construction in recovery returns no_smaller_projection, never uncompressed continuation. Preventive and local-exact failed-refusal strategies create no primary turn/error row. Authoritative terminal refusal first projects the real original primary context-failure attempts exactly once with null terminal output; successful authoritative recovery instead retains existing combined-attempt ownership. Failed summary exchanges are never reprojected as primary attempts. Autonomous-card failure returns the unchanged strict compaction-summary-blocked literal/schema with source session and summary UUID through the ordinary BLOCKED path. Global failure returns the existing fixed-safe error and settles its lease: Analyst appends the ordinary safe notice and accepts a later explicit submission; Oversight settles failed and creates a fresh check only at its later normal cadence. Successful omission does none of that blocked settlement. Publication uncertainty remains fatal before handoff, notice, lifecycle settlement, cleanup, reuse or retry.
Exact context and compaction limits
text
constant.analyst-orientation-max-bytes = {"unit":"bytes","value":8192}
constant.analyst-title-preview-max-bytes = {"unit":"bytes","value":128}
constant.tool-result-envelope-max-bytes = {"unit":"bytes","value":32768}
constant.summarizer-completion-tokens = {"unit":"tokens","value":2000}
constant.summarizer-output-target-bytes = {"unit":"bytes","value":12000}
constant.compaction-refine-max-invocations = {"unit":"logical invocations","value":16}Compaction configuration, endpoint choice, estimates, prepared static context and call-local refusal metadata are not persisted in genesis. Composed record facts belong to conversation12, not the provider-evidence envelope. Their historical introducing contracts/source parent are in the record document0 format entry. The current format reference, not those historical introducing numbers, determines today's baseline. Old/mixed data blocks. Adoption requires the separately authorized complete reset or external offline workflow; equal text proves neither origin nor authorship, acceptance or observation. No converter/backfill or binary-only downgrade is supported. Configurable record initialization is delivered through selected node-entry preparation, not immediate card-creation scaffolding. Source completion authorizes no live action.
Preventive actor compaction assigns each visible row weight max(1, ceil(UTF8-byte-length(projected visible content plus the existing aggregate structural text) / 4)); provider_private rows have exactly zero actor weight, their marked visible projection carries the conversational weight, and protected bundles remain indivisible. This row-derived weighting drives only the preventive threshold: it is neither complete-wire accounting nor fit proof. Separately, each eligible protocol candidate builds and canonical-serializes its actual complete transport body once and measures ceil(UTF8 bytes / 4) plus raster reservation for each emitted image, so a Responses estimate includes private output bytes and full image data URLs present in that exact body. Image-incompatible candidates reject before unsupported serialization. That complete serialized body is reused unchanged for admission, hashing, retries, and final send. For every prepared ordinary, pinned, and retained-recovery request, each candidate compares this exact estimated input with its own I; equality fits. Any fitting candidates execute in original route order, while smaller nonfitting fallbacks are excluded and do not force compaction for a fitting larger candidate. Retained recovery membership never expands after compaction. The byte heuristic is deterministic and best-effort, not a provider-token fit proof; Codex intentionally does not transmit maxTokens, though exact O remains admission authority. Prepared autonomous requests omit ordinary modelParams.maxTokens; their one prepared requestedCompletionTokens controls hard ceiling, candidate context/output admission, model authority, and the Chat and public Responses wire output limits. Only genuinely absent utilization authority defaults to 0.80, yielding floor(.80 * C)-O. Explicit prepared or unprepared utilization—including configured summary U below .80, above .80, or exactly 1—is retained unchanged in service limits and execution bindings. An unprepared summary carries its successful admission's U, not fabricated prepared compaction. The default is heuristic headroom, not a token-fit guarantee. The Codex backend transport uses it for admission/options authority but intentionally serializes no output-limit field.
Every Invocation Service attempt, including retries and direct summarizer calls, unconditionally enters the shared runner with the candidate's CandidateRequestPlan as the sole request authority and observes cancellation before unconditionally verifying the hash of its canonical serialized bytes. Admitted execution declares either primary or internal-summary purpose; purpose is never inferred from an agent name, session ID, tools, or prepared context. A hash mismatch raises the one internal integrity failure and performs no generic capability work, credential/auth-profile read or refresh, recorder creation, network or app-log write. Invocation Service rethrows that same failure before classification, attempt or candidate accounting, indexing, availability mutation, wait, retry, failover, later-candidate credential/provider I/O, or publication. After a valid hash, the shared runner performs exactly one generic capability check; mismatch likewise precedes credentials, recorder, network, and writes. The selected closed protocol adapter then carries its credential requirement into delayed resolution, which performs no capability or protocol selection, and the exact planned bytes are sent and reused on retry. The selected adapter directly consumes the successful fetched Response; the structured recorder retains response status, usage, finish reason, terminal-tool evidence, and raw caught error evidence without buffering or capturing a raw response body.
For every started attempt whose success or error reaches the runner, the runner settles exactly one raw in-process exchange before returning or constructing recovery. Raw caught name/message and an available typed status are evidence before classification; typed Codex protocol failures classified after HTTP 200 retain response and error status 200. The request-local inactivity error supplies no HTTP status. An aborted signal relabels a caught in-attempt value as typed cancellation only when that value is identity-equal to signal.reason. The request scope preserves exact abort-associated reasons across fetch/body APIs. If owner cancellation races with inactivity timeout before a failed outcome is delivered, the original owner reason wins only for that timeout/abort path; unrelated parse/network failures are not relabeled merely because the owner was later aborted. The Codex parser rethrows the exact caught effective request-signal reason (owner cancellation or inactivity timeout) or Error/DOMException named AbortError, releases its reader lock, and leaves raw evidence and classification to the shared runner. An independent AbortError is cancelled/abort, not timeout; an unrelated failure during an aborted signal remains its parse failure. Before natural EOF, every exit requests cancellation with rejection handled immediately, without awaiting cancellation or draining the stream. Pending or rejected cancellation never delays or replaces a known completion or original failure; natural EOF does not request cancellation. For owner cancellation, a direct Invocation Service caller performs no retry or availability mutation and retains the indexed raw exchange. Once an ordinary or pinned attempt returns known success, racing abort does not erase that completion, private context, or indexed attempts. No late availability success update occurs after abort. A retained graceful owner persists known output and publishes evidence through its normal path, matching cancelled tool calls without executing or continuing them; there is no alternate publication path or completeness guarantee. In contained production, normal actor output persistence and per-attempt redacted owner-session evidence publication execute inside the admitted raw lifecycle callback before wrapper settlement and consumer delivery. Immediate wrapper abort may abandon unresolved work; later runner settlement and Invocation Service aggregation remain possible internally, but currentness fences every late outcome from actor persistence, provider-exchange publication, read-model hints, actor-level retry/reconciliation, and delivery.
Provider failure classification and transport completion
Provider input-context evidence is strict and structured. input_context_exhausted requires the supported transport's exact direct code/type/param shape: an eligible parsed non-OK HTTP error object only when the actual response status is exactly 400, a failed public Responses terminal object, or a Codex error/failed event after a response opened. Matching is case-sensitive; message prose, recursive/nested markers, malformed bodies, status alone, generic token language, and output-limit evidence do not qualify. Responses incomplete_details.reason: 'max_output_tokens' is the distinct output_token_limit_exceeded failure. Typed terminal evidence remains eligible after HTTP 200. Every failure retains the actual HTTP response status; an embedded terminal status is classification evidence and never replaces transport evidence. The durable lifecycle blocker_cause: 'token_budget_exceeded' is an independent current card-result value and remains unchanged.
Direct provider failure classification has one explicit source boundary and one precedence across non-OK HTTP responses, Codex error/response.failed terminals, and public Responses failed terminals, including terminals delivered after HTTP 200: HTTP or embedded 401 is permanent authentication; HTTP or embedded 429, exact rate-limit evidence, and exact usage_limit_reached are rate limit, retaining valid timing metadata; HTTP or embedded 5xx and exact transient markers are server transient; eligible simultaneous exact input-context and explicit content evidence is a fail-closed provider-protocol ambiguity; eligible exact input context follows; then direct content-policy evidence; then remaining HTTP or embedded 403/direct auth; then the existing opened-200 prompt-policy rejection; then valid retry/reset metadata alone is rate limit; then the path's existing unmatched failure. Thus 503 plus Retry-After uses transient cooling, residual 403 plus timing remains terminal auth, and explicit 429 retains its timing horizon. Exact HTTP 400 gates context eligibility only at the non-OK HTTP boundary; typed opened-terminal context evidence remains eligible after HTTP 200. Direct content-policy evidence precedes residual embedded-403 authentication evidence, and every classified result reports the actual HTTP response status rather than an embedded terminal status. Exact direct lower-cased code or type tokens server_is_overloaded and overloaded classify as server_overloaded, including supported Codex error and response.failed events received after an HTTP 200 stream opens; the exchange retains truthful response status 200. For objects containing either alias, HTTP/embedded 401 or 403 and recognized direct auth take precedence, then 429/exact rate limit, then eligible context/content ambiguity, exact context, content policy and opened-200 prompt-policy rejection. Only unambiguous remaining overload gains unlimited retry authority; generic 5xx/transient evidence is compatible. Objects without an overload alias retain the precedence above. Retry hints alone, prose, nested values and bare 503/529 never establish overload. Prose, nested values, prefixes, suffixes, and near matches do not qualify; explicit status/token evidence precedes timing-only hints. Content evidence is limited to direct code, type, or message evidence for exact cyber_policy, content_filter, or the bounded supported phrases. Generic policy/permission prose, 403 alone, and recursive JSON searching do not qualify. A distinct factual protocol-error evidence value, prompt_policy_rejection, is attached only to an opened HTTP-200 terminal whose direct code/type is invalid_prompt and whose direct message contains the supported “prompt was flagged as potentially violating our usage policy” phrase, compared case-insensitively. Authentication, explicit rate-limit evidence (not timing-only hints), server-transient, context ambiguity/exhaustion, and recognized content-policy classifications retain precedence. Plain invalid_prompt, nested data, wrapped exception prose, and other wording do not gain this evidence. Primary execution remains terminal for this protocol error. A public Responses content refusal retains the exact complete JSON response body as providerResponse; a Codex stream refusal retains the normalized parsed SSE dataText. Request bytes are never evidence.
Response mode is protocol-owned. Chat Completions and public Responses request and parse one complete JSON response; Codex alone requests SSE and uses the incremental SSE line reader. Every ordinary, pinned, and internal-summary provider HTTP request has one application-owned, code-fixed 120,000-ms inactivity window. It starts immediately before fetch, including the preheaders interval; headers alone do not renew it. Chat/public Responses and non-OK provider bodies renew it on each nonempty received body byte chunk. Codex success streams renew it only on a dispatched SSE data: output actually consumed, before application JSON handling, including EOF-dispatched data; comments, unknown fields, event: alone, and partial framing do not count. Empty data: remains malformed JSON and [DONE] before valid completion remains truncation. Expiry aborts only that request and records the distinct safe ProviderInactivityTimeoutError as raw error evidence before classification to existing timeout. A separate absolute 900,000-ms HTTP-consumption deadline starts immediately before fetch and never renews; ProviderRequestDeadlineError likewise classifies as finite timeout. OAuth refresh and model requests have separate bounded scopes. Ordinary timeout retains finite recovery; pinned timeout is terminal. A stalled non-OK body is a timeout, not a fabricated parsed HTTP failure. Owner cancellation remains distinct, and an already-aborted owner prevents fetch. Known successful consumption ends the scope before recorder settlement, with no post-success timeout/signal check: later timeout cannot invalidate a valid completion or create a retry. Recorder and durable evidence publication failures retain their own authority, never provider-timeout classification. Inactivity is not semantic progress or a minimum-throughput guarantee: a fragmented SSE event taking over 120,000 ms to dispatch can time out even while bytes arrive. Continuous bytes or valid nonterminal SSE cannot extend an attempt beyond the absolute deadline. Both timers end before recorder/evidence publication and on every exit; neither bounds an entire overload invocation or its recovery waits. Timers are event-loop scheduled, not a hard real-time guarantee. There is no configuration knob. The Codex line reader's fatal UTF-8 decoder accepts CRLF, standalone LF, standalone CR, and valid mixtures, retaining a pending CR so chunk-split CRLF is one line ending. Blank lines dispatch fields; EOF finalizes decoding, commits pending CR and a nonempty unterminated line, and deliberately dispatches one still-accumulated data event. Comments and unknown fields are ignored. The last case-sensitive event wins and empty/absent event means message; each data value removes at most one space after the first colon and values join with exactly \n. Only exact normalized [DONE] is the sentinel. The unchanged joined dataText is parsed once and is the sole Codex stream-failure evidence; framing, comments, chunks, and line-ending spelling are not evidence. Malformed UTF-8 or JSON is a typed parse error, never skipped. The Codex parser validates every recognized output-text delta but never assembles deltas into returned text, and nested content-part/output-text done forms are not completion authorities. Function items require a real nonempty string call_id, or, only when absent, a real nonempty string id; no identifier is fabricated or stringified. Argument deltas require a usable real call/item reference resolving to a pending function and a string delta; unknown targets and malformed deltas fail at consumption. Real item aliases are removed on finalization, and arguments-done followed by item-done deduplicates under the real call identity. A self-contained completed function item remains supported. Provisional function names remain raw and may be absent or malformed until replaced by a valid late completion name. Each new finalization selects the completion name when non-nullish, otherwise the pending name, and requires a string of length greater than zero; missing, empty, or non-string selected names fail as typed parse_error without exposing the raw value. Explicit non-nullish invalid completion names override pending names and fail immediately. Names are not trimmed or checked against a tool registry, and already-finalized identities are ignored before name validation. Its message candidate is the latest complete assistant response.output_item.done, replacing any prior completed message in full. Only response.completed carrying a non-null, non-array object response with a string response.id permits success; a malformed completion is a typed parse failure even when a message or finalized tool candidate exists. Physical EOF or [DONE] before that valid completion is typed truncation even with a candidate, while an EOF-finalized valid completion event retains the line reader's dispatch semantics. A pre-completion error or response.failed supersedes every candidate. After valid completion the parser returns finalized tools before the latest message and stops immediately without awaiting physical closure or consuming later framed or physical events.
Local request admission and authoritative recovery
Ordinary primary execution uses one exact local admission state machine. InvocationService.preparePrimaryRequestAdmission() walks one ordinary route pass once: for every candidate in configured chain order it resolves effective capabilities, calls the pure supportsCapabilityRequest() exactly once against the one immutable known CapabilityRequest, and builds/canonical-serializes exactly one immutable request plan per projection; each candidate then receives exactly one verdict after capability, declared-limit, positive usable-input, and exact-size classification are all known. admitted requires conjunctively capability support, declared context/output limits, candidate output capacity, positive usable input, and exact estimated input no greater than that candidate's floor(U*C)-O ceiling. Explicit prepared or unprepared U is authoritative; only absence uses .80, irrespective of request purpose. A failed capability match, undeclared required limit, insufficient output maximum, or nonpositive usable input is non-size-fixable candidate_ineligible; an otherwise eligible candidate whose exact serialized projection exceeds its usable-input ceiling is projection_too_large. Aggregate no-fit is decided only after every route candidate is classified: at least one admitted plan executes only admitted plans in original relative order with reused serialized bytes; otherwise at least one projection_too_large yields local_compaction_required; otherwise local_admission_failed. Capability-ineligible candidates neither cause nor suppress compaction—a fit-but-incapable candidate cannot block compaction needed by a compatible oversized one, and after the unchanged capability request re-classifies it ineligible again. The verdict precedes execution; execution-time capability checking is only a fail-fast invariant reassertion of the immutable admitted plan, never a first decision, exhaustion, or failover event. Both local outcomes are strictly separate from provider-reported input_context_exhausted, provider attempts, availability state, and recovery retries; neither is normalized into ProviderTurnFailure, LlmTransportFailure, candidate availability, a provider exchange, or a model issue.
On the first aggregate local_compaction_required, and always before any not-yet-performed initial turn-start append and primary provider I/O for that route pass, the actor performs exactly one local_exact_admission compaction with the same fresh prepared context and exact rejected projection, replaces only providerConversation while retaining instructions, compiled tools, selected blocks, prepared compaction and capability request, and invokes exact admission once more over the new projection with one fresh build and verdict per candidate. Actual source-session correspondence and admission bindings remain strict. Send happens only if that second outcome is admitted; a second local_compaction_required, local_admission_failed, no_smaller_projection, construction failure, or uncoverable safe prefix is one terminal LocalExactAdmissionError with bounded aggregate/per-candidate capability/limit/output/size reasons and no request body, provider attempt, or error row for the rejected pass. An owned non-provider construction failure appends its fixed safe reason/count/available-measurement diagnostic to that outer message without exposing its internal cause. An initial local_admission_failed fails immediately as LocalExactAdmissionError with local_compaction_attempted:false. That error carries required owner-assigned source (primary_local or internal_summary) and reason (capacity, configuration or summary_construction). Complete typed verdicts classify direct no-fit as capacity when any route has projection_too_large, max_output_too_small or nonpositive_usable_input; pure capability/missing-declaration failures are configuration. Size-triggered local no-smaller is primary-local capacity; wrapped construction is always summary-construction. An internal-summary admission rejection passes through preventive/local compaction with its exact identity and source unchanged and remains poisoning, even with a capacity reason. Summary-policy blocking and all strategy-specific summary failure handling remain unchanged. There is no third admission, compaction retry, stale reread, alternate route pass, excluded-candidate bypass, or budget/capability weakening.
Provider-reported input_context_exhausted remains separate post-I/O recovery for ordinary admitted execution only, recognized solely from a real ordinary error provider exchange after an admitted request was sent and a valid AdmittedProviderTurnFailure transfers the typed suspended execution. Ordinary authoritative recovery retains one immutable admitted-candidate membership (frozen from only the admitted verdicts of the ordinary admitted object that reached execution, in original route order, with hash) plus one invocation-owned process-local per-candidate state record with explicit untried, temporarily_unavailable, retry_waiting/retry_ready (standard, rate-limit or overload), context_failed, and exhausted kinds. Total evidence attempts are distinct from the four-failure non-overload budget; finite failure count and capped overload backoff survive context suspension. The actor holds that suspension untouched during the one authoritative_context_recovery compaction—custody only, no mutation, scheduling, or exposure—and returns it to the recovery-scoped preparation/resume boundary, which verifies membership/hash, exactly one context_failed member matching the real failure, record uniqueness/order, settled-attempt indexing, and unchanged input/prefix/tool/dynamic/capability/deadline bindings before any retry transport; missing, copied, reordered, extra, duplicate, or malformed state fails closed with no fallback to route configuration or provider exchanges. The context-failed member must re-admit and is attempted first with the compacted bytes; if it does not re-admit or reports context exhaustion again, recovery is terminal with the preserved real attempts. Mandatory-candidate re-admission failure remains ProviderTurnFailure: ordinary error/evidence publication and Analyst notice/response leave the Analyst reusable. Another retained candidate's invalid re-admission remains an integrity rejection that poisons. Neither is a primary-local rejected-submission capacity case. Only afterward does the existing scheduler resume over the retained records—an earlier rate-limited, retry-waiting/ready, or temporarily unavailable member remains viable, later untried members remain viable, and exhausted members stay excluded—while attempt counts, the absolute unavailability deadline, last-failure values, and all settled primary exchanges survive unchanged with monotonically appended indexes. Originally rejected or exhausted identities are never rebuilt even if compaction would make them fit; there is no route widening, state reset, or limit weakening. A pinned failure never satisfies this predicate and never produces the suspension.
Content-policy pin and confirmed overload
The terminal content-policy pin is one logical authorization, not a one-member ordinary route. preflightPinnedContentPolicyRequest() performs one pure configuration/capability/declared-limit/usable-input/exact-size classification and one canonical request build for the single refusing candidate only, with no availability read/write, transport or auth resolution, provider-exchange publication, wait, retry, route enumeration, or compactor call. Both candidate_ineligible and projection_too_large terminate pinned execution immediately with bounded diagnostics, the unchanged original refusal attempts, no compaction or substitution, and no provider I/O for the pin. If and only if preflight admits, its exact retained serialized bytes may repeat only after confirmed overload, through the common invocation loop and capped overload schedule. The pin never consults availability to substitute, widens a route, compacts or suspends. A second actual refusal ends BLOCKED with no third logical policy request or additional safety notice. All other failures—including context exhaustion, cancellation, timeout, protocol failure, rate limit and auth—terminate directly. The actor alone combines original refusal attempts followed by pinned attempts once through combineProviderAttempts(), overwriting the activation input ID and assigning contiguous attempt indexes; diagnostic indexes include the original-attempt offset. Each wire attempt has a fresh exchange identity, not a fresh source input. Subsequent authorized tool/plaintext-repair continuation uses a fresh UUID and the full original ordinary route. Summary exchanges remain separate.
Confirmed overload retries indefinitely with candidate-local delays of 30, 60, 120, 240, 480, then 600 seconds repeatedly. Neither attempt count nor the ordinary two-hour availability-wait deadline exhausts a candidate whose latest failure is overload. A different failure resets that streak and restores its own finite or terminal policy. Mandatory context retry remains first; available untried admitted alternatives can rescue ordinary work. Ready overload precedes waiting finite work, ready finite work precedes an overload wait, and ready overloads use oldest due time with route-order ties. With no eligible work, wait abortably for the earliest applicable due time/deadline and recompute; expired finite alternatives cannot kill viable overload or cause busy spin. Retry reuses each candidate's admitted bytes/hash, membership and source input without rereading history, re-admission, new ingress or compaction. Shared process-local COOLING/server_overloaded is advisory, not unlimited authority for another invocation; a fresh invocation needs its own actual overload and starts its own streak. Successful consumption returns once: already settled tools/results never replay on retry. Publication failures never create retry authority; publication uncertainty propagates immediately before follow-up effects. Pause is the existing cooperative admission gate, not provider cancellation: an already admitted attempt/backoff continues and pausing can remain pending indefinitely. Stop/shutdown cancel active requests and waits with the exact owner reason and retain currentness fencing. Outstanding execution/cooling is not successful progress or proof of bytes in flight. Exchanges accumulate until owning settlement, potentially without durable visibility during a long outage; no retry-progress row, countdown or tool/child/process waiting barrier is invented. Evidence memory can grow indefinitely and interruption can lose it; eventual success is not guaranteed.
A clean no_smaller_projection result after successful local or authoritative construction appends no compaction, performs no second provider pass, and produces the existing owning terminal behavior. Preventive may publish a strictly shrinking hard-budget-fitting candidate above the desired trigger; the next genuinely refreshed invocation may compact again, but the same successful result is not rechecked in a loop. The normal and corrective prompts target at most 12,000 and 6,000 UTF-8 bytes respectively; neither is an acceptance ceiling. Complete nonempty prose is valid even above target or when it mentions recoverable evidence. Empty text, tool-call output, and Chat finish_reason:'length' are correction-eligible SummaryResultValidationError values. A final successful Chat content_filter completion is a safe fixed content_policy ProviderTurnFailure and permits fallback but no regeneration; unknown explicit finish values and stop/tool_calls result mismatches become safe fixed provider_protocol_error failures and permit neither. Null/absent finish metadata uses ordinary result validation. Responses and Codex retain their native completion handling. Only owned non-provider construction failures become CompactionSummaryConstructionError. Its fixed safe message reports reason, logical invocation count/limit, correction count/limit, and available summary-byte/target or projection-token/ceiling measurements; it never stringifies arbitrary provider prose. Eligible reasons are empty_output, tool_calls, incomplete_output, request_context_capacity, fold_limit, residual_capacity, and no_reduction. Provider failures—including typed output/input capacity and recognized content policy—remain the exact original ProviderTurnFailure when no candidate/correction succeeds, except that successfully projected exhausted prompt-flag evidence becomes the narrow typed summary-policy signal described above. Abort, summary-exchange projection/publication, publication-unknown, source/projection/hash/invariant, and append failures retain their exact owning identities. PublicationOutcomeUnknownError authorizes no inspection, retry, or compensation, and canonical append failure remains CompactionAppendError. Summary refine attempts are projected exactly once into the known source session's evidence stream under the internal internal:compaction-summary:<source-session-hash> evidence ID and fresh source-input identity, with no assistant output IDs, and are never merged into the triggering persisted input.
Last-chance context recovery has narrow publication ownership after the triggering planner provider boundary has already been entered. If summary recovery exhausts with ordinary ProviderTurnFailure after summary attempts were published, including the narrow Chat refusal/protocol conversion after its successful exchange was published once, the actor validates and publishes the original triggering planner context-failure attempts exactly once under the planner input, with null terminal conversation output and no assistant output. An exhausted typed summary prompt flag performs the same original-attempt publication and then returns the strict blocked result rather than the last-chance wrapper. Publication failure or uncertainty remains authoritative and creates no marker. Only after successful triggering-attempt publication does the actor throw fieldless LastChanceSummaryProviderUnavailableError, whose safe generic message and exact summary ProviderTurnFailure cause prevent generic rejection from reidentifying or republishing summary attempts or fabricating a planner model_issue. Provider exhaustion gains no construction reason/count diagnostic. The marker has no provider exchanges, failure phase, route pass, candidate, or classification and makes no recovery decision; it is solely an actor-operation publication-ownership signal.
Primary refusal settlement and global ingress
Every persisted ConversationLLMActor has required immutable purpose. {kind:'autonomous-card',cardId} must exactly match a card-scoped session; {kind:'global-agent'} must match a global session. CardProcess, AnalystSession, and each fresh OversightSession check are the explicit production constructors. Purpose is declared explicitly, never inferred from prepared context, tools, or names. Every LlmInvocationInput, provider adapter call, and Invocation Service request carries one required route pass: ordinary with its bound candidate chain, or the private actor-produced pinned content-policy retry with the first refusing candidate. Global agents and direct summarizers never produce the pinned variant.
On the first autonomous-card content_policy failure, the actor durably appends exactly: “Saivage authorizes only assistance that the provider can give within its applicable safety requirements. This automated message is not an operator attestation about the request's purpose, locality, or benignity. If compliant assistance is possible, continue within those requirements; otherwise refuse.” It rereads canonical conversation and requests one pinned pass to the same provider/account/model. The pinned pass retains request-plan integrity/admission, cancellation, credentials, attempt recording, and exchange indexing. It repeats only positively classified overload on the same admitted bytes and capped delay, may expose advisory cooling, but never consults availability for substitution, fails over, compacts or suspends. No third logical policy request is authorized. Once planning, admission, credentials, and cancellation checks admit transport, one logical pinned request may have multiple overload wire attempts. Cancellation, invalid plan, admission rejection, or credential failure before transport is a final pre_provider zero-call result. Analyst content refusal remains a one-call terminal model error with no retry row or pinned request.
The pin applies only to that logical invocation. Successful pinned output and all original/pinned attempts retain the original source-input UUID and actual candidate attribution; completion persistence and terminal handoff describe the pinned attempt, not an ordinary call. If existing workflow rules authorize a subsequent tool continuation or configured plaintext repair, that new logical turn receives a fresh UUID, restarts attempt indexing at zero, and enters ordinary admission with the complete original configured candidate chain in its original order. It preserves the frozen prepared context and rereads canonical history, including the retry notice, successful output, and settled tool result or plaintext-repair context under existing projection rules. Completing or accepting output does not itself authorize another turn; terminal tools, accepted plaintext, and cancellation retain their existing no-continuation behavior. Pinned preflight rejection or any failure other than overload remains terminal for that invocation, with no ordinary fallback.
If the pinned call also refuses, the actor combines and reindexes all original/pinned error attempts under the original source input, appends one strict system content_policy_refusal marker, publishes the combined safe exchanges exactly once, then hands off typed BLOCKED. The marker contains only version/type, source input, candidate, and the second terminal refusal's exact provider_response; it contains no request conversation, first response, generated paraphrase, tool input, or partial output. Physical marker content never enters provider/summarizer input. Projection replaces it once, including across compaction, with fixed generic replanning/safety text and its exact /agents/<session>?entry=<marker> locator. A final marker is clean recovery state; malformed/colliding rows or rows after it fail. Raw Agent API projection preserves marker structure while redacting terminal response text; rendered views show only synthetic text. The first raw response is intentionally absent from the marker and UI.
Provider-exchange success and error identities are disjoint. Success has assistant_output_ids and no terminal-error identity. A transport-successful attempt whose output later fails local validation remains a success and links the resulting assistant model_issue through assistant_output_ids. Every provider error attempt has required nullable terminal_conversation_output_id and no assistant IDs. Both repeated-refusal attempts use the marker ID; ordinary provider-error attempts use their model-issue row ID; direct summarizer errors use null. Marker append precedes one combined exchange publication, which precedes handoff; any uncertain append permits no retry, inspection, handoff, or fallback settlement.
Repeated refusal settles the running card BLOCKED with {kind:'content-policy-refusal',summary:'Provider content policy blocked this card after one safety-respecting reframing attempt.',session_id,marker_id,evidence_url} and null completed_at. Persistent typed internal-summary flagging instead settles the running card BLOCKED with compaction-summary-blocked, the fixed safe summary, owning session ID, summary input UUID, null completed_at, and no refusal marker or content-policy metadata. Every compiled node has a code-owned execution:blocked edge beside execution:failed. The waiting parent's successful activate_card result contains only nested {card_id,outcome:'blocked',summary,result} and no duplicated evidence fields or provider prose. The parent remains active and may re-scope work; there is no replanning cap.
For an ordinary Analyst submission, successful preparation precedes one physical [activation_open marker, user text] append and publication. Optional strict {view,entityId,refinement} workspace context is at most 2048 serialized UTF-8 bytes; views are cockpit, files, system, or null, and refinements are opaque route metadata. Null view requires null entity and refinement. The submitting client copies the route at Send; the server reads the same strict active linked-card list used for orientation once during preparation. The analyst.workspace_focus activation-local latest-snapshot block (primary and summarizer) is complete on each ordinary invocation, frozen through continuations and compaction, and at most 4096 serialized UTF-8 bytes. It includes exact matched card or card-session owning-card ID, type, version, lifecycle, bounded title and nullable card status-text preview/update time; global sessions, Files and System provide identity only. Missing active matches are unavailable, not proven nonexistent. The block is advisory observation of this submission only, not evidence of viewed content, authorization, continuous or cross-tab focus, or a historical compaction source. Explicit user targets win; no focus implies no target. Route redaction expansion or mandatory block size beyond budget produces only a fixed safe pre-effect recoverable HTTP 400 with issue workspaceContext, before unmatched settlement or ingress; strict card-list, orientation, and canonical failures remain exact-or-throwing, not HTTP 400. Existing history and assistant replies can still mention earlier focus. Construction of the bounded Analyst orientation snapshot is part of that preparation and is exact-or-throwing: failure produces no substitute context, ingress, context diagnostic, provider call, or tool effect. Orientation and prepared-static capacity rejection use the existing generic operator error response, not the focus-specific HTTP 400. The retained owner permits a later explicit fresh send after normal settlement; unchanged pressure can reject freshly again. Busy remains HTTP 409. A rejected initial/continuation capacity request is not a success acknowledgement or proof of no prior effects/history, and is never automatically resent. The session then rereads its canonical conversation once and constructs the sole source-identified provider projection; there is no pending-row concatenation or second projection owner. Preventive compaction runs before the initial provider call and before every continuation whose refreshed canonical projection reaches threshold. A tool continuation first settles the waiting call, appends its one canonical result, allocates a fresh continuation UUID, rereads canonical history, and retains the same prepared prefix/tool/capacity value. The newest Analyst round is open; a following exact marker completes the prior round, which changes only compaction eligibility—a closed round and every uncovered summary-only bundle remain fully primary-visible below the trigger until a later successful validated compaction covers them.
An accepted Analyst tool invocation and its external/domain effect occur before the continuation provider seam. If that continuation receives eligible authoritative context rejection, recovery retries only the same continuation UUID after one strictly reducing compaction: it does not replay or rebroadcast the tool, repeat its result, or append another marker/workspace/user batch. Successful initial output containing a tool call likewise leaves the recovery seam before the tool runs. Direct summarizers are the explicit unprepared, nonpersisting exception; each refine call uses the one fixed Registry-validated summarizer_candidate, the token limit in the exact context and compaction limits, a no-tools prose request without an exclusive-tool-choice requirement, the internal compaction-summary evidence identity, and no self-compaction or replay. Analyst primary and continuation calls use the selected configured global Analyst agent's compiled named route and that route's ordered candidates and failover.
Every settled provider attempt passes through a provider-exchange-specific publication projection immediately before final schema validation and append to its canonical owner's evidence stream. Publication explicitly supplies the canonical owner session and primary or internal-summary purpose; the latter derives its unchanged internal evidence ID from that owner. Only successful primary publication emits the canonical-session exchange freshness hint. It preserves exact contract/provider/model/account, transport, source-input, attempt/status/time, response, finish-reason, token, terminal-tool, and assistant-output identities. Request parameters are one closed transport variant: generic has the common endpoint/POST/stream/tool-count members plus numeric temperature and max tokens; codex has only the common members; openai-responses adds optional max-output/store/include and exact reasoning-key values. The HTTP(S) endpoint preserves scheme, host, port, and path while replacing nonempty userinfo, redacting the query as a whole, and removing the fragment. Error name and numeric status remain exact; only error message is classified prose. Unknown transport members and invalid endpoints fail rather than entering generic recursion. Logged events and control actions likewise pass through their discriminated projectors before app-log append and again on public read: event/action/target/actor/surface and contract-state identities remain exact, event/control prose redacts, contract_response_violation directly preserves operation/status/failure-code, and invalid_enum_value preserves field while projecting only its rejected value as opaque. This publication projection does not mutate in-process attempts or effects, and generic app-log persistence selects no redaction behavior.
API and safe operator projection
Public resources are exact independently admitted observations. Safe projection does not grant a write token, reconstruct private request context or make separate resource samples atomic. This chapter owns card/history/Files projection and confidentiality; presentation details live in the UI contracts.
Dedicated operator JSON data presentation follows the shared JSON data-display contract. Lossless lexical decoration and exact received Files text affect presentation only, not data validity, API, outbound projection/redaction or persistence semantics.
REST remains authoritative. Application bootstrap owns only the initial runtime/project read and the sole ordinary root card-hierarchy request; it performs no Agent, event, error, process, graph, doctor, or MCP read. The persistent Analyst panel owns its exact mounted chat resources independently. Token identity change resets CardStore, aborts its hierarchy/detail/history requests, and performs the ordinary root load under the new identity. /api/state contains no card inventory, cardIndex, status/type totals, or hidden card traversal. Operator projections and disposable reader-local indexes never authorize writes or persist beyond their owning read. The operator presents exact card sessions as the Conversations selection of the shared owning-card cockpit, preserving card/tree/facet context while keeping /agents/:id?entry=… as the sole exact-session address. Exact scope resolves before card context, global scope fabricates none, and transcript availability is independent of owning-card admission. The complete presentation and navigation rules are owned by the Operator UI contracts.
Exact shared operator error contracts
text
error.analyst-turn-busy = {"strict":true,"variants":[{"fields":{"error":{"kind":"literal","value":"analyst_turn_busy"},"message":{"kind":"literal","value":"Another Analyst turn is active. Retry after it finishes."}},"strict":true}]}
error.unauthorized = {"strict":true,"variants":[{"fields":{"error":{"kind":"literal","value":"Unauthorized"},"statusCode":{"kind":"literal","value":401}},"strict":true}]}
error.unexpected-internal = {"strict":true,"variants":[{"fields":{"error":{"kind":"literal","value":"InternalServerError"},"message":{"kind":"literal","value":"Internal server error"}},"strict":true}]}Historical refusal inspection
Authenticated GET /api/runtime/content-policy directly invokes persistence-owned linked history traversal. Starting at exact project, it consumes reached current heads/selected snapshots and their ordinary predecessor chains, follows only committed child IDs after parent/namespace proof, includes reached tombstones but never descends through them, and neither enumerates nor calls ancestor-rereading public readers. Request-local artifacts are actual immutable ordinary/tombstone versions with validated change metadata, never fictional prior queue snapshots. refusal_high_water counts retained terminal refusal metadata, including reopened versions and reached tombstoned cards. blocked_at is terminal history.changed_at, not null completed_at; latest is the final ascending (changed_at,card_id,version) item. Malformed canonical data fails the request. No counter, cache, registry, timer, watcher, polling, or persisted projection exists. This is a read-derived endpoint, not an application-bootstrap UI store or a complete internal-summary refusal census. The browser bootstrap reads runtime/root; ordinary card result and conversation evidence retain their own inspection paths.
Queue-free cards, history and diffs
The operator card representation is current strict card state plus operator actions and the pure card-only summary; it contains no backend logical_path, authored content, or pending-notification field. Analyst create/reopen CardView results and ordinary immutable card artifacts use the same strict queue-free card projection. Planner reopening instead returns only its queue-free {card_id,status:'changed'} success data. Its typed projection treats title, every lifecycle result summary, blocked resume_reason, lifecycle error, operator_summary.error, and status_text as prose. Card, child, and dependency IDs, allowed actions, status/type/result/action/blocker discriminants, timestamps, and other public structural scalar/null fields remain exact even when credential-shaped. The same classification is used by cards.get, the parent and every child from cards.children, card tools, immutable card-version artifacts, and field-directed diffs; unknown diff fields fail rather than receiving opaque projection. Its allowedActions values derive from the canonical action schema and an operator-only action projection, not mutation admission for runtime roles. The action vocabulary is exactly card.start, card.create, card.cancel, card.delete, and card.reorder_child; card.start applies only to backlog, changed, and stopped, so blocked cards neither start nor restart through this projection. Operator projections do not advertise card.create, card.reorder_child, or card.restart. The hierarchy operation is GET /api/cards/:id/children, returning the requested active card and only its active immediate children in committed parent order. Retained tombstone links are read and omitted, and traversal stops there. GET /api/cards/:id returns only selected current detail. Card-version history is separate: GET /api/cards/:id/history returns ascending items exactly {entry_id,version,published_at,artifact_kind,change} with card_id and total in the outer response; GET /api/cards/:id/history/:version returns outer {card_id,version,entry_id,published_at,artifact} where the artifact is exactly {kind:'card-version',card,change} or {kind:'card-tombstone',final_card,change}; and GET /api/cards/:id/diff compares explicit resulting versions or one version to current. History listing traverses committed ordinary predecessors; selected content/diff follows exact selectors—sparse ordinary card versions or consecutive accepted record document versions—and never substitutes another version. Loaded card/record catalogs do not veto exact version requests: the server determines success or missing selection, and selection-local errors preserve current detail. Current diffs use tagged current projection rather than treating current mutation revision as an immutable entry. Current authored Records are a separate exact resource and do not expose authored-record history through card-version routes.
Every public history change slot is required and nullable. Non-null change is exactly {summary,changed_fields,actor}: nonempty redacted ordinary summary, finite ordinary fields title, priority, urgency, lifecycle, status_text, status_text_updated_at, child_membership, active_child_order, deleted, and recorded update/delete agent or null. Initial change is null; queue-only mutations have no history entry. Terminal publication exposes only ordinary metadata, rebuilding summary from ordinary changed fields with result prose in the queue-free snapshot. No durable reason/kind/surface, terminal-summary object or repeated identity/time is projected. Legal generic updates remain title/priority/urgency. list_card_versions lists actual sparse entries with nullable change; immutable get_card_version emits exact entry/version/time/hash/section locators. Artifact hashes cover queue-free {kind,card,change} or {kind,final_card,change}. Current diffs have tagged mutable target metadata without a fabricated immutable target UUID; no public diff row names pending_notifications.
Ordinary card query surfaces provide no explicit queue collection, count, membership, IDs/bodies, availability field, direction discriminator, or delivery receipt. Generic version/time/diff/invalidation observations may signal or support inference of hidden queue activity and are not a supported queue query. This structural guarantee includes current/history/version, card diffs, Analyst create/reopen results, Planner reopen results, and Files; explicit enqueue attempts/results, delivered context, and opaque retained conversation evidence are excluded.
Every successful card-diff response contains a strict diff array whose rows are exactly required {field,before,after} objects. field is a nonempty string; before and after are both present and each is recursive JSON: null, boolean, string, finite number, an array of recursive JSON values, or a string-keyed object of recursive JSON values. Missing members, additional row properties, undefined values, non-JSON values, and non-finite numbers fail the contract. The one shared row schema validates the backend's final projected response and the browser's declared status-200 response before CardStore admission. Malformed valid JSON at declared status 200 is therefore a contract/protocol failure, not validated API error data or an OperatorApiError.
Source-owned outbound projection
Card, provider-exchange, event/control, diff, config, process, invocation, conversation, WebSocket and MCP owners project their own contracts source-adjacently; there is no generic source-tag dispatcher. Card history uses the direct card-artifact projector. Webfetch arguments use invocation projection; results use its ordinary opaque-result projection. Only owner-classified opaque leaves use dynamic recursive defense. Known owners preserve exact identity/discriminant/topology/count/time/ boolean/null/canonical-locator fields while transforming classified prose, locations, secret containers, URLs and opaque values. Confidentiality is unchanged.
The invocation owner recognizes the identities and relations in the architecture's exact shipped tool identities. Each retains direct valid-argument classification for complete live invocations and canonical call rows. Public workspace call locations (path for read/image/write/edit, glob directory, grep path, command cwd, and webfetch save_as) receive complete outbound text defense against recognized credentials. Strict valid record selections, including &v=..., retain exact spelling; safe ordinary paths and canonical work locators remain usable. A record/work scheme prefix alone grants no exemption. Webfetch url keeps its existing URL filtering. Skill names retain lookup-key aliases but filter explicit secret assignments and query values. Argument schema acceptance is not confidentiality admission: public filtering applies even to failed or final unmatched calls, without changing execution arguments, canonical bytes or execution-target admission. It is not an arbitrary-secret detection guarantee. Call rows never gain a result and result rows never gain arguments. A structurally valid failed call remains readable when its identity is unsupported, its argument string is malformed JSON, or a known tool's parsed arguments fail its schema: unsupported/schema-rejected values project only that opaque value, malformed raw arguments receive text defense, and exact invocation identities remain unchanged.
Durable tool results have one strict generic union: {success:true,data?:unknown,content?:[text|image,...]} or {success:false,error:string,data?:unknown}. Optional content is the nonempty ordered typed block array specified under image observations, never arbitrary data promoted to pixels. Failure error is nonempty and failure has no content field. The optional data is opaque persisted JSON and historical projection never validates it against the current named tool implementation. Known and unsupported result rows use the same shape-independent recursive outbound redaction for data and error; there is no result-side tool-name switch. Historical Analyst list arrays, bounded session wrappers, earlier raw glob/grep match arrays, plaintext collection JsonSlice bodies, and earlier emit_result data therefore remain valid because they satisfy the current unknown field, not because of a compatibility reader or legacy-shape branch. They are not rewritten, decoded, retroactively bounded, or granted the current hex reconstruction guarantee. Current producer correctness is separate and source-owned; this does not add output schemas to ToolDefinition, ordinary invocation, MCP, or provider contracts. Navigation is an executable browser effect over that opaque data: only a successful navigate_workspace or navigate_back result whose data strictly satisfies the shared workspace-navigation intent contract and whose intent exactly matches the invoking tool is admitted to route application. Malformed or cross-wired successful navigation data fails before any route effect.
reconfigure is a closed restart-only union: set_agent_model_route accepts one existing agent and existing route; set_model_failover accepts one known source model and duplicate-free ordered known models; and set_server_setting has strict host and positive-port variants. The current YAML is interpolated, source/effective-schema decoded, and checked against mutation preconditions without compiling its workflows; schema-invalid current data fails before mutation. The complete edited candidate is schema-validated and structurally compiled before file replacement. A compiler-only error in the current configuration does not independently veto a supported correcting mutation whose complete candidate is valid. Success returns requires_restart:true and does not mutate current workflows, routing, tools, MCP, or listener state. Deleted role, runtime, and MCP mutation actions are schema-invalid.
Outbound text defense recognizes the existing sk-, tid=, ghu_, rt_, tok_, and Bearer forms, plus ghp_, github_pat_, shaped AKIA access-key IDs (sixteen uppercase ASCII alphanumeric characters after the prefix), and Slack xoxb-/xoxp-/xoxa-/xoxr-/xoxs- token forms. Family placeholders are stable and expose no credential suffix. Certified text prefixes treat these placeholders as indivisible spans. This incremental defense does not claim exhaustive credential detection.
Effective configuration has distinct strict internal and outbound contracts. Source YAML carries the optional card_types map; after ResolvedConfigAuthority resolves it, internal and outbound contracts contain the complete effective card_types map and no source-only form. The one typed outbound projection preserves namespace keys and ordinary model/profile/failover/provider/account/capability/process/auth-reference values, structurally omits provider and account baseUrl, replaces provider and account apiKey values, replaces every stdio MCP env value regardless of key spelling, and retains each streamable-HTTP MCP url only after redactUrl removes userinfo credentials, query contents, and fragments. REST config, show_config, invocation results, and Files selected-config preview share that effective projection; none merges source and effective shapes or exposes a set-selection API. Files identifies the startup-selected config by admitted lexical path or resolved real target, including custom paths and aliases, loads the effective config, and serializes it without a second text pass. Every other admitted work:/// text preview, including process logs and webfetch content_url output, receives text defense and sensitive-redacted metadata; normal denial, containment, binary, size, listing, and card behavior is unchanged.
GET /api/mcp/tools returns the one displayed hierarchy: ordered servers with name, transport, status, tool count, and ordered tools carrying name and direct invocation statistics. It omits integration errors, descriptions, schemas, annotations, _meta, duplicate flat tool/server arrays, and a duplicate keyed statistics map. Browser totals derive only from this hierarchy. The obsolete mcp.status, runtime.cardRuns, and processes.get operations do not exist; callers use runtime status, the process list, and the MCP tools hierarchy.
For target depth d with root depth zero, active detail, hierarchy, and current records require an active target. History list, exact history entry, and diff prove a committed target path: a tombstoned final target is permitted, a tombstoned ancestor blocks access. Catalogs traverse ordinary predecessors; exact entries/diff historical sides follow those references to exact sparse selectors. Current diff uses a distinct current projection. At depth d, current path proof reads d + 1 heads and selected snapshots, with no authored records. History operations additionally open reached predecessors; their cost depends on selected history depth, not merely card depth. Hierarchy adds one read for each committed immediate-child ID, including an omitted tombstone. A current authored-record operation uses the path reads plus the requested record head and selected acceptance; all-declared metadata uses one path proof plus exact reads per declaration. These operations derive exact namespaces and record paths from compiled definitions without sibling enumeration; unrelated and unrequested heads and documents are not inspected.
Card history and diff use the exact backend card history and diff contract. HTTP syntax validation returns 400 before handler/read-model or persistence work. Tool JSON, direct application/read-model, and CardService/domain boundaries independently reject zero, negative, fractional, non-finite, or unsafe supplied numbers before exact path/history reads. A direct operator read-model rejection uses that operation's declared ValidationError 400 envelope with the exact seq, from, or to issue path. Valid resolved from > to remains the separate semantic 400.
Exact backend card history and diff contract
text
error.cards-history-404 = {"strict":true,"variants":[{"fields":{"cardId":{"kind":"schema","name":"cardIdSchema"},"error":{"kind":"literal","value":"Card not found"}},"strict":true},{"fields":{"error":{"kind":"literal","value":"historical_version_not_found"},"owner_id":{"kind":"schema","name":"cardIdSchema"},"resource":{"kind":"literal","value":"card"},"version":{"kind":"schema","name":"HistoricalVersionSchema"}},"strict":true}]}
error.cards-diff-404 = {"strict":true,"variants":[{"fields":{"cardId":{"kind":"schema","name":"cardIdSchema"},"error":{"kind":"literal","value":"Card not found"}},"strict":true},{"fields":{"error":{"kind":"literal","value":"historical_version_not_found"},"owner_id":{"kind":"schema","name":"cardIdSchema"},"resource":{"kind":"literal","value":"card"},"version":{"kind":"schema","name":"HistoricalVersionSchema"}},"strict":true}]}
pivot.cards-diff-from = {"field":"from","mapping":"fromVersion","presence":"required","refinement":"positiveSafeIntegerSchema.safeParse(Number(raw)).success","regex":"^[1-9][0-9]*$","transform":"Number","variants":[{"kind":"canonical-positive-safe-integer"}]}
pivot.cards-diff-to = {"field":"to","mapping":"toVersion","meanings":{"current":"current-projection","numeric":"historical-version","omitted":"current-projection"},"presence":"optional","refinement":"positiveSafeIntegerSchema.safeParse(Number(raw)).success","regex":"^[1-9][0-9]*$","transform":"Number","variants":[{"kind":"literal","value":"current"},{"kind":"canonical-positive-safe-integer"}]}CardStore keeps hierarchy slices, selected detail, and selected history as disjoint authorities: only a children response can install its complete parent slice, and only detail can install selected detail. Completion order never merges them. Each parent key has at most one exact in-flight owner object; same-parent callers share that object's promise. Success, failure, finalization, and owner removal may update state only while that exact object remains current. Reset aborts and clears owner objects before hierarchy state, so an old completion cannot install state or remove a later owner. Route reveal has separate action-local monotonic ownership: after every await and before a later ancestor request, a superseded invocation stops. Supersession never aborts shared ensureChildren() work, changes its keyed owner, installs or invalidates a slice, or grants persistence/currentness authority.
Files has exactly two concerns: a pre-generic admission/classification gate, followed by either the generic read path or the canonical-card virtual read model. For an ordinary project spelling, the gate performs decoded traversal rejection and lexical derivation/outside-root rejection, lexical blocked-source admission, lexical card reservation, and then bounded symlink classification for an allowed lexical non-card source. POSIX backslash is an ordinary filename character, not a card-grammar separator. For work:///, existing parse, query/fragment rejection, exact canonical round-trip validation, project-relative .saivage/work derivation, and traversal/outside-root checks precede the same lexical blocked-source, reservation, and allowed-source symlink stages. Directory and metadata reads represent the exact admitted .saivage/work root as work:///; descendants retain canonical single encoding, including spaces, literal percent signs, and Unicode. # and ? remain unrepresentable path characters, and raw query or fragment syntax remains rejected by the work resolver. A lexically blocked explicit source returns 403 and a blocked listed child is omitted; both stop before any classifier lstat, readlink, or card-target I/O, even if the source aliases into cards. A canonical project card spelling dispatches virtually. Any allowed non-singular project spelling that normalizes into cards, any allowed card alias, and any allowed validated-work spelling or alias that enters cards is reserved but opaque as 404 for an explicit request or omitted from a listing; no generic fallback follows. The classifier is request-local and bounded, grants no read authority, and its expanded result is never used for content. A conclusively non-card result enters the generic resolver, which still applies blocked policy to both the lexical identity and any resolved in-project real-target identity before metadata, listing, or content I/O. Thus a generic alias to a blocked real target remains 403 or omitted. After those policy decisions, the generic resolver performs one direct metadata stat of the requested target: exact ENOENT alone is the existing 404, while every other metadata error reaches the ordinary opaque server-failure boundary. During listing, child admission, reservation, containment, and blocked checks likewise precede metadata; exact ENOENT from a reached child's stat is the sole disappearance-race omission, and every other reached-child metadata error fails the request rather than returning a partial snapshot. Direct outbound redaction remains post-read and applies when either admitted identity is explicitly redacted. Canonical work:/// response spelling and VFS behavior remain unchanged.
The virtual grammar is closed:
text
cards-root := .saivage/cards
card-namespace := .saivage/cards/project ( /children/<segment> )*
children-dir := <card-namespace>/children
artifact-file := <card-namespace>/card.json[?v=N] | record:///<name>?card=<card-id>[&v=N]Each segment and the derived stable ID must satisfy the exact card identity contract. No conversations, physical streams, physical conversation indexes, version directories, UUID filenames, or arbitrary descendants are exposed. An active namespace returns virtual children, card.json, and present configured record URLs in declaration order; a missing optional record head is omitted. As a Files-only current-head exception, a directly addressed retained tombstone returns only its terminal card.json; explicit card.json?v=N selects from the same committed history catalog. Neither form grants records, children, active-card access, or descendant access through that tombstone. Child listing reads each membership fold once, emits live entries in filtered active_child_order, and never enumerates a physical directory.
Every virtual operation derives exact paths from the lexically resolved project root and committed identities/references. Parent membership is validated before child access; missing links are 404 without child access, while linked missing/invalid current authority fails. Intermediate tombstones terminate access; root tombstones fail. No sibling enumeration, physical ancestor proof or hostile-root-agent containment is claimed. Exact owner validation applies at consumption; card/record documents gain no torn-suffix permission. External workspace/tool admission is unchanged.
Card-namespace listing consumes heads/selected snapshots required for linked reachability, then projects present configured record descriptors from exact current record heads/accepted selections: effective-content UTF-8 byte length is size, draft updated_at or accepted committed_at is modified time. A missing optional record head omits that non-bootstrap record; missing bootstrap authority and every malformed or unreadable consumed document fail listing.
Explicit content is selector-bound and strict. card.json returns {kind:'card-current',head_id,card_id,version_seq,history_version,updated_at,card}, with exact current selection identity/revision/time and queue-free fields. card.json?v=N selects exact immutable ordinary revision N. Its wrapper is {format_version:5,kind,entry_id,card_id,version,published_at,card,change}; tombstones substitute final_card and retain prior_card_version. Historical wrappers retain publication identity/time and ordinary nullable change, never pending fields. Current retained tombstones return their terminal historical wrapper. Record URLs use mutable current or exact accepted document v=N selection; accepted document versions are consecutive from 1, independent of mutable revision, and drafts have no immutable locators. For either current or historical record:/// content, an absent owning card returns HTTP 404 with exactly {error:'workspace_card_not_found',path,card_id}. Files classifies the existing record-read result before record/content or historical-selection absence, without a second card lookup. Existing-card record/content misses and exact accepted-document version misses retain their own 404 forms; consumed corruption remains a server error, never card-not-found. Files applies backend outbound text redaction to the complete selected record-URL content, preferring the current draft when open and selecting only an exact accepted document version for v=N. It reports redacted:true and sensitivity:'sensitive-redacted' even when prose is unchanged. Canonical authored content and hashes remain unchanged; absent accepted document versions remain exact not-found. The three preview size/admission branches are distinct: record:/// reports raw effective-source UTF-8 bytes and has no Files preview cap; the physical-spelling virtual record preview (.saivage/cards/.../<record>) admits raw source content up to 1 MiB but reports the redacted preview's UTF-8 byte length; virtual card.json[?v=N] admits and reports the selected serialized virtual document's UTF-8 bytes with the 1 MiB cap. Record namespace listing retains source-byte sizes, so its size need not equal a physical-spelling record preview's projected size. Record-URL identity stays current or exact &v=N, and its version is the mutable current revision or selected accepted revision respectively. Current record modifiedAt uses draft updated time or accepted committed time; exact acceptance uses its committed time. Current card modifiedAt uses head updated_at; historical card uses immutable publication time. Publishing unrelated history cannot change older selected virtual bytes or their admission outcome. Current card listing and preview agree; their metadata derives from the head's time and current virtual bytes, not historical source time. Physical heads, history/mailbox documents and remaining JSONL bytes/mtimes are opaque to Files. Malformed or unreadable consumed card/record state fails through the server-error boundary; only established absent numeric selection is exact not-found. No scan, semantic correction, cache, repository or physical fallback exists. Card/record JSON documents gain no torn-suffix truncation permission.
The UI displays title/hierarchy labels and preserves immutable hierarchical card links. Project start/stop and pause/resume are requested through the Analyst conversation, not direct UI controls. The sole direct UI runtime mutation, Restart server, appears only when required restart_server_available is true and requires confirmation; it does not start project work. No UI action optimistically writes a running card cancelled. Public conversation reads project compacted genesis separately from entries rather than returning durable compaction JSON verbatim. The checksum-free context shape specified above retains explicit protected-message projection and outbound redaction. Projected protected extraction coordinates use segment_version and row_index; required-model-fact members retain their declared camelCase keys, and inherited-continuation activation uses its declared snake_case keys. Projection is explicit at this owner boundary; no recursive key conversion occurs. Files authorization remains unchanged; canonical card traversal is the virtual behavior above rather than generic physical browsing. The typed missing-card record-preview response is presented as Card not found.
Repair, reset and adoption
Normal runtime accepts only its exact current contract. Core and packaged utilities have no migrations, old-format readers, probing, normalization, bridges or dual paths in startup/init/reset/repair/ordinary deployment. Equal envelope numbers or successful readiness do not prove compatibility. Binary-only downgrade over incompatible state is unsupported. The format reference records source contracts, not installation certification; prospective changes follow AGENTS Storage Policy.
Exact current-format repair and coarse loss
The repair ladder is ordinary validated torn-tail truncation, unchanged owning semantic closings, stopped/excluded/backed-up/consented exact-target repair, and separately destructive eligible card discard. It promises neither preservation nor availability. Startup reads no previous selector, synthesizes no required empty history and launches no work to cure damage. Current linked-card admission excludes unconsumed predecessors; history validates each reached document. Required indexes, current sessions/evidence, records and app log retain strict owning scopes; valid empty indexes and genuinely lazy absent Oversight remain valid.
Repair requires strict identity/config/workflows and a bound exclusive lifecycle lock across interactive prompts; every pre-existing lock blocks it. Before inspection, acknowledge a successful complete fresh preserved stopped-project backup after service stop and positive absence verification. Backup existence is neither successful completion nor a loss bound. Backup/report paths must be absolute and outside exact generated/lifecycle roots (cards, agents, logs, work, repair-attic, locks under .saivage), lexically and after alias resolution. The fresh absent report is also outside the backup; an ordinary source-side project path is allowed.
Publish the external provisional report before exact action consent; recheck inspected bytes/absence afterward. No unattended/all-target mode, head regeneration, scanning, replay, automatic restart or compatibility is permitted. The runbook owns the complete procedure.
Card/record restoration fresh-publishes the complete strict previous selection with new head UUID; it never renames prev or invents a historical revision. It can lose latest queue/membership/lifecycle/acceptance/draft changes, resurrect prior state and unlink descendants. Queue-only prev may select the same broken artifact. An unusable candidate fails without older-history search. Record-only damage never permits discarding a healthy card.
Conversation inspection shares pure prefix/schema/identity/source-metadata/semantic validation with ordinary consumers; no mutation precedes consent. Valid current prefix plus torn suffix proposes tail-only truncation. Restoring a previous index whose selected candidate has a torn tail publishes the index first, then truncates that reported now-current tail. Rollback from a strict-valid index removes exactly its missing/complete-malformed newest selection only when the immediate indexed predecessor validates wholly. Historical tails are never truncated to qualify. First missing segment, malformed complete prefix (even with a torn suffix), broken predecessor or unusable prev fails without empty synthesis or genesis discovery. Reports name segment UUIDs/versions, known coverage/time/counts and unknown potentially days-long loss since compaction. Healthy retained prefixes remain unchanged and external effects are not undone. Ordinary automatic tails/later semantic appends retain their separate authority.
Separately confirmed synthetic card discard
Only a strict ancestor-reached card with independently unusable current and previous selections qualifies for --discard-card and separate destructive target consent. Root type is configured project. Non-root --card-type is explicit operator input checked against compiled parent permission and depth-twelve leaf rule, never recovered evidence. Move six exact own roots wholesale without inventory: current and previous heads, history, mailbox, records and conversations. Never move the namespace/children, parent link, siblings, globals/evidence, source/work, identity, config, credentials or logs. All original own data and entire former descendant reachability are lost with unknown count/content; root discard unlinks every former descendant. Physical unlinked children remain ignored forever. Other dependencies remain untouched and may block restart.
Synthetic state is only runtime/repair initial v1, no predecessor, fresh artifact/ head UUIDs, title Recovered card <ID> — data discarded, FAILED runtime-failure summary/error/completion, empty membership/order/dependencies/pending, normal priority/urgency and no assignment/metrics/metadata. Ordinary initial creation remains backlog. Publish fixed accepted loss status.md with configured or generic definition, all required bootstrap placeholders with lost-requirements guidance and new-card provenance, and empty configured sessions. Retain no old verdict or completion. Repair/startup launch no work. FAILED rejects direct activation only while FAILED: later explicitly authorized healthy-ancestor Run may let its Planner reopen the child to CHANGED and execute unchanged placeholders. Replacing lost requirements first is recommended, not an enforced operator-only hold.
Only exact corrupt canonical paths/eligible own roots move into fresh .saivage/repair-attic/<UUID>. No attic/orphan inspection or adoption is permitted. Files omits/refuses attic and resolved aliases before list/content/preview inspection; similar ordinary names are unaffected. Reset does not own attic. Effects are nontransactional: report completion only after known success; first failure stops without retry or compensation. Move/publication/truncation uncertainty is fatal before reads, writes, logging, report updates, descriptor/lock/prompt cleanup. Independent errors may still block a separately authorized restart.
Incompatible adoption and destructive reset
Same-format deployment stops the service and starts the matching build against retained valid state. Incompatible adoption instead chooses separately consented destructive reset or explicitly owner-requested external offline migration under the complete gates, outside every product path. Deployment approval, source completion, a plan or a backup alone grants neither migration authority nor concrete loss consent.
Current card heads/mailbox are format1; ordinary/tombstone bodies4 include exact executor results and external creator/history attribution. Accepted records4 carry discriminated authors; heads3 support current document0/drafts. Conversations12, shared app/provider envelope2, execution1, workflow input2 and initializer input1 are distinct owning contracts. None is a global release-compatibility discriminator. Notification query-surface removal and current-activation enqueue admission change no durable format. They require no reset, migration, prompt rematerialization, or special deployment operation; existing materialized prompts remain instance-owned. The current widening of the unchanged card-ID field grammar from the former bound of five to the maximum in the exact card identity contract is instead a same-format forward deployment: every previously valid ID remains valid, so deployment requires no reset. Once a deeper card is created, an old binary cannot read that state and must not be used against it. Old or mixed structural formats are rejected and never converted or accepted as current by Saivage. Incompatible adoption remains stopped until the separately authorized reset or external-migration workflow produces matching state/release. Same-format retained state must already satisfy exact current contracts. Participating card-agent, selected-Analyst and existing workflow-state identity cutovers are likewise incompatible after generated work exists, despite no schema change; lazy Oversight qualification remains unchanged.
Historical incompatibilities include refusal/result/evidence payloads, context and compaction interpretation, checksum removal, image selections and ordered content, loss-tolerant maintenance, recorded instructions, process completeness/inline heads, five-field provider usage and per-session provider evidence. Their exact old→new numbers, source parents, equal-number exceptions and unknown deployment applicability remain in the format reference, not present-tense cutover instructions here. Optional payload fields mean unknown or omitted provider facts, not compatibility. Adoption covers the complete selected boundary, never conversation-only/log-only/evidence-only reset; there is no old-log splitter, fallback, retained-row normalization or binary-only downgrade. Source completion grants no installation action, including on previously named projects. When destructive reset is explicitly selected/consented, keep the exact service stopped and positively verify no owner/process; confirm concrete loss of generated cards/records/conversations/logs/work; finish a full preserved stopped backup; preserve config/credentials/identity/operator inputs/prompts/skills/instructions/source/docs; run the identified matching build's reset over all four roots, then separately authorized start/verification. Reset loses history and proves neither missing-index cause nor absence of prior effects. Explicit external migration instead uses an independent complete candidate, strict full validation and evidence-based factual/history fidelity. Structural conversion is not automatically loss; unavoidable concrete loss needs confirmation, unresolved required provenance blocks. Neither branch grants automatic start/Run, retries or rollback after uncertainty. Plan/deployment approval, backup existence or startup rejection is not migration or loss consent. Test and E2E generated-state fixtures use only this singular current format; omitted old fields, old blocked/tool shapes, mixed streams, compatibility readers, migrations, normalization, and backfill are unsupported.
Initial publication is separately forbidden whenever any of the four exact generated roots remains without a valid canonical project card. Every exact-path object is an obstruction; Saivage does not inspect whether it is empty, complete, repairable, or directory-shaped. The failure names the first blocking exact path and directs the operator to keep Saivage stopped and follow the authorized current-build whole-generated-state reset procedure before retrying; the failure itself is not destructive authorization. Neither init nor start --create-runtime repairs, adopts, combines with, or selectively deletes retained state.
Reset first acquires the exact .saivage/locks/runtime.lock; every pre-existing exact lock blocks the command before deletion. While retaining its acquired lock, reset recursively force-removes exactly .saivage/cards, .saivage/agents, .saivage/logs, and .saivage/work as whole trees, without probing or enumerating their descendants, then publishes a new canonical project root card and releases only its exact lock. Every path outside those four roots is preserved, including configuration, credentials, project identity, operator inputs, source, skills, instructions, and canonical project documentation. Reset never inventories or cleans lock siblings or other noncanonical paths. Incomplete namespaces and publication temps require no action. Malformed exact canonical targets require operator repair or reset.
File persistence provides no no-data-loss guarantee. Interrupted external effects may repeat, notification context may duplicate, process-local availability resets, concurrent auth refresh may overwrite credentials, and orphan files may remain permanently.
Publication outcome-unknown fatal boundary
Every Saivage-owned durable replacement, JSONL append, owning-reader suffix truncation, lifecycle-lock publication, process-output append, and work-artifact replacement classifies failure at its direct syscall boundary. Preparation and safe acquisition failures remain ordinary. Once canonical mutation is attempted and its result is unknown, the owner throws PublicationOutcomeUnknownError. The first actor, Analyst, process-callback, server, startup, direct-mutation, or CLI boundary writes exactly one credential-free line to process stderr synchronously and exits with status 1. The line contains the fixed fatal sentence followed, when the direct syscall cause was captured, by Cause: and that cause's message, including the ordinary errno, syscall, and path detail carried by Node filesystem errors. It performs no logging, response conversion, card settlement, process cleanup, descriptor cleanup, retry, inspection, or lifecycle-lock release.
Replacement becomes uncertain at rename. JSONL append-open ENOENT remains the explicit missing selection result; process-output append-open ENOENT is an ordinary exact-file error. Truncation becomes uncertain at ftruncate. Successful exclusive lifecycle-lock creation is a known empty canonical namespace effect; subsequent record publication uncertainty leaves it for fail-closed manual repair. The storage ceiling permits repetition only for a first-write EINTR proving zero transferred bytes. Node/libuv handles interrupted writes below JavaScript, so Saivage does not retry a thrown write; positive short writes continue with only the unsent suffix.
Captured process output is synchronously appended one chunk at a time without retained writers or descriptors. Terminal process presentation and successful termination results require both registry-confirmed group absence and stdout/stderr readable drain; failed or unconfirmed groups are not awaited. The runner immediately observes each original terminal-settlement rejection while retaining that same rejecting promise for later identity-preserving delivery. Raw readable errors and ordinary append-open failures therefore cannot become an unhandled rejection merely because a background launch has no current waiter. A later terminal wait, including timeout zero against an already-terminal record, and direct-scope closure still receive the original error. Process-output mutation uncertainty remains the fatal boundary above and is not converted into capture failure.
This fatal exit occurs before Supervisor halt or runtime-status mutation. Ordinary Stop, application close, actor-main failure, and containment failure retain the existing closing -> stopped | error contract. A genuinely new process performs strict canonical startup full-chain settlement before availability; a later explicit Run launches only project. Conversation stabilization and append-only stopped correction remain best-effort and lossy, complete malformed rows fail, and another publication-unknown failure exits again without retry.
Workflow configuration and shipped policies
This chapter specifies source selection, compiled identities, shipped graphs and role/node responsibility. These are current contracts, not an upgrade checklist. Input/adoption procedures belong to state adoption; source-version chronology belongs only to the durable format reference.
Configuration selection and prompt closure
The selected strict YAML chooses card-type definitions through exactly one source form: a complete card_types map, or omission of card_types, which selects the bundled classic definitions. The deleted card_type_set selector key fails strict source validation as an unknown key; it never defaults, falls back, or merges with an explicit map. saivage init [--profile <classic|classic-typed|paad-typed>] materializes the complete selected system template on a config-absent project: the template's full prompt tree into .saivage/config/prompts, a provenance marker into .saivage/config/template.json, and the complete config YAML—containing its explicit card_types map—published last as the single configuration-materialization completion commit. This input publication is separate from generated-state admission and does not make retained generated work compatible with the materialized identities. An existing .saivage/saivage.yaml gates the whole template/config materialization: init preserves the prompt tree, marker, and YAML, and --profile is inert. After successful init the instance is the single runtime authority; templates are materialization data and are never consulted at runtime. Template provenance is not authority for the effective identities that produced retained generated work. ResolvedConfigAuthority resolves the source once to a complete effective card_types map before structural compilation. All compiler, runtime, REST config, show_config, and selected-config Files consumers receive only that singular effective map.
The selected strict YAML is structurally compiled before any generated card publication. Structural compilation validates every configured card-type workflow, exact named-agent/model/skill/session references, resolves every ordered operational tool through the exact global/card catalog scope, and freezes each route/profile plus array-form equivalence and direct-failover expansion as orderedModelIds; it also validates child narrowing, record/bootstrap definitions, local source-node terminal exports, graph reachability, latest_node existence/path, and every selected agent/process prompt. Prompt roots mirror agents|process|fragments/<card-type|_shared>/<reference>.md; project card-specific, project shared, bundled card-specific, then bundled shared is the exact root-major order, and only exact absence advances. Agent filenames use the configured prompt reference; the global Analyst uses shared scope only. Direct {{> fragment-id}} inclusion is one level, uses the host card type, and is validated under the host policy. Each shipped Planner, Executor, Reviewer, and Analyst agent prompt directly includes project-guidance-common and its matching role fragment. These hooks compose only when that selected prompt contains them: custom prompt references and full overrides are not automatically modified or required to include them, and the Analyst remains shared-scope only. Workflow-agent system prompts require {{contractDescription}} exactly once after composition; process prompts allow only {{cardType}}, render eagerly, and are frozen as final text. After optional initial publication and canonical admission, startup binds the capacity participant set and installs one immutable agentBindings map before actor construction. Each installation resolves only its explicit model IDs through the Provider Registry and retains its exact capability request: complete card surfaces include generated emit_result last and require tools plus exclusive choice, while Analyst has only its operational surface and may retain no-tools plus exclusive choice. Provider admission consumes that retained request unchanged. Real turns bind only selected invocation-scope executor/cleanup closures. Offline init and reset, and the initial-publication step of start --create-runtime, use the same structural compiler without live provider binding. Start then continues through server binding, recovery and MCP reconciliation; the flag does not make Start an offline validation command. Offline init additionally materializes the selected template's complete prompt tree and provenance marker before publishing the config YAML last; the copy skips already-existing destination paths, so a crashed config-absent materialization attempt can be completed by re-running init, while an existing config preserves the tree, marker, and YAML bytes exactly. If generated work already exists, that retry still undergoes strict current-state admission and is not an identity-changing upgrade or repair. reset and start --create-runtime materialize nothing. Actors receive only the bound artifact and perform no source-config, prompt, route, capability, or tool selection.
models.equivalents is optional and defaults to []; when present, it is exactly an array of string arrays. Mapping/object forms and malformed members fail selected-YAML validation as a complete input and are neither rewritten nor omitted.
Shipped project completion and profile graphs
The three registered system templates are classic, classic-typed and paad-typed; classic is the bundled default (the omission card_types source and the bundled prompt root), and each template is materialized whole by saivage init --profile <name>. All are complete definitions containing project, goal, architecture, code, test, doc, data, research, and ops, in that declaration order. paad-typed reuses classic-typed's immutable configuration exactly: graphs, outcomes, ordered tools/routes, record initialization and prompt declarations/retention are identical. Its complete independent physical prompt tree differs only in the shared Planner, Executor, Reviewer and architecture component/system review prose. It adapts PAAD pushback, bidirectional alignment, bounded structural review and anti-degradation discipline through existing owners, without new gates, BLOCKED reasons, roles, records or runtime selection. Analyst/Oversight and all six neutral project-guidance fragments remain unchanged. This is optional instruction guidance, not measured model quality; see delivery and attribution. Existing-config init preserves YAML, prompts and marker, never upgrading or switching profiles. Later adoption requires separately authorized stopped reconciliation and independent identity/format compatibility assessment. Classic project uses plan, review, recover and project-only Planner assess-completion; every root DONE edge originates at assessment. goal retains direct completion from plan/recover and a conditional Planner handle-notifications node after accepted review when recipient context is pending. The other seven types each use one execute node. Planner is the planning-card recipient and Executor is each leaf recipient. A template is one complete TypeScript-owned config plus its exact prompt closure; its config contains the card types, workflows, and record declarations and does not own or override global agents, analyst_agent, model routes, providers, compaction, server, or MCP configuration beyond publishing the template's complete values for them at init. The resolved effective configuration owns the actual complete map. A card-type name matches [a-z][a-z0-9-]{0,63}; project is the sole reserved name, is required as the fixed root entry, and cannot be a child. Every child reference is unique and names a key in the same map. Compilation preserves declaration order in one immutable map and one ordered all-key vocabulary. There is no planning/terminal family or autonomous role classifier. Nodes reference card-scoped named agents; the configured global Analyst uses one global named session plus explicit parent/card targets. Creation authority is the named agent's global can_create_children and create_card ceiling intersected with the selected parent's compiled permitted_child_types; activation separately requires activate_card. Default Planner retains 21 tools, Reviewer 13 without MCP, Executor 20 and Analyst 45. Analyst/Executor select mcp_server_control, mcp_tools and mcp_tool_call as independent permissions; explicitly configured Reviewer may select them, but Oversight admits none. Annotations are descriptive only.
In classic-typed, project and goal permit all eight non-root types in declaration order; every other type is a leaf. project, goal, and architecture declare brief.md, status.md, and review.md; all other types declare brief.md and status.md. brief.md is always the sole bootstrap record. The classic-typed and paad-typed configured graphs are identical:
| Type | Exact configured flow |
|---|---|
project | Recipient Planner. plan/recover: assess_completion -> assess-completion, admit_review -> review, blocked -> BLOCKED, failed -> FAILED. review: approved -> assess-completion, revision_required -> plan, blocked -> BLOCKED, failed -> FAILED. Assessment: replan -> plan, admit_review -> review, complete_direct/complete_reviewed -> DONE, blocked -> BLOCKED, failed -> FAILED. STOPPED enters recover; there is no project handle-notifications node or review pending-notification alternative. |
goal | Recipient Planner. plan: complete_direct -> DONE, admit_review -> review, blocked -> BLOCKED, failed -> FAILED; review: approved -> DONE when no context is pending, otherwise accepted approved -> handle-notifications; revision_required -> plan, blocked -> BLOCKED, failed -> FAILED. handle-notifications: admit_review -> review, blocked -> BLOCKED, failed -> FAILED. STOPPED enters recover, which has the same outcomes as plan. |
code | red: red_confirmed -> green, already_green -> refactor; green: green -> refactor, still_red -> green; refactor: done -> DONE, regressed -> green; each node also has configured blocked and failed terminals. |
test | diagnose: coverage_ready -> verify, coverage_gap -> add-coverage, failing_test -> repair; add-coverage: coverage_passing -> verify, repair_needed -> repair; repair: tests_passing -> verify, still_failing -> repair; verify: done -> DONE, coverage_gap -> add-coverage, repair_needed -> repair; each node also has configured blocked and failed terminals. |
research | explore: evidence_ready -> assess, more_exploration -> explore; assess: supported, refuted, or bounded_inconclusive -> report, and evidence_gap -> explore; report: done -> DONE; each node also has configured blocked and failed terminals. |
data | schema: schema_ready -> validate; validate: valid -> implement, schema_invalid -> schema; implement: done -> DONE, implementation_retry -> implement, schema_revision -> schema; each node also has configured blocked and failed terminals. |
architecture | Recipient Executor. draft: ready_for_component_review -> component-review; component-review: approved -> system-review, revision_required -> draft; system-review: approved -> DONE when no context is pending, otherwise accepted approved -> draft; revision_required -> draft; each node also has configured blocked and failed terminals. A notification return must repeat component and system review. |
doc, ops | One execute node: done -> DONE, blocked -> BLOCKED, failed -> FAILED. |
Typed research treats evidence_ready as readiness to assess the bounded question, including reasonably exhausted or inconclusive investigation, rather than support or exhaustive coverage. A return to exploration requires concrete obtainable evidence or materially different useful analysis capable of changing a named assessment or decision; the status names that discriminator, the change it could support, and the next work. When the bounded question remains uncertain, successive recorded hypotheses have been refuted without convergence, and no useful discriminator remains, assessment selects an honest bounded-inconclusive report; a conclusively refuted question remains refuted. Completing the report accepts only that bounded research deliverable: unresolved questions, limits, required exhaustive follow-up, and actionable handoffs remain explicit, while evidence promotion, policy approval, and parent or project acceptance remain their respective owners' decisions. An accepted brief remains authoritative; a required deliverable that cannot proceed without specific scope, input, or a decision uses the existing blocked route rather than being silently narrowed.
Typed data validation distinguishes correct rejection of invalid samples from a schema defect and technical acceptance from owner or policy approval. Only an actually mandatory missing decision or input blocks; ordinary engineering and accepted graph nodes require no new human signoff. Across all templates, shared Executor guidance compares the affected path branch-by-branch with the applicable reference or specification—including state transitions, ordering, and error behavior—and repairs the owning primitive within the brief rather than accumulating symptom patches. It preserves earlier failed or unresolved checks despite a later successful tool call and reuses evidence only while relevant inputs remain unchanged and the evidence applies. Changed checks, currently required checks, and unresolved verification questions require execution; prior failure remains explicit and required validation is never waived. Typed code red owns adequate reproduction and diagnosis of relevant branch and boundary behavior without beginning repair; green owns the complete coherent primitive repair and affected-path regression coverage. Typed refactor may reuse recent valid focused evidence after a reasoned no-change decision unless current criteria require a rerun.
Typed test diagnosis classifies adequate meaningful coverage plus passing focused tests as coverage_ready, including on fresh entry or re-entry after interruption. That route requires no manufactured test, source, or metadata change: it reuses test-to-verify and enters the existing verify node, which owns affected-suite and coverage acceptance and remains the only node that can complete the test card successfully. Verify may cite immediately preceding same-card focused-suite evidence only while relevant inputs are unchanged and that coverage establishes the cited part of acceptance; it records provenance and applicability, runs every remaining affected or acceptance-required check, and reruns after relevant changes or unresolved verification questions.
Unfinished assigned source/control/caller investigation and acceptance reconciliation remain verification work, not missing coverage. Against the agreed fixed input, verify resolves the brief's required items with supported acceptance, a concrete defect, or a precise evidence limit; an evidence limit cannot waive a mandatory check, and a generic whole-product qualification cannot enlarge the brief's non-goals. Re-adjudication uses the actual final integrated repaired input, preserves exact provenance and applicable settled dispositions, and inspects affected changes rather than repeatedly reviewing obsolete input plus an unintegrated overlay. An unresolved final-input decision is named, not silently substituted.
coverage_gap identifies an uncovered required behavior and actionable missing meaningful assertion/fixture whose authorship is authorized; repair_needed identifies a concrete verified failure whose repair is authorized. Add-coverage satisfies that identified task, including truthful reuse of already delivered applicable coverage, not status-only changes or unchanged-test reruns presented as new coverage. If a mistaken handoff supplies only unfinished review and no missing assertion, it records the precise assignment mismatch and needed Planner correction and returns blocked, not a fictional no-op coverage_passing. Ordinary authorized test/fixture repair and expressly assigned product support remain legitimate; checks of one's own repairs are author evidence, not separate independent acceptance. A specifically independent review instead returns actionable source/counterexample, significance and correction-owner findings without silently becoming fix author and independent approver.
Only the four typed test nodes extend their local blocked guidance to genuinely unavailable required input, concrete assignment/correction requiring another owner (including independent findings), or applicable explicit owner-directed reassignment. Status preserves current work/effects and names the true reason and needed owner/action before the ordinary terminal return. Explicit owner return need not exhaust technically possible work; unfinished permitted investigation alone is not blocking. Notification delivery alone does not release the active child or schedule its Planner: the awaited activation returns only at the child's actual supported boundary. The Planner resolves that handoff, coordinates correction through the permitted implementation owner, establishes combined repaired input and supplies concrete fresh direction to the same review child. BLOCKED re-entry starts at diagnose, then adequate passing coverage routes to verify; direction cannot amend the brief, type or permissions or jump nodes. Recognition of this test-child handoff does not expand the Planner's own blocking criteria or those of other workflows.
The existing retry outcomes at typed code green, test repair, and data implement require materially changed diagnosis or implementation evidence supporting a concrete useful next attempt. Their existing failed outcomes apply when the bounded task remains unsatisfied after materially distinct reasonable attempts with named hypotheses and observed failures, no diagnosis or implementation is converging, and no justified useful next attempt remains, or when evidence establishes that the task cannot succeed within its accepted constraints. Existing status records the attempts, exhausted hypotheses, residual uncertainty, and remaining work without claiming global impossibility. Code green and data implement retain blocked for a specifically identified genuinely unavailable required input, access, or decision; test repair additionally has the typed-test-local assignment/owner returns above. Ordinary uncertainty or difficult engineering is not a blocker. This is prompt policy at the current outcome owners; it adds no retry counter, state, preflight, or engine mechanism and does not redefine Planner strategy exhaustion or bounded-inconclusive research.
Shipped typed-test diagnose/verify guidance conditionally permits useful trustworthy independent discovery within the already-assigned finite boundary on an exact stable input and unchanged criteria before an original-owner repair handoff. Diagnosis remains classification. Verify separates rejection of acceptance from cancellation: a safe, useful, within-budget ongoing independent command may finish on unchanged relevant inputs in the same node before result emission; unsafe, invalid, masked/dependent, budget-conflicting or explicitly curtailed work is reported honestly, not forced to completion. Node-scope process cleanup is unchanged. Findings are consolidated by original semantic owner; Planner coordinates scoped corrections and shared-file ordering through direct children, uses focused/combined checks while input changes, then establishes actual stable combined input for independent non-author adjudication and every required full gate. Applicable settled evidence is reused without stale acceptance. This is prepared operating guidance, not a first-finding runtime transition, batching guarantee, new legal outcome, mandatory Reviewer-role gate or acceptance waiver. Classic retains its one-node Executor outcomes; PAAD retains its intentional additions. Existing materialized/custom prompts require deliberate authorized stopped reconciliation and a fresh process, not an init upgrade.
All classic-typed and paad-typed non-planning entries begin at the first node shown; STOPPED adds stopped-recovery. Node IDs use the exact lowercase hyphenated form, notably assess-completion, goal handle-notifications, add-coverage, component-review, and system-review; underscore-bearing outcome IDs remain exact outcomes rather than node IDs.
Planning, assessment and current status
Classic-typed planning by default reuses existing immediate children rather than duplicating them, subject to the scoped local-guidance override rule. Planner may activate BACKLOG, CHANGED, BLOCKED, or STOPPED children through their matching entries with explicit instructions; only configured globals produce public notifications. edit_card is only for a genuine immediate-child metadata change to title, priority, or urgency; it cannot edit a brief, dependency, parent, type, or lifecycle and is not a generic reopen. An owning Planner with the configured card-scoped reopen_card capability may reopen only its exact DONE or FAILED immediate child through reopen_card({card_id:"<id>"}); success changes only that child to CHANGED, after which explicit activation supplies the corrective direction under separate ordinary admission. An actionable BLOCKED child normally needs only explicit Planner activation direction. CANCELLED never reopens. The distinct global Analyst operation retains reopen_card({cardId:"<id>"}): while intervention-ready it may target BLOCKED, DONE, or FAILED work and applies the existing target-to-ancestor changed propagation and notifications. The two scoped contracts share a tool name but are not aliases or interchangeable authority.
All shipped templates use one shared Planner instruction per template for project and goal, interpreted against the frozen card-type context supplied at invocation. At project, Planner owns the coherent strategy for carrying the owner's complete objective to evidenced acceptance: it maintains coverage, assumptions, risks, dependencies, integration, progress, remaining work, and acceptance evidence through the existing brief.md and status.md records. The owner's outcome and acceptance remain stable unless the owner changes them. At goal, Planner owns local decomposition, ordering, coordination, and repair within the delegated outcome and reports evidence and genuinely cross-scope implications upward; routine local choices do not require root approval.
Goals may add planning value for uncertain or evolving work, several coordinated deliverables, or decisions that should remain outside root context, including serial workstreams; independent review and parallelism are benefits rather than admission requirements. A bounded defect with implementation and regression coverage can remain one terminal assignment, and a small project or isolated bounded crosscutting assignment may use direct root leaves. Planner direct work remains coordination, investigation/evidence assessment, and permitted record work rather than Executor implementation, build, or test work.
With an explicit rationale, Planner may use its existing cancel_card tool for an abandoned, superseded, or unproductive direct-child approach only when that makes the work obsolete or explicitly rejected and current tool and lifecycle admission permits cancellation; cancellation is not scheduling deferral. Cancellation is terminal, cannot be reopened, and does not satisfy dependencies requiring DONE, so it must preserve honest status and outstanding acceptance and must never hide failed tests or unfinished obligations.
Planner reflects purposefully when child results, review findings, blockers, or meaningful new evidence arrive: it treats findings as evidence rather than blindly adopting a suggested remedy, retains sound direction and useful evidence, changes tactics or strategy only when facts warrant it, and never replans ceremonially or claims completion against uncovered acceptance. It distinguishes research delivery, implementation, evidence promotion, and project acceptance, compares specification requirements with card- or agent-introduced sequencing, and removes unnecessary local sequencing without lowering acceptance, erasing gaps, or discarding genuine dependencies. Exhaustive obligations remain in planning records and delegated outstanding work; all-goal signoff or exclusion decisions do not block unrelated scoped implementation absent a real dependency. A failed local tactic is not automatically a disproved strategy. A substantively different, evidenced path to the unchanged outcome may use a different goal; autonomous relabeling or cloning of failed work is not that path. By default, for ordinary in-scope correction, the owning Planner reopens the same DONE direct child and activates it with concrete corrective direction; a FAILED-child retry requires material new diagnosis, input, correction, or a justified different route stated in existing status or explicit activation direction. A genuinely distinct bounded follow-up may cite the earlier delivery or review without cloning its whole scope.
An explicit operator override of the reuse/anti-cloning policy under the shared scoped-override rule may instead request supported cancellation and fresh replacement with the same intended assignment, without invented novelty. There is no atomic recreation, history transfer, automatic dependency rewiring or inherited acceptance: the replacement has a new identity, cancellation does not delete or erase the old card, and useful work, failed checks, review findings, refusal facts and accepted records retain their true applicability while unresolved obligations remain outstanding. Ordinary caller-specific tool and lifecycle limits, including terminal cancellation and DONE dependencies, still apply.
Root reactivates an actionable BLOCKED goal or reopens its own DONE/FAILED goal so that goal's Planner can repair its children; root has no grandchild reopening authority. Planner exhausts reasonable in-scope tools and strategies before blocking. Real owner decisions, unavailable resources or configured capabilities, and materially exhausted strategies are escalated; ordinary correction and routine engineering judgment are not. Existing tool admission remains authoritative: Planner cannot reparent, cannot edit a child's brief or dependencies, and can choose dependencies only among existing immediate siblings at child creation. Fake metadata, pretend credit, weakened acceptance, cancelling failures to hide progress, and bypass of refusal, cancellation, or security limits remain forbidden.
Planner and Executor author current status.md as a synthesis against the accepted brief, not a diary or prepended continuation inventory. Conversation is the work history: its timeline must not be relocated into a separate log/history record or document; actual deliverables and necessary primary evidence remain permitted work products and records. Status distinguishes actual delivery from proposals/in-progress work, acceptance from still-unaccepted work, and names remaining obligations and next scoped actions. Superseded current sections are consolidated while actionable scope, material decisions/blockers/uncertainty, partial or failed validation qualifications, exact source/release/artifact identities and evidence applicability remain explicit. Preserve current resource ownership, unresolved effects and applicable cumulative accounting when needed for current conclusions or safe continuation; consolidation neither confirms release/cleanup nor restarts an accounting period. A passing check on another input does not resolve a prior gap. Existing work products and retained accepted record history supply detail through precise references; references must not hide outstanding work or load-bearing qualifications. This guidance imposes no fixed headings, length quota, new artifact-per-action requirement or acceptance gate, and a summary cannot replace substantive required deliverables. A genuinely complete status still states delivery/acceptance and supporting evidence. See the single illustrative before/after example. No historical content, permissions, record mechanics or retention rules change; Reviewer independently evaluates evidence and writes its own review, not status.
Shared Planner owns generic project/goal responsibility, strategy and remedy judgment, complete direct-child inspection and reuse, lifecycle repair order, metadata boundaries, blocker materiality, configured capability limits, and operation-result handling. Typed plan and recover prompts are current-node deltas: plan assesses the current brief, status, children, records, delivered context, and review evidence before choosing a current outcome; recover rebuilds that assessment from durable facts without reconstructing a discarded graph position. They do not repeat or replace the shared child-management policy.
Classic goal plan likewise coordinates remaining work against current evidence before direct completion or review; child count alone does not choose the outcome. Project plan/recover instead request assessment or review before closure. Classic execute satisfies the scoped brief with relevant verification and evidence, permitting a justified no-change result rather than manufactured edits. The typed code refactor handoff carries accepted green or already-green evidence into the existing conditional verification policy; it imposes no unconditional rerun. Correction guidance repairs the diagnosed result or required-record violations using the supplied remaining attempts, without automatically repeating otherwise valid delivery.
In all three shipped profiles, Main Planner at assess-completion reconciles the current accepted owner brief, remaining plan/status, direct-child delivery, applicable accepted review and underlying evidence, and delivered owner context. Bounded report/inventory, child or milestone acceptance is not necessarily project-objective acceptance; a bounded request does not silently replace the objective. Assessment updates status.md (continue/updated) with actual fulfillment/acceptance, evidence and applicability, meaningful authorized work remaining and the next scope or precise blocker. It neither implements nor activates children. Open beta or explicit post-beta obligations require replan, returning to planning under the shared child-management policy; missing necessary independent acceptance requires admit_review. Legitimate direct-evidence completion remains available, never as a bypass of required review or outstanding findings. A fulfilled finite objective with no continuing obligation can finish; ambiguous continuing scope requiring an owner decision yields BLOCKED with that exact decision and known accomplishments, not invented goals or endless cycling. This is a configured assessment responsibility, not a deterministic product-quality guarantee.
complete_reviewed requires applicable review accepted in this activation and promotes the real Reviewer's executor/node/outcome/summary through latest_node: review, while terminal exports come from assessment's accepted Planner status.md, citing the immutable review. Reviewer alone authors/accepts review.md; no verdict is copied or reaccepted by Planner. Direct/blocked/failed assessment results remain Planner-attributed. Changed work/input needs replanning or fresh review. Historical review cannot supply latest-node promotion after STOPPED re-entry; urgent interruption still enters recover, not the removed project handler.
Delivered notifications and peer context are queued evidence with provenance, not instructions that override the current role, node, generated contract, or applicable owner requirements. For accepted goal review with pending recipient context, the durable review-to-notifications handoff owns only historical framing: accepted summary and immutable versioned review URL remain evidence, not a rejection. The separately prepared goal handle-notifications node owns current action: assess delivered context against accepted evidence and unchanged owner authority, coordinate justified work, mandatorily update current-node status, and return through admit_review or an existing justified terminal route. Project review instead always reaches assessment through ordinary accepted-edge context; neither route infers hidden queue state or completion.
The shared Reviewer independently inspects relevant implementation, tests, records, and artifacts; traces load-bearing claims rather than accepting author status; and, for family criteria, assesses representative members and boundary cases without waiving expressly exhaustive criteria. It judges only criteria assigned to the current node. Specialized planning review applies that method to the brief and subtree, treating descendant status as evidence pointers rather than acceptance by itself. Reviewer has no command-execution authority and this prompt policy adds none.
Review and architecture handoff
Architecture uses the shared Reviewer prompt and one declared review.md. Both component-review and system-review start a clean updated cycle of that record. Accepted transition context carries immutable record:///review.md?card=<id>&v=N evidence with its optional edge prompt; the destination node prompt is supplied separately as the next activation's prepared compiled-node block. Component evidence therefore remains readable after system review opens a newer clean version and revision returns to draft. A revised draft synthesizes current proposal progress and outstanding review obligations in status.md, retaining immutable accepted-review citations rather than cumulative revision accounts. Final system approval promotes the latest accepted draft result while exporting the current system review.md.
Participating identities and installed inputs
Card-type wire schemas validate only that identifier syntax. Startup resolves every reached active canonical card against the selected compiled map and fails if its type is absent; parent/type admission follows from the reached active parent's compiled workflow. A reached retained tombstone is strictly consumed and terminates traversal before workflow, record, or session admission for that card or any descendant behind it. The same ordered compiled vocabulary, always including project, supplies the Analyst prompt, Analyst create_card schema/preflight, both global-Analyst and card-agent list_cards schemas, and Planner execution membership. Analyst create_card requires one exact existing parent card ID; omission, null, and malformed card IDs fail invocation-schema admission. Analyst and Planner creation accept type, title, bootstrap content, optional priority/urgency, and optional depends_on; Analyst additionally supplies the explicit parent while Planner infers it from the session. Neither creation contract accepts tags or a generic related-card list. Current get_card sections are exactly summary, workflow, dependencies, children, and records: bounded scalar summary and workflow reject any supplied position, including all-zero; only collection sections dependencies, children, and records accept optional position. Omit position for their first page and copy a non-null next unchanged for the same section and stable input to continue. Immutable get_card_version sections are summary, dependencies, and children: only summary is scalar and rejects every supplied position, including all-zero. Omit position for summary; after a supplied-position error, remove it rather than retrying it. Dependencies and children are collections: omit position initially and copy a non-null next unchanged for the same section over stable input. Historical children retain the selected row's tombstoned links and do not establish current liveness. list_cards filters are exactly status, type, and parent, plus paging controls. Vocabulary membership is not creation authority: Analyst project calls with an explicit parent still reach the one-root domain denial, Planner retains its explicit root denial, and node/parent admission remains later. The fixed project root is published only by bootstrap and cannot be created by an agent tool. Planner's wire schema stays a plain string. list_cards rejects an unconfigured scalar or array filter at invocation-schema admission before its shared executor filters cards. The shipped nine-name prompt and tool schemas remain byte-identical.
Changing templates/maps/types or matching prompt/config inputs requires stopped reconciliation (fresh init only under its own preconditions) and authorized start. Identity-retaining prompt/route/tool/host/port/edge/outcome changes require no reset solely from the identity rule; independent format cutovers still apply. After initialization, renaming/removing/replacing/changing scope of participating card agents, selected Analyst changes, rebinding reached-card nodes to other named agents, or renaming/removing/replacing workflow node/state identities is incompatible adoption. Choose separately consented complete reset or explicitly requested external migration before use. This applies to reached active cards including non-tombstoned DONE/FAILED, not just running actors. New types/unused declarations/changes not altering existing participating identities remain outside that predicate. Startup strictly admits retained generated state against the newly resolved effective map. It requires the exact current selected-Analyst index and every distinct node-agent index derived from each reached active card's compiled workflow. Missing expected indexes reject rather than being created; this catches some identity changes and incomplete or lost first publication, but it is not a comparison with prior configuration and does not certify that every identity was retained. In particular it cannot generally detect same-agent node-ID changes or changes whose newly expected indexes already exist. Removing the former project handle-notifications identity to adopt shipped assessment therefore requires separately consented complete reset or explicitly owner-requested external offline migration when the retained project is reached and non-tombstoned, including DONE/FAILED. No evidence that the handler ran is required. Stopping and reconciling YAML/prompts alone is insufficient; unchanged family formats, successful compilation/startup/readiness and preserved custom guidance do not certify adoption. A reached tombstone terminates admission as above; unused declarations remain outside the participating-identity predicate. This source change authorizes no live adoption or historical DONE correction. If a reached active type is absent, parent/child policy no longer admits the linked relationship, or a required session index is absent, startup fails before subsequent app-log/provider/current-conversation/record consumption. Card JSON has no torn-tail permission. Prior lifecycle-lock publication, configuration/template materialization, identity work and any completed initial publication are not rolled back. Later owning growing-file admission can truncate a fully validated torn tail before a still later failure; successful earlier consumption is not a transaction. The operator may restore compatible inputs, or choose separately consented reset or explicit external migration for incompatible adoption. Saivage itself has no migration/probing/fallback/aliasing/compatibility/normalization/merge path; current-format exact-target repair remains separate, not identity conversion.
Selected Oversight remains lazy, outside startup required-index admission. Changing a never-used unpublished Oversight identity needs no reset solely because unrelated card/Analyst history exists. Replacing known published Oversight history is operator-assessed incompatible identity adoption with the same reset/external-migration choice. No scan/inventory/prior-identity detector or startup certification is added.
Prompt packaging closure
Bundled prompts live in per-template trees under src/config/system-templates/<name>/prompts/. Packaging compiles each registered template standalone against its own source prompts root, observes the bundled agent, process, and direct-fragment artifacts actually selected, and requires that template's physical tree to equal its sorted compiled closure exactly. Each template's physical prompt tree is locked to its complete compiler-observed source closure. classic contains the five shared agent prompts, including both global hosts, six shared project-guidance fragments, project-scoped plan/recover/assessment hosts, and every shared process prompt selected by its goal planning, review, notification-handler, recovery, and execution graph. classic-typed retains its typed card-specific process prompts, project-scoped specialized-plan/specialized-recover/assessment hosts, shared goal notification-handler prompts and the architecture notification-return prompt. paad-typed owns the same complete selected path set as classic-typed, with byte equality except its five intentional adapted prompts; classic/classic-typed family parity remains separate and unchanged. The full pinned upstream MIT notice travels in the Planner's non-procedural attribution section, not an extra unselected artifact. Startup strictly composes the selected direct hooks and freezes the result. Existing materialized prompt trees remain instance-owned and are not upgraded by init; no hook is injected into a custom prompt and no automatic reconciliation occurs.
Prompt scope is a discriminator—global agent, workflow agent with card type, or process with card type—not a pseudo card name. Therefore a configured card type literally named global receives ordinary card-specific agent, process, and fragment tiers and workflow/process placeholder rules, while the global Analyst remains shared-only. A workflow with nonempty permitted_child_types is the existing capability used for Analyst record-edit ancestor notifications. Analyst creation always uses its required explicit parent card ID. Planner creation remains separate: its current parent is inferred from the active Planner session and cannot be supplied.
Record and edge acceptance
Records are arbitrary configured safe Markdown names, not three code-owned slots. Each record owns a strict format-3 record-head and immutable format-4 accepted-record predecessor documents with exact card/name/format/schema identity and discriminated author. Current-only baseline0 cannot coexist with draft/acceptance. Drafts have explicit document/target versions; draft0 and empty drafts cannot be accepted. Bootstrap first-publishes factual accepted1 then head. Open/edit/close/discard advance mutation revision independently of consecutive accepted document versions; already-open open is a strict-read no-op. Baseline/draft history is not retained. Historical accepted projections have null head/revision, never an inferred mutation number. Each mutation freshly reads exact current authority; no carried write token exists. Every successful acceptance freshly proves its exact active card and stores observed accepted.card_version_seq plus the selected immutable ordinary reference, exposed as card_history_version and card_history_entry_id. Observed current 3/history 1 stays (3,1), not a fabricated card artifact 3. Global Analyst mutations resolve explicit card/definition, require configured writer glob and operation-required tools (write for whole writes, edit for edits), reject an existing workflow draft, and synchronously first-publish acceptance then replace the head before existing lifecycle effect/record-specific propagation. Subsequent admitted corrections remain possible while CHANGED without Run/activation; unchanged content rejects and each success can emit distinct notices without repeating changed transitions. Record Webfetch requires both webfetch and write, sharing read-only preflight and fresh final admission, not an exemption or carried write authority. Card detail, Files, REST, live sync, and Cards UI all use the selected card type's ordered descriptors and exact dynamic record names.
Every node edge uses one acceptance algorithm. All requirements, descendant freshness, terminal completion, promotion, and exports are validated before mutation. A terminal edge alone claims the terminal winner before closes. Updated records close exactly once in declaration order; downstream context, accepted URLs, and terminal exports use retained closed projections and the exact close returns without rereading. Primitive-classified close uncertainty permits no later close, read, transition, correction, accepted result, lifecycle publication, parent settlement, or Supervisor halt; the first fatal boundary exits. Successful terminal routes promote either the current accepted result or an existing graph-reachable latest accepted node result and return ordered exported record references through lifecycle state and activate_card; ordinary pre-commit close and execution exceptions produce ordinary node failure.
reconfigure has only strict set_agent_model_route, set_model_failover, and host/port set_server_setting variants. Every successful replacement reports applied:true, requires_restart:true; candidate structural compilation is discarded and the current workflows, installed agent bindings, tools, MCP manager, and listener remain unchanged until restart. Role routing, runtime timeout/continuous-improvement mutation, and MCP add/edit/remove actions do not exist. Old YAML needs deliberate stopped current-input reconciliation; incompatible role sessions, record rows and lifecycle results need the separately authorized reset/external-migration decision before a matching-release start. No runtime translation.
Project Oversight
Oversight is an independently scheduled selected global participant. It observes and advises through the shared designated-recipient notification contract; it is neither an alternative dispatcher nor a project mutation/review authority.
Identity, authority, and configuration
Each project has one independently configured global Oversight agent with its own identity, prompt, conversation, model route, check lifecycle, tools, output limits, and model-aware context budget. It is distinct from the operator-driven Analyst and the sole-dispatcher Supervisor; its checks may coexist with both, subject to ordinary provider capacity. Planners retain strategy, acceptance, corrective action, and the right to reasoned disagreement. Oversight uses the ordinary named-agent catalog, explicit route and configured failover, invocation/capability admission, and conversation-history contracts, with no root-Planner model inheritance or special provider system. The required strict configuration is oversight:{enabled,agent,interval_seconds} even when disabled. New-project defaults select global oversight, enable it, and use 7200 seconds. The selected agent must differ from Analyst, be global, disable skills and child creation, declare no record writes, use a resolvable route, and select only the observation inventory below. Invalid references, intervals, prompt closure, tools, or capabilities fail startup rather than falling back or silently disabling checks. Existing projects adopt or disable it only through deliberate complete configuration and prompt work; configuration is epoch-frozen, with no automatic discovery, upgrade, compatibility default, missing-field normalization, or live toggle. Deployment applicability must still be established for every other retained durable-format cutover.
Schedule and lifecycle
A check is eligible only while Oversight is enabled and authoritative project runtime status is exactly running; a live server or any other status is insufficient. The first check waits one full continuous eligible interval, and losing eligibility discards that wait. Each later check waits another full interval after the previous check, including continuations, compaction, and settlement, safely settles. At most one check is in flight. There is no persistent timer, backlog, catch-up, burst, scheduling SLA, or server-triggered Run. A check may run alongside normal card work and Analyst activity, but ordinary provider limits may delay it. Leaving running, including pausing, completion, shutdown, or effective disablement, disarms the wait and requests cancellation. Resume or a new Run begins a fresh full interval only after prior ownership settles, with no missed or pending check accumulated.
Cancellation and truthful settlement
Cancellation admits no new investigation read, model call or continuation, notification submission, or urgent interruption request, but bounded truthful settlement of obligations already owned by the check remains required.
- A call not durably recorded and not entered is not fabricated.
- A durably recorded call whose operation has not entered receives its ordinary matching non-executed result.
- Entered work is joined or cancelled only through its existing owner and is never reported as unexecuted.
- A safely known completed result is published exactly once unchanged; an already published result is not duplicated.
- A confirmed notification enqueue remains confirmed and is not retracted, while urgency not yet entered is suppressed after cancellation.
When outcomes are known and every required publication succeeds, ordinary cancellation or safely settled provider, refusal, context, or investigation failure releases check admission with continuable history. It requires no final model report or immediate retry and does not fail cards or disrupt Analyst; a later check waits the next full interval. An unknown effect or failed/outcome-unknown required result, evidence, or terminal publication retains the exact fatal owner policy: no follow-up effect, read, inspection, retry, repair, invented settlement, or admission release. Strict history remains strict; startup reconstructs no check/known outcome. Its bounded configured-global final-unmatched mate states uncertainty only, not a check.
Read-only intervention boundary
Oversight is read-only with respect to project work. Its exact default/maximum inventory is get_status, list_cards, get_card, get_tree, list_card_versions, get_card_version, diff_card_versions, read_record_version, read, glob, grep, read_runtime_events, read_runtime_errors, list_processes_tool, list_agent_sessions, read_agent_session, and queue_notification; custom selected Oversight agents may use subsets. Agent, process, runtime-event, runtime-error, card, record, search, and file collections use the ordinary stateless byte-bounded response packing contracts. Its sole project-affecting operation is a shared notification contract bound to the Oversight-specific effect port: compiled capability, exact active-check cancellation signal, current running admission, and planning eligibility are checked by that owner rather than inferred from agent-name spelling or borrowed Analyst/runtime authority. Analyst uses its distinct audited notification authority without paused/stopped intervention readiness; no card agent has the public notification tool. Analyst non-record write/edit and cancel likewise require no intervention readiness; create, reorder, reopen, delete, reconfigure, and record mutation retain their operation-specific readiness admission. Oversight gains no file-mutation or cancellation authority. It cannot edit source, records, briefs, configuration, prompts, or policy; mutate, activate, reopen, cancel, delete, or reorder cards; run shell, build, test, process-kill, or effectful MCP operations; control project or model lifecycle; approve work; or obtain those effects through a broad tool. Ordinary publication of its own conversation, provider evidence, and notification result remains allowed. It normally selects the nearest responsible planning scope, uses root for strategic or cross-scope concerns, and never broadcasts. This is workflow role discipline that simplifies ordinary work and preserves role separation, not containment of a malicious root-capable agent.
Proportionate observation
Each check performs bounded, proportionate observation of objectives, linked work, records, conversations, process evidence, and source, distinguishing facts, interpretations, uncertainty, and current evidence from historical evidence. Recurring delivery gaps may be evidenced across checks or from bounded history inspected during one check; another check is not a prerequisite. Local progress is positive evidence, not an automatic exemption from convergence assessment. Relevant evidence includes a running card accumulating retry or repair executions over a sustained window without substantive delivery, growing prerequisites or disconnected patches and passing checks leaving required integration unresolved, cycles sustaining unnecessary machinery, or repeated unresolved findings directed at a terminal implementation owner that cannot consume or act on them. Mere status versions, repeated old revision citations, and repeated defect restatement do not establish convergence, but an ordinary review mentioning a terminal card does not establish futility either. Reads are non-atomic observations and never authorize a later write; partial or failed reads do not prove absence. Healthy slow work, research narrowing uncertainty, truthful red cycles and ordinary deep review remain legitimate; elapsed time, ordinal magnitude, call volume, difficulty or a red test alone establish no futility. Advice needs a concrete recurring gap and useful reassessment. On such newly actionable evidence, Oversight submits concise, qualified normal advice to the nearest responsible nonterminal card whose workflow reports planning_target: true, identifying the observed window, card, relevant progress, recurring pattern, unchanged acceptance gap, and any terminal owner. It asks that Planner to reconsider the approach or decomposition or coordinate repair at the actual owning card; it does not notify the terminal card, broadcast, prescribe automatic reopening, or claim strategy failure as fact. Project guidance supplies context but cannot waive owner scope or acceptance. Planners retain discretion and may reasonably disagree. A short no-intervention response is successful when there is no new actionable concern, but does not certify the whole project. Oversight uses ordinary conversation and compacted history, repeats equivalent advice only when new evidence or a stated material reason adds value, and corrects disproved advice. Previously queued advice with no observed handling is neither evidence of delivery or failure nor, by itself, new value warranting repetition or escalation. This advice does not interrupt active descendants and may await the designated recipient's existing handling opportunity. Enqueue proves neither receipt, consideration, action, eventual delivery nor containment. It does not broaden the separate urgent predicate below and requires no mechanical normal-plus-urgent pair. Oversight records unavailable recipients and owner-decision needs in its inspectable conversation without promising an operator alert; no runtime detector, delivery SLA, intervention ledger, deduplication registry, or guaranteed-attention channel is introduced.
Shared designated-recipient workflow
Generic designated-recipient notification routing is current behavior and a prerequisite for Oversight. Every card type designates a workflow participant and provides defined opportunities for that participant to handle queued context. Oversight targets additionally require that at least one node of the declared recipient has nonempty compiled child-creation and child-activation types. get_card exposes this bounded workflow policy projection as planning_target, together with recipient, permitted child types, and the current process position when available; it exposes no queue state. Planner-directed context waits for that designated Planner. A nonrecipient node, especially a same-card Reviewer, must not consume it, skip review, fabricate a result, or jump the workflow graph. Successful ordinary completion provides the generic handling opportunity through recipient arbitration or a configured nonrecipient-DONE conditional edge. Current routing reconciles pending context with BLOCKED or failure settlement, cancellation, postclaim denial, and every successful completion route. It preserves exceptional terminal clearing, notification-empty terminal states, and append-before-remove duplication/loss limits. It promises neither eventual nor exactly-once delivery and adds no second queue, receipt service, transaction, replay, or recovery protocol.
Urgent notification ordering
An ancestor notice's immediate-child status is an observation captured when queued, not a claim about status at delivery or consideration. Later ordinary child activation remains discretionary; reopening is appropriate only if the child is DONE or FAILED when considered and ordinary admission permits it. The workflow may decline.
Public queue_notification belongs only to configured global Analyst/Oversight. Analyst normal enqueue requires no runtime execution readiness; Oversight retains its exact active-check, running-runtime and planning-target admission. Normal enqueue does not Pause, Resume, or Run. Claimed target winners and terminal targets reject. Urgency is call-local and justified when ongoing harm, avoidable waste, or loss of information value makes waiting materially worse. The original target note is appended first. For urgent context, the Supervisor follows exact committed parent links, skips DONE, FAILED, CANCELLED, and BLOCKED only as synthesized ancestor recipients, and appends notices bottom-up through the nearest eligible real running owner. A notice identifies the original descendant and the recipient's immediate child on the path, including across skipped intermediates. Direct BLOCKED targets remain notification-capable; a parent may activate an actionable BLOCKED direct child without reopening. A willing parent may instead reopen a DONE/FAILED immediate child and activate it ordinarily, or decline. No skipped child receives later automatic backfill, no grandchild is directly dispatched, and no model compliance or target delivery is guaranteed.
With an eligible active boundary, after known appends the Supervisor synchronously claims the exact open owner scope before cancellation. It stops the competing/targeted owned suffix leaf-to-root, settles matching child tool results, and joins the boundary's old node. That boundary activation stays running; its synchronous derived interruption event installs a guarded recipient node, which executes only after settlement. The recipient uses its configured STOPPED recovery prompt and ordinary workflow tools. Stopped targets and bystanders later enter their same recipient-first STOPPED route only if a parent elects ordinary activation. For an urgent project target, the entire chain including the old root stops; after all joins the old owner retires and exactly one STOPPED root replacement starts within the same admitted run, without a public Run, gate reset, parent lease or overlapping root processor. The new root recipient consumes the original note.
With no eligible real running ancestor, even if inactive ancestors can accept notices, urgency only queues selected notes through project and creates no owner or automatic Run. An ineligible project receives no note. Explicit Run permits later execution but cannot by itself reopen terminal ancestors or guarantee delivery. Pause already owning admission suppresses interruption but preserves confirmed enqueues; Pause after claim gates the new recipient or replacement until explicit Resume. Stop/close takeover prevents further urgent publication or launch. Retained historical pending receipts confirm only the original recorded enqueue, never completed interruption or delivery. External completion returns interrupted with exact completed stopped IDs (possibly empty), or suppressed with reason and completed prefix; normal is not_requested, queue-only urgent is not_applicable. All confirmed forms include card/notification identity. Enqueue and interruption do not prove delivery, action, effect rollback, or guaranteed eventual attention. Publication uncertainty is fatal with no follow-up.
Privacy and failure containment
Pending queue state stays private. Authenticated/redacted sender body/result evidence and context actually appended to the recipient conversation remain normal inspectable transcript facts; they prove neither queue membership, receipt, model consideration, nor action. There is no queue browser, count/body projection, delivery inference, or separate report store. Existing file, history, redaction, and fatal-publication policies remain unchanged. Expected provider, refusal, context-admission, and investigation failures settle as process-local failed attempts and allow a later full interval. Publication uncertainty enters the existing nonreturning publication-fatal boundary. Other impossible owner/protocol, malformed-data, or required-settlement failures synchronously close application admission, run bounded existing cleanup, emit only a fixed safe Oversight owner-failure diagnostic plus safe shutdown warnings, and exit 1. No poisoned-health latch, report store, schedule persistence, retry queue, or feature-specific recovery exists.