feat(compliance): documented allowlist-screen exceptions (waivers) - #134
Merged
Conversation
Adds a general, per-asset per-criterion exception mechanism so a human can record a DOCUMENTED, auditable waiver of an admission criterion -- the motivating case being PAXG's 441 daily bars falling short of the 4-year history floor while shariah/liquidity screening pass clean. The set of criteria that may EVER be waived is restricted to WAIVABLE_CRITERIA (currently just `history`): the shariah checks (attestation, haram_sector, riba_yield, dayn/unknown backing) and `settlement` are never consulted for a waiver, both by construction (screen_asset only reads `waived` inside the history branch) and by an explicit WAIVABLE_CRITERIA membership guard as defense in depth. A waiver is surfaced as a loud warning (never a silent pass) and is self-retiring: once bars clear the floor, screen_asset emits nothing about it. - keel/data/db.py: `screen_exceptions` table, SCHEMA_VERSION 8 -> 9, a documented no-op v9 migration (table creation is handled by the additive `_SCHEMA_STATEMENTS` pass, same pattern as v6/v7). - keel/data/repository.py: upsert/get/list/delete_screen_exception. - keel/compliance/screen.py: `screen_asset(..., waived=None)` and `WAIVABLE_CRITERIA`. - keel/cli.py: `assets exempt`/`assets unexempt` (Choice-restricted to WAIVABLE_CRITERIA), `assets screen` now loads and passes waivers, and `assets list` prints a `exceptions:` section when any are recorded. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adversarial review of #134 found 2 should-fix + 2 nits: - WAIVABLE_CRITERIA guard was tautological (`"history" in WAIVABLE_CRITERIA` is a compile-time True) and its safety test was vacuous (default bars=2000 never entered the history branch, so `waived` was never consulted). Replaced the inline check with an up-front `effective_waived` filter in `screen_asset` -- real defense-in-depth against a future WAIVABLE_CRITERIA shrink or a careless new branch -- and rewrote the safety test to actually enter the history branch, plus added a test that a stray non-waivable key riding alongside a real waiver is dropped, not honored. - A blank/whitespace rationale used to ADMIT with an empty "WAIVED by documented exception: " warning -- an undocumented "documented exception." `screen_asset` now only honors a waiver whose rationale is non-blank (mirrors the existing unsourced-attestation guard); `assets exempt` rejects a blank `--rationale` at the CLI boundary too. - `assets exempt`/`assets unexempt` now uppercase `--asset`, matching the uppercase asset code `_screen_product` looks waivers up by -- a `--asset paxg` waiver no longer silently no-ops against `PAXG-USD`. - `delete_screen_exception` now returns the rowcount removed; `assets unexempt` only echoes a revoke confirmation when a row actually existed, otherwise reports "no such exception" instead of a false success. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
What & why
Adds a general, per-asset / per-criterion allowlist-screen exception mechanism, plus
keel assets exempt/unexemptto record and revoke one.Motivating case: PAXG passes shariah (
ayn, KB §65.5/§72.4/§86) and liquidity screening but fails the 4-year daily-history floor (441 on-chain-USDbars < 1460). Gold has centuries of off-chain price history and PAXG is a thin tokenised wrapper, so a human wants to record a documented, auditable exception that waives only the history criterion — never a silent exemption.Design — honest and tightly bounded
screen_exceptions(asset,criterion,rationale,granted_by,granted_at; PK(asset, criterion)). Schema v8→v9 via a documented no-op migration (the table DDL is in the schema block;migrate()creates it on existing DBs before the version loop — mirrors the v6/v7 attestation/profile pattern).WAIVABLE_CRITERIA = frozenset({"history"})— only a DATA/market criterion may ever be waived. The shariah core (missing attestation,haram_sector,riba_yield,dayn/unknown backing) and settlement can never be waived. Enforced two ways: the CLI--criterionis aclick.Choicerestricted to this set, andscreen_assetonly reads a waiver inside the history branch (structural isolation) and checks membership — belt-and-suspenders.! ... WAIVED by documented exception: <rationale>warning and the verdict flips to ADMIT. Self-retiring: the waiver is only consulted when the check would otherwise fail, so once-USDbars ≥ 1460 it becomes inert automatically.assets attest(an exception cannot itself place an order; the 17guards.pyrails still enforce every trade). Revocable viaunexempt(a de-risking action). Shown inassets list.Tests (TDD, written first)
exempt→screenADMITs with WAIVED; bad--criterionrejected;listshows it;unexemptreverts to REJECT.WAIVABLE_CRITERIAguard (emptying it fails the positive-path tests).uv run pytest -q→ 1399 passed;uv run ruff check→ clean.🤖 Generated with Claude Code