Files
Panama/docs/superpowers/specs/2026-08-25-escalation-ladder-and-usage.md

11 KiB
Raw Permalink Blame History

The escalation ladder, and usage worth trusting

Approved from mock agents.html (2026-08-25). Two builds in one cycle: no failure surface is a dead end (crash → one click → your agent mid-investigation), and the bar's usage number becomes a multi-agent panel backed by Omarchy's battle-tested collectors. Borrowed code is MIT — carry Copyright (c) David Heinemeier Hansson + the MIT permission notice in a header comment on every derived file, and add an Omarchy entry to docs/UPSTREAM-INSPIRATION.md.

Decisions log

Decision Choice
Scope Ladder + usage panel, one cycle
Ladder surfaces v1 App crashes, shell reload failures, Health red checks, update/migration failures, plus panama diagnose by hand
Agent Configurable via preferredAgent; Claude and Codex both fully supported on the ladder (per-agent argv table); default none — quiet until chosen, like Omarchy
Permission posture Auto-approve (user's explicit choice), behind a toggle; the diagnose skill still teaches reads-not-fixes; root still via panama-sudo
Usage Collectors ported near-verbatim (Claude + Codex), Panama-native popover panel, no cross-machine sync
Settings home New agents leaf under System
Notification click mechanism Omarchy's command-as-data hint (panama-exec), executed by the shell — survives restarts, never blocks the watcher

Schema (orchestrator adds first — do not re-add)

key type def group notes
preferredAgent enum none/claude/codex none agents none = crash toasts carry no action
crashDiagnoseOffer bool true agents gates the crash rung (still requires an agent chosen)
reloadFailureOffer bool true agents shell-reload rung
healthAgentHandoff bool true agents Health-page rung
agentAutoApprove bool true agents off → agents launch in their default prompting mode
agentUsageClaude / agentUsageCodex bool true agents per-collector enables
agentUsageRefreshMinutes int 560 15 agents collector fan-out interval

showAgentUsage stays where it is (vitals) and keeps its meaning; the Bar page row remains, the Agents page gets the full set (declared mirror in settings-ownership-contract if both surface the same key). New group agents needs a groupPages route to the new leaf (search-routing-contract).

The launcher: bin/panama-agent

Ported from omarchy-agent, trimmed to Panama's needs, attribution header included.

  • Reads preferredAgent and agentAutoApprove from ~/.config/panama/settings.json with jq at press time (the power-button precedent) — none exits 0 silently.
  • cd "$PANAMA_PATH" before spawning: the checkout is where the skills, the repo trust, and the pre-approved diagnostic permissions live.
  • Per-agent argv table — verify every flag against the installed binaries' --help, do not trust the Omarchy source or this spec: Claude ≈ claude [auto-approve flag] -- "$prompt", Codex ≈ codex [auto-approve flag] -- "$prompt" (Omarchy used --permission-mode auto and --approve-for-me; use whatever the real flags are, and the prompting-mode variants when agentAutoApprove is off).
  • Spawn: setsid kitty --directory "$PANAMA_PATH" -e <argv…> (kitty is the house terminal, its own health check exists; --app-id-style fixed class panama-agent so window rules can target it later).
  • --prompt "text" input; no --pick menu in v1 (the Agents page is the picker).
  • Repo .claude/settings.json gains a pre-approved allowlist for the read-only diagnostic commands (coredumpctl, journalctl, rpm -q, panama doctor) so even non-auto-approve launches investigate smoothly.

The rungs

1. Crashes. bin/panama-crash-watch gains COREDUMP_PID + COREDUMP_SIGNAL_NAME extraction. When preferredAgent != none and crashDiagnoseOffer, the notification carries the command as data: notify-send … --hint=string:panama-exec:"$(printf 'panama-agent-crash %q %q %q %q' pid comm exe signal)" with body "Click to diagnose with ". Otherwise today's actionless notification, verbatim. Existing session-dedupe and portal-crash rationale untouched. New bin/panama-agent-crash ports omarchy-agent-crash: coredump facts + coredumpctl list timestamp into a heredoc prompt naming the diagnose-crash skill with its absolute path as fallback (the harness-agnostic trick — this is what makes the ladder work for Codex too), then exec panama-agent --prompt "$prompt".

2. The panama-exec hint. services/Notifs.qml: read hint panama-exec into the notification model (one field, carried through popup persistence like any other). modules/notifications/NotificationCard.qml: on body click, if the entry carries an exec, run it via Quickshell.execDetached(["sh", "-c", command]) and dismiss — the branch sits beside the existing defaultAction handling and defers to it when both exist. Security note in-file: the session bus is local; any sender could carry the hint, which grants nothing an local process lacks.

3. Shell reload failure. shell.qml's onReloadFailed (old shell keeps running — it can still notify): when reloadFailureOffer and an agent is chosen, send a notification through the shell's own Notifs with the exec hint → new bin/panama-agent-reload builds the prompt from the failure string plus the last ~40 matching journal lines.

4. Health page. Red check with no repair, or whose repair just failed → an "Ask the agent" SettingsButton beside it (visible only when healthAgentHandoff and an agent chosen), running panama-agent --prompt with the check's JSON snapshot (panama doctor check <id>) embedded.

5. Update/migration failures. The failure summary in install gains one line: Hand it to an agent: panama diagnose. bin/panama-migrate-notify's failure path likewise.

6. panama diagnose (cmd_diagnose). By-hand rung: bundles panama doctor --summary, the last journal errors, and "what feels wrong" free text (panama diagnose [words…]) into a prompt and hands it to panama-agent. House three-touchpoint rule + README list (readme-contract).

The skill: skills/diagnose-crash/

Written by the orchestrator, shipped un-gated like the other two, structure adapted from Omarchy's: triggers name the notification and panama-agent-crash; coredumpctl info <pid> first and read the command line; rule out OOM before blaming the program; correlate the crash time against recent package updates and journal context; Fedora debuginfod for symbols; the security rule (a core holds secrets — scratch files via mktemp, deleted after); "never invent function names"; "diagnosis reads; it does not fix"; report structure. Claims auto-pinned by skills-contract (it already scans every skill in skills/).

Usage collectors and panel

  • config/dot/quickshell/scripts/panama-agent-usage-claude — port of Omarchy's Python collector (attribution header): OAuth usage endpoint + model-scoped limits[], transcript scan with message-id dedupe and scan cache, cached-limits-expire-on-window-rollover, waiting-vs-expired auth states, retryAdvised transport/HTTP distinction, percent-scale voting. Keep Panama's secrets discipline: the token stays out of argv and out of the record (their in-process Python read already satisfies it; the contract proves it).
  • …/panama-agent-usage-codex — port: app-server JSON-RPC limits, session-file token stats with the last_token_usage rule.
  • …/panama-agent-usage-update — fan-out: runs enabled collectors in parallel, jq -e validity gate, atomic writes to $XDG_STATE_HOME/panama/agents/usage/<agent>.json.
  • services/AgentUsage.qml → directory model: one FileView per record file, discovered by listing the dir; aggregate headline = fullest window across enabled agents; timer from agentUsageRefreshMinutes, running only while showAgentUsage.
  • modules/bar/AgentUsageWidget.qml: keeps the two-tier amber-75/red-90 coloring and the hide-when-nothing rule; click now opens the panel popover (right-click keeps Settings).
  • New modules/bar/AgentUsagePanel.qml (or popover home matching how other bar popovers are built — follow the house pattern found in the code): per the mock — agent tabs, tier chip, updated-ago line, limit meters with reset countdowns (model-scoped rows included), tokens by day (today bolded), by-model bars, honest auth states. No keyboard-nav requirement in v1; Escape closes like every popover.
  • Old scripts/panama-agent-usage retires (delete; its consumers migrate). The five pins of tests/quickshell/agent-usage-contract survive against the new collectors: token never in argv, never in any record, credentials never written, expired token → no request + honest state, widget hidden unless asked-for AND real numbers. Extend for: per-agent enables honored, record validity gate, atomic write.

New settings leaf

agents under the System category: 4-file ceremony (SettingsRoutes categories entry, SettingsShell case + Component, qmldir, AgentsPage.qml), search entries, regen docs+commands (orchestrator). Page per the mock: preferred-agent picker (install-state aware: show "not installed" detail for an absent binary), the four escalation toggles (auto-approve copy states the trade in plain words), usage card (master switch mirroring showAgentUsage, per-agent enables, refresh interval).

Contracts

  • crash-watch-contract: extend for PID/signal fields and hint gating (none-agent → no hint; chosen → hint present; never both mechanisms).
  • agent-usage-contract: as above.
  • New tests/quickshell/panama-agent-contract: hermetic — stubbed settings.json + stub kitty/claude/codex on PATH; none → silent exit; claude/codex → correct argv shape including the auto-approve/prompting variants; prompt passed as one argv element; cwd is the repo. panama-agent-crash: prompt contains pid/comm/exe/signal + the skill path; never runs a real agent (stub PATH). NOT in the desktop-hijacking ledger — it must run stubbed.
  • skills-contract covers diagnose-crash automatically once the skill lands.
  • settings-nav/search-routing/settings-docs/readme-contract obligations as usual.

Ownership (disjoint)

  • Orchestrator: this spec; schema keys first; the diagnose-crash skill; UPSTREAM-INSPIRATION.md + license attribution file check; docs/commands regen; README count; seam audit.
  • Agent A — usage: the three collector scripts, AgentUsage.qml, AgentUsageWidget.qml, AgentUsagePanel.qml (+ its qmldir if bar modules use one), agent-usage-contract, deletion of the old collector.
  • Agent B — ladder scripts: bin/panama-agent, bin/panama-agent-crash, bin/panama-agent-reload, bin/panama-crash-watch, bin/panama (cmd_diagnose), install failure line, bin/panama-migrate-notify failure line, .claude/settings.json allowlist, crash-watch-contract, panama-agent-contract, README subcommand row.
  • Agent C — shell & settings UI: services/Notifs.qml (hint field), NotificationCard.qml (exec branch), shell.qml (onReloadFailed rung), modules/settings/HealthPage.qml ("Ask the agent"), AgentsPage.qml + routes/shell-case/qmldir, search entries in SettingsSearch.qml.

Standing rules apply: live desktop (valid QML every save, journal checks), no real agent launches or notifications left uncleaned during builds, contracts as you go, never the desktop-hijacking set unannounced.