# `@zyahav/pi-turn-budget` Public Events v1

## Scope

This document defines the first public, machine-readable budget lifecycle contract. It is transport-neutral and usable by standalone Pi users, Pi extensions, Pi SDK hosts, adapters such as XState adapters, and other supervisors or harnesses.

The runtime assumptions behind this contract were calibrated against **Pi 0.84.2**. A Pi upgrade requires compatibility re-verification.

## Event types

Exactly these event types are defined:

- `pi-turn-budget:armed` - budget enforcement began for one owner-submitted Pi request.
- `pi-turn-budget:released` - the budget guard ended before expiry. This does not mean the task passed, tools were correct, or external side effects succeeded.
- `pi-turn-budget:expired` - the budget expired and the package requested abort. This does not prove whether an external side effect committed; `externalState` is always `"unknown"`.

## Common fields

Every event contains:

| Field | Type | Meaning |
|---|---|---|
| `schemaVersion` | `"1"` | Contract schema version. |
| `eventId` | `string` | Unique identifier for this event. |
| `requestId` | `string` | Identifier shared by the lifecycle events for one owner-submitted request. |
| `eventType` | one of the three event types above | Event discriminator. |
| `packageName` | `"@zyahav/pi-turn-budget"` | Emitting package. |
| `packageVersion` | `string` | Package version that emitted the event. |
| `occurredAt` | `string` | ISO 8601 local wall-clock timestamp for the event. |
| `budgetMs` | non-negative integer | Enforced request budget in milliseconds. |
| `correlationId` | `string` (optional) | Generic host- or adapter-supplied correlation identifier. |

`correlationId` must not contain secrets or sensitive personal information because it may be persisted locally.

## Event shapes

### `pi-turn-budget:armed`

Adds:

- `startedAt: string` - ISO 8601 local wall-clock timestamp.
- `deadlineAt: string` - ISO 8601 local wall-clock timestamp.

Consumers must not compare wall clocks from separate VMs as though they are perfectly synchronized.

### `pi-turn-budget:released`

Adds:

- `elapsedMs: number` - locally measured elapsed duration in milliseconds.

### `pi-turn-budget:expired`

Adds:

- `elapsedMs: number` - locally measured elapsed duration in milliseconds.
- `abortRequested: true`
- `externalState: "unknown"`

`nextBudgetMs` is intentionally excluded because it belongs to local UI/configuration state.

## Lifecycle and terminal-state rule

For one `requestId`, valid emitted sequences are:

```text
armed -> released
armed -> expired
armed -> no terminal event
```

`released` and `expired` are mutually exclusive. Exactly one terminal budget state may win. If expiry and settlement race, the first synchronous terminal-state claim wins and the losing path emits nothing.

An unmatched `armed` event is legal when the Pi process or VM disappears before a terminal event can be emitted. It means only that the terminal outcome is unknown; it does not imply release or expiry.

Budget lifecycle is separate from task correctness and external side-effect outcomes.

## Data and dependency boundaries

Events must never include prompts, model responses, tool inputs or outputs, credentials, cookies, browser connect URLs, API keys, XState state names, Convex document IDs, or Buzz identities.

The package has no dependency on XState, Convex, Buzz Relay, Browserbase, Herdr, or ZurOt. Consumers integrate through generic hosts or adapters.
