Claude tooling on this box — sessions, hub, routines, memory, skills¶
Claude Code runs from /home/angel (so ~/CLAUDE.md is the L0 router for every session). Around it: a tmux launcher, a web hub, cron-driven prompt "routines", the auto-memory directory, and the icm-architect skill that produced this map.
Shape¶
- Binary:
~/.local/bin/claude→ the current version under~/.local/share/claude/versions/(2.1.278 verified 2026-09-20). Settings~/.claude/settings.json(dark theme, push notifications on). - Sessions:
~/claude-start.sh [name] [-c]— persistent tmux session with--remote-control <name>enabled by default, so SSH drops do not kill it and the same session appears in the Claude mobile app and at claude.ai/code.~/claude-kill.shstops interactive sessions but deliberately notclaude-hub, the builder MCP proxy, ortunnel-client. - claude-hub:
~/claude-hub/app.py, user unitclaude-hub.service(Restart=always),hub.lan— Garmin + Apple Health analytics, a Garmin coach, an app-builder prompt box; DBhub.db; reads Garmin MCP at127.0.0.1:8000.claude-remote.service(starts in~/claude-remote, not~: Claude Code 2.1.283+ will not run--bgin an untrusted home; memory symlinked),codex-builder-tunnel.service,garmin-tunnel.serviceare sibling user units (tunnels for phone/ChatGPT access). - Routines:
~/.claude/routines/{evening-plan,prealarm-check,morning-sync}.md+_shared-facts.md, run bytraining-syncviaclaude -p --output-format json --tools "". Outputs.last-output-<mode>.json, statuslast-run-<mode>.json, logtraining-sync.log. Backupsbackup-2026-09-05/. - Shared AI memory:
~/.claude/projects/-home-angel/memory/—MEMORY.mdindex + one file per fact, used by both Claude and Codex. Codex reaches the same directory through~/.codex/memoryand its global~/.codex/AGENTS.mdrequires reading the index at session start. Holds user and feedback facts; server facts belong in this map (seemap/CONTEXT.md). Since 2026-10-14 the directory is a symlink to~/Notes/claude-memory/plex-server/(old dir kept asmemory.pre-vault-20261014), so the vault copy is the only copy and every machine shares it:~/Notes/rules/CLAUDE.md(=~/.claude/CLAUDE.md) imports itsMEMORY.md, and any machine writes there then runsnotes-sync.notes-nightlyskips its old copy step when the path is a symlink. The folder is committed to the private notes repo and excluded from the published notebook. Verified 2026-10-14: a freshclaude -pin an unrelated folder recalled the email-draft-only entry. Codex's internalmemories_1.sqliteremains application-owned; it contained no memory rows when unified on 2026-09-18 and is not treated as a source of truth. - Daily update:
claude-update.timer04:30 (Persistent) runs~/.local/bin/update-claude.sh:claude update, restartsclaude-remoteonly if the version changed, rolls the~/.local/bin/claudesymlink back and exits 1 (job-failure alert) if it will not start. Built-in autoUpdates stays off so updates never land mid-day. - Craft MCP servers:
~/.claude.jsonmcpServersholdsclaude-builder,garminand 13 Craft servers (photocraft, vectorcraft, wordcraft, gridcraft, pdfcraft, deckcraft, designcraft, lightcraft, filmcraft, effectcraft, soundcraft, cadcraft, figcraft); install and update: sudo-and-tools,~/Notes/bin/craft-setup. doc-check FILE [--final](~/.local/bin, 2026-10-14): gate every .docx/.pdf before it reaches the owner or an email draft. Fails on floating header/footer text boxes, one-section-per-page PDF conversions, sentences split across paragraphs, overlapping words or nearly empty pages in the render (viawordcraft-cli convert), and open tracked changes with--final.claude-mail-guard(global PreToolUse hook in~/.claude/settings.json, 2026-10-14): blocks Gmail send/reply/forward and SMTP sends from Bash (owner rule: drafts only), and blocks Gmail draft attachments,SendUserFileand IMAP draft appends whose .docx/.pdf failsdoc-check. Server only; the Mac has no copy. 2026-10-23: matcher now alsomcp__.*__(send_message|reply|forward|create_draft|update_draft)(the Gmail connector's server name varies per session, the oldmcp__claude_ai_Gmail__matcher missed it) and it blocks connector drafts containing a link (connector rewrites links to google.com/url). Backupclaude-mail-guard.bak.20261023.mail-draft(2026-10-23): Gmail draft over IMAP APPEND with links kept and made clickable;--reply-to-idthreads it; never sends. Needs~/.config/gmail-drafts/env(GMAIL_USER, GMAIL_APP_PASSWORD; not created yet).- Skills:
~/.claude/skills/icm-architect/(upstream commit inUPSTREAM; verified current ate16cafe6on 2026-09-18). Invoke with "ICM this / map this / audit this folder". - Builders:
~/claude-builder/,~/codex-builder/,~/codex-workspaces/(ownCLAUDE.md: everything stays inside/workspace, localhost-bound, no secrets) — the app-builder sandbox, separate from server work. -
Artifacts: the hardening report is a claude.ai artifact (URL on the hardening-plan card). Redeploy by passing
url; rebuild the local copy from the saved HTML each time. -
GitLab (2026-10-16): legacy personal token
plex-claudeof useradiaz40(scopes api, write_repository, read_api; expires 2027-10-15) in~/.config/gitlab/env(0600,GITLAB_TOKEN) and Vaultwarden "GitLab – plex-claude". Groupembeddederaholds the KiCad fork https://gitlab.com/embeddedera/kicad. Owner rule: nothing is pushed to GitLab until the owner says so.
Session gotchas (each cost tokens once):
- Phone/web "new session on Plex" needs server mode, not --remote-control: claude-rc-server.service (2026-10-15) runs claude remote-control --name plex --no-create-session-in-dir in ~/claude-remote under script (it wants a tty; tmux under systemd died), log /tmp/claude-rc-server.log. That is what lists Plex as an environment at claude.ai/code. claude-remote.service (--remote-control plex --bg) only makes one session, never an environment.
- 2026-10-23 — one server, claude-remote.service. The 10-22 switch to server mode (claude remote-control --name plex --permission-mode auto) kept Type=oneshot+RemainAfterExit from the old --bg unit, so systemd killed it at TimeoutStartSec=360 and restarted 60s later: 265 restarts in 31h, phone link dropping every 7 min, nobody paged (job-failure ignores non-catalogue units). Now Type=exec, no RemainAfterExit, and the --bg-era ExecStop (ran claude stop on every background agent machine-wide) removed. claude-rc-server.service (duplicate plex server) stopped + disabled at owner's choice. update-claude.sh now checks is-active 20s after its restart. Backups: ~/.local/state/unit-backup-2026-10-23/. Undo: copy them back, daemon-reload, enable --now claude-rc-server.
- claude remote-control is a shell subcommand; inside an existing interactive session use /remote-control. The launcher passes the top-level --remote-control option when creating new sessions.
- Background Bash gets killed by the memory supervisor here (it reads free); run long jobs in the foreground — the 10-min timeout is not enforced on run_in_background, but the supervisor is.
- The garmin MCP has 155 tools. Interactive sessions defer their schemas, but claude -p loads them all (~54k tokens, measured 2026-10-21), so a headless job loads only the tools it uses.
- Headless jobs (2026-10-21): run from /var/tmp/claude-headless (outside $HOME, so no CLAUDE.md is found by walking up) with --setting-sources project --disable-slash-commands (no user hooks, plugins, skills). That also drops user-scope MCP servers (garmin), so add them back with --mcp-config; the claude.ai connectors (Gmail, Calendar) still load. Unused tools are denied by name, because --allowedTools only auto-approves and never restricts. Startup tokens: quote-drafts 43k -> 16k, training-sync 89k -> 21k (21:00 evening run, real garmin + calendar calls). The ~13k floor is the base prompt plus the schemas the job needs.
- The claude.ai 5-hour usage window is shared with the phone/web; cron passes can hit "session limit" (the wrapper waits for the reset).
- cat is bat in interactive shells; tool shells are fine.
AI allowance monitoring (verified 2026-09-17)¶
~/ai-capacity is the private EmbeddedEra/ai-capacity checkout. The installed ~/.local/bin/ai-capacity reads Codex's native app-server quota interface and collects Claude status-line observations into ~/.local/state/ai-capacity (private files, no credentials). ai-capacity-refresh.timer refreshes Codex every five minutes; it performs no inference. Claude's statusLine setting points at ai-capacity claude-statusline; all previous settings were preserved in a dated backup.
The admin role can read /home/api/ai-capacity on the existing assistant service and see the card on Home. It reads cached sanitized state and never launches a model on page load. Missing, stale and expired observations remain explicit. Thresholds are advice, not a universal execution gate. Source, installation/rollback and tests: ~/ai-capacity/README.md; operating decisions: projects/ai-capacity.
Home now has explicit Refresh (both providers), native Claude get_usage model meters including Fable, and confirmed admin-only Codex reset redemption. The native CLI method is experimental and may return cached data. Reset attempts use a private persistent journal at ~/.local/state/assistant/ai_capacity_reset_pending.json; unknown outcomes never authorize a new key. Normal CLI reads, timers and page loads do not redeem credits. See the project note for the disclosed development incident and the required offline test runner.
Social illustration generation (verified 2026-09-21)¶
The existing user assistant.service exposes publisher-only artwork start/status routes under
/home/api/content/<slug>/artwork; content_artwork.py uses the existing Claude SDK credentials,
then renders with ~/.local/share/shots-venv/bin/python. One generation may run at a time.
Job state lives in the existing content-approval SQLite ledger; the latest three artwork backups
live under each package's artwork-backups/. assistant-sandbox.service refuses regeneration
when its content root is the production /home/angel/embeddedera-content; a separate fixture
root is required. No new service, timer, API key or publication permission was added.
Source: assistant main 3ac6d26; user procedure and validation: projects/content-pipeline.
If you change this¶
- Hits: editing
~/CLAUDE.mdchanges every future session's first 1k tokens — keep it a router · movingmap/breaks the router table · renaming a routine file breakstraining-sync:19-23. - Does not hit: the server; nothing operational depends on Claude being present except the three training-sync passes.
See¶
- Source:
~/claude-start.sh·~/claude-kill.sh·~/claude-hub/claude-hub.service·~/.local/bin/training-sync
Background AI drafts (verified 2026-09-21)¶
~/ai-work-runner implements the scheduled subset of SOP-025. The user timer
ai-work-runner.timer checks at :15/:45 hourly with up to 45 seconds jitter;
user lingering is enabled, so logout does not stop it. ai-work-runner.service
refreshes both usage sources before a bounded batch; empty queues consume no
model turns. The runner only reads existing Kanboard tasks and executes jobs
registered with an explicit authority record and hashed brief/context snapshot.
Changed scope, reassignment, closure, stale quota or a changed CLI binary blocks
launch. It never closes tasks or sends messages.
Adapters use explicitly selected Claude Fable/Sonnet/Haiku or lower GPT models
with model tools disabled and private per-job homes. Landlock ABI 8 is available
on this kernel; isolation probes passed. bwrap still fails uid-map setup, so
this does not use the historical full-access launcher. Inputs are snapshots;
outputs are draft text/proposed patches only. No source changes are applied.
Pinned CLI versions: Claude 2.1.278, Codex 0.155.1. A pin mismatch requires
revalidation, not a silent update. Temporary subscription auth copies are cleaned
after completion and by ExecStopPost following interruptions; raw state is private.
The initial batch limit is two jobs with two pending-review slots, 20 minutes
per job, 25 minutes per service, 3GB memory cap. The lock prevents overlapping
runner batches, not arbitrary foreground agent work; foreground orchestrators
must account for the scheduled load. Pause: ai-work-runner pause; stop current
work: systemctl --user stop ai-work-runner.service; resume: ai-work-runner resume.
Accepted/rejected drafts free review slots without applying output or closing a task.
The deterministic publisher writes projects/ai-background-status.md and inert
fenced results under projects/ai-work-results/, using existing notes-sync and
notebook publication. Source/tests: ~/ai-work-runner; private state:
~/.local/state/ai-work-runner. Phone status.
Note publication uses ~/.local/bin/publish-note, sourced from assistant/scripts/publish-note.
As of 2026-09-22 it promotes the latest committed version of only the reviewed note, including
saved edits, rather than cherry-picking the creation commit. Unsaved draft edits and dirty
production checkouts block publication. Procedure and recovery evidence: SOP-018 v5.8 and
content-pipeline.
Rolling content research supply (2026-09-22)¶
The owner-directed hourly content-buffer-fill wrapper now maintains seven future days of
feed-content supply. It supersedes the old daily one-draft/three-pending-draft limits. Researchers
run as explicit claude-fable-5-1, medium effort, with only WebSearch/WebFetch and empty MCP
configuration; structured output is written, validated and deployed to the protected preview by
weekly-research. Node remains on PATH for existing hooks/plugins. A full buffer makes no model
calls. Readiness and actual approved feed dates are separate on Home Content. Scope, limits and
approval procedure: SOP-018 v6.0, content-pipeline. Sources: assistant content_buffer.py,
scripts/content-buffer-fill, scripts/weekly-research, docs/research-worker.md.
Codex sandbox (bwrap) — 2026-09-27¶
Ubuntu 24.04 sets kernel.apparmor_restrict_unprivileged_userns=1, which made every bwrap sandbox fail with bwrap: loopback: Failed RTM_NEWADDR: Operation not permitted (Codex -s workspace-write could not run any shell command). Owner installed /etc/apparmor.d/bwrap (root-owned): profile bwrap /usr/bin/bwrap flags=(unconfined) { userns, include if exists <local/bwrap> } and reloaded with apparmor_parser -r. Verified: systemd-run --user … bwrap --unshare-user --unshare-net … and codex exec -s workspace-write both work. Nested sandboxes still fail inside Claude's own Bash sandbox, so launch Codex workers with systemd-run --user. Revert: delete the file and apparmor_parser -R.
Google Chrome + Claude in Chrome (2026-10-13)¶
Google Chrome 155.0.8059.39 installed system-wide from Google's .deb (sudo dpkg -i, no apt; deps were already satisfied). The package added /etc/apt/sources.list.d/google-chrome.sources, so Chrome updates with normal system upgrades. Profile ~/.config/google-chrome/Default; the Claude extension (fcoeoabgfenejglbffodgkkbkcdhcgfn, 1.0.100) is installed there, which is what the mcp__claude-in-chrome__* tools drive — use it when a task needs the owner's real logged-in sessions; the desktop app's built-in browser stays the default otherwise. No sudoers grant left behind (/etc/sudoers.d/ holds only README). Remove: sudo dpkg -r google-chrome-stable and delete the .sources file.
Daily vault + ICM upkeep (2026-10-15)¶
~/.local/bin/notes-daily, cron 0 23 * * * (before notes-nightly 23:55). Owner asked 2026-10-15 for "another tool that keeps the icm architect organized … like an end of the day memory update"; it replaces notes-weekly, which had skipped every Sunday since 2026-09-13 because its first PR was never merged. Commits straight to master (owner choice 2026-10-15: no PR gate, git is the undo); summary to ntfy.
- Inputs, precomputed by the wrapper: ~/.claude/routines/.daily-digest.md = the owner's own messages from the day's interactive Claude and Codex sessions (session-digest.py; claude -p, Agent SDK and codex exec runs skipped), .daily-inputs.md = commits since the last Daily consolidation commit plus full vault-lint --json. The pass can drill in with episodic-memory search (shim in ~/.local/bin, resolves the plugin's current version).
- Pass: one claude -p --model sonnet (subscription, not the API key), prompt ~/.claude/routines/daily-consolidation.md: fix cards missing a server change, route owner corrections and likes by ~/Notes/where-lessons-go.md (flagging any correction said twice), add missing Log lines, one home per fact, delete done to-dos, fix unambiguous broken links. Never touches rules/, sops/, SETUP.md; the wrapper reverts any edit outside projects map decisions hardware claude-memory README.md.
- Out: map/reports/daily-<date>.md with a Needs you list; log ~/.claude/routines/notes-daily.log. -n dry run, -f force. Quiet day (no commits, no sessions) = no model call.
- Gotcha: headless runs could not run git log (first pass 2026-10-15 was blind to commits), hence the precomputed inputs file. The prompt and digest script live in ~/.claude/routines/ (not in git); the wrapper is in the ~/.local/bin repo (3fd18a9).
Advisor model (2026-10-14, trial)¶
~/.claude/settings.json has "advisorModel": "opus" (Claude Code's experimental advisor; SOP-025 v3.2, review 2026-10-21). Because it is user-level, it applies to interactive sessions, subagents, and every claude -p wrapper running as angel (training-sync, weekly-research, quote-drafts, note-to-social, notes-weekly (now notes-daily), bom-check's draft path), unless the wrapper sets CLAUDE_CODE_DISABLE_ADVISOR_TOOL=1. Done 2026-10-14 (owner OK): all six now export it (bash: line 2; bom-check: os.environ.setdefault after import os; backups *.bak.20261014; repo copy assistant 8adb5fe). Verified: claude -p /advisor prints Advisor: Opus 5.5 without it and /advisor isn't available in this environment. with it. A new claude -p wrapper must add the same line. The assistant's Agent SDK calls (agent, adminchat, content_artwork, daily_review, intake, partnorm) pass setting_sources=[] and never see it. A Fable-main run can't take an Opus advisor (rank rule). Off: /advisor off or delete the key.
REA reverse-engineering MCP (2026-10-15)¶
REA (MIT, npm rea-agents 6.5.0 by morluto, the only official source; lookalike repos exist). Owner asked for it 2026-10-15. Installed user-space, checksums verified: Ghidra 12.1.4 at ~/.local/opt/ghidra_12.1.4_PUBLIC (the free engine; Hopper/IDA are paid, JADX for APKs not installed) and Temurin JDK 21.0.12.1 at ~/.local/opt/jdk-21.0.12.1+1. rea is the nvm global (~/.nvm/versions/node/v24.20.0/bin/rea); skill reverse-engineer-anything in ~/.claude/skills and ~/.agents/skills.
- Registrations: mcpServers.rea in ~/.claude.json and [mcp_servers.rea] in ~/.codex/config.toml, env GHIDRA_INSTALL_DIR, JAVA_HOME and PATH (nvm bin first).
- Gotcha: rea starts with #!/usr/bin/env node, and Claude's MCP launcher has no nvm on PATH, so the setup-written entry failed with CONNECTION_CLOSED until PATH was added to its env (backups *.bak.rea-20261015). Re-running rea setup or rea update may drop it again: check claude mcp list | grep ^rea. Same cause breaks the pdf-viewer plugin (npx).
- Verified 2026-10-15: rea doctor all Ghidra checks healthy; claude mcp list shows rea Connected; rea decompile <copy of /usr/bin/true> entry --provider ghidra returned C in ~17 s (stripped binaries have no main; use entry or an address).
- Use: only on software we may analyse. Remove: rea uninstall, delete the two ~/.local/opt dirs.
Codex login expiry — 2026-10-18¶
The ChatGPT login (~/.codex/auth.json) died 2026-10-17 20:50 ("refresh token has expired", 401) and stayed dead 14 h: ai-capacity-refresh, codex-remote-control and codex-update failed, job-failure opened tasks #304/#312/#322 with only provider_returned_error. Only a person can approve a device-code login (codex login --device-auth, https://auth.openai.com/codex/device). codex-auth-heal.timer (15 min) now detects it, pushes the link + code to ntfy and restores the daemon/units afterwards (see alerting card). Cause unproven: the token died at the same minute as a normal refresh, so suspect a refresh-token race between several codex processes (leftover pids in the codex-remote-control cgroup) or a login of the same account on another machine. Do not store an API key as a fallback (rule 2: free beats paid).
erp-backup secrets set too big for R2 — 2026-10-18¶
~/.config/Claude (347 MB) and ~/.config/google-chrome (334 MB) appeared 2026-10-14 and grew the nightly secrets-*.tar.gz from 1.8 MB to 318 MB; R2 answered 413 on the push from 10-17, so the offsite copy of the server secrets (the key-escrow set) was missing for two nights and the Sunday erp-verify failed (task #324). erp-backup now excludes both directories (no keys in them); a manual backup pushed 12/12 and verify passed. The verify check also had a latent bug (tar tzf | grep -q under pipefail); it now reads the listing first. Backup before the edit: ~/.local/bin/erp-backup.bak.20261018. If a new app drops big profile dirs into ~/.config, the set grows again — du -sh ~/.config/*.
Claude Desktop on Plex does not survive a reboot (2026-10-21)¶
Claude Desktop 2.31226.1 (/usr/bin/claude-desktop, config ~/.config/Claude, ccRemoteControlDefaultEnabled=true) was started by hand on 2026-10-13 and had no autostart, so the hard power loss at 12:16 on 2026-10-21 (previous boot's journal ends mid-line, no shutdown sequence) left it dead after GDM autologin. Added ~/.config/autostart/com.anthropic.Claude.desktop (15 s delay); a manual launch from SSH (DISPLAY=:0 XAUTHORITY=/run/user/1000/gdm/Xauthority) starts fine.
Second cause, still open: autologin never unlocks the GNOME login keyring (created 2026-10-13, non-blank password; Locked=true over D-Bus), so Electron safeStorage is basic_text and the app starts signed out (log: isEncryptionAvailable=false, accountId=null) — Remote Control cannot register until it is signed in. Fix is either a blank keyring password (Seahorse, needs the old one, typed at the Plex screen) or --password-store=basic in the autostart Exec plus one new sign-in. The CLI units (claude-rc-server, claude-remote) are unaffected and come up on their own.
Update 2026-10-21 17:30: keyring now unlocked (blank password), app signed in (autostart Exec is now claude-desktop --password-store=gnome-libsecret, not changed by Claude), remote-tools-device authenticated. Plex still did not show as a Remote Control machine: remote-control.log says state pending → off trigger=gate intent=default, reason:"off", register_ok:0 since launch. Eligibility passes; the in-app Remote Control switch ("Let your phone and claude.ai start sessions on this computer?", Settings → Remote Control, with a folder list) was never turned on. ccRemoteControlDefaultEnabled=true only covers per-session remote control (worked once at 13:57, session archived at 13:58). The switch is a user consent made in the app UI, so Claude does not flip it.
Resolved 2026-10-21 19:06: owner turned Remote Control on in the app at 17:42 (state off → serving trigger=ipc intent=on); two folders registered (/home/angel/claude-remote and the ai-capacity folder), watch stream connected, work being served, 3 active sessions. Not yet proven: a cold reboot (autostart + blank-password keyring + serving, all unattended). If Plex is not listed after the next reboot, check remote-control.log for intent=on and Locked on the login keyring first.