The invariants in CLAUDE.md were tested functions nobody called. There was no config type at all: --config was accepted and ignored by all three binaries, figment was an unused dependency, and the 124-line config.toml.tmpl was aspirational. assert_not_anthropic could not run at startup because nothing read the provider it checks. Add tireless_core::config, validated on construction so a Config in hand is a checked one, and tireless_agent::preflight, called by the worker at startup and by `tireless preflight` on demand — the same function, so the two cannot disagree. Move the Anthropic guard to tireless_core::policy where validate() can reach it; tireless_agent::opencode re-exports it so the documented path resolves. Also make /v1/ready honest. It returned "ok" unconditionally while its own doc comment promised dependency checks, so the deploy probe greened on a process that could do nothing. It now reports per-dependency state, with unwired ones saying not_implemented rather than ok. Tests parse the shipped template rather than a fixture, so template and code cannot drift apart silently. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013TxK1CWPkFXqdcXMJ4hVe6
tireless
Watches labelled issues on Gitea (and GitHub, for legacy repos), claims them, and either decomposes them into an epic with child issues or implements them and opens a pull request. A human reviews everything; nothing merges itself.
Two coding agents do the work, each spawned as the vendor's own binary:
- Claude Code — planning, and implementation of issues that need interpretation. Uses the operator's Claude subscription by default, or pay-as-you-go if an API key is supplied.
- OpenCode — implementation of issues that a tireless plan already specified, against the self-hosted helexa fleet. Never Anthropic; enforced at startup.
The rule of thumb: Claude Code gets judgement, OpenCode gets specification.
Full design, constraints and the staged implementation plan:
doc/plan/design.md.
Status
Stage 0 (foundations) is scaffolded. Ingestion, claiming and the agent lanes are not built yet — see §7 of the design document for what lands when.
Build
cargo test --workspace
cargo clippy --all-targets --all-features -- -D warnings
cargo fmt --all
cd dashboard && npm ci && npm run build
Run locally
cargo run -p tireless-api -- --config ./config.toml
cargo run -p tireless-worker -- --config ./config.toml poll
cargo run -p tireless-worker -- --config ./config.toml run
cd dashboard && npm run dev # proxies /v1 to 127.0.0.1:23296
tireless preflight verifies configuration and credentials without starting a
service: it reports which billing mode a Claude Code run would use and asserts
the OpenCode lane is not pointed at Anthropic.
Deploy
CI-driven via Gitea Actions on merge to main
(architecture/deployment-gitea-actions.md). One-time host provisioning —
including the interactive Claude Code login and the Gitea bot account — is
script/infra-setup.sh.
| Host | bob.hanzalova.internal |
| API port | 23296 (registered in architecture/port-allocations.md) |
| Dashboard | https://tireless.internal (mesh only) |
| Database | magrathea.kosherinata.internal:5432, mTLS |
Conventions
Follows lair/architecture;
generic.md is the baseline. Two deliberate deviations:
tireless-agentcrate beyond the standard entities/core/data split. Process orchestration is not data access, and it is shared by the runner and the CLI. (§1)MemoryDenyWriteExecute=falseontireless-runner. Both agents are Node programs and V8's JIT needs write-then-execute pages. The API and poller keep the setting. (§8)