Skip to main content
Date: 2026-10-10

Status

Accepted. Amended by 0016: each environment and scope now has its own branch, sponson-receipts/<env>/<scope>; sponson/receipts is read only as a fallback for scopes not yet migrated.

Context

Drift detection, destroy and resuming a partial apply all need to know what the previous run did. The recommended place to run apply is CI, where every job starts on a clean machine, so a receipt kept in the working directory is gone by the next run. Options considered:

Decision

Receipts are written through an interface, ReceiptStore (packages/core/src/types.ts): read, write, list, and the lock operations acquireLock, renewLock, readLock, releaseLock. Two implementations ship:
  • git-branch (default): an orphan branch sponson/receipts in the user’s repository, on the origin remote. CI already has a token that can push to it (contents: write), so no new credential is needed.
  • local: .sponson/receipts/, used when there is no remote and by tests.
Both use one layout (packages/core/src/receipts/layout.ts): <environment>/<scope>/latest.json, one <runId>.json per run, and lock.json. Each run adds a file, so concurrent scopes never conflict on content, only on the ref. The git store treats a rejected non-fast-forward push as a failed compare-and-swap: fetch, re-check, retry with jittered backoff within a time budget (SPONSON_STORE_BUDGET_MS, default 60 s). When the budget runs out it reports STORE_CONTENDED; when the server keeps refusing the push it keeps a copy under .sponson/unpushed/ and reports STORE_REJECTED.

Consequences

  • A CI run sees what the previous run did without any service outside the repository.
  • The receipts branch history is a record of every run. Because receipts hold only keyed hashes and non-sensitive outputs (ADR 0009), the branch is safe to keep.
  • A hosted store is a third implementation of the same interface; nothing in the engine changes.
  • Every store operation is a fetch and a push, which is slow under contention. The lease’s renewal paces itself so a slow store cannot queue renewals (ADR 0008).
  • Deleting the branch makes Sponson forget what it manages: plan then reports Sponson’s resources as unmanaged rather than failing.