Version-controlled ~/.claude/ framework plus a resource coordinator and
internal dashboard for an agentic research setup.
📐 System map — a visual,
framework-level tour of every part and how they connect (source:
docs/index.html).
| Path | Purpose |
|---|---|
claude/CLAUDE.md |
Durable user instructions (symlinked to ~/.claude/CLAUDE.md). |
claude/settings.json |
Claude Code settings (hooks, permissions). |
claude/rules/ |
Scoped rules auto-loaded by path (e.g. evaluation.md = HCE discipline). |
claude/skills/ |
Global slash-command skills — the knowledge-base group (/discover, /ingest, /curate, /lint, /headroom, …), loaded in every project. |
claude/skills-experiment/ |
The experiment-loop group (/propose, /implement, /iterate, /new-experiment, /derive-experiment) — not global; linked per-project via <project>/.claude/skills (see "Growing a lit repo" below). |
claude/hooks/ |
Lifecycle hooks (SessionStart, Stop = token logger, PreToolUse = safety net). |
claude/templates/ |
Project and note templates copied by /new-project / /ingest. |
coordinator/ |
Python package. state.db schema + writers, hardware poller, agency verdict. |
registry/ |
Service & port registry template (services.example.yaml → copy to untracked services.yaml). |
dashboard/ |
FastAPI + HTMX + SSE dashboard. Reads state.db + project files. LAN-only. |
scripts/ |
Maintenance + bootstrap helpers. |
install.sh |
Idempotent bootstrap for a fresh machine. |
Runtime state — ~/.claude/sessions/, ~/.claude/cache/,
~/.claude/.env, ~/.claude/state.db, ~/projects/* — is not
tracked by this repo.
The cron wrappers that drive scheduled agent runs live in
~/.claude/schedule/ and are intentionally not tracked here, because
this repository is public and those scripts are machine-specific by
nature. They accumulate:
- Push-notification topics. On a public notification service the topic name is the credential — anyone who learns it can read the channel and publish to it. Committing one here would hand it over.
- Absolute paths and project names, including private repos, which the redaction rule for public repos forbids.
--permission-mode bypassPermissionsinvocations — working recipes for unattended autonomous runs, tied to a specific host's layout.
install.sh therefore links skills, hooks, and templates, but never
schedule. .gitignore also carries a claude/schedule/ guard so that
copying the directory into the repo cannot silently commit it.
Consequence to be aware of: those scripts have no version history and no
backup. Keep them short, and put any reusable logic in claude/lib/ (tracked,
secret-free) rather than in the wrappers themselves — see below.
| Path | Purpose |
|---|---|
claude/lib/ |
Secret-free shell helpers the untracked cron wrappers source. Tracked so the logic survives even though the wrappers don't. |
- Linux with
systemd --user(runs the dashboard + hardware poller). git,uv(Python env manager), Python 3.12.jq(every lifecycle hook parses its payload with it) and a systempython3with PyYAML (the session-end hook readsbudget.yaml).- Optional:
gh(GitHub CLI — lets/new-projectcreate repos and enable Pages);ccusage(npm i -g ccusage— accurate quota in/headroom); an NVIDIA GPU with drivers (GPU stats; absence is tolerated).
git clone git@github.com:eschmitt88/claude-system.git ~/claude-system
cd ~/claude-system
./install.shClone to ~/claude-system — that's the canonical path skills and hooks
reference. If you clone elsewhere, install.sh creates a ~/claude-system
symlink to your clone so those references still resolve.
install.sh is idempotent: it symlinks the framework into ~/.claude/
(backing up anything it replaces), provisions the coordinator + dashboard
venvs, initializes ~/.claude/state.db, bakes your config into the systemd
units, and starts them. Re-run it any time to upgrade or to apply config
changes.
⚠️ Review before installing — you adopt this repo's agent behavior.install.shsymlinksclaude/CLAUDE.mdandclaude/settings.jsoninto~/.claude/as your global instructions and settings (existing files are backed up first). These carry opinionated defaults: the instructions tell agents to commit and push automatically at checkpoints in every git repo, and the settings suppress permission prompts (skipDangerousModePermissionPrompt,skipAutoPermissionPrompt) and pin a default model. If you don't want that posture, edit those two files (or your~/.claude/copies) before letting agents loose.
All machine-specific settings live in one file: ~/.claude/.env
(seeded from .env.example; never committed). Every key is
optional and falls back to the default below. Edit, then re-run
./install.sh to apply.
| Key | Default | Purpose |
|---|---|---|
PROJECTS_ROOT |
~/projects/research |
Where research projects live. |
DISK_MONITOR_PATH |
~/projects (else ~) |
Volume sampled for free disk in /headroom + dashboard — point at your data drive. |
CLAUDE_DASHBOARD_BIND |
0.0.0.0:8080 |
Dashboard host:port. Use 127.0.0.1:8080 for localhost-only. |
NTFY_TOPIC |
— | ntfy.sh topic for notifications. |
QUOTA_WEEKLY_COST_LIMIT_USD |
see ccusage.py |
Calibrated weekly plan ceiling (USD) for /headroom + dashboard %. |
QUOTA_WEEKLY_TOKEN_LIMIT / QUOTA_5H_TOKEN_LIMIT |
see ccusage.py |
Token fallbacks for the weekly / 5h windows. |
QUOTA_WEEKLY_RESET_HOUR / QUOTA_WEEKLY_RESET_WEEKDAY |
17 / 0 (Mon) |
Weekly quota reset boundary as shown on claude.ai. |
The quota defaults are one Max-20x subscription's calibration — see the
calibration note at the top of the constants block in
coordinator/coordinator/ccusage.py
for how to derive yours from a live claude.ai reading.
The service/port registry rendered by the dashboard's /ports page is
machine-specific and untracked: copy
registry/services.example.yaml to
registry/services.yaml and describe your own services (the page is
empty without it).
These resolve through coordinator/config.py, so the code works even with
an empty .env; the file only overrides defaults.
Model roles are a per-project setting, not machine config: each
project's budget.yaml sets models.ideator / models.implementer. The
template defaults both to opus — a floating alias that resolves to the
latest Opus release, so it tracks new versions without edits. Pin a concrete
slug (e.g. claude-opus-4-8) for reproducibility, or use haiku/sonnet
for a cheaper implementer.
~/claude-system/coordinator/.venv/bin/claude-coordinator-status # what /headroom shows
systemctl --user status claude-dashboard.service claude-hw-poller.timer
# dashboard → http://localhost:8080 (or the bind you configured)Skills follow a propose → implement → iterate → ensemble loop driven
by a user in Claude Code. /propose does ideation; /implement is the
only skill that spawns a subagent; /iterate drives chain cycles.
Experiments live at ~/projects/research/<project>/experiments/YYYY-MM-DD-<slug>/
with a standard layout (README, config.yaml, metrics.json, results/,
splits.yaml where applicable). See individual SKILL.md files for
contracts.
The agentic-research project is the meta-hub: concepts/ files are
@imported by downstream projects, and /sync-imports appends
used_by: back-references.
Every project scaffolded from the template ships
docs/index.html — a zero-build, convention-driven viewer of its
knowledge graph (literature / concepts / MoCs / experiments / candidates
/ decisions). It is byte-for-byte identical across repos: it auto-detects
owner/repo from the Pages URL, reads the live file tree in one GitHub
API call, and pulls file bodies from the raw CDN. No build step, no
regeneration, no pre-commit hook — it reflects the repo as it stands on
each page load.
The automated process for any repo following the layout:
- The template already contains
docs/index.html(edit the template copy to update every project's viewer at once). - The repo must be public (GitHub Pages on a private repo needs a
paid plan).
/new-projectcreates public repos by default (--privateto opt out). - Enable Pages from the
/docsfolder —/new-projectdoes this automatically; by hand it is one call:gh api -X POST repos/<owner>/<repo>/pages \ -f "source[branch]=main" -f "source[path]=/docs"
- The site is live at
https://<owner>.github.io/<repo>/within ~1–2 min.
The framework's own map (this repo) lives the same way at
docs/index.html →
https://eschmitt88.github.io/claude-system/.
Private projects skip Pages and are browsed via the internal dashboard instead, which renders the same directory convention.
A single sqlite database at ~/.claude/state.db tracks:
- Claude quota — tokens consumed in the current 5h and weekly windows.
- Hardware — GPU/CPU/RAM/disk samples (30s cadence).
Skills consult the coordinator via /headroom. (An admission layer —
job queue, admit/defer policy, per-session PreToolUse cap — was removed
2026-08-01 after three months of telemetry showed it never fired; see
agentic-research docs/system-proposals/2026-07-31-instruction-ablation-program.md.
Recoverable from git history if parallel multi-project autonomy ever
needs arbitration.)
Agency verdict. claude-coordinator-agency (shown in /headroom)
turns the quota + hardware state into a GO / SLOW / HOLD recommendation,
using reset-anchored token pacing (unused weekly quota is wasted, so being
behind pace near the reset means spend now) plus live CPU/RAM/GPU headroom.
A project opts into autonomy with agency: max in its budget.yaml: in
those repos /digest auto-fetches and ingests the top candidates, and
/iterate chains cycles, while the verdict permits and the budget.yaml
ceilings hold. Default agency: standard keeps the propose-and-confirm
behavior. See claude/rules/agency.md.
FastAPI app bound to CLAUDE_DASHBOARD_BIND (default 0.0.0.0:8080,
i.e. reachable on the LAN at http://<host>:8080). Views: /
(live now — loop sessions, live gauges, quota meters), /queue
(coordinator queue), /project/<name> (per-project cycle table +
DIAGNOSTICS). Auto-refreshes via SSE. Runs as a systemd user unit.
The experiment-loop skills are scoped, not global, so a project's shape is a choice. Two ways it grows, decided by whether the experiments serve this repo's reading or start a new line of work:
- Spawn a downstream project when the experiments have their own
identity (own data, own budget, many runs) or the concepts serve
more than one consumer:
/new-project --experiments, then@importthe hub's concepts (the import contract writesused_by:back-references). - Graduate in place when there is a single consumer and reading
and running are one thread:
ln -s ~/claude-system/claude/skills-experiment .claude/skills- Declare intent in
budget.yaml(ceilings,agency:, model roles). - Define the holdout before the first optimization run —
splits.yaml+test/flips the HCE opt-in (claude/rules/evaluation.md). The only order-sensitive step: a holdout carved out after iterating against the data is contaminated from birth. dvc initif absent;.worktrees/for destructive runs.- First move:
/derive-experimenton the ripest literature note. - Record the graduation in
docs/decisions/.
/lint warns when a repo has dated experiment folders but no linked
experiment-loop group.
Because the claude/ tree is symlinked into ~/.claude/, editing
files under either path updates the same content. Commit from
~/claude-system/ so history stays coherent.
Runtime state must never be committed. The .gitignore is strict on
*.db and .env; add to it if a new runtime artifact appears.
MIT.