Skip to content

feat(tui): the compliance menu — admission flows, the scout handler, Shariah in force (#389 C3) - #403

Merged
eaitbrahim merged 2 commits into
mainfrom
feat/389-compliance-console
Aug 19, 2026
Merged

feat(tui): the compliance menu — admission flows, the scout handler, Shariah in force (#389 C3)#403
eaitbrahim merged 2 commits into
mainfrom
feat/389-compliance-console

Conversation

@eaitbrahim

@eaitbrahim eaitbrahim commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

C3 of the TUI-operator-console PRD (issue #389): the Compliance sub-menu (PRD §3), the scout-results admission handler (O6) and the "Shariah in force" browser (O10) — all dispatching to the existing C1 services; the TUI stays thin (O2). Test-first: the new tests were red before the implementation landed.

Entry → service dispatch (every entry, an existing service or repository function)

Compliance entry Dispatches to
screen admission.build_screen_report over assets.screen_product (the one admission gate)
propose admission.build_propose_view over proposer.parse_proposal/build_proposal_report
attest (typed) Repository.upsert_asset_attestation — the exact call/args keel assets attest makes, same echo line
attest-instrument Repository.upsert_instrument_attestation (product uppercased, wrapper vocabulary = KNOWN_WRAPPERS)
exempt / unexempt Repository.upsert_screen_exception / delete_screen_exception (criterion restricted to WAIVABLE_CRITERIA, blank rationale refused)
holdings assets.gather_holdings + render_holdings behind ONE Enter-gated get_accounts (opens ARMED, holds the result — the discover overlay's own gating story)
discover admission.build_discover_report over one Enter-gated list_products
Scout results… the browser below (O6)
Shariah in force… the browser below (O10)
subscription show / attest / set the bodies extracted into keel/commands/subscription.py (subscription_show_lines / apply_subscription_attest / apply_subscription_set) — one implementation, two front-ends; the CLI commands delegate, byte-compatible (their tests unchanged and green)
withdrawals attest (typed) the CLI's own two set_state writes + echo lines
purification compliance.purification.build_report + render_purification_report (report-only)

The typed contract (O3), sacred

  • withdrawals attest --enabled keeps the CLI's own gate — clis_typed_withdrawals_gate calls _require_interactive_confirmation("attest withdrawals as ENABLED", …) verbatim, rendered in-console via the curses suspend/restore dance, failing closed; --suspended stays ungated (the CLI's asymmetry). A declined gate means not a single state row is written (pinned).
  • attest — marked "(typed)" by the PRD §3 tree — ends its form with a typed gate demanding the operator type the asset code back (the scout flow's attest step is the same form). Never pre-filled, never piped; a wrong phrase writes nothing (pinned for both the menu form and the scout flow, including end-to-end through the live loop).
  • Forms collect their fields at the terminal inside the console session (_run_terminal_form: suspend → prompts → restore) and their confirmation line toasts on the menu — every write shows what it did.

The scout-results handler (O6)

Lists the operator-local shortlists from config's proposals_dir (the already-documented key, default ~/keel/proposals — the path comes from config, never a TUI-side guess; the empty state names the directory and the naming convention), lets the operator select one, renders it through the admission services (propose → screen, REJECT for unattested candidates — the gate really ran), and offers — never runs — the typed attest step for a chosen candidate. Proposer-never-decider, in the console too. A real proposal file is driven end-to-end through the live loop in the tests, including a refused typed phrase writing nothing to the deployment's own database.

The "Shariah in force" browser (O10)

For the ACTIVE profile, read-only, rendered from records alone:

  • attestations in force over the active allowlist via the new service read assets.gather_attestations_in_force (the assets list repository reads, scoped to config.allowlist, instruments keyed by the same (venue, product) the screen looks up) — each with source, ruling and recorded date; unattested allowlisted assets named as the fail-closed gap they are;
  • documented exemptions in effect; rail 17's live qabd state (the same read the rail makes);
  • the fiqh-derived constraints, each a verbatim quote from docs/fiqh-basis.md with a citation pinned by test to a real section heading of that document — nothing on the screen is a TUI-authored fiqh summary; the vocabulary (qabd, riba, maisir, attestation, exemption, purification) is quoted from the document the same way, and gharar — which fiqh-basis does not state — is rendered as not-stated-there rather than defined (the document's own honesty rule, inherited);
  • the standing honesty lines — "keel is not a fatwa engine", "No scholarly review of keel's fiqh basis has occurred"always visible, pinned two-sided against the document so neither side can drift.

Service-layer additions (reads/extracts, not TUI logic)

  • admission.list_shortlists — every *shortlist.json, newest first, total, never creating the directory;
  • assets.gather_attestations_in_force — the allowlist-scoped attestation inventory (O10's read);
  • the subscription attest/set/show bodies extracted from the CLI commands into keel/commands/subscription.py.

Tests & gates

New: tests/commands/test_compliance_console.py (38 tests: menu model, every form's exact repository call via a recording repo, both typed gates' refusal, the scout browser, the shariah browser's sourcing) + 5 run_live loop-wiring tests (navigation, screen view offline + broker-never-constructed, form dispatch with the curses suspend dance recorded, the scout browser, and the end-to-end typed attest). Two C2-era placeholder pins in test_console.py updated: Compliance now lands instead of saying "lands in C3".

  • uv run pytest -q3386 passed, 3 skipped
  • uv run ruff check keel tests packages — clean
  • uv run mypy — clean

Fixes #389

…Shariah in force (#389 C3)

The console's third slice: the Compliance sub-menu (PRD §3), the scout-results
admission handler (O6) and the "Shariah in force" browser (O10), all dispatching to
the existing C1 services — the TUI stays thin (O2).

Entry -> service dispatch (every one an existing service or repository function):
- screen -> build_screen_report over screen_product (the one admission gate)
- propose -> build_propose_view over parse_proposal/build_proposal_report
- attest / attest-instrument / exempt / unexempt -> the exact repository writes the
  CLI commands make (upsert_asset_attestation / upsert_instrument_attestation /
  upsert_screen_exception / delete_screen_exception), same args, same echo lines
- holdings -> gather_holdings + render_holdings (one Enter-gated get_accounts)
- discover -> build_discover_report over list_products (Enter-gated, like d+Enter)
- subscription show/attest/set -> the bodies extracted into subscription.py
  (apply_subscription_attest / apply_subscription_set / subscription_show_lines) —
  one implementation, two front-ends; the CLI commands delegate to them unchanged
- withdrawals attest -> the CLI's own set_state writes and echo lines
- purification -> build_report + render_purification_report (report-only)

Typed contract (O3) preserved: withdrawals attest --enabled keeps the CLI's OWN gate
(_require_interactive_confirmation with the command's exact wording, rendered
in-console via the curses suspend/restore dance, failing closed); attest — marked
"(typed)" by the PRD tree — ends its form with a typed gate demanding the asset code
be typed back (never pre-filled, never piped; a wrong phrase writes nothing). Tests
prove both gates refuse without the phrase.

The scout handler (O6): lists the operator-local shortlists from config's
proposals_dir (the existing documented key, default ~/keel/proposals — no new config
born; empty state names the directory and the naming convention), renders the chosen
file through the admission services (propose -> screen), and offers — never runs —
the typed attest step per candidate. Drove a real proposal file end-to-end through
the live loop in tests, including a refused typed phrase writing nothing.

The Shariah-in-force browser (O10): attestations in force over the ACTIVE allowlist
via the new service read gather_attestations_in_force (assets.py — same repository
reads and (venue, product) keys as assets list/screen), exemptions in effect, rail
17's live state, and the fiqh-derived constraints each a VERBATIM quote from
docs/fiqh-basis.md with a citation pinned to a real section heading (and the
vocabulary anchored the same way; gharar rendered as not-stated-there rather than
TUI-defined). The honesty lines — "keel is not a fatwa engine", "No scholarly review
of keel's fiqh basis has occurred" — always visible and pinned two-sided against the
document.

New service reads: admission.list_shortlists (every shortlist, newest first, total);
assets.gather_attestations_in_force (the allowlist-scoped assets-list read); the
subscription writes/show extracted from the CLI bodies. Console gains four modes
(compliance, compliance-view, scout-list, scout-view); the top menu's Compliance
entry is no longer a placeholder.

Fixes #389

Signed-off-by: Elmehdi Aitbrahim <eaitbrahim@gmail.com>
@eaitbrahim
eaitbrahim merged commit 9883522 into main Aug 19, 2026
5 checks passed
@eaitbrahim
eaitbrahim deleted the feat/389-compliance-console branch August 19, 2026 13:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

C3: compliance menu + the scout-results admission handler

1 participant