11 KiB
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 5–60 | 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
preferredAgentandagentAutoApprovefrom~/.config/panama/settings.jsonwith jq at press time (the power-button precedent) —noneexits 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 autoand--approve-for-me; use whatever the real flags are, and the prompting-mode variants whenagentAutoApproveis off). - Spawn:
setsid kitty --directory "$PANAMA_PATH" -e <argv…>(kitty is the house terminal, its own health check exists;--app-id-style fixed classpanama-agentso window rules can target it later). --prompt "text"input; no--pickmenu in v1 (the Agents page is the picker).- Repo
.claude/settings.jsongains 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-scopedlimits[], transcript scan with message-id dedupe and scan cache, cached-limits-expire-on-window-rollover, waiting-vs-expired auth states,retryAdvisedtransport/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 -evalidity 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; aggregateheadline= fullest window across enabled agents; timer fromagentUsageRefreshMinutes, running only whileshowAgentUsage.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-usageretires (delete; its consumers migrate). The five pins oftests/quickshell/agent-usage-contractsurvive 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 — stubbedsettings.json+ stubkitty/claude/codexon 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-contractcoversdiagnose-crashautomatically once the skill lands.settings-nav/search-routing/settings-docs/readme-contractobligations as usual.
Ownership (disjoint)
- Orchestrator: this spec; schema keys first; the
diagnose-crashskill;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),installfailure line,bin/panama-migrate-notifyfailure line,.claude/settings.jsonallowlist,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(onReloadFailedrung),modules/settings/HealthPage.qml("Ask the agent"),AgentsPage.qml+ routes/shell-case/qmldir, search entries inSettingsSearch.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.