feat(transparency): decision explainer + doberman tui (ADR 0029)#87
Merged
Conversation
- explain.py: deterministic template explanation for any redacted decisions row + OPT-IN Claude-Haiku narrator (anthropic installed AND ANTHROPIC_API_KEY AND DOBERMAN_EXPLAIN_LLM, use_llm can only restrict); allowlist-projected payload only; every failure falls back to the template, never raises. - tui.py + 'doberman tui': Textual decision-log browser with a why-panel; markup-inert rendering (Text cells, markup=False panel), debounced cached thread-worker enrichment with a stale-selection guard; lazy import behind an importlib guard so core stays standalone without the extras. - policy/checklist.py: enforcement docstring corrected to the Option A monitor-floor semantics (objective floor stays live in every state). - pyproject: 0.13.0; extras tui=[textual], explain=[anthropic]; textual added to dev so CI exercises the TUI tests. - tests: 26 explain tests (gate matrix, pinned allowlist, payload-egress secret check, keyword-strict fake SDK) + 6 TUI pilot tests (secret never visible end-to-end, markup literal, reload, quit, empty repo).
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.
Slice
doberman tuiWhat this PR does
Turns any redacted decision-log row into a plain-language "why", and adds a Textual TUI to browse the log with an explanation panel.
doberman.explain—template_explanation()(deterministic, offline, always available) + an opt-in Claude-Haiku narrator gated on all three of:anthropicimportable,ANTHROPIC_API_KEY,DOBERMAN_EXPLAIN_LLMtruthy.use_llmcan only restrict (a caller can force the template but can never bypass the env opt-in). The LLM sees only an allowlist projection (REDACTED_FIELDS) of the already-redacted row — narrator, never judge. Any failure falls back to the template; the module never raises.doberman tui— Textual decision browser (qquit /rreload). Row-derived strings render markup-inert (rich.text.Textcells,markup=Falsepanel) so a crafted stored value can't restyle/spoof the browser. LLM enrichment runs in a debounced, cached thread worker with a stale-selection guard, so a slow narrator call never freezes the UI or overwrites a newer row. Lazily imported behind animportlibguard: without thetuiextra the command prints an install hint and exits 1, and core stays fully standalone.policy/checklist.py—enforcementdocstring corrected to the Option A monitor-floor semantics (the objective floor stays live in every enforcement state).pyproject.toml— 0.13.0; new extrastui=[textual],explain=[anthropic];textualadded todevso CI actually exercises the TUI tests (anthropicdeliberately NOT in dev — tests fake the SDK).Tests added (run in CI)
tests/unit/test_explain.py(26) — template wording per verdict, malformed reason codes never raise, pinnedREDACTED_FIELDSallowlist (the constant can't bless itself), payload projection, full opt-in gate matrix incl. non-truthy flag values anduse_llmrestrict-only semantics, request-egress check that a stray secret on the row never reaches the (fake) SDK, keyword-strict fakemessages.createmirroring the real surface, LLM failure/blank fallback, and thedoberman tuino-textual CLI guard (kept here so it runs in the standalone venv).tests/unit/test_tui.py(6,importorskip("textual")) — pilot tests: seeded rows + explanation shown, empty-repo placeholder,qquits, a synthetic secret in the raw target is never visible in any cell or the panel (end-to-end through storage redaction), crafted Rich markup renders literally,rreloads newly recorded decisions.Public-release safety
Security checklist
Edge cases covered / Deviations from plan / Risks introduced
use_llm=Truecannot bypass the env opt-in; non-truthy flag values ("0", "false", "no", "off", "", " ") stay off.doberman explaincommand — the TUI is the rich surface,doberman logthe plain fallback.