Autonomous issue-to-PR driver for Claude Code and OpenCode, structured per lair/architecture generic.md. Workspace: entities/core/data/agent library crates plus api, worker and cli binaries. Two pieces of real logic land with tests — lane routing (cc for judgement, oc for specification) and the limit governor. Constraints encoded as code rather than comments: - agents are spawned as vendor binaries; tireless never calls a provider API - ANTHROPIC_API_KEY is never set by tireless, only passed through - assert_not_anthropic refuses to start an OpenCode lane pointed at Anthropic - every run passes the governor; provider rate-limit signals win over our own accounting Deployment assets target bob.hanzalova.internal:23296 (registered in port-allocations.md), fronted by hanzalova at tireless.internal. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DHhHtohxcdk1PL3tfnYJdH
94 lines
3.0 KiB
Cheetah
94 lines
3.0 KiB
Cheetah
# tireless configuration.
|
|
#
|
|
# Rendered from this template by the deploy workflow, substituting {{PLACEHOLDER}}
|
|
# values from Gitea repo secrets. The rendered file is never committed.
|
|
#
|
|
# Secrets that belong in the *environment* rather than here (tokens, and the
|
|
# optional ANTHROPIC_API_KEY) live in /etc/tireless/tireless.env — see
|
|
# asset/systemd/tireless-runner.service.
|
|
|
|
[api]
|
|
# Loopback: nginx on the same host fronts it. Registered in
|
|
# architecture/port-allocations.md.
|
|
bind = "127.0.0.1:23296"
|
|
|
|
[database]
|
|
# mTLS, passwordless (architecture/generic.md §5). The host cert identifies the
|
|
# client; pg_ident.conf maps its CN to the role. Certs rotate every 24h.
|
|
host = "magrathea.kosherinata.internal"
|
|
port = 5432
|
|
database = "tireless"
|
|
user = "tireless_rw"
|
|
client_cert = "/etc/pki/tls/misc/{{DEPLOY_HOST_FQDN}}.pem"
|
|
client_key = "/etc/pki/tls/private/{{DEPLOY_HOST_FQDN}}.pem"
|
|
root_cert = "/etc/pki/ca-trust/source/anchors/root-internal.pem"
|
|
|
|
[forge.gitea]
|
|
base_url = "https://git.lair.cafe"
|
|
# Token for the dedicated `tireless` bot account, from the environment.
|
|
token_env = "GITEA_TOKEN"
|
|
|
|
[forge.github]
|
|
enabled = false
|
|
token_env = "GITHUB_TOKEN"
|
|
|
|
[poll]
|
|
# Floor on per-repo poll interval, in seconds. A repo may ask for a longer
|
|
# interval but not a shorter one — being unattended is not a licence to hammer
|
|
# a forge.
|
|
min_interval_seconds = 120
|
|
default_interval_seconds = 300
|
|
# Jitter added to each repo's schedule so N repos do not all fire together.
|
|
jitter_seconds = 30
|
|
|
|
[labels]
|
|
opt_in = "tireless"
|
|
mode_plan = "tireless/plan"
|
|
mode_implement = "tireless/implement"
|
|
force_cc = "tireless/agent:cc"
|
|
force_oc = "tireless/agent:oc"
|
|
state_claimed = "tireless/claimed"
|
|
state_blocked = "tireless/blocked"
|
|
state_done = "tireless/done"
|
|
|
|
[work]
|
|
# Per-repo bare mirrors and per-job clones live here.
|
|
root = "/var/lib/tireless"
|
|
# Keep a failed job's clone for this long so it can be inspected. Successful
|
|
# jobs are cleaned immediately.
|
|
failed_retention_hours = 72
|
|
|
|
[lane.cc]
|
|
# Claude Code. A subscription is one person's allowance: one session at a time.
|
|
max_concurrent = 1
|
|
max_runs_per_window = 12
|
|
window_hours = 5
|
|
# Wall-clock ceiling for a single run.
|
|
timeout_seconds = 3600
|
|
model = "opus"
|
|
# Consecutive failures before the lane stops asking for work.
|
|
failure_threshold = 3
|
|
|
|
[lane.oc]
|
|
# OpenCode against helexa cortex. The constraint here is the GPU fleet, not a
|
|
# bill, so the ceilings are far higher.
|
|
#
|
|
# This lane must never be pointed at Anthropic: OpenCode is a third-party
|
|
# harness, and driving an Anthropic subscription through one is the pattern
|
|
# Anthropic blocks. tireless refuses to start if `provider` or `base_url` looks
|
|
# Anthropic-shaped. See doc/plan/design.md §3.
|
|
provider = "lair-helexa"
|
|
model = "Qwen/Qwen3.6-27B"
|
|
base_url = "http://hanzalova.internal:31313/v1"
|
|
max_concurrent = 2
|
|
max_runs_per_window = 240
|
|
window_hours = 5
|
|
timeout_seconds = 2700
|
|
failure_threshold = 3
|
|
|
|
[quiet]
|
|
# Optional global quiet window in local time. No poll runs and no job is claimed
|
|
# between these times. Leave unset to run around the clock.
|
|
# from = "23:00"
|
|
# until = "07:00"
|