rein-aharness/docs/close-evidence-outbox.md
tegwick c97e5ea92d Build durable close evidence outbox
Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a02b6f-7db1-7222-918b-e813a6bda38d
2026-08-23 14:05:50 +02:00

2.9 KiB

Close-evidence outbox

Status: durable core implemented, not connected to the live claim loop.

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:

$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. Re-enqueuing the identical intent returns its existing pending or delivered state. Reusing that identity for a different 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 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.

A network timeout may occur after Activity Core accepted a close. Retrying the same close intent can therefore repeat the queue mutation request, but cannot repeat repository workload. Activity Core remains authoritative for terminal row state and must provide idempotent or reconcilable close semantics.

Activation gates

The live claim loop still returns an in-memory failure when close delivery fails. Connecting it to this outbox requires:

  1. the lease-bound repository transaction and accepted-result envelope;
  2. enqueue-before-close using the same run and transaction identities;
  3. Activity Core review of repeat close behavior for already-terminal rows;
  4. startup/periodic replay that never calls workload code; and
  5. operator status, quarantine inspection, and replay controls.

Until those gates are implemented and reviewed, no production close behavior changes.