ADR 0013 — A capability-only command may explicitly claim a snapshot decision
accepted (2026-10-04). ADR 0013 was the next free number in docs/decisions/ on main before this file was written.
Status: accepted (2026-10-04). ADR 0013 was the next free number in docs/decisions/ on main before this file was written.
Context
EFFX2501 rejects a Command whose HTTP access declares decisionTime: "SnapshotRead" (packages/compiler/src/extensions/access-contract.ts). This default prevents a command from authorizing against mutable facts and then writing after those facts change.
Contact is a narrower case. Its server-to-server ObjectCapability authorizes the CapabilityHolder through a backend token comparison. The access declaration has no mutable-fact requirements. The command still consumes a visitor-IP quota and attempts delivery; its department-recipient resolver routes the message. Changing the accepted SnapshotRead AccessSpec to Transaction would misstate the authority decision and alter the contract (docs/specs/0028-contact-migration.md).
Decision
The source-only HttpAccessOptions declaration may set snapshotDecisionForCommand: true. The compiler accepts that claim only when all of these declared conditions hold:
- The operation is a
CommandwithdecisionTime: "SnapshotRead". requirementsis empty.acceptedCredentialscontains exactly"ObjectCapability".principalKindscontains exactly"CapabilityHolder".
The flag is absent by default. Without it, the existing EFFX2501 error remains. An invalid flag emits EFFX2506; an unsafe Command/SnapshotRead also retains EFFX2501. A Query or a Transaction decision cannot use the flag and receives EFFX2506. The compiler records the flag in AccessContractData only when true, so every existing declaration without it retains its IR, semantic hash and generated bytes (packages/runtime/src/Annotation.ts; packages/compiler/src/extensions/access-contract.ts).
The flag is not part of HttpAccessAnnotationSpec. The generated endpoint still passes only the original AccessSpec fields to the application annotator, preserving the Contact OpenAPI and AccessSpec bytes (packages/compiler/src/generate/http.ts). effx inspect displays snapshotDecisionForCommand: true for an opted-in operation (packages/cli/src/inspect.ts).
Limit and obligation
The compiler sees the canonical resolver as a SymbolRef. AOT analysis cannot prove that the canonical scope resolver is routing-only, that the token comparison is correct and constant-time, or that an application guard uses no mutable authority facts. Those remain responsibilities of the runtime middleware and guard. The flag is an explicit reviewable assertion, not runtime authorization or a purity proof. The application must validate the ObjectCapability token in its security middleware, fail closed, and keep any mutable authority decision in the committing transaction. A committed visitor-IP quota write is not itself an authority fact. No other credential or principal shape receives this exception (docs/specs/0028-contact-migration.md; ADR 0007).