Status: proposed freeze for project Gate P0 Owners: A owns canonical schemas; B and C own additive provider/recovery extensions Change rule: expand-migrate-contract only
- Cardinality:
1 Business Intent / N Attempts / <= 1 committed Settlement. business_intent_idis stable across retries, redelivery, restarts, workers, and agents.- OneShot durable state grants submission ownership.
- Privy authorizes and constrains the wallet action but is not the durable duplicate lock.
- Arc receipt plus expected ERC-20 Transfer evidence establishes committed settlement.
- Direct Privy/Arc evidence resolves known transaction identities. The selected v1 hashless path queries the live OneShot/Arc Subgraph through Studio GraphQL (with optional Subgraph MCP for Network-served deployments) and lets an LLM Recovery Agent recommend a bounded action after C01; all indexed/model evidence remains non-authoritative.
- Subgraph MCP and the LLM expose no signing, settlement, retry, Attempt-creation, or submission-ownership capability.
- Any possibly submitted but unconfirmed outcome is
UNKNOWN; reconciliation precedes another submission.
| Field | Rule | Redaction |
|---|---|---|
business_intent_id |
caller-supplied UUID/opaque stable string; length bounded | safe operational ID |
attempt_id |
server-generated UUID; append-only attempt identity | safe operational ID |
correlation_id |
validated inbound or generated; never grants idempotency | safe if non-secret |
payload_fingerprint |
deterministic hash of normalized immutable payload | safe hash |
amount_atomic |
canonical unsigned base-10 integer string, no sign/decimal/exponent/whitespace | safe business datum; do not over-log |
asset |
exactly USDC |
public |
network |
exactly the enabled Arc deployment profile; v1 live proof uses eip155:5042002; mainnet remains disabled until official values are pinned and human-approved |
public |
token_contract |
exactly the enabled profile USDC interface; v1 testnet uses 0x3600000000000000000000000000000000000000; no implicit mainnet default |
public |
recipient |
normalized EVM address; allowlist/policy checked | display only where required |
privy_idempotency_key |
stable derivative of intent identity; same key requires same body | never log raw if classified sensitive |
privy_reference_id |
stable lookup identity derived from intent | sanitized evidence only |
memo_id |
optional bytes32 hash of the Business Intent used only when the Arc Memo path passes B01 policy validation |
public correlation hash |
purpose is a bounded, non-secret display/audit string. It participates in the immutable payload fingerprint and is redacted from routine logs by default.
{
"business_intent_id": "018f-example-stable-id",
"recipient": "0x1111111111111111111111111111111111111111",
"amount_atomic": "1250000",
"asset": "USDC",
"network": "eip155:5042002",
"purpose": "Invoice INV-1001"
}Normalization order is fixed: validate types and bounds; normalize EVM address; retain canonical integer string; encode asset/network constants; normalize the permitted purpose representation; serialize with a deterministic field order; hash the canonical bytes.
Identical ID and fingerprint is a replay. Identical ID with a different fingerprint is INTENT_PAYLOAD_CONFLICT and creates no new settlement right.
| Operation | Success behavior | Stable error families |
|---|---|---|
POST /v1/intents |
202 accepted; 200 identical replay |
400 INVALID_REQUEST, 401/403 UNAUTHORIZED, 409 INTENT_PAYLOAD_CONFLICT, 429 RATE_LIMITED |
GET /v1/intents/{id} |
authoritative intent, attempts, settlement, sanitized evidence, version | 404 INTENT_NOT_FOUND |
POST /v1/intents/{id}/reconcile |
enqueue/read-trigger only; never submit | 404 INTENT_NOT_FOUND, 409 RECONCILIATION_NOT_ALLOWED |
GET /v1/intents/{id}/recovery-view |
local authority plus labeled provider/index observations | 404 INTENT_NOT_FOUND, 503 EVIDENCE_UNAVAILABLE with local state retained |
GET /health/live |
process liveness only | 503 when process cannot serve |
GET /health/ready |
DB/config ready and Arc identity checks satisfied | 503 NOT_READY with sanitized reason |
Mutations require service authentication, schema validation, request-size limits, correlation IDs, rate limits, and sanitized stable errors.
Input includes intent/attempt identity, immutable request fingerprint, wallet/policy expectation, recipient, amount, network, token, method, native value, and correlation identity.
Results:
AUTHORIZED: exact expected scope is permitted.DENIED: no submission; terminal authorization rejection for this attempt.UNAVAILABLE: retryable only before submission ownership crosses the external boundary.
Input includes persisted Privy idempotency key/reference ID and exact request fingerprint.
Results:
CONFIRMED: verified final Arc receipt and exactly matching Transfer evidence.DEFINITELY_NOT_SUBMITTED: narrow documented proof that no broadcast or external effect occurred.POSSIBLY_SUBMITTED: timeout, lost/truncated response, uncertain provider/RPC failure, crash window, or any doubt.
Results:
FINAL_SUCCESSFINAL_REVERTPENDINGNOT_FOUNDUNAVAILABLE
NOT_FOUND alone never proves that no payment occurred.
The v1 implementation obtains candidate transfers from a deployment-pinned
Subgraph MCP tool call and returns observations plus observed block/time,
provider/deployment/tool identity, chain-head comparison, lag, provider health
details, retrieval time, and health classification: FRESH, LAGGING,
UNHEALTHY, UNAVAILABLE, or UNKNOWN_FRESHNESS. The adapter validates tool
arguments, target deployment, result schema, _meta, size bounds, and untrusted
text. Credentials never enter prompts, tool results, fixtures, logs, or evidence.
No IndexViewPort result grants settlement permission.
Input is a bounded, sanitized recovery view containing durable-state summary, authority labels, exact identity bindings, Arc/Privy observations, and validated Subgraph MCP observations. The only accepted recommendations are:
WAIT: preserveUNKNOWNuntil fresher or authoritative evidence exists.RECONCILE: request another read-only evidence cycle.ESCALATE: request operator investigation with no financial effect.RETURN_EXISTING_RESULT: return candidate/evidence references for a result the deterministic core must independently prove already exists.
The response includes a bounded reason, referenced evidence IDs, model
configuration identity, and decision ID. Unknown actions, free-form tool calls,
missing/fabricated references, malformed output, prompt/tool injection, or model
unavailability fail closed to WAIT plus a sanitized diagnostic.
The safety core treats the recommendation as advisory and rechecks the current
state version and authoritative OneShot/Arc evidence. RECONCILE can enqueue
only a read-only lookup, WAIT maps to HOLD_UNKNOWN, ESCALATE maps to
ESCALATE_UNKNOWN, and RETURN_EXISTING_RESULT can produce
MARK_COMMITTED/an existing terminal response only when independently proven.
No mapping calls SettlementPort, creates an Attempt, or grants submission
ownership.
| Current | Trigger | Next | Submission permission |
|---|---|---|---|
NONE |
validated intent accepted | AUTHORIZING |
No |
AUTHORIZING |
policy authorizes | READY |
No |
AUTHORIZING |
policy denies | REJECTED |
No; terminal |
READY |
atomic owner grant persists request identity | SUBMITTING |
Exactly one owner crosses boundary |
SUBMITTING |
verified final receipt/Transfer | COMMITTED |
No; terminal |
SUBMITTING |
authoritative proof of no submission/final failure | FAILED_SAFE |
Policy may schedule a new attempt |
SUBMITTING |
possible submission, crash, timeout, doubt | UNKNOWN |
No |
UNKNOWN |
verified success | COMMITTED |
No; terminal |
UNKNOWN |
authoritative matching final revert/no-effect proof | FAILED_SAFE |
Policy may schedule a new attempt |
UNKNOWN |
pending/not found/unavailable/lag/error/contradiction | UNKNOWN |
No; escalate by age |
All transitions are compare-and-set with monotonic versioning. No database transaction remains open during a provider/RPC call. Startup treats orphaned SUBMITTING work as reconciliation-required UNKNOWN, never as a new lease to submit.
- Business Intent primary identity and immutable fingerprint.
- Append-only Attempts with stage, timestamps, sanitized error class, and correlation ID.
- At most one Settlement row per Business Intent; provider transaction hash unique when present.
- Transactional outbox/job record.
- Persisted request body fingerprint, Privy request identities, wallet/policy identity, chain/token/recipient/amount, provider transaction ID/hash/nonce when learned.
- Receipt block/hash/status and verified Transfer log transaction hash plus log index.
- When enabled, Arc Memo ID, call-data hash, event log identity, and proof that the Memo and Transfer share the verified transaction.
- Append-only evidence observations with source, retrieval time, block/freshness, sanitized payload or digest, and authority label.
The canonical fixture root is packages/contracts/fixtures/v1/. Every fixture has a JSON Schema validation test and explicit expected durable transition and external-submission count.
| Fixture | Required expectation |
|---|---|
intent/accepted.json |
new intent, one queued execution, zero settlement at API boundary |
intent/replay-identical.json |
same durable intent, no duplicate job/right |
intent/replay-conflict.json |
409, explicit conflict, zero additional right |
authorization/allowed.json |
exact scope authorized |
authorization/denied-*.json |
wrong chain/token/method/recipient/value/amount denied, zero submission |
settlement/confirmed.json |
matching final receipt and one Transfer |
settlement/final-revert.json |
final failure, zero committed settlement |
settlement/pending.json |
remain unresolved, no resubmission |
settlement/lost-response.json |
POSSIBLY_SUBMITTED -> durable UNKNOWN |
settlement/mismatched-transfer.json |
not confirmed, hold safely |
evidence/not-found.json |
no permission change |
index/candidate-one.json |
one bindable candidate still requires Arc verification |
index/candidate-multiple.json |
remain UNKNOWN; no candidate selection by guess |
index/empty.json |
labeled observation through observed block, no permission change |
index/lagging.json |
LAGGING, no permission change |
index/provider-error.json |
UNHEALTHY, no permission change |
index/unavailable.json |
UNAVAILABLE, local authority still returned |
mcp/malformed.json |
rejected before agent input; fail-closed WAIT |
mcp/injected-content.json |
content remains untrusted evidence, never an instruction |
agent/wait.json |
WAIT -> HOLD_UNKNOWN, zero external submissions |
agent/reconcile.json |
RECONCILE -> read-only evidence cycle only |
agent/escalate.json |
ESCALATE -> operator escalation only |
agent/return-existing-result.json |
accepted only when authoritative evidence independently proves the result |
agent/unsupported-action.json |
rejected; fail-closed WAIT, zero external submissions |
- Domain simulator exposes the HTTP seam and deterministic clock/IDs with an external-submission counter.
- Settlement simulator consumes canonical requests and emits each SettlementPort/EvidencePort result family without network access.
- Subgraph MCP simulator consumes pinned-deployment query fixtures and emits validated Graph candidates without network or credentials.
- Recovery-agent simulator consumes the labeled recovery view and emits every allowed/invalid recommendation deterministically.
- Recovery simulator passes recommendations through the deterministic safety core and emits commands and a labeled recovery view.
- Simulators reject unknown fixture versions and schema drift.
- Simulators never silently default an unknown enum to a successful or retryable result.
- HTTP API plus returned durable state.
- Worker task plus durable state and external-submission counter.
- Adapter ports plus official-response fixtures.
- Reconciliation command plus durable transition and evidence record.
- Graph-provider candidate query result (or optional MCP tool result) plus deployment-specific freshness and ambiguity classification after C01.
- RecoveryAdvisorPort recommendation plus deterministic safety-core command and external-submission counter.
- Browser UI through frozen OpenAPI/mock server after Gate P4.
- A owns base schemas, OpenAPI, error codes, state vocabulary, and fixture validation tooling.
- B owns provider-specific optional evidence fields and response-to-port classification fixtures.
- C owns index/MCP/recovery observation fields, recovery-agent recommendation fields, and reconciliation-decision fixtures.
- Optional fields must not change existing result meaning.
- Unknown enum values fail closed at boundaries.
- A breaking change requires ADR, new fixture version, dual-form simulator support, independent consumer migration, and later removal.
- Every field has type, normalization, authority, and redaction rules.
- Every terminal result has a durable transition and external-submission expectation.
- Every lane can run a simulator with no credentials.
- No unresolved item can change settlement cardinality, monetary precision, Privy enforcement, Arc identity,
UNKNOWNsemantics, or the non-authoritative MCP/LLM boundary. - Human approval records the exact Git tree containing this contract pack.