# Provider Delegation Contract

This document defines the provider-neutral contract for running PWK roles outside the main agent. It is an integration contract for host adapters and extensions. The workflow skills describe the same behavior in portable language; they do not require this document’s TypeScript notation or any specific transport.

## Logical operations

A provider may support either operation independently:

- `codebase-recon` — run one `pwk-recon-scout` role and return its five-section observation map.
- `feature-review` — run the requested review roles over one feature scope and return one outcome per role.

The logical role name is not the provider’s concrete agent type. A provider may map `pwk-recon-scout` to a safe built-in explorer, a custom agent definition, or a host-native read-only task.

## Required capabilities

A provider advertises capabilities independently from its name:

| Capability | Meaning |
|---|---|
| `codebase-recon` | Can run one fresh, read-only recon worker and collect its report. |
| `named-role-dispatch` | Can map each requested logical role to an appropriate worker. |
| `parallel-review` | Can run independent review roles concurrently when requested. |
| `result-collection` | Returns one distinguishable outcome for every requested role. |
| `read-only-enforcement` | Prevents delegated workers from writing files or running destructive commands. |
| `fresh-context` | Starts each requested worker without reusing an unrelated prior conversation. |
| `bounded-execution` | Applies a timeout, turn limit, or equivalent resource bound. |

A provider must not claim `read-only-enforcement` when it only adds a prompt instruction. Providers may support `codebase-recon` without supporting `parallel-review`.

## Role resource hints

Role definition frontmatter may declare optional resource hints: `model` (a host-resolvable model name), `thinking` (a reasoning-effort level), and `max_turns` (a turn budget — the per-role instance of `bounded-execution`). Hints are advisory:

- Hosts that support per-role resources SHOULD honor them; hosts that do not ignore them without failing the operation.
- If a `model` hint cannot be resolved to an available model, the host runs the role on its default model — an unresolvable hint MUST NOT fail the review.
- `max_turns` is a graceful backstop: the role wraps up and reports rather than running unbounded. A capped role still follows the normalized outcome rules — a non-empty report is required for completion.

## Request shape

The following TypeScript is illustrative. Implementations may use Pi events, tool calls, CLI processes, native task APIs, or another transport. A reference implementation of the outcome normalization lives in `extensions/workflow-guard.ts` (`assessDelegationCoverage`), exported as a pure helper so future adapters and tests share one definition of complete coverage.

```ts
type DelegationOperation = 'codebase-recon' | 'feature-review';
type DelegationRole =
  | 'pwk-recon-scout'
  | 'pwk-spec-reviewer'
  | 'pwk-tracing-reviewer'
  | 'pwk-smell-reviewer'
  | 'pwk-hazard-reviewer';

type DelegationRequest = {
  operation: DelegationOperation;
  roles: DelegationRole[];
  prompt: string;
  cwd: string;
  constraints: {
    readOnly: true;
    freshContext: true;
    parallel: boolean;
    bounded: true;
  };
};
```

The provider must preserve the logical roles and the repository root when translating a request. `parallel: true` requests concurrency; it does not permit unbounded concurrency. If the provider cannot satisfy a requested constraint, it must reject the delegated operation so the host can use inline fallback.

## Normalized outcome shape

```ts
type DelegationStatus = 'completed' | 'failed' | 'timed-out' | 'skipped';

type DelegationResult = {
  role: DelegationRole;
  status: DelegationStatus;
  report?: string;
  error?: string;
  provider?: string;
  runId?: string;
};
```

Rules:

- `role` is required and must identify one requested logical role.
- `completed` requires a non-empty `report` that follows the role contract.
- `failed` and `timed-out` require an `error` or equivalent failure explanation.
- `skipped` is explicit and is not equivalent to completion.
- `provider` identifies the adapter or host that produced the outcome when known.
- `runId` is an opaque provider-local identifier when available; consumers must not interpret its format.
- A multi-role operation is complete only when every requested role has a completed result or an explicit fallback result approved by the workflow.

An empty report is not a successful result. A missing role is not silently discarded.

## Fallback protocol

Absence is established by enumeration, not by a visible tool list: a tool-hiding executor keeps the spawn mechanism out of the model's tools while it stays callable. If no provider satisfies the requested capabilities, the host performs the role inline using the same logical contract. For recon, it reports:

```text
Scout: unavailable — inline recon used.
```

For feature review, successful delegated reports remain usable. A failed or timed-out role is retried or performed inline. The host does not report a complete review while a required role has neither a delegated result nor an inline result.

## Provider discovery and selection

Provider discovery and transport are host-specific. A future Pi adapter may use a capability registry, an event-bus handshake, a shared extension RPC, or explicit configuration. The core PWK package does not assume that all Pi extensions are discoverable or that a package name identifies a compatible provider.

If multiple providers are available, selection must be deterministic. A host may use explicit configuration or a documented priority order. It must not select a provider based only on extension load order when that changes safety or result semantics.

## Role setup and provider loading

The Pi-only `/pwk-setup` command copies the canonical role definitions into `.agents/agents/`. This makes named roles available to providers that discover the shared directory, including `@tintinweb/pi-subagents`. Setup creates role definitions; it does not install or configure a provider.

Providers may cache role definitions. Hosts should tell the user when `/reload` or a new session is needed. Setup must not reload a session implicitly.

## Safety boundary

The provider is responsible for enforcing any capability it advertises. The role prompt is defense in depth, not a security boundary. The Pi workflow guard protects the main Pi session's design phase; it does not automatically protect Claude Code or another host, and it does not replace delegated-worker tool restrictions.

The `/pwk-setup` installer itself is hardened in depth: it opens the target with `O_NOFOLLOW` and does every subsequent check, read, and write through that one descriptor (`fstat` regular-file check, content comparison, and post-write verification), so nothing swapped in on the path afterward can affect what is read or written. On platforms where `O_NOFOLLOW` is unavailable, a symlink swapped in before the initial open remains a narrow advisory window; setup is a user-invoked development command, not a security boundary.
