feat(tui): the console completed — account menu, ceremony audit, thinness pin, runbook (#392 C6) - #408
Merged
Merged
Conversation
…ness pin, runbook (#392 C6) The final slice of the TUI-operator-console phase (PRD §5 C6): the tree's last placeholder turns real, the typed-confirmation + live-guard adversarial review is encoded as tests, the thinness ask of PRD §6.2 is pinned architecturally, and the runbook documents the console. The Account menu (keel/commands/account_console.py): pnl -- the EXACT `keel pnl` report through the C1 services (commands.pnl.build_pnl_report + render_pnl_report) over the active deployment's imported transactions, with its honest empty state naming `keel db import` when nothing is imported; versions -- the same lines `keel versions` prints, through ONE shared renderer extracted to keel/commands/versions.render_versions_lines (the CLI output is byte-identical), disagreement styled loud where the CLI exits non-zero, the environment scan run ONCE per entry and its rows held (the venues browser's contract). All read-only: the branch has no write path at all, stated as its own ceremony row. Same console contracts as the other branches: banner-aware, m-close, cursor reset on entry, `?` contextual help (three new modes registered in help_console). The ceremony audit (tests/commands/test_console_ceremony.py), C6's dedicated pass encoded: a table-driven suite over EVERY state-mutating console action -- attest (typed asset code), attest-instrument / exempt / unexempt / subscription attest / subscription set (ungated by design, the CLI's own contracts), withdrawals attest (typed `yes` to enable, ungated suspend), the scout browser's offered attest step (typed), rules add (ungated: lands candidate), retry's promote (y/N confirm) and `--force` (typed), simulate / agent cycle / monitor poll / fetch / fetch --check / repair-gaps (ARMED + Enter), autonomy (typed ON, ungated OFF), record-flow / reset-hwm / resume / resume-entries (typed), kill (one key, no ceremony, its own CLI contract), db import (ungated, CLI-validated), the LIVE profile switch (y/N confirm; paper ungated), and the dashboard's `a` (typed arm gate) and `f` (money- safe fetch) keys. Classes: typed-phrase / confirm-step / armed-enter / ungated-by- design; every cell names its existing proof test (asserted to exist), and the rows no existing test covered get one compact generic refusal proof (all-empty prompts: the cancellation line AND a byte-identical state). Cross-cutting invariants pinned: `.rebind` is called only from `switch_profile` (no key path around the LIVE confirm; CLI flags remain the wrapper's documented path); the executor allowance admits only the two READ helpers and the sole order-capable dispatch is agent.run_once with the CLI's confirm gate; every terminal-prompting run goes through the one _run_terminal_form suspend/restore seam. THE TEETH: the mutating keys are derived from the dispatch registries (everything not declared read-only per module) -- a new mutating entry without a ceremony row fails the suite. The thinness pin (tests/commands/test_console_thinness.py), PRD §6.2's architectural test: an AST scan over console.py + tui.py + every *console*.py enforcing four rules -- (1) no imports from the compute trees (keel.strategy *, keel.execution.{guards,sizing,executor}, keel.compliance.{screen,purification}, keel.analysis *) except constants/types (read from the source module's own AST: the gate-wording and screen-vocabulary imports) and the audited function imports; (2) no CALLS resolving into those trees outside (module, enclosing function, callee) allowances, each justified inline (the ledger's insights-mirrored paper- gate read; the Enter-gated verdict's dispatch to the engine's own backtest+gate; the retry's next_status wording; the shared purification renderer and the two executor READ helpers) -- allowances are site-scoped and asserted to name real callees, so they cannot leak; (3) Decimal is display-only (no arithmetic over a Decimal construction); (4) broker construction only inside the build_broker/ build_client lambda seam or the audited bounded read sites. Proven false on deliberate scratch violations of every rule during development, then removed. The runbook (docs/operator-runbook.md, "## The TUI console", between the equities profile and "How much money moves", pinned by tests/test_runbook_console.py): what the console is (the menu tree over the C1 services, thin by construction), profile switching + the live guard, the session banner, the typed contracts (identical to CLI, never pre-filled) and kill's one-key contract, the ARMED/block- ing surfaces (Ctrl-C exits the console), the Venues browser + `keel brokers list`, the help/glossary system, and the safety design notes (cursor resets; the Account menu is read-only; the console runs no loop of its own). The README has no TUI/ dashboard mention to amend (checked), so no README change rides here. Fixes #392
…d runbook state exactly what is typed where
This was referenced Aug 19, 2026
C6: safety & polish pass — typed-confirmation adversarial review, keybinds, runbook TUI section
#392
Closed
Merged
eaitbrahim
added a commit
that referenced
this pull request
Aug 20, 2026
Minor, not patch. Three things since v0.9.3 change what an implementor or a deployment can rely on: * A NEW DISTRIBUTION. `keel-broker-alpaca` (#382, #384) plus the paper-equities profile that selects it (#386), so a deployment can now be US equities via the broker port rather than crypto only. * THE PORT CONTRACT MOVED TWICE. `market_clock`/`market_schedule` made venues session-aware (#385), and `place_order` gained `idempotency_key` (#419). Both carry defaults so no CALLER breaks, but a third-party adapter that does not accept them is no longer a `Broker` -- the conformance suite now says so. That is exactly the kind of change a patch bump must not hide. * THE OPERATOR CONSOLE. The TUI became keel's console across #399-#408, and `keel update` (#415/#417) makes a deployment self-updating. Every pinned sibling moves with it. The four production distributions are required `==` at this exact version (`RELEASING.md`, "Release assets"), so a bump that missed one would install a mixed set -- the `keel-trader 0.5.7` against `keel-core 0.5.5` failure `keel versions` exists to catch, and which `~/keel` actually ran across two releases. Also in this window, on the Robinhood adapter: the best_bid_ask fixture corrected against the live venue (#414), a credential guard that catches the error it only claimed to (#416), pre-flight sizing reported on the preview (#418), transport backoff (#420), and the fenced one-order probe (#421). Co-authored-by: Claude Opus 5 (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.
C6, the final slice of the TUI-operator-console phase (PRD §5 C6, §6.2, O2/O3/O4). Four deliverables:
1. The Account menu — the last placeholder (
keel/commands/account_console.py)keel pnlreport through the C1 services (commands.pnl.build_pnl_report+render_pnl_report) over the active deployment's imported transactions; when nothing is imported it renders its honest empty state namingkeel db import, never a confidenttotal realized P&L: 0.keel versionsprints, through ONE shared renderer extracted tokeel/commands/versions.render_versions_lines(CLI output byte-identical, existing tests unchanged); disagreement renders loud where the CLI exits non-zero; the environment scan runs ONCE per entry, rows held (the venues browser's contract).?contextual help (three new modes registered inhelp_console).2. The architectural thinness pin (
tests/commands/test_console_thinness.py, PRD §6.2)An AST scan over
console.py+tui.py+ every*console*.pywith four rules:keel.strategy.*,keel.execution.{guards,sizing,executor},keel.compliance.{screen,purification},keel.analysis.*except constants/types (verified against the source module's own AST — the gate-wording/screen-vocabulary imports) and the audited function imports.(module, enclosing function, callee)allowance, each justified inline (the ledger's insights-mirrored paper-gate read; the Enter-gated verdict dispatching to the engine's own backtest+gate; the retry'snext_statuswording; the shared purification renderer; the executor's two READ helpers). Allowances are site-scoped and asserted to name real callees, so they cannot leak.Decimal(...)construction.build_broker/build_clientlambda handed to a service (therun_fnseam) or the audited bounded read sites.Proven false during development on deliberate scratch violations of every rule (sizing/guards/screen calls, Decimal math, a stray
_build_broker) — each failed the pin by name — then removed.3. The typed-confirmation + live-guard adversarial audit, as tests (
tests/commands/test_console_ceremony.py)Every state-mutating console action has a row: its ceremony class — typed-phrase (attest ×2 incl. the scout step, withdrawals
--enabled, promote--force, autonomy ON, record-flow, reset-hwm, resume, resume-entries, the dashboard'sa), confirm-step (the LIVE profile switch, retry's promote y/N), armed-enter (agent cycle, monitor, fetch/check/repair, simulate), ungated-by-design (kill's one-key contract, autonomy off, withdrawals suspend, rules add→candidate, enable/disable/demote, the ungated compliance forms, db import, paper switches, thefkey) — and a refusal proof: existing tests referenced by name (asserted to exist), plus one compact generic all-empty-prompts proof (cancellation line AND byte-identical state) for the rows nothing covered. Cross-cutting:.rebindis called only fromswitch_profile(no key path around the LIVE confirm; CLI flags remain the documented direct binding), the sole order-capable dispatch isagent.run_oncewith the CLI's confirm gate, and every terminal-prompting run rides the one_run_terminal_formsuspend/restore seam.The teeth: the mutating keys are DERIVED from the dispatch registries (everything not declared read-only per module) — a newly added mutating entry fails the suite until classified (proven with a scratch fake
TRADING_MENUentry), and a stale row fails the same equality.4. The runbook's console section (
docs/operator-runbook.md)"## The TUI console", between the equities profile and "How much money moves", pinned by
tests/test_runbook_console.py: what the console is (the menu tree over the C1 services, thin by construction), profile switching + the live guard, the session banner, the typed contracts (identical to CLI, never pre-filled) and kill's one-key contract, the ARMED/blocking surfaces (Ctrl-C exits the console), the Venues browser +keel brokers list, the help/glossary system, and the safety design notes (cursor resets, the read-only Account branch, the console runs no loop of its own). The READMEs contain no TUI/"dashboard" mention to amend (checked both), so no README change rides here.Fixes #392