# Runtime contracts

`simplicio-prompt` is being shaped to be **callable as a library** by the
future unified native Simplicio runtime — a single local program that
coordinates the ecosystem (`simplicio-mapper`, `simplicio-dev-cli`,
`simplicio-prompt`, `simplicio-sprint`) on top of a local LLM stack
(`llama.cpp` / GGUF). This document captures the machine-readable
contracts a supervisor binds to. The full manifest is at
[`contracts/manifest.json`](../contracts/manifest.json); the per-schema
files live under [`contracts/schemas/`](../contracts/schemas).

Two design rules govern everything below:

- **Generic, not project-specific.** No package-internal naming, no domain
  bindings (no EVT, no BeyondLabs, no consumer-product labels). A
  third-party orchestrator must be able to consume this without knowing
  what we built it for.
- **Forward-compatible.** Consumers MUST treat unknown fields as
  additions, not errors. Supervisors trail kernel versions; the kernel
  cannot wait on every downstream to upgrade.

## Manifest

[`contracts/manifest.json`](../contracts/manifest.json) is the entrypoint.
It declares:

- The two prompt templates (`oneshot`, `batch`), their on-disk paths,
  and the input/output expectations for each.
- The native runtime adapter schema (`simplicio.prompt-envelope/v1`)
  that the runtime compatibility matrix uses when binding
  `simplicio-prompt`.
- The deterministic edit schemas owned by `simplicio-runtime`:
  `simplicio.edit-plan/v1` for plans and `simplicio.edit-result/v1`
  for executor results. `simplicio-prompt` references these names but
  does not fork their schemas.
- The `kernel.subagent_runtime` execution modes (`fanout`, `arena`,
  `consensus`), their canonical entrypoints, and the JSON Schema for
  each mode's output.
- The local-agent status + escalation protocol (see "Agent protocol"
  below).

A supervisor reads `contracts/manifest.json` once at startup, validates
the schemas it cares about, and binds to the entrypoints by name.

## Native runtime compatibility schemas

The unified runtime treats `simplicio-prompt` as an optional adapter whose
compatibility contract is `simplicio.prompt-envelope/v1`. The envelope is
created by the runtime, cached by content hash, and handed to prompt logic
as scoped context; prompt tools must not invent a repo-local replacement.

For deterministic file edits, the runtime-owned schemas are
`simplicio.edit-plan/v1` and `simplicio.edit-result/v1`. Mechanical-edit
prompt guidance may help a model choose compact operations, but the
executor-facing artifact is still the runtime edit plan. Any richer
planner/context envelope must be compiled to the standard edit plan before
calling `simplicio edit`.

## Prompt contracts

Two templates ship; both are stable runtime-callable surfaces.

### ONE-SHOT — `prompts/agent-runtime-execution-prompt.md`

- **Role:** executor.
- **Input:** free-form task X. May be raw, or wrapped in `[CLI-6LAYER]` /
  `[GOAL]` / `[TARGET]` / `[CONTRACT]` / `[OUTPUT]` blocks supplied by
  `simplicio-cli`'s 6-layer contract.
- **Output:** a single artifact whose shape is determined by the contract
  or the user's request — file, diff, snippet, answer, classification —
  emitted verbatim with no preamble or commentary.
- **Non-narrating** by design: discourages tuple-graph annotation,
  status blocks, or runtime ceremony.

### BATCH — `prompts/agent-runtime-batch.md`

- **Role:** executor (orchestration-aware).
- **Input:** free-form task X plus orchestration intent (fan-out, audit
  at scale, parallel analysis).
- **Output:** structured analysis, plan, or per-item delivery; may
  include a status block when `YOOL_TUPLE_STATUS=true`.
- **Opt-in** via `YOOL_TUPLE_FULL_RUNTIME=1` or explicit user invocation.

## Subagent execution contracts

Three modes are exposed by `kernel.subagent_runtime.SubagentRuntime.run`:

| Mode | Entry | Output schema |
|---|---|---|
| `fanout` | `run(task, subagents=N, aggregator?, oracle?)` | [`fanout-report.schema.json`](../contracts/schemas/fanout-report.schema.json) |
| `arena` | `run(task, mode="arena", M=N, critic_provider=..., oracle?)` | [`arena-report.schema.json`](../contracts/schemas/arena-report.schema.json) |
| `consensus` | `kernel.aggregator.BehaviorConsensus.pick(candidates, oracle_fn)` | [`behavior-consensus.schema.json`](../contracts/schemas/behavior-consensus.schema.json) |

All three have `to_dict()` implementations whose output validates against
the linked JSON Schema. Supervisors should parse with their preferred
validator (we recommend `jsonschema` on the Python side; `ajv` on Node).
The CLI (`simplicio-subagents`) exposes the same surface — `--json`
emits the same payload, `--report-dir` persists `*-report.json` and a
human-readable `*-summary.md`.

## Agent protocol — 20-100 logical agents

The native runtime runs many small logical agents concurrently under a
single local LLM. The contract is two NDJSON streams:

### Status

[`contracts/schemas/local-agent-status.schema.json`](../contracts/schemas/local-agent-status.schema.json)
— one line per state transition. Fields:

- `agent_id` (stable id within the supervisor session)
- `role` — `planner` / `executor` / `reviewer` / `critic` / `aggregator`
  / `router` / `worker`
- `phase` — `starting` / `thinking` / `tool_call` / `waiting` /
  `completed` / `failed` / `cancelled`
- `task_ref`, `iteration`, `started_at`, `emitted_at`, `elapsed_ms`,
  `progress_pct`, `output_excerpt` (up to ~512 chars), `usage`,
  `metadata`.

The full output of an agent goes through the run's `*Report` schema. The
status stream is for UI / scheduling / supervision — not for final
delivery.

### Escalation

[`contracts/schemas/escalation-signal.schema.json`](../contracts/schemas/escalation-signal.schema.json)
— a deliberate, auditable signal that a local agent needs the
supervising remote/paid LLM to review, replan, or unblock. Kinds:

- `blocked` — agent can't make progress
- `uncertain` — agent is producing low-confidence output
- `policy_violation` — agent attempted something disallowed (route to
  human/remote LLM for arbitration)
- `scope_exceeded` — task is larger than the agent was scoped for
- `consensus_failure` — fan-out / arena failed to converge
- `cost_ceiling` — supervisor-configured budget reached (typically
  resolved locally, not by escalating)
- `oracle_unavailable` — behavior-consensus oracle could not run
- `external_dependency` — agent is blocked on a non-LLM input
- `deadlock_suspected` — runtime watchdog detected a stall

Severity is `info` / `warn` / `error`. Supervisors route on `kind` +
`severity`: e.g. `cost_ceiling` never reaches the remote LLM,
`consensus_failure` at severity `error` does.

The escalation protocol is deliberate by design. Routing every
uncertainty up to a paid remote model defeats the point of running
20-100 local agents.

## Consuming the contracts

### Python

```python
import json
from importlib.resources import files

manifest = json.loads(files("contracts").joinpath("manifest.json").read_text())
fanout_schema = json.loads(
    files("contracts.schemas").joinpath("fanout-report.schema.json").read_text()
)
```

(Both the wheel and the sdist include the `contracts/` tree.)

### Node

```js
import manifest from "simplicio-prompt/contracts/manifest";
import fanoutSchema from "simplicio-prompt/contracts/schemas/fanout-report";
```

The `package.json` `exports` map ships `./contracts/*` so consumers
import by name without spelunking into `node_modules`.

## Versioning

`contracts/manifest.json::version` carries semver for the contract
surface itself. It is **not** tied to the package version: a contract
bump is a deliberate breaking-change signal for supervisors. Adding a
new schema or a new field is a minor bump; removing or repurposing a
field is a major bump. The current contract version is `1.0.0`.
