clawrium
AGENTS.md67 starsChanged 2 months agoApache-2.0View on GitHub
Clawrium is a CLI tool (clawctl) that manages AI agent fleets across your local network. Point it at any machine, and it handles deployment, configuration, and lifecycle management via SSH and Ansible. Install instructions live in docs/installation.md. This is the canonical source — when updating install steps: The website docs MUST follow docs/installation.md exactly. Do not edit website/docs/installation.md directly. Host preparation instructions live in docs/host-preparation.md. This is the canonical source — when updating host-setup steps: The website docs MUST follow…
- Reads credentials
Where files are headingNew AGENTS.md files have more rules about keeping secrets out of the code, and fewer about code style.
Of the AGENTS.md files we read, the 31,542 first seen in the last six months, against the older ones: the kind of rule that gained most share, and the kind that lost most. About files like this in general, not this one, and an estimate, since a kind is a pattern in the words.
In this repository26 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- clawctl-lmworkSkill
- clawctlSkill
- clawctl-uatSkill
- create-playwrightSkill
- create-vhsSkill
- itx-bug-newSkill
- itx-bug-updateSkill
- itx-executeSkill
- itx-issue-newSkill
- itx-issue-updateSkill
- itx-noteSkill
- itx-plan-createSkill
- itx-plan-scaffoldSkill
- itx-pr-statusSkill
- itx-releaseSkill
- itx-review-prSkill
- itx-triageSkill
- itx-verifySkill
- sync-upstreamSkill
- tddSkill
- blog-authorSkill
- daily-digestSkill
- docs-syncSkill
- issue-triageSkill
- release-watcherSkill
The file
Show its rules and text
We split this AGENTS.md into the rules it gives an agent
30 rules. About 0 are common, 16 are distinctive, and 14 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈16
Distinctive · estimate
This author's own choices. Worth a look.
14
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 24
Homelabbers: Run multiple AI assistants on spare hardware
- Very specific: line 81
current, unreleased version only: All new work is documented here as it lands, under the ## [Unreleased] heading.
- Very specific: line 95
Update the root CHANGELOG.md (under ## [Unreleased]) as part of the same change that introduces the behavior — not as an afterthought:
- Very specific: line 103
Every breaking change: MUST be documented under ### BREAKING.
- Very specific: line 115
When a Hermes agent (e.g. Maurice) is asked to work on this repository, it MUST load skills from .hermes/skills/ and treat them as …
- Very specific: line 117
All Hermes skills shipped from this repository are scoped to the clawrium namespace — the name: field in their SKILL.md frontmatter MUST be …
- Very specific: line 308
MUST install at sync time via a dedicated single-purpose Ansible runbook invoked from core/lifecycle_canonical.py:sync_agent_canonical.
- Very specific: line 310
NOT from the agent's configure.yaml.
- Distinctive: line 333
One runbook per binary: Do not group multiple integration installs into one playbook.
- Very specific: line 406
Always use make commands to run tests and validate changes:
- Very specific: line 443
Use /clawctl to manage the Clawrium fleet from an AI assistant session.
- Very specific: line 501
IMPORTANT: Always commit the .itx/ directory with your changes.
- Very specific: line 506
When completing work on an issue, ensure .itx/<issue-number>/ is included in your commit.
- Distinctive: line 513
Read implementation plan from issue
- Distinctive: line 514
Create an implementation task for each phase or step using the harness's task tracker
- Distinctive: line 515
Create verification tasks (tests, lint, review if MCP enabled)
- Distinctive: line 536
If execution feels unclear or you lose orientation:
- Very specific: line 538
Check which task is in_progress
- Very specific: line 563
When mcp.review_enabled is true, all code changes MUST attempt automated review before merging.
- Distinctive: line 575
Document each review iteration in commit message and PR body
- Distinctive: line 578
Before creating a commit with code changes
- Distinctive: line 579
After fixing issues from previous review
- Distinctive: line 580
Before marking PR as ready for merge </atx-review-requirements>
- Very specific: line 679
When mcp.review_enabled is false, not configured, or both ATX transports are exhausted, use manual review with self-attestation.
- Distinctive: line 684
Run all tests and ensure they pass
- Distinctive: line 685
Run linter and fix any issues
- Distinctive: line 687
Document testing approach in PR </manual-review-requirements>
- Distinctive: line 744
Tests must pass: All tests must pass before merge
- Distinctive: line 745
Linter must pass: No lint errors allowed
- Distinctive: line 773
One log per stage: if a stage runs multiple times, append additional H2 sections to the same file rather than creating new files.
≈ “Common” is an estimate: at least 1 in 5 of the 40,246 AGENTS.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Clawrium
How It Works
Clawrium is a CLI tool (clawctl) that manages AI agent fleets across your local network. Point it at any machine, and it handles deployment, configuration, and lifecycle management via SSH and Ansible.
Your Machine (clawctl CLI)
│
├── Host A ──> zeroclaw instance
├── Host B ──> openclaw instance
└── Host C ──> nemoclaw instance, zeroclaw instanceWhy
- Single pane of glass: Manage all agents from one CLI instead of SSH-ing to each host
- Consistent lifecycle: Same commands for install, configure, start, stop, remove across all agent types
- Secrets management: Secure API key storage with per-agent isolation
- Fleet visibility:
clawctl agent getshows status of all agents across all hosts
Who Is This For
- Homelabbers: Run multiple AI assistants on spare hardware
- Teams: Standardize agent deployment across developer machines
- Experimenters: Try different models/agents without manual setup on each host
Quickstart
# Install
uv tool install clawrium
# Add a host (see docs/host-preparation.md for the xclm setup commands)
clawctl host create 192.168.1.100 --user xclm --alias mybox
# Install an agent
clawctl agent create <agent-name> --type openclaw --host mybox
# Configure and start
clawctl agent configure <agent-name>
clawctl agent start <agent-name>
# Run a command against the agent's native CLI on its host
clawctl agent exec <agent-name> -- --version
# Install a skill onto the agent (catalog: skills/)
clawctl skill registry get
clawctl skill registry describe clawrium/tdd # Inspect a skill before installing
clawctl agent skill add <agent-name> --from-template clawrium/tdd
clawctl agent sync <agent-name>
# Check fleet status
clawctl agent getInstallation Source of Truth
Install instructions live in docs/installation.md. This is the canonical source — when updating install steps:
- Edit
docs/installation.mdfirst. - Mirror the body verbatim to
website/docs/installation.md. Keep the Docusaurus frontmatter and the mirror-warning HTML comment at the top of that file; everything below them must matchdocs/installation.mdexactly. - The README's install section is intentionally brief (just the two
uv tool install/uvxcommands). Only update it if those commands change.
The website docs MUST follow docs/installation.md exactly. Do not edit website/docs/installation.md directly.
Host Setup Source of Truth
Host preparation instructions live in docs/host-preparation.md. This is the canonical source — when updating host-setup steps:
- Edit
docs/host-preparation.mdfirst. - Mirror the body verbatim to
website/docs/guides/host-setup.md. Keep the Docusaurus frontmatter and the mirror-warning HTML comment at the top of that file; everything below them must matchdocs/host-preparation.mdexactly (modulo Docusaurus:::note/:::warningadmonitions where the engineering docs use>blockquotes).
The website docs MUST follow docs/host-preparation.md exactly. Do not edit website/docs/guides/host-setup.md directly.
Changelog & Release Notes
Clawrium tracks every change across every release. There are two layers:
- Root
CHANGELOG.md— the working log for the current, unreleased version only. All new work is documented here as it lands, under the## [Unreleased]heading. docs/releases/<version>/— one folder per shipped release, each containing a frozenCHANGELOG.md. On every release cut theitx-releaseskill archives the root changelog into a newdocs/releases/<version>/CHANGELOG.mdand then resets the root file to an empty[Unreleased]template. The per-version folder may also hold detailed migration instructions for that release.
docs/releases/ is therefore the single place to read the full history of
what changed in any release. Do not delete entries from it.
Update Rules
Update the root CHANGELOG.md (under ## [Unreleased]) as part of the same
change that introduces the behavior — not as an afterthought:
- Every new feature gets an entry under
### Addedwith a short, plain description of what it does (one or two lines, user-facing language). - Every behavior change goes under
### Changed; every notable bug fix goes under### Fixed, referencing the issue/PR number where one exists (e.g.#555). - Every breaking change MUST be documented under
### BREAKING. State what breaks, why, and exactly what operators must do to recover (commands, config edits). If there is no automated migration, say so explicitly and give the manual steps. Breaking changes are non-negotiable to document — an undocumented breaking change is a release blocker. - Documentation-only changes go under
### Documentation.
The itx-release skill handles archiving and resetting the changelog at cut
time; contributors only ever edit the root file's [Unreleased] section.
Hermes Skills
When a Hermes agent (e.g. Maurice) is asked to work on this repository, it MUST load skills from .hermes/skills/ and treat them as available alongside its built-in skills. Each skill follows the upstream Hermes skill format documented at https://hermes-agent.nousresearch.com/docs/developer-guide/creating-skills.
All Hermes skills shipped from this repository are scoped to the clawrium namespace — the name: field in their SKILL.md frontmatter MUST be prefixed with clawrium- (hermes' only documented separator is the hyphen).
Currently available:
| Skill name | Path | Purpose |
clawrium-release-announcements | .hermes/skills/release-announcements/ | Daily release blog draft on Blog Pipeline kanban + Discord announcement |
Key Concepts
- Host: A machine in your network that runs one or more agents
- Agent: An AI assistant instance (zeroclaw, nemoclaw, or openclaw)
- Agent Type: The specific AI assistant implementation (e.g., zeroclaw, nemoclaw, openclaw)
- Agent Name: The unique identifier for an installed agent instance
- Agent Registry: Platform-defined agent types with versions, dependencies, and templates
- Skill Registry: A namespace under bundled
skills/and the user overlay at~/.config/clawrium/skills/from which skill templates can be copied onto an agent (clawrium,openclaw,hermes,zeroclaw). Catalog templates are referenced as<skill-registry>/<name>(e.g.clawrium/tdd); once copied onto an agent, the local desired state stores the bare skill name (e.g.tdd). Distinct from the Agent Registry.
Resources
- Repository: https://github.com/ric03uec/clawrium
- Project Board: https://github.com/users/ric03uec/projects/1
- Version: 26.8.1
- Changelog:
CHANGELOG.md(current unreleased) ·docs/releases/(per-release archive)
Gateway Token Lifecycle (zeroclaw)
The zeroclaw gateway authenticates clawctl agent chat sessions with a bearer token
the daemon mints via a /pair/code → /pair loopback handshake. The
daemon does not persist that bearer across systemd restarts, so the only
way for clawctl to guarantee hosts.json.gateway.auth equals the bearer the
daemon will enforce on the next request is to always re-pair on every
lifecycle op that touches the daemon.
Rules (issue #437):
clawctl agent configure,clawctl agent sync, andclawctl agent restartall mint a fresh bearer and overwritehosts.json.gateway.authatomically.- There is no idempotent-skip path. Do not add a
--no-rotateflag — branching here is the bug the original ATX Round 1 B3 code introduced. - Remote
clawctl agent chatsessions (running on a different machine than the one that ran the lifecycle op) will get a clean 401 on their next request. They must reconnect — that's the documented trade-off. - Local
clawctl agent chatreconnects transparently: on 401 it reloadshosts.jsononce, compares the bearer in memory vs disk, and rebuilds the backend with the fresh token if they differ. - A single
gateway_token_rotatedevent is emitted fromlifecycle.pywhenever the bearer is overwritten. The CLI renders it as a yellow notice duringconfigure/sync/restart.
Hermes Config Rendering (issue #622)
~/.hermes/config.yaml and ~/.hermes/.env are rendered exclusively
by src/clawrium/core/render.py:render_hermes. Both clawctl agent
configure (via lifecycle.configure_agent) and clawctl agent sync
(via lifecycle_canonical.sync_agent_canonical) call the same
renderer; the configure playbook copies the pre-rendered bytes via
ansible.builtin.copy, it does not template them server-side.
To change the on-host shape, edit the canonical templates in
src/clawrium/platform/registry/hermes/templates/ (hermes-config.canonical.yaml.j2
and hermes-env.canonical.j2) and the render_hermes plumbing in
core/render.py. There is no second template path.
Multi-provider attachments (primary + N auxiliary slots) are a
hermes-only feature. Zeroclaw, openclaw, and nemoclaw enforce
single-provider invariants at the CLI and renderer layers; their
templates do not iterate config.providers.
Native Dashboards (issues #478, #491)
Three agent types ship a native web UI today: hermes (issue #478), zeroclaw (issue #491), and openclaw. The manifest's features.web_ui block is the single gate — clawctl agent open <name> and the GUI's Open Agent UI button both consult the resolver in src/clawrium/core/web_ui.py, which returns None for any agent whose manifest does not declare features.web_ui.
Hermes (issue #478) runs the dashboard in a separate systemd unit on the agent host:
hermes-<agent_name>.service— the OpenAI-compatible API gateway (the existing unit).hermes-dashboard-<agent_name>.service— the SPA dashboard, withPartOf=hermes-<agent_name>.serviceandAlso=hermes-<agent_name>.servicein[Install]. systemd propagates stop/restart of the gateway to the dashboard automatically; we explicitlyenablethe dashboard unit on first start.
The hermes dashboard binds 127.0.0.1:<port> only (features.web_ui.bind: loopback). install.py picks the per-instance port in 45000..46999 and persists it under hosts.json.agents.<name>.config.dashboard.port.
Zeroclaw (issue #491) does not run a separate dashboard unit — the gateway daemon itself serves the SPA on the same port as config.gateway.port (features.web_ui.bind: wildcard, port persisted under gateway.port). install.py picks the per-instance gateway port in 40000..41999.
Openclaw uses the same shape as zeroclaw: the SPA is served by openclaw gateway run on the same port as config.gateway.port (features.web_ui.bind: wildcard, port persisted under gateway.port). install.py picks the per-instance gateway port in 40000..41999 via the same allocator branch as zeroclaw. Unlike zeroclaw, openclaw is intentionally NOT in _PAIRING_AGENT_TYPES (see gui/routes/fleet.py) — its WebSocket auth uses the gateway bearer token already persisted under hosts.json.agents.<name>.config.gateway.auth (flat string; the nested .token form only appears inside ~/.openclaw/openclaw.json on the agent host). The GUI's Show Connection Token button surfaces it via POST /api/fleet/agents/<name>/connection-token; the canonical lookup is _resolve_openclaw_credentials in gui/routes/agents.py, which reads the secrets store first and falls back to the legacy hosts.json field. A dedicated pairing UX for openclaw is a follow-up.
Auth boundary (hermes / zeroclaw). There is no in-process auth on the dashboard for hermes or zeroclaw: the SSH key Ansible already uses for the host is the auth boundary. Anyone with shell access to the agent host could reach the dashboard directly, so layering a token wall on top would not raise the security floor. Zeroclaw's 0.0.0.0 bind is an upstream property; this trust model still holds because reachability is gated at the network/SSH layer. Openclaw additionally requires the gateway bearer token at the WebSocket handshake — see the openclaw section above.
Tunnel reuse. The tunnel manager at src/clawrium/core/web_ui_tunnel.py is idempotent — a second invocation for a live tunnel reuses the existing local port (state at ~/.config/clawrium/tunnels/<agent_key>.json, PID + cmdline-guarded). The GUI auto-reaps tunnels idle > 30 minutes; CLI tunnels close on Ctrl-C or process exit. The remote target is always loopback regardless of bind: both BIND_ADDRESS_MAP["loopback"] and BIND_ADDRESS_MAP["wildcard"] resolve to 127.0.0.1, because the SSH local-forward terminates on the remote loopback interface — reachable for both bind modes.
No default_port in bundled manifests. Hermes, zeroclaw, and openclaw manifests all omit features.web_ui.default_port. install.py always persists a per-instance port at port_field; a manifest-wide default would silently collide on hosts running multiple agents of the same type. The resolver surfaces a missing persisted port as "no UI available" rather than inventing one. Third-party manifests may still opt into default_port — the schema accepts it.
Workspace Overlay (issue #760)
Operator-supplied files dropped under
~/.config/clawrium/agents/<type>/<name>/workspace/ are mirrored onto
the agent host at the manifest-declared destination root on every
clawctl agent sync and clawctl agent configure. The shared push
helper is src/clawrium/core/workspace_sync.py:push_workspace_phase;
both lifecycle_canonical.sync_agent_canonical and
lifecycle.configure_agent call it.
Manifest contract — per-agent manifest.yaml:
features:
workspace_overlay:
destination_root: "<absolute path under agent's home>"
excludes: # optional, relative-path strings
- <relpath> # exact-file match
- <relpath>/ # trailing slash = dir-prefix matchPer-type destinations:
| Agent | destination_root | Linux path | macOS path | Notes |
| openclaw | ~/.openclaw/workspace | /home/<name>/.openclaw/workspace | /Users/<name>/.openclaw/workspace (#770) | No excludes — workspace zone is operator-owned. macOS GA via #770. |
| zeroclaw | ~/.zeroclaw/workspace | /home/<name>/.zeroclaw/workspace | /Users/<name>/.zeroclaw/workspace (#771) | No excludes — workspace dir holds operator memory files; canonical render writes elsewhere. Every sync also rotates the gateway bearer (#437); macOS GA via #771. |
| hermes | ~/.hermes | /home/<name>/.hermes | /Users/<name>/.hermes (#772) | Renderer-output paths excluded. Shares destination root with core/render.py output, skills_apply.yaml writes, and daemon state (SQLite + WAL companions). macOS GA via #772 — the per-file workspace_excluded Jinja filter applies identically on darwin and Linux. |
Hermes excludes (manifest source of truth):
| Excluded path | Why |
config.yaml, .env | Written by core/render.py:render_hermes. U5 enforces excludes ⊇ render output keys via AST scan. |
auth.json | Operator-paired upstream auth state. |
state.db, state.db-journal, state.db-wal, state.db-shm | SQLite DB + all three WAL companion files. Overlaying any while the daemon holds the WAL open corrupts the database silently — all three companions MUST stay listed (W13 iter-2). |
sessions/ (dir) | Per-conversation transcripts. |
logs/ (dir) | Daemon log output. |
skills/clawrium/ (dir) | Reconciled by hermes/playbooks/skills_apply.yaml. U33 enforces this dir is a strict superset of the playbook's write target. |
Hermes is the only agent type whose playbook re-applies exclude
semantics inside a per-file when: clause (via the
workspace_excluded Jinja filter in
hermes/playbooks/filter_plugins/). The filter mirrors
core.workspace_sync._is_excluded exactly. Drift between the two
would either let an excluded file through (Python applies the filter
but the playbook does not) or silently drop a legitimate file (the
playbook is stricter than Python) — any change to either side MUST
land in the same commit. Openclaw and zeroclaw playbooks omit the
filter because both ship empty exclude lists; their playbook ignores
the workspace_excludes_* extravars.
CLI flags on clawctl agent sync:
--workspace-only— push the overlay alone, skip canonical render / restart / verify. Mutually exclusive with--diff. For zeroclaw the bearer rotation still runs (Phase 2, #768).--no-restart— canonical + overlay, skip restart. Bearer rotation still runs for zeroclaw (Phase 2, #768).--workspace— removed (BREAKING). Exits 2 with a hint to the two replacements above. No automated migration.
Host-write path is Ansible only — per-agent
playbooks/workspace.yaml (Linux) and playbooks/workspace_macos.yaml
(macOS; openclaw GA via #770, zeroclaw GA via #771, hermes GA via
#772 — the workspace-overlay macOS matrix is complete). The Python
side stages files into a
managed tempfile.TemporaryDirectory under
${clawrium_config}/staging/workspace/<name>/ and passes the file
list as the workspace_files extravar; ansible-runner reads the
staged copies, never the operator's original path. Symlinks are
rejected at enumeration (os.path.islink); follow: no on the
ansible.builtin.copy task is belt-and-suspenders. The Linux playbook
asserts the rendered destination starts with /home/{{ agent_name }}/;
the macOS playbook asserts /Users/{{ agent_name }}/. The OS→home-root
mapping is centralized in core.playbook_resolver.home_root_for and
consumed by core.workspace_sync._expand_destination_root; no other
module is allowed to branch on os_family. Neither variant references
ansible_user_dir (B1 iter-3: become_user does not re-run setup,
so the fact would resolve to the SSH user, not the agent user). This
ansible_user_dir ban is a project-wide invariant — any new or
refactored Ansible task that needs the agent user's home MUST use the
literal /home/{{ agent_name }} (Linux) or /Users/{{ agent_name }}
(macOS) pattern, never the fact. The macOS playbook also hardcodes
group: staff (gid 20) instead of the Linux group: "{{ agent_name }}"
convention, because macOS does not create a per-user primary group.
Secret-pattern files (*.key, *.pem, *.env, .env,
*credentials*, *secret*, *token*, *password*) get mode: '0600'
regardless of local perms. Operator-controlled strings in NDJSON
event payloads (path, remote_path, reason) are passed through
cli/output/_sanitize.py:sanitize_passthrough so bidi/zero-width
codepoints cannot spoof terminal output.
Workspace-phase failure short-circuits before restart — the daemon is
never restarted on a half-applied overlay. The failure surfaces as
CanonicalSyncError → CLI emit_error → typer.Exit(code=1).
Integration Binary Install (architectural pattern)
When an integration attaches a subprocess binary to an agent (MCP
servers, plugins, sidecars — anything the daemon execs), the binary
MUST install at sync time via a dedicated single-purpose Ansible
runbook invoked from core/lifecycle_canonical.py:sync_agent_canonical.
NOT from the agent's configure.yaml. NOT from install.yaml.
This is the operator's mental model: clawctl agent integration attach
<X> + clawctl agent sync materializes X on the host. The host
install (install.yaml) is agent-lifecycle only — it does not know
which integrations exist yet. The configure playbook
(configure.yaml) is not invoked by the modern kubectl-style CLI at
all — putting integration installs there produces a rendered config
pointing at a binary the daemon never spawns. This regression class
was closed for openclaw brave plugins in #755 and again for hermes
Slack MCP in #834.
Canonical examples
| Integration | Runbook | Sync-time entry point |
| Openclaw brave (+ any future openclaw plugin) | Direct SSH via openclaw plugins install --pin | _openclaw_install_plugins (lifecycle_canonical.py:1307) |
Hermes Slack MCP (slack-user, slack-cookie) | hermes/playbooks/install_slack_mcp.yaml (+ _macos sibling) | _hermes_install_slack_mcp (lifecycle_canonical.py, #834) |
Openclaw Slack MCP (slack-user, slack-cookie) | openclaw/playbooks/install_slack_mcp.yaml (+ _macos sibling) | _openclaw_install_slack_mcp (lifecycle_canonical.py, #835) |
Zeroclaw Slack MCP (slack-user, slack-cookie) | zeroclaw/playbooks/install_slack_mcp.yaml (Linux-only; macOS sibling deferred — follow-up #836) | _zeroclaw_install_slack_mcp (lifecycle_canonical.py, #851; darwin hosts refuse loudly with a clawctl agent integration detach hint) |
Rules (non-negotiable)
- One runbook per binary. Do not group multiple integration
installs into one playbook. A single-purpose runbook fails loudly
when one integration is broken without dragging unrelated
integrations into the failure. Naming:
install_<integration>.yaml(Linux) +install_<integration>_macos.yaml(darwin, if the integration supports macOS). - Runbook is a dumb consumer of extravars. Version pin, per-arch
asset map, per-arch SHA256 map: all top-of-file
vars:. Nowhen: ansible_os_family == "Darwin"inside install tasks (dispatcher-only OS fork invariant — see themac-test host availablememory context). One narrow exception: a task-0 dispatcher-contract guard that fails-fast when the wrong-OS sibling was routed here by mistake (e.g.,Refuse to run on non-Linux hosts) IS permitted — the check is against the opposite OS so a mis-routed run trips loudly. This guard MUST be task-0 (before the arch guard, before any install work), MUST onlyfail:(never conditionally install), and MUST have a matching mirror in the sibling runbook. If a runbook has more than onewhen: ansible_os_familyclause, or any that gates an install task, it violates the invariant. - Idempotency via checksum, not sentinel files.
get_urlwithchecksum: "sha256:{{ map[ansible_architecture] }}"short-circuits when the file exists and matches. Version bump changes URL + hash → auto-fires reinstall via mismatch. Nocreates:guard; no.integration-installed.<version>sentinel unless the install command is not itself checksum-verifying (e.g.,npm installfor openclaw plugins does use a sentinel — see #755). - Gate at the sync-time entry point, not inside the runbook.
The Python helper checks
any(i.type in <supported_types> for i in inputs.integrations)before invoking ansible-runner. Runbook itself assumes the integration IS attached — if it runs, it does the install. - Fail-fast on unsupported architecture. First task in the runbook: fail with a clear message that names the arch and links to the upstream release page. Do not silently skip. armv7l support is not universal — the plan must document which arches ship upstream at pin bump time.
- Failure short-circuits before restart. The helper raises
CanonicalSyncErroron any playbook failure.sync_agent_canonicalpropagates before_restart_unitruns. Mirrors the workspace- overlay failure contract (line 302 above). - Runbook runs BEFORE the file-write loop. So the freshly- rendered config.yaml + env AND the freshly-installed binary land in a single daemon restart. Do not restart between install and config write.
- Version pin lockstep. The Python constant referenced by
render.py(e.g.,_HERMES_MCP_SLACK_VERSION) MUST equal themcp_<integration>_versionvar at the top of EACH runbook (Linux + darwin sibling). A test intests/platform/MUST directly assert<render_constant> == vars['mcp_<integration>_version']for every runbook file. Transitive equality via a Linux ↔ darwin pin-equality assertion is not sufficient — an intermediate test being renamed / skipped would silently break the darwin lockstep.
When NOT to use this pattern
- Integrations that add ONLY environment variables + config lines,
no subprocess binary. Those are pure render-layer concerns; no
install runbook is needed. (Example:
github— just wiresGITHUB_TOKENinto.envand optionally runsgh auth login.) - Package-manager-installed system dependencies. Those belong in
install.yaml(host-lifecycle), because they are host-scoped, not integration-scoped.
Tech Stack
- CLI: Python + Typer
- Execution: ansible-runner
- Packaging: uv/uvx
- User Data:
~/.config/clawrium/
Development
Always use make commands to run tests and validate changes:
make test # Run tests (required before commits)
make lint # Check code style
make format # Format code
make test-cov # Run tests with coverageDevelopment Workflow
GitHub Issues are the single source of truth. See CONTRIBUTING.md for full workflow documentation.
Worktree Convention
For parallel issue execution, use git worktrees with this naming:
<repo-parent>/<repo-name>-issue-<number>/Example:
~/projects/clawrium/ # Main repo
~/projects/clawrium-issue-35/ # Worktree for issue 35Trigger with: /itx-execute 35 in a subtree or /itx-execute 35 --worktree
Quick Reference
New Issue → /itx-triage → /itx-plan-create → /itx-plan-scaffold → /itx-execute → /itx-verify → /itx-review-pr → MergeFleet Management
Use /clawctl to manage the Clawrium fleet from an AI assistant session. The skill embeds the full clawctl CLI reference — hosts, agents, providers, channels, integrations, skills, secrets, and memory. Always use clawctl, never clm.
| Command | Purpose |
/clawctl | Manage fleet: hosts, agents, providers, channels, integrations, skills, secrets |
Workflow Commands
| Command | Purpose |
/itx-bug-new | Create bug issue (asks for customer outcome) |
/itx-bug-update <n> <text> | Add context to an existing bug |
/itx-issue-new | Create feature issue (asks for customer outcome) |
/itx-issue-update <n> <text> | Add context to an existing issue |
/itx-note <text> | Capture an idea in NOTES.md |
/itx-triage | Review unlabeled issues |
/itx-plan-create <n> | Create high-level implementation plan |
/itx-plan-scaffold <n> | Create phased execution with entry/exit criteria |
/itx-execute <n> | Execute issue (parent or subtask) |
/itx-verify | Run tests and lint |
/itx-review-pr [n] | Review PR (MCP or manual) |
/itx-pr-status | Check open pull request status |
/itx-release [version] | Cut and publish a release |
Cross-Harness Command Source
The thirteen .claude/skills/itx-*/SKILL.md files are the canonical ITX
workflow definitions for Claude Code, OpenCode, and Pi.
- OpenCode discovers the Claude skill directory directly. Do not add
.opencode/commands/itx-*.mdor.opencode/skills/itx-*/copies; explicit OpenCode commands shadow skills and create drift. - Pi 0.84.2 or newer loads the canonical directory through
.pi/settings.jsonwhen started from the repository root. Its.pi/extensions/itx-command-aliases.jsadapter dynamically registers exact/itx-*aliases for Pi's native/skill:itx-*commands and queues delegated commands as follow-up messages when Pi is streaming. .claude/itx-config.jsonremains the shared ITX configuration file for all harnesses.itx-executeorchestrate mode and its tmux-backed standalone worktree path intentionally launch Claude Code child sessions through theclaudeCLI. They must verify that launcher before creating worktrees. Cross-harness child orchestration is a separate behavior change.
See docs/architecture/itx-cross-harness-commands.md for the implementation
plan and maintenance contracts.
Planning Artifacts Directory (.itx/)
The .itx/ directory stores implementation plans and execution documentation for each issue. These are NOT ephemeral files - they document how an issue was planned and executed.
.itx/
└── <issue-number>/
├── 00_PLAN.md # High-level implementation plan (from /itx-plan-create)
└── 01_SCAFFOLD.md # Phased execution plan (from /itx-plan-scaffold)IMPORTANT: Always commit the .itx/ directory with your changes. These files serve as:
- Historical record of implementation decisions
- Context for future maintenance
- Documentation of the execution approach
When completing work on an issue, ensure .itx/<issue-number>/ is included in your commit.
Task-Based Execution
The /itx-execute skill uses a structured task checklist approach to prevent getting lost during execution:
Planning Phase (Mandatory):
- Read implementation plan from issue
- Create an implementation task for each phase or step using the harness's task tracker
- Create verification tasks (tests, lint, review if MCP enabled)
- Set dependencies between tasks if needed
- Review task list to confirm structure
Execution Phase:
- Get the next unblocked pending task
- Mark the task
in_progress - Execute the task requirements
- Mark task
completed - Inspect the task list for remaining work
- Repeat until all tasks done
Example Task List:
[pending] Implement: Update CLI help text
Requirements: Update all help text in src/clawrium/cli/agent.py
[pending] Run test suite
Requirements: Execute make test and ensure all tests passRecovery Mechanism: If execution feels unclear or you lose orientation:
- Inspect the task list to see current state
- Check which task is
in_progress - Review that task's description
- Complete current task before starting next
Review
Review requirements depend on whether automated review is enabled in
.claude/itx-config.json and an ATX transport is available.
Check Review Mode
ITX_CONFIG="$(git rev-parse --show-toplevel)/.claude/itx-config.json"
if [ -f "$ITX_CONFIG" ]; then
REVIEW_ENABLED=$(jq -r '.mcp.review_enabled // false' "$ITX_CONFIG")
else
REVIEW_ENABLED="false"
fiIf Automated Review Enabled (ATX)
<atx-review-requirements>
When `mcp.review_enabled` is `true`, all code changes MUST attempt automated
review before merging. Try an ATX MCP request-review tool available in the
current harness first. If it is unavailable, fails, or times out, try the
stateless `atx review request` CLI and require `atx server status` to report
`Running: true`. If the CLI is unavailable, stopped, fails, or times out, follow
the manual review requirements below and document each failed or unavailable
transport.Iteration Requirements
- Attempt review using the MCP → CLI → manual fallback sequence above
- Fix ALL blocking issues (B1, B2, etc.)
- Iterate until: Rating > 3/5 AND no blocking issues remain
- Document each review iteration in commit message and PR body
When to Request Review
- Before creating a commit with code changes
- After fixing issues from previous review
- Before marking PR as ready for merge
</atx-review-requirements>
<commit-format-atx>
### Commit Message Format (ATX)Include ATX review summary after the commit body:
feat(component): short description
Detailed explanation of changes.
Closes #XX
ATX Review Summary
Review 1: Rating 2/5 | Cost: $0.12 | Time: 45s | Agents: leader, cli-ux
Blocking issues:
| # | Status | Issue |
|---|--------|-------|
| B1 | Fixed | Description of issue and fix |
| B2 | Out-of-scope | Pre-existing issue, tracked in #YY |
Warnings:
| # | Status | Warning |
|---|--------|---------|
| W1 | Fixed | Description |
| W2 | Acknowledged | Will address in follow-up |
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: @atx-ci <269048218+atx-ci@users.noreply.github.com></commit-format-atx><pr-format-atx>
### PR Body Format (ATX)Include detailed ATX review after Summary and Testing sections:
## ATX Review Summary
**Final Review: Rating 4/5**
**Total Cost: $0.20 | Total Time: 1m 17s**
| Review | Rating | Blocking Issues | Status | Cost | Time | Agents |
|--------|--------|-----------------|--------|------|------|--------|
| 1 | 2/5 | B1, B2, B3 | All fixed | $0.12 | 45s | leader, cli-ux, test-coverage |
| 2 | 4/5 | None | Ready | $0.08 | 32s | leader, cli-ux |
> **Note**: ATX does not expose model information per agent.
<details>
<summary>Review 1 Details (Rating 2/5)</summary>
**Blocking Issues:**
| # | File | Issue | Resolution |
|---|------|-------|------------|
| B1 | `module.py:42` | SQL injection risk | Fixed - parameterized query |
| B2 | `test_module.py` | Missing edge case test | Fixed - added test |
**Warnings:**
| # | File | Warning | Action |
|---|------|---------|--------|
| W1 | `module.py:15` | Consider adding timeout | Added 30s timeout |
| W2 | `config.py` | Magic number | Deferred to #XX |
**Suggestions:**
| # | Suggestion | Action |
|---|------------|--------|
| S1 | Add docstring | Added |
| S2 | Consider caching | Deferred |
</details>
Co-Authored-By: @atx-ci <269048218+atx-ci@users.noreply.github.com>See PRs #19, #21, and #205 for real examples of this format.
</pr-format-atx>
<enforcement-atx>
### Enforcement Rules (ATX Result Available)- No merge without review: PRs lacking ATX review section will be rejected
- No unresolved blockers: All
B#issues must beFixedorOut-of-scopewith justification - Rating threshold: Final review must be > 3/5
- Attribution required:
Co-Authored-By: @atx-cimust appear in both commit and PR - Iteration tracking: Each review round must be documented with its rating
</enforcement-atx>
If Automated Review Disabled or Unavailable (Manual Review)
<manual-review-requirements>
When `mcp.review_enabled` is `false`, not configured, or both ATX transports
are exhausted, use manual review with self-attestation. If review was enabled,
record each unavailable, stopped, failed, or timed-out transport in the PR.Requirements
- Run all tests and ensure they pass
- Run linter and fix any issues
- Self-review changes for quality and security
- Document testing approach in PR
</manual-review-requirements>
<commit-format-manual>
### Commit Message Format (Manual)feat(component): short description
Detailed explanation of changes.
Closes #XX
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com></commit-format-manual><pr-format-manual>
### PR Body Format (Manual)## Summary
<1-3 bullet points describing what this PR does>
## Testing
### Test Results
- [ ] All existing tests pass (`make test`)
- [ ] Linter passes (`make lint`)
- [ ] New tests added for new functionality
### Test Coverage
- Files changed: <list files>
- New tests: <list new test files or "N/A">
- Coverage impact: <increased/maintained/decreased>
### Manual Testing
<Describe any manual testing performed>
### Security Checklist
- [ ] No hardcoded secrets or credentials
- [ ] Input validation for user-provided data
- [ ] No SQL injection, XSS, or command injection risks
- [ ] Dependencies are from trusted sources
## Reviewer Notes
<Any additional context for reviewers>
---
🤖 Generated with [Claude Code](https://claude.com/claude-code)</pr-format-manual><enforcement-manual>
### Enforcement Rules (Manual)- Tests must pass: All tests must pass before merge
- Linter must pass: No lint errors allowed
- Testing documented: PR must include Testing section
- Security reviewed: Security checklist must be completed
</enforcement-manual>
Prompt Logging Standard
Skills that produce artifacts as part of an issue (plans, scaffolds, demos, etc.) append a prompt log to .itx/<issue-number>/<NN>_<STAGE>.md. The log captures the prompt, model, and skill name so the path from human input → artifact is reproducible.
Format (markdown, append-only):
## <Stage Name>
**Stage**: <stage-slug> # e.g. demo, plan, scaffold
**Skill**: /<skill-name> # e.g. /create-vhs
**Timestamp**: <ISO-8601 UTC> # e.g. 2026-05-19T15:30:00Z
**Model**: <model-id> # e.g. claude-opus-4-7
```prompt
<verbatim user prompt that triggered the skill>Output: <one-line description of what was produced>
**File numbering**: `00_PLAN.md`, `01_SCAFFOLD.md`, `02_EXECUTE.md`, `03_DEMO.md`, … Match the order in which stages run for the issue.
**One log per stage**: if a stage runs multiple times, append additional H2 sections to the same file rather than creating new files.Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

