Skip to content

feat(tui): the console completed — account menu, ceremony audit, thinness pin, runbook (#392 C6) - #408

Merged
eaitbrahim merged 2 commits into
mainfrom
feat/392-safety-polish
Aug 19, 2026
Merged

feat(tui): the console completed — account menu, ceremony audit, thinness pin, runbook (#392 C6)#408
eaitbrahim merged 2 commits into
mainfrom
feat/392-safety-polish

Conversation

@eaitbrahim

Copy link
Copy Markdown
Contributor

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)

  • 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; when nothing is imported it renders its honest empty state naming keel db import, never a confident total realized P&L: 0.
  • versions — the same lines keel versions prints, through ONE shared renderer extracted to keel/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).
  • The whole branch is READ-ONLY — no write path at all — and keeps the console contracts: banner-aware, m-close, cursor reset on entry, ? contextual help (three new modes registered in help_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*.py with four rules:

  1. No compute-tree imports — nothing from 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.
  2. No compute-tree calls — any call resolving into those trees must match a (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's next_status wording; the shared purification renderer; the executor's two READ helpers). Allowances are site-scoped and asserted to name real callees, so they cannot leak.
  3. Decimal is display-only — no arithmetic operator over a Decimal(...) construction.
  4. No broker construction outside the seams — only inside the build_broker/build_client lambda handed to a service (the run_fn seam) 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's a), 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, the f key) — 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: .rebind is called only from switch_profile (no key path around the LIVE confirm; CLI flags remain the documented direct binding), the sole order-capable dispatch is agent.run_once with the CLI's confirm gate, and every terminal-prompting run rides 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 newly added mutating entry fails the suite until classified (proven with a scratch fake TRADING_MENU entry), 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

…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
@eaitbrahim
eaitbrahim merged commit 996f0f3 into main Aug 19, 2026
5 checks passed
@eaitbrahim
eaitbrahim deleted the feat/392-safety-polish branch August 19, 2026 18:47
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>
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.

C6: safety & polish pass — typed-confirmation adversarial review, keybinds, runbook TUI section

1 participant