---
name: panama
description: Use when working on the Panama repository itself — editing anything under config/, setup/, bin/, tests/, or the Quickshell shell and Hyprland Lua it ships. Covers the live-desktop hot-reload rules, the contract-test discipline, the fixture seams, and where the real documentation lives.
---

# panama

You are editing a running desktop, not a codebase that gets deployed later. Every dotfile in this
repository is symlinked into `~/.config`, so a save is live the moment it lands. That single fact
drives every rule below.

Read before large work, in this order:

- `README.md` — layout, the `panama` command, how installing and updating work
- `config/dot/hypr/README.md` — the compositor config is **Lua, not hyprlang**; read "The one
  thing to know first" and "Never use `hyprctl keyword`" before touching it
- `config/dot/quickshell/modules/settings/README.md` — adding a setting, setting ownership,
  "Things that will bite you", and where state lives; the most load-bearing document here
- `docs/settings.md` — generated reference for every settings key (never edit by hand)

## The live-desktop covenant

Editing `config/dot/quickshell/**` hot-reloads the live shell on every save.

1. Every save must leave valid QML. An intermediate broken save is a broken desktop, not a broken
   build. Convert a component and its body in ONE edit, never two.
2. After each batch of saves, check the journal for errors AND "Unable to assign" warnings:
   `journalctl --user -u panama-quickshell.service --since '-2 minutes' --no-pager`
3. Every `.qml` file in `config/dot/quickshell/modules/*` must be registered in that directory's
   `qmldir` in the same save batch. An unregistered component fails the whole configuration and
   takes down the bar, dock, and settings together. `tests/quickshell/qmldir-registration-contract`
   is pure file inspection — run it before the change lands.
4. Never `qs kill` from a copied configuration: Quickshell derives the shell ID from config
   content, so a content-identical harness can share the live shell's ID and kill the desktop.
   Harnesses use a distinct entry file, `qs -p`, the PID from `qs list --all`, and `kill` that PID.
5. Never `hyprctl keyword` — Lua-configured Hyprland refuses it, prints to stdout, and exits 0.
   `hyprctl eval` also exits 0 on errors. The only proof a write landed is `hyprctl getoption`
   reading it back. Batch `hyprctl reload` to one per verified change-set, and say when you do it.
6. IPC: annotate every parameter and return type or Quickshell silently skips registration; never
   duplicate an `IpcHandler` target — one silently shadows the other.
7. In `config/dot/quickshell/config/PreferenceSchema.qml`, `readAs` describes the compositor's
   ANSWER shape, not the setting's; a wrong one makes every successful write look rejected.
8. No continuously repainting animations (pulse, shimmer, spinners) — they peg the GPU on
   high-refresh displays.
9. `hyprlock.conf`, `hypridle.conf`, `hyprpaper.conf`, `hyprtoolkit.conf` never moved to Lua. Do
   not "fix" them. Do not install a notification daemon — the shell is one.
10. Scripts carry no `.sh` extension (shebang + `chmod +x`), with one deliberate exception:
    `migrations/*.sh`, which `bin/panama-migrate` globs. No secrets under `user/` — it is
    world-readable and `tests/setup/user-content-contract` greps for key material.

## The contract discipline

~180 executable contracts under `tests/` pin behavior. The rules:

- `panama contracts <file>` names the contracts that mention a file; run those after touching it.
- During a desktop session, run `panama test --safe [pattern]` — it skips everything listed in
  `tests/desktop-hijacking` (contracts that restart the shell, open real overlays, or write
  through the live compositor). Run the full `panama test` only with the user's go-ahead, and say
  so first: it visibly hijacks the desktop.
- Contracts run directly too: `tests/setup/interview-contract`.
- After changing `PreferenceSchema.qml` or `services/SettingsRoutes.qml`, regenerate:
  `config/dot/quickshell/scripts/panama-settings-docs` (writes `docs/settings.md`) and
  `config/dot/quickshell/scripts/panama-settings-commands` (writes the launcher deep links).
  Both take `--check`; `tests/quickshell/settings-docs-contract` fails when stale.
- `README.md` pins the contract count and the `panama` subcommand list
  (`tests/setup/readme-contract`); adding a contract or subcommand means updating it.

## Seams for testing

Helpers are built hermetic: validated inputs, JSON out, secrets on stdin only (never argv).
Contracts reach them through env seams — `PANAMA_NETWORK_HELPER`, `PANAMA_FINGERPRINT_FIXTURE`,
`PANAMA_EXTRAS_DIR`, and the `PANAMA_*_STATIC_ONLY` family that cuts a contract to its
file-inspection half. Prefer exercising a helper through its contract's stubbed PATH over running
it against the real system.

## Safety idioms (contract-enforced)

- Destructive actions are two-stage: `ConfirmAction` with a unique `actionId`, arbitrated through
  `ShellState.armedConfirm` — one armed confirm app-wide. Danger tone marks only the confirming
  press.
- Failures render through `ErrorRow`; honest empty states through `NotMeasuredRow` with a
  `because:`; paragraph-length notes through `SettingsNote`.
- Stored user actions (custom shortcuts, gestures, window rules) are DATA — an enum kind and a
  validated target resolved through whitelist tables in `config/dot/hypr/actions.lua`. Nothing in
  `~/.config/panama/settings.json` may ever be an executable command; the contracts
  (`tests/quickshell/settings-idiom-contract`, `tests/quickshell/keybind-rebind-contract`) pin all
  of this.

## Root work

Never bare `sudo` — load the `panama-sudo` skill. Migrations already follow the rule.
