refactor(idempotency): cache only successes; drop the vestigial status column (#96)#119
Merged
Merged
Conversation
…s column (#96) ADR 0007 said to cache successes AND hard validation rejections and reserved a `status` column to tell them apart. Only the success half was ever built: every denial (insufficient budget, unknown model, unauthorized scope, key reuse) raises and rolls its transaction back, so `claim` decides FRESH/REPLAY/MISMATCH from `command_fingerprint` and `response` alone and never reads `status`. The column had one value forever ('succeeded') and the ADR, schema, and code disagreed. Caching only successes is also the safer policy: a cached hard rejection (e.g. unknown-model) would pin a stale answer even after a later price-book version adds the model — the same stale-cache hazard ADR 0007 forbids for budget denials. - ADR 0038 supersedes ADR 0007, recording the as-built "cache only successes" policy; ADR 0007 marked Superseded. - Drop the write-only `status` column from both idempotency tables (idempotency_key and the durable metered_receipt) via migration 0007; store_response no longer takes a status argument, and the RESPONSE_SUCCEEDED constant is removed. No behavior change: the column was never read. Integration checks that asserted a persisted success now key on `response IS NOT NULL` (equivalent, since only a success caches a response). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BjJ4QScUKbrMzF1jujL5Bg
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
ADR 0007 (Accepted) said to cache successes and hard validation rejections, and reserved a
statuscolumn to tell them apart. Only the success half was ever built: every denial — insufficient budget, unknown model, unauthorized/unknown scope, key reuse — raises and rolls its transaction back, soclaimdecides FRESH/REPLAY/MISMATCH fromcommand_fingerprintandresponsealone and never readsstatus. The column has one value forever ('succeeded'), so the ADR, the schema, and the code disagree.The as-built behaviour is also the safer policy (as the issue notes): caching a hard rejection would pin a stale answer past truth — an unknown-model rejection would keep replaying even after a later price-book version adds that model, the same stale-cache hazard ADR 0007 forbids for budget denials.
Changes
Superseded by 0038); README index updated.statuscolumn from both idempotency tables (idempotency_keyand the durablemetered_receiptfrom meter / grace-backfill have no durable exactly-once backstop; a retry after the key TTL double-applies spend #92) via migration 0007.store_responseloses itsstatusargument across the port, the repo, and all five handlers, and theRESPONSE_SUCCEEDEDconstant is removed.No behavior change
The column was never read; nothing that decides an outcome depended on it. Integration checks that asserted "a success persisted its key" now key on
response IS NOT NULL— equivalent, since a success is the only outcome that caches a response.Local gate: ruff, mypy --strict, import-linter, full unit (497), full integration (182, incl. the migration-drift check) — all green.
Closes #96.