RFC-001: First vertical slice — Irrevon core
- Status: historical record — slice definition and acceptance test remain canonical here; state tables are canonical in RFC-002 §3; the identity procedure is canonical in ADR-0020.
- Date: 2026-07-20 · amended 2026-07-21 (citation fixes; RFC-002 cross-references)
- Scope: master doc M3 (state model, intent contract + identity, persist-before-dispatch ledger, commit gate, crash-recovery replay, orphan sweep) with the flagship demo (§16, item 6) as the acceptance test
- Reads with RFC-002, which carries the implementation-ready
engine mechanics (storage, locked transitions, the canonical state tables, retry/replay
semantics, calibrated absence). Where this RFC left cells
[OQ], RFC-002 §3 decides them. - Contains NO code. Everything here is language-neutral by design and was written while ADR-0013 (language) was open; ADR-0013 has since been ratified (Python), but this RFC remains implementable in any candidate by construction.
Summary
The slice is the smallest system that proves the thesis mechanism end-to-end: an intent contract keyed on stable business identifiers is persisted before dispatch; a deterministic commit gate guards dispatch; ambiguous outcomes are adjudicated by querying the destination’s authoritative status; and a re-synthesized retry (different model-generated arguments, same business intent) is rejected as a duplicate with evidence — on a fault schedule that makes the strongest baseline (B5, durable runtime + native idempotency) produce a duplicate effect.
Motivation
The residual gap after the strongest baselines lives on C2 destinations, and the master doc’s first public artifact is a reproducible demo of that gap being closed (§1, §16 item 6). This RFC pins everything about the slice that is cross-cutting, expensive to change later, or required by the M3 conformance tests (§12.1) — and explicitly defers everything else. Read master doc §6–§7 first; this document does not restate them.
Guide-level explanation (the demo, as a story)
An agent asks Irrevon to create an order for order_id: 9410. Irrevon validates the intent
contract, persists an INTENDED→PERSISTED record, passes the commit gate, and dispatches to the
C2 destination. The response is lost on cue; the process is killed. On restart, replay finds
the AMBIGUOUS record and — before any new dispatch — queries the destination’s status by the
operation reference: the order exists. The record settles SETTLED_COMMITTED with a
CONFIRMED_UNIQUE finding and an evidence bundle. The agent then retries with re-synthesized
arguments (different wording, same order_id: 9410); identity derivation maps it to the same
intent_id, and the gate rejects it as a duplicate, citing the settled record. The identical
fault script run under the B5 baseline creates a second order, proven by destination
read-back. That contrast is the whole project in one run.
Reference-level explanation
1. Identity: canonicalization and hashing
intent_id = hash(canonical(stable_ids) ‖ effect_type ‖ scope) (§7.2). This is the least
reversible decision in the slice — changing it re-keys every ledger record — so the byte-level
procedure is pinned here [DD]:
- Canonical form: the tuple is encoded as a JSON object
{"effect_type": …, "scope": …, "stable_ids": {…}}and canonicalized per RFC 8785 (JCS): lexicographic member ordering, no insignificant whitespace, canonical number and string encoding, UTF-8 bytes. - Normalization before canonicalization: stable-id values are strings, compared and hashed exactly as supplied (no case folding — upstream identifiers are opaque); absent optional members are omitted, never null (absent ≠ null; null is rejected by the schema).
- Hash: SHA-256 over the canonical UTF-8 bytes;
intent_idis the lowercase hex digest. operation_id=intent_id ‖ ":" ‖ stepwherestepis a zero-based integer assigned by the workflow, not the model. Idempotency evidence derives only fromoperation_id.- Conformance rule (tested at M3): no derivation path reads model output. Model-generated argument payloads are not inputs to any function in this chain.
Per §12.5 this is invariant-affecting: implementing it requires a short ADR ratifying items 1–4 (JCS + SHA-256 are this RFC’s proposal; the ADR is the record of acceptance).
2. State-transition matrix (dimension A, exhaustive)
Legal transitions; every pair not listed is illegal and must be rejected (M3 exit test: exhaustive state-matrix tests, §12.1).
| From \ To | PERSISTED | DISPATCHED | SETTLED_COMMITTED | SETTLED_FAILED | AMBIGUOUS | CANCELLED |
|---|---|---|---|---|---|---|
| INTENDED | durable write | — | — | — | — | branch cancelled pre-persist |
| PERSISTED | — | commit gate allow | — | — | — | cancelled pre-dispatch |
| DISPATCHED | — | — | confirmed receipt | confirmed failure | timeout / lost response / crash | — |
| AMBIGUOUS | — | — | reconciled: effect exists | reconciled: confirmed absent | — | — |
SETTLED_COMMITTED, SETTLED_FAILED, and CANCELLED are terminal.
Dimension-B attachment rules (reconciliation classification, §7.1):
- ORPHANED is representable only as a Finding keyed by
(adapter, destination_ref)— never as a state of a ledger record (a ledger-keyed machine cannot express a record it doesn’t have). - CONFIRMED_UNIQUE, DUPLICATE, and LOST attach only to records that reached DISPATCHED or later; records in INTENDED/PERSISTED/CANCELLED are UNRECONCILED by definition.
- The formerly-
[OQ]cells (e.g. SETTLED_FAILED × DUPLICATE) are now decided: the exhaustive attachment matrix — including the CONTRADICTED classification added by amendment AM-18 — is canonical in RFC-002 §3, and the M3 matrix tests are generated from it.
Dimension-C resolution (per finding): OPEN → COMPENSATED | REDISPATCHED (fresh authority + new idempotency evidence only) | ACCEPTED_AS_IS | ESCALATED_HUMAN → CLOSED.
3. Ledger discipline
Append-only: no UPDATE on settled facts; corrections and state changes are new rows
referencing the prior row. Single writer in the POC; concurrency is per-scope serialization —
one in-flight dispatch per (scope, effect_type) via row locking (§7.4 item 3, documented
scaling limit; the crash-safe mechanical form — durable open-attempt rows, locks never held
across wire I/O — is pinned in RFC-002 §5). Physical schema, indexes, and migration tooling are deferred (ADR-0013 and the
storage decision refine them; see schemas/README.md for which record
shapes are schema-fied now vs deferred).
4. Commit gate: checks and order
Order pinned [DD] (cheapest/absolute first; evidence-richest last):
- Deny-list — incident containment (§12.4); a denied effect class aborts immediately.
- Authority freshness/binding —
authority_refpresent, unexpired at gate time, bound to this scope; expiry between persist and dispatch → deny, safe abort (§7.4 item 4). - Branch-lineage validity — the intent’s workflow branch has not been cancelled.
- Dedup — no record with the same
intent_idin DISPATCHED/AMBIGUOUS/SETTLED_COMMITTED; a hit denies with the settled record as evidence (this is the re-synthesis defeat).
Every deny writes an evidence record (which check, inputs digested, timestamp). The gate is deterministic: same ledger state + same contract → same outcome.
5. Crash-recovery replay
On restart, before accepting any new work:
- Scan the ledger for records in DISPATCHED or AMBIGUOUS.
- For each, run
reconcile(effect_id)before any new dispatch of the sameoperation_id. - Re-dispatch requires: confirmed-absent at the destination (C2) or in-window idempotent replay (C1), plus fresh authority. Never re-dispatch on belief (§7.4 item 2).
- Crash-before-persist is provably effect-free (nothing was dispatched without a durable PERSISTED record) — the kill-before-persist test asserts zero external effects (§12.1).
6. Orphan sweep
sweep(adapter, window): list destination effects in the window via the adapter’s list
query; match each against ledger records by, in order, stamped client reference
(client_ref_field), destination_ref recorded on receipts, then declared queryable keys.
Unmatched destination effects emit ORPHANED Findings (dimension B) with the destination
payload digested into the evidence bundle. The sweep is only possible where the capability
declaration says list_queryable: true — which is exactly why that field exists (draft
field, amendment AM-9).
7. Error taxonomy
| Transport outcome | Meaning | Lifecycle effect |
|---|---|---|
| OK | authoritative success receipt | → SETTLED_COMMITTED |
| FAILED | authoritative, recognized failure semantics from the destination | → SETTLED_FAILED |
| TIMEOUT | no response within deadline | → AMBIGUOUS |
| LOST | connection/process died before response | → AMBIGUOUS |
Unknown or unrecognized destination errors map to AMBIGUOUS, never FAILED (§9) — evidence is never discarded by optimistic classification. Only reconciliation (a status query) or a human may move AMBIGUOUS to a settled state.
8. API operations (language-neutral signatures)
| Operation | Pre | Post |
|---|---|---|
registerIntent(contract) → effect_id |
contract validates against the intent-contract schema (≥1 stable id) | INTENDED→PERSISTED record exists; idempotent: same contract returns the same effect_id |
dispatch(effect_id) → receipt | AMBIGUOUS |
record is PERSISTED; gate passes | receipt recorded with attempt_no + idempotency evidence; lifecycle per §7 taxonomy |
reconcile(effect_id | scope) → findings |
record(s) DISPATCHED/AMBIGUOUS or settled-for-audit | classification findings recorded; AMBIGUOUS records settled per status query |
sweep(adapter, window) → findings |
adapter declares list_queryable |
ORPHANED findings for unmatched destination effects |
resolve(finding, action, evidence) |
finding OPEN; action in dimension C; evidence present | resolution recorded; REDISPATCHED requires fresh authority + new idempotency evidence |
status(effect_id) → record view |
record exists | read-only; no state change |
9. Test plan (= the M3 rows of §12.1, plus the demo)
- Property tests over derivation paths: keys derive only from stable identifiers, never model output (≥1,000 cases/invariant, §12.2).
- Kill-before-persist: zero external effects.
- Exhaustive state-matrix tests over §2 above, including the
[OQ]cells decided. - Architectural test: classifier output cannot reach gate/resolve APIs (ADR-006) — the advisory path has no code path to authority.
- Acceptance test — the flagship demo script against the stub destination:
- Fault 1 (response-lost): dispatch, drop response, crash, restart → replay reconciles to SETTLED_COMMITTED + CONFIRMED_UNIQUE; no re-dispatch.
- Fault 2 (crash-after-effect-before-response): same shape, crash between destination effect and response.
- Fault 3 (semantic re-synthesis): frozen variant retry with different arguments, same stable ids → gate rejects duplicate with evidence.
- B5 contrast leg: the identical schedule under stable-op-ID + retry with no honored native idempotency produces a duplicate destination effect, proven by read-back. The demo graduates from the stub to the real C2 sandbox at M4 (after ADR-0012), and gains the C1 null leg (same harness on Stripe shows no advantage) at M4 as well.
10. Stub-destination contract (pre-M4 demo runner)
The stand-in C2 destination must implement: dispatch(op) creating an effect with a
destination-assigned ref; status(query) authoritative by ref or client reference; a
client-reference field (so the slice exercises the same reconcile hooks as a real C2); and
scriptable fault hooks — drop-response-on-cue and effect-created-but-response-lost. It must
be deterministic under a seed.
Drawbacks
A stub-first demo weakens the “real API” claim until M4; disclosed openly (§8.2 already scopes what client-side injection can and cannot simulate). Pinning JCS/SHA-256 pre-language constrains implementations to have a conformant JCS encoder (available in all candidate ecosystems).
Rationale and alternatives
The architecture choices this slice implements are already decided and recorded — identity
(ADR-001), persistence (ADR-002), tiers (ADR-003), state (ADR-004), adapters (ADR-005),
authority (ADR-006), compensation (ADR-007) — see master doc §11 via
decisions/README.md. This RFC adds only the byte-level identity
procedure (§1, needs its ADR), the gate order (§4, [DD]), and the demo/stub contracts.
Unresolved questions
Implementation language and property-test framework— resolved: ADR-0013 accepted 2026-07-21.The— resolved: RFC-002 §3 is the canonical matrix.[OQ]cells of the lifecycle × classification matrix (§2)- The short ADR ratifying the §1 identity procedure (items 1–4) is still owed at implementation time (T-101 proposes it; ADR-0013’s encoder/hash pins are its inputs).
- C2 sandbox for the M4 graduation — ADR-0012 (OPEN).
- Stripe version pin for the C1 null leg — ADR-0010 (OPEN).
- Bi-temporality (
as_of_time) stays conditional per ADR-002; the ledger design must not preclude it.
Future possibilities
M4+ real adapters with cited capability declarations; the orphan-sweep demo against a destination with out-of-band effects; the effect-contract registry (M9).