rob thijssen 581e6ae738 feat(discover): add the discovery lane and the autonomy boundary
The docs described a reactive executor: every entry point was a human label, and
the only issue tireless ever created was a plan child. Nothing surveyed a repo or
proposed work, which is the half that makes this continuous rather than
on-demand.

Add JobKind::Discover, routed always to Claude Code (proposing work is the
highest-judgement, lowest-volume task), the tireless/discover and
tireless/proposed labels, and prompt/discover.cc.md as a fourth member of the
versioned prompt set. The contract version does not move: the plan structure is
unchanged, and bumping for less than a shape change trains people to bump
reflexively.

With discovery comes the question of where the loop closes, which was previously
unspecified — routing inferred that plan children are auto-admitted, but nothing
said so. State it as a rule and enforce it:

  Admission is inherited, never invented.

may_opt_in() lets tireless label a plan child, because a human admitted its
parent, and refuses to label a discovered issue, because nothing has been
admitted. It is a function rather than a config flag on purpose: the failure it
prevents is unbounded, not merely wrong, so relaxing it should require review.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013TxK1CWPkFXqdcXMJ4hVe6
2026-08-07 15:36:35 +03:00

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-agent crate 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=false on tireless-runner. Both agents are Node programs and V8's JIT needs write-then-execute pages. The API and poller keep the setting. (§8)
Description
Autonomous issue-to-PR development driver for Claude Code and OpenCode
Readme 618 KiB
Languages
Rust 83.5%
Shell 8.6%
TypeScript 7%
JavaScript 0.5%
CSS 0.3%
Other 0.1%