Repository navigation
Make idempotency/outbox-drain an explicit driver capability; refuse loudly on drivers without it (#228, #229) - #256
Merged
Merged
Conversation
RepositoryDriver.supports_idempotency() / supports_outbox_drain() signatures fixed by the t1 plan; t1 implements them in place (refs #228, #229). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015TZZN39cJVjPsK2Ha3C5LD
…tes (refs #228, #229) RepositoryDriver.supports_idempotency() / supports_outbox_drain() replace the contract stub. Default is introspection: True only when every method of the set (IDEMPOTENCY_METHODS / OUTBOX_DRAIN_METHODS) is callable on the instance, so SqliteRepositoryDriver and any external driver implementing the set report True without overriding; a missing or partial set is False. No base-class definition is added for the five optional methods. RepositoryDriverTCK gains 3 idempotency and 3 drain/ack cases with declared-unsupported semantics (no skipTest): a False predicate must come with none of the set's methods present. Reverse controls in test_driver_contract.py prove each case fails a driver broken for it; the SPI demo driver passes all six as declared-unsupported. docs/backends.md §8 maps serve/CLI features to the optional methods and the predicate that announces them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MNwYixWBqEu1AM7hBArND1
… contract (refs #229) `lnpl outbox drain`, `lnpl outbox ack` and `lnpl relay` now check RepositoryDriver.supports_outbox_drain() right after the `fake` branch. A driver lacking drain_outbox/ack_outbox (e.g. lnpl-postgres) gets one stderr line naming the scheme, is closed, and the command exits 2 — before any drain, ack, or publisher open — instead of an AttributeError traceback. sqlite and `--backend fake` behave as before; DriverError still maps to rc 1. cli-surface.md documents the precondition in the outbox and relay sections. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VV85S9kXrpZ98cjrodSvwB
…otency (refs #228) lnpl serve / build_app() used to gate the idempotency claim on hasattr(repository, "idempotency_begin") and silently ran a keyed request when the driver lacked it, so a retried POST executed twice. Both claim sites now ask RepositoryDriver.supports_idempotency() (t1) through _idempotency_mode: - keyed workflow request, or any POST /-/events/<slug>, on a driver whose predicate is False -> 501 idempotency-unsupported (problem+json, correlation_id), nothing claimed, written or run (M22 / E8) - unkeyed requests and --backend fake behave exactly as before; non-driver test doubles keep the pre-#228 opt-in (coordinator ruling t2 D1 r2) - boot: one stderr line naming the driver when the predicate is False - /-/readyz 200 carries {"capabilities": {"idempotency": <bool>}}; the closed list of five checks and the 503 contract are unchanged docs/serving.md documents the per-backend rule, the readyz field and the boot line; lexer.RESERVED_PROBLEM_CODES gains the new code. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KFLTNeouGmzkiMoo9NcXWg
CHANGELOG.md Unreleased gains the capability predicates + TCK entry (Added) and the serve 501 idempotency-unsupported and outbox CLI rc 2 refusal entries (Fixed). README.md / README.ko.md now state the measured full-suite count: Ran 5394 tests in 154.320s (prose ~5,390). Refs #228, #229 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CjcjDfZcNtdLQiFjJnQJLY
…rain contract (refs #229) The relay flag table still said the backend was sqlite-only, which contradicted the outbox drain/ack cells and the relay precondition paragraph. It now names sqlite or any registered driver implementing drain_outbox and ack_outbox, with everything else (and `fake`) refused with rc 2. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VV85S9kXrpZ98cjrodSvwB
This was referenced Oct 15, 2026
Closed
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.
Refs #228, #229. Tracking: #255 (P0 group).
What this does
The only production DB driver (
lnpl-postgres) implements none of the optionalRepositoryDrivermethods for idempotency claims (idempotency_begin/finish/release) or outbox drain (drain_outbox/ack_outbox). Before this PR the core selected those features byhasattr, so on Postgres anIdempotency-Keywas silently ignored (#228) andlnpl outbox drain/lnpl relaydied with anAttributeError(#229). This PR makes the capability explicit and makes both consumers fail loudly.fc74d99RepositoryDriver.supports_idempotency()/supports_outbox_drain()— introspection default (True iff every method of the set is callable; Sqlite True, external drivers lacking the methods False, no base-class stubs).RepositoryDriverTCKgains 6 declared-unsupported cases (noskipTest; a driver that reports False must have the methods absent).docs/backends.mdcapability table.c251bd3,192c0fclnpl outbox drain/outbox ack/relay: check the predicate before any drain/ack/open_publishercall; False → one stderr lineerror: backend <scheme> does not implement the outbox drain contract (drain_outbox/ack_outbox) — see docs/backends.md §3, driver closed, rc 2, empty stdout.--backend fakekeeps its existing rc 2 refusal.cli-surface.mdupdated.6430ba3lnpl serve/build_app(): aRepositoryDriverwhose predicate is False → requests carryingIdempotency-Key(M22) and everyPOST /-/events/<slug>delivery (E8) are refused with 501idempotency-unsupported(problem+json), the workflow does not run; requests without the header run as before. One boot line on stderr (lnpl serve: repository driver <module.qualname> -- idempotency: unsupported …),GET /-/readyz200 body carries"capabilities": {"idempotency": <bool>}(never a 503). Non-RepositoryDriverobjects (test doubles) take an isolated legacy branch that preserves the previous behaviour.lexer.RESERVED_PROBLEM_CODESentry;docs/serving.mdrows.08196ac[Unreleased]entries; READMEs re-pinned to the measured suite size.Decisions recorded during the run
Idempotency-Key가 경고 없이 무시된다 — 드라이버 opt-in 미지원을 기동 시 감지하고 TCK에 멱등성 계약을 넣는다 #228): refuse with 501 rather than warn-and-run or refuse at boot.lnpl outbox drain/lnpl relay가drain_outbox/ack_outbox없는 드라이버(postgres)에서 AttributeError로 죽는다 #229 scope item 2): a stub would have madehasattrtrue for every driver and turned silent skips into 500s before the consumers switched.module.qualname— no backend scheme string reacheswsgi.pyon thelnpl servepath.linkly-synclatest-mainjob runs the 6 new TCK cases through the declared-unsupported branch (verified against its sources).Verification
08196ac, own venv:Ran 5394 tests in 174.778s/OK (skipped=1)(mode B included);rfc_lint,gen_plugin_references --check,check_doc_snippets,check_version_sync,ruff check impl/lnpl scriptsall rc 0. Doc-cell fix192c0fcre-checked:gen_plugin_references --checkrc 0,test_plugin_references/test_readme_currency/test_cli_relayRan 106 tests … OK.return True/any, oldhasattrselection, guard removal — all caught), integration-reviewer approve.🤖 Generated with Claude Code
https://claude.ai/code/session_015TZZN39cJVjPsK2Ha3C5LD