Files
dotfiles/common/claude/CLAUDE.md
T
connor e6644d0616 Rebuild dotfiles as one branch with per-host layers
Replaces the previous repo, which had split into two histories that never
met (mainframe on a dead GitLab remote, the laptops on Gitea) with 146
dirty files across three machines and the NAS never enrolled at all.

Branch-per-machine is gone. One main, with host differences expressed as
small files under hosts/<hostname>/ rather than as branches, so there is
nothing to merge. The reconciled zsh layer reduces 15-33 line forks to
1-7 effective lines per host; distro differences (oh-my-zsh prefix,
syntax-highlighting path, fd vs fdfind) are probed in common/ instead.

Fresh history: the old one carried six plaintext credentials, 45 MB of
mail caches, browser caches and vendored binaries. 5,096 tracked files
and 144 MB become 462 files and 2.6 MB. The .gitignore is now an
allowlist, which is what keeps that true.

Root cause of the rot: ~/.local/bin was a symlink to scripts/ with GOPATH
inside it, so every go install wrote into version control (2.2 GB on the
work laptop). PATH now points at the repo instead of the reverse.

Also: Hyprland replaces sway and is sourced in two halves so $browser is
defined before use; singleton automations carry ConditionHost= alongside
host-layer-only placement; ddns moves from cron to a guarded timer;
package manifests and pkg-snapshot/pkg-restore replace the X11-era
install_scripts/; networkmanager-dmenu added to system76 (the binding
always existed, the package never did).
2026-09-14 14:24:37 -04:00

79 lines
4.4 KiB
Markdown

# Working notes for Claude on mainframe
## LLM / inference stack
Connor runs a self-hosted inference stack: LiteLLM proxy in front of a local GPU
model (llama-swap), OpenAI, Anthropic, and Moonshot/Kimi, with OpenCode + OMO on
top. **Before touching anything under `~/config/litellm/`, `~/.config/opencode/`,
or the model routing, read `~/guides/llm/robots.md`.** It carries the design
decisions, the measured data, and — more importantly — the specific wrong
conclusions this project has already reached and corrected. `~/guides/llm/README.md`
is the short human version.
The five things most likely to waste an hour if you don't know them:
1. **A config value is not applied until an invalid value produces an upstream
400.** LiteLLM silently discards parameters it doesn't route (`store: false`
bare in `litellm_params` returns 200 and never reaches OpenAI; it needs
`extra_body`).
2. **Editing bind-mounted config does nothing without
`sudo podman restart <container>`.** Changing an *env var* needs a full
`sudo podman-compose up -d <svc>` — a restart is not enough. The reverse trap
is worse and bites silently: **`podman-compose up -d` will NOT restart a
container when only a bind-mounted file changed**, because the compose
definition is unchanged. It prints the container name and does nothing. This
applies to Grafana provisioning as much as to `litellm/config.yaml`.
3. **If every hosted model hangs (not errors) while the host has internet**, it's
Docker: `dockerd` sets `ip filter` FORWARD to policy drop and kills all podman
egress. DNS still resolves, which makes it look like a broken vendor. Fix:
`sudo bash ~/config/host/podman-docker-forward.sh`. Don't remove Docker — the
Gitea Actions runners need it.
4. **HTTP 200 with empty content is not necessarily a bug.** Three causes: an
Anthropic refusal, a reasoning model eating its whole `max_tokens` budget
before emitting text, or a dropped parameter. Local models do it too. Give
reasoning models real headroom before concluding anything.
5. **Zen (`zen/*`) is under a ToS block — make no calls to it**, including
"harmless" ones. Entries stay in config in an access group no key holds.
6. **The OpenCode sidebar under-reports cost — badly.** It omits delegated child
sessions, where most agent work happens: it showed ~$5.80 against a
vendor-verified $17.39. Use `opencode stats` or the LiteLLM spend log.
7. **Open WebUI gives every model its full built-in tool suite by default**
(`meta.builtinTools` defaults to True when absent). Models that are weak at
tool discipline loop on `query_knowledge_files` and friends even with no
knowledge base configured. Symptom: many round trips with 25-30 token
completions. Disable per-model in Admin → Models; there is no global switch.
8. **On this box, local inference is RAM-bandwidth-bound, not VRAM-bound.**
Decode speed is set by a model's *active* parameters, not its total, so an
80B MoE with 3B active runs at the same speed as a 30B with 3B active. Free
VRAM buys prefill, not decode. Don't recommend a bigger GPU without checking
which wall you're actually against.
9. **The OMO agent aliases embed their model family on purpose** —
`personal/atlas-kimi-k2.7-code`, not `personal/atlas`. OMO picks its
per-model prompt by string-matching the *last path segment* of the model
name, so a "tidy-up" rename silently disables every Kimi/GPT-specific prompt
and guard with no error. **Never shorten them.** `local/librarian` and
`local/explore` are deliberately left un-suffixed — `isGptModel` matches a
bare `"gpt"` substring and `gpt-oss-20b` would trip it.
Cost work: read `model_group`, never `model`, to attribute a request. Cost is
driven by cache-read volume and context size far more than by model choice.
## General
- Repos here use an **allowlist** `.gitignore` (`**/*` plus explicit `!` entries).
`~/config/` and `~/docs/` are deliberately untracked — back up config edits by
hand as `<file>.pre-<thing>` snapshots, which is the established convention.
- Don't create project-level `CLAUDE.md` files in `~` — Connor keeps guidance at
the `~/.claude` level and reference material in `~/guides/`.
- Measure before concluding, and when a previous conclusion turns out to have
been reached from bad data, **record both the correction and why the original
reasoning was unsound** — otherwise someone re-opens it.