# Eve SOTA triage contract — `eve.triage.v1`

- **Task:** 2.4
- **Evaluated:** 2026-09-05
- **Decision:** typed-triage-enforced
- **ADR gate:** `typed-triage`
  (docs/adr/ADR-0076-eve-governed-delivery-lifecycle.md)
- **Record digest:**
  `3cf2b21b96ea38743b813408a50d7256b2b9a31dd943e7370b7208003b9812c4`

## The rule

A requeue out of a held status and a close both refuse without a triage record,
and the refusal writes nothing. Every record names one of a closed set of
classes, carries at least one piece of evidence that points at something, and
takes the retry disposition its class earns rather than the one its writer
preferred. Triage explains; it never moves an item.

The classes, their meanings and their dispositions are read out of the contract
module; each guarded exit must name a guard the store calls and a spec that
watched it refuse.

## The classes

| Class                   | Named by    | Means                                                                                               | Retry disposition      |
| ----------------------- | ----------- | --------------------------------------------------------------------------------------------------- | ---------------------- |
| `ship-verify-gap`       | task 2.4    | The artifact-diff verifier observed the knowledge plane and the shipped claim did not hold.         | `needs-human-decision` |
| `test-failure`          | task 2.4    | The agent's own checks failed; the work did not reach a reviewable state.                           | `retry-when-ready`     |
| `conflict`              | task 2.4    | A repository, lease, or state conflict blocked the attempt; the work itself was not judged.         | `retry-when-ready`     |
| `ambiguous-requirement` | task 2.4    | The brief could not be executed as written, so no amount of retrying it will help.                  | `needs-human-decision` |
| `budget-exhausted`      | task 2.4    | A spend, time, or attempt budget ended the attempt before the work was finished.                    | `needs-human-decision` |
| `agent-refusal`         | task 2.4    | The agent declined the work on policy, safety, or capability grounds and said so.                   | `needs-human-decision` |
| `lease-lapsed`          | the runtime | The acquisition ended without a report — an expired lease, a vanished holder, or a superseded hold. | `retry-when-ready`     |
| `not-observed`          | the runtime | The item could not be read back after the attempt, so nothing is claimed about what happened to it. | `needs-human-decision` |

6 of the 8 classes are the ones task 2.4 names; the other 2 are shapes the
runtime genuinely produces and cannot express with the six. All 8 meanings are
distinct, and the classes resolve to 2 different dispositions — a taxonomy that
answered the same way every time would decide nothing.

## The two exits that may never be silent

| Exit             | Requirement                                                                                                                              | Guard               | Proved by                                   |
| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | ------------------------------------------- |
| `silent-requeue` | An attempt that leaves a held status for ready or parked must record why it ended; the refusal writes nothing to the append-only ledger. | `triageRequiredFor` | refuses a silent requeue and writes NOTHING |
| `quiet-close`    | A rejected item must record why it was refused, and a human is no more exempt than a machine.                                            | `triageRequiredFor` | refuses a quiet close and writes NOTHING    |

## Where the contract is reachable

- Store refuses on transition: true
- Store appends a standalone record: true
- Queue exposes the canonical operation: true
- BFF route: true
- MCP tool: true
- Fleet drain records it: true
- Drain declares it as an allowed operation: true
- End-to-end probe asserts it: true
- The lease sweep triages automatically: true

## Honest limits

- This record closes task 2.4 only. It does not add the operator read model
  (2.5), execution isolation (2.6), the orchestrator model and parity proof
  (2.7), or any live drain or soak result (2.8).
- The retry disposition is recorded and enforced as a value; ACTING on it —
  re-leasing a retry-when-ready item, holding a needs-human-decision one — is
  the queue retry budget and quarantine from task 2.2, and no scheduler consumes
  the disposition field yet.
- The six named classes are proved reaching the ledger from a spec that writes
  them deliberately. Only lease-lapsed is produced automatically today, by the
  sweep; the other five arrive when an agent or a drain reports the signal that
  selects them.
- Classification from agent signals is provider-free and tested, but no real
  coding agent has yet emitted those signals: an agent that never says "my
  checks failed" is triaged lease-lapsed, which is honest rather than precise.
- Triage does not itself move an item. An operator still decides what happens
  next, and nothing in this task automates that decision.
- The two runtime classes beyond the task list are marked runtime-observed
  rather than folded into the six, so a lapsed lease is never counted as a spent
  budget.
