rein-aharness/docs/close-evidence-outbox.md
tegwick d00ffcb402 feat(runtime): consume governed Activity Core closes
Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a06ba0-10aa-7ea0-b20a-4f3fac39efe9
2026-09-04 19:54:07 +02:00

79 lines
3.4 KiB
Markdown

# Close-evidence outbox
Status: **active for profiled Activity Core terminal closes**.
The outbox preserves one bounded Activity Core completion or failure intent
after repository work. Replaying an outbox entry calls only the queue close
adapter; it never dispatches a model, invokes an approach, or repeats repository
mutation.
## Storage and identity
The default root is:
```text
$REIN_AHARNESS_STATE_DIR/close-outbox/
```
When that variable is absent, the harness uses
`$XDG_STATE_HOME/rein-aharness` or `~/.local/state/rein-aharness`. State lives
outside target checkouts. Outbox directories are mode `0700`; lock and JSON
files are mode `0600`.
An entry is keyed by the digest of the bounded Activity Core `run_id` and
repository `transaction_id` and retains the original worker identity.
Re-enqueuing the identical intent returns its existing pending or delivered
state. Reusing that identity for a different worker, action, result, error, or
reopen decision is a conflict and is refused.
The v1 close intent contains:
- `action`: `complete` or `fail`;
- bounded normalized JSON `result` evidence;
- a caller-sanitized bounded error and explicit `reopen` decision for failures;
- no prompt, provider body, credential, raw tool stream, or workload callback.
Results allow only finite JSON values, bounded depth, collection size, string
length, integer range, key length, and a total encoded size of 64 KiB.
## Durability and replay
Enqueue and retry-state updates use a same-directory temporary file, file
`fsync`, atomic replace, and directory `fsync` under a process-safe `flock`.
The retry attempt is durable before the delivery callback begins.
Normal callback return, including Activity Core `close_disposition=applied` or
`reconciled`, marks the entry delivered and atomically moves it from
`pending/` to `delivered/`. An ordinary exception retains it in `pending/` with
only the exception class and a generic failure marker; exception text is not
persisted. Process interrupts propagate; the already-recorded attempt remains
pending. Invalid, oversized, or directory/state-mismatched records are moved
intact to `quarantine/` with a bounded reason sidecar and are never delivered
automatically.
Activity Core refusal codes `not_found`, `wrong_owner`, `expired_lease`,
`state_conflict`, `evidence_conflict`, and `terminal_conflict` are conclusive,
not transient. Such entries move to quarantine rather than retrying forever.
An unclassified HTTP failure or transport outage remains pending.
A network timeout may occur after Activity Core accepted a close. The worker
replays pending close-only work before it claims another row. Retrying the same
intent can repeat the queue request, but cannot repeat repository workload;
Activity Core reconciles the exact normalized terminal repeat.
## Operator controls
```bash
rein-aharness close-outbox status
rein-aharness close-outbox replay
rein-aharness close-outbox replay --limit 10
```
`status` and `replay` return non-zero while pending or quarantined material
exists, making them suitable for readiness/recovery checks. Quarantine content
and its bounded reason sidecar remain under the private state path for explicit
operator inspection; it is never silently deleted or treated as delivered.
Profile-absent compatibility closes are not yet outboxed. Granted rows without
an authoritative `harness_profile_ref` are refused before execution so they
cannot cross that weaker path.