# Pi host capability matrix

> Package line: `0.2.x` (verifiability increment)
>
> Host audited: `@earendil-works/pi-coding-agent` 0.83.0
>
> Audit kind: static source/API/runtime-code inspection; passive preflight is a separate negative check; executable Phase 0 probes are still required
>
> Decision: **NO-GO for native harness integration**

The architecture requires H1–H10 before any live compiled path. A partial or simulated substitute does not pass a gate.

| ID | Capability | Pi 0.83.0 status | Evidence and consequence |
| --- | --- | --- | --- |
| H1 | Multiple correlated native phase turns in the current session | Partial | SDK `AgentSession.prompt()` can run repeatedly, and an extension can enqueue messages, but `ExtensionAPI.sendUserMessage()` is `void` and exposes no phase handle, completion correlation, or atomic orchestration for the current session. A nested SDK session is explicitly forbidden. |
| H2 | Per-turn model override without changing selected session model | Missing | `ExtensionAPI.setModel()` changes the session model and persists a model-change entry. No turn-scoped model option is exposed. |
| H3 | Atomic per-turn toolset snapshot | Missing | `setActiveTools()` mutates session-global active tools. Snapshot/restore is neither turn-scoped nor atomic against other extensions/queued work. |
| H4 | Fully materialized request, inspected and blockable fail-closed immediately before dispatch | Missing | `before_provider_request` can inspect/replace a provider payload, but extension exceptions are caught and dispatch continues; headers are assembled in a separate hook. There is no structured block result over the exact final dispatch object. |
| H5 | Host-authenticated phase/finality metadata on native events and persisted entries | Missing | Native events contain no `runId`, `phase`, `attempt`, `finality`, or host signature. Custom entries are extension-owned sidecars and forgeable by peer code. |
| H6 | Disable built-in compaction for a run | Candidate | Settings support `compaction.enabled=false`, and `session_before_compact` can cancel. An executable probe must verify threshold, overflow, and manual paths and restoration after the run. |
| H7 | One abort scope cascading through stream, tools, reservations, and cleanup | Partial | SDK `AgentSession.abort()` exists, but third-party extensions receive no owned run/phase abort handle. Tool and controller cleanup cannot be proven atomic through the extension API. |
| H8 | Mandatory request/turn usage including nested work | Partial | Assistant usage is available and tool-result usage is optional. Pi does not enforce nested accounting or pre-dispatch reservation/settlement for extension work. |
| H9 | Atomic checkpoint/tree association and deterministic replay | Partial | Session trees and custom entries exist, but extension `appendEntry()` returns `void`; no API atomically binds native phase events, projection snapshots, and checkpoint IDs. |
| H10 | Cross-provider attested actual response model identity | Missing | `responseModel` is optional and provider support is inconsistent. Requested model/session metadata is not provider attestation. |
| H11 | Per-request cache controls and cache lineage | Partial/optional | Provider payload replacement may expose provider-specific fields, but no stable host-level, turn-scoped cache contract exists. Safe fallback is no cross-request cache reuse. |
| H12 | Native RPC events with run/phase/attempt/finality | Missing/optional | RPC emits native agent/turn/message/tool events. Sidecar custom entries do not add authenticated native phase/finality fields. |

## 0.2 passive preflight

The 0.2.0 `preflight` command is a bounded negative check, not a Phase 0
probe and not a substitute for the static source/API/runtime-code audit above.
It first runs the caller-selected or default Pi binary's unverified executable
`--version`, then runs with isolated HOME/temp/cwd/config and sends only four
passive RPC requests: `get_state`, `get_session_stats`, `get_messages`, and
`get_commands`. Production acceptance is a strict ordered eight-record
schema/shape with fixed lifecycle and zero-activity fields and bounded audited
variants (for example session and UI request ids within schema bounds)—startup
status/notification, the four responses (including `/ctx-status` registration
and zero activity-bearing state), and two status-clear records. It is not a
single normalized byte-exact transcript; exact normalized transcript matching is
reserved for the independent `smoke:pi` check. Hosts other than the audited
0.83.0 intentionally fail closed by design.

A pass establishes only that the schema/shape validated for that executable in
that isolated run. The default binary may resolve package-local or ambient PATH
and is always unverified; the version is self-reported; provider-call
attestation is unavailable. Runner constants such as `promptSent=false` and the
passive-request set are fixed by the preflight runner; `transcriptValid` and
activity observation are derived from the validated transcript. H1–H10
executable proof is not performed; the static audit still marks every required
H1–H10 capability missing, so Phase 0 remains `NO_GO` and the extension remains
status-only.

## Gate result

None of H1–H10 is fully available. H1, H7, H8, and H9 are partial; H6 is only an unverified candidate; H2, H3, H4, H5, and H10 are missing. Under the fail-closed gate, all H1–H10 therefore remain required-missing:

```text
phase0 = NO-GO
phase1Eligible = false
extensionState = capability-blocked
```

The installable extension exposes capability status only; offline inspection and synthetic evaluation run through the CLI. It deliberately does not register `/ctx on`. Any future enable command must refuse the live compiled path until executable Phase 0 evidence establishes every H1–H10 capability.

## Prohibited workarounds

The following remain non-compliant even if they look functional:

- background/nested sessions with widget playback;
- a custom stream wrapper that runs a private agent loop;
- session-wide model/tool switching presented as turn-scoped;
- custom text markers presented as host-authenticated metadata;
- replacing the final answer after `message_end`;
- using a tool result or virtual provider as the terminal answer;
- continuing dispatch after a failed egress check;
- claiming `responseModel` or requested model metadata is attestation.

## Minimal upstream/fork API needed

A compliant host needs a native phase handle similar to:

```ts
const phase = await session.beginPhase({
  runId,
  phase: "reasoner",
  attempt,
  modelOverride,
  toolSnapshot,
  abortSignal,
});

phase.onMaterializedRequest((request) => gate.checkOrBlock(request));
const result = await phase.run(message);
await phase.commit({ finality, usage, responseModelAttestation });
```

The host—not an extension—must own phase metadata, exact-dispatch blocking, lifecycle cancellation, persisted checkpoint linkage, and attestation validation.
