Files
tireless/CLAUDE.md
rob thijssen 6545d1980b
All checks were successful
deploy / deploy (push) Successful in 5m35s
feat(data): add the initial schema and migrations
Closes #2. Three tables mirroring tireless-entities, plus the sqlx
compile-time-checking decision the rest of stage 1 inherits.

RUNTIME QUERIES, NOT `query!`. No .sqlx metadata, no DATABASE_URL to build.
lairball, the other house project on this cluster, does the same. The deciding
argument is specific to tireless: compile-time checking makes a database a build
dependency, and this crate is meant to be modified by a 27B model working
unattended. A build that fails without a database it cannot provision is one
that model cannot fix, and its documented failure mode is to improvise. The cost
is named in store.rs — a malformed query is caught by a test, not by cargo.

Enums are text with a check constraint, not Postgres enum types: adding a
JobKind variant should be a migration, not an ALTER TYPE holding a lock. Unit
tests assert every serde variant appears in the schema, so adding a variant
without a migration fails the build rather than the first job of that kind.

That surfaced a spelling that would have been permanent: `rename_all =
"snake_case"` turns Forge::GitHub into `git_hub`, across the database, the JSON
API and the generated TypeScript. Renamed to `github` now, while nothing is
persisted and GitHub support is still disabled.

The live-issue index is PARTIAL, and both directions of getting it wrong are
silent. A plain unique constraint on (forge, owner, repo, number) would forbid
re-running a terminal job, and would forbid the discovery lane outright, since
discovery recurs against one tracking issue on a cooldown. Partial on the
non-terminal states gives at most one live job per issue and unlimited history.

The claim index orders by created_at alone and leaves kind as a filter, because
the claim takes LIMIT 1 and can stop at the first match. Leading with kind sorts
every pending row on every claim: measured at 720 buffers versus 4, and the gap
grows with the backlog rather than staying fixed. INCLUDE (kind) was measured
too and dropped — FOR UPDATE visits the heap regardless.

Verified against Postgres 18, the same major as the house cluster: migrations
apply to an empty database and are a no-op on the second run; all eight
constraints reject what they should and admit what they should; and two
concurrent claimers of one pending job produce exactly one winner, with the
loser skipping rather than blocking.

Those live tests are #[ignore]d, not skipped on a missing variable, so a green
`cargo test` never implies the schema was exercised. CLAUDE.md says how to run
them and that an applied migration must never be edited.

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

115 lines
5.3 KiB
Markdown

# tireless — agent instructions
Read [`doc/plan/design.md`](doc/plan/design.md) before making changes. It carries
the constraints, the staged plan, and the reasoning behind decisions that look
arbitrary in isolation.
House conventions live in [`~/git/architecture`](https://git.lair.cafe/lair/architecture)
(`generic.md` is the baseline). This project's deliberate deviations are listed
at the bottom of `readme.md`.
**tireless works on tireless.** This repo is its own first subject, so changes
here are both the product and the test of it. If you are an agent working from a
tireless-authored issue, design.md §10 says what that implies — in particular,
that some areas of this repo are constraint-bearing and are routed deliberately.
## Invariants — do not "clean these up"
These are terms-of-service and safety constraints expressed as code. Each has
tests. If one seems redundant, read design.md §3 before touching it.
1. **Never construct a request to a model provider.** Agents are spawned as the
vendor's own binary and authenticate themselves. Adding an HTTP client that
talks to `api.anthropic.com` would break the arrangement that lets a
subscription back this app.
2. **Never read or forward agent credentials.** `has_credentials()` stats
`~/.claude.json` and nothing more. Do not parse it, copy it, or pass its
contents anywhere.
3. **Never set `ANTHROPIC_API_KEY`.** It reaches Claude Code only if an operator
put it in the unit environment. Its presence selects pay-as-you-go; its
absence selects the subscription. That choice is the operator's.
4. **Never point the OpenCode lane at Anthropic.** `assert_not_anthropic` is
checked at startup against both provider id and base URL. Local gateways that
merely serve an Anthropic-compatible surface (helexa cortex) are fine and are
tested for.
5. **Never let a lane run unbounded.** Every agent invocation passes through
`tireless_core::budget::Governor`. Provider rate-limit signals are checked
first and are authoritative.
6. **Postgres is the authority on claims, not forge labels.** Labels are a
best-effort mirror. Do not make a decision by reading a label that could be
made by reading the database.
7. **Never use `--system-prompt` for Claude Code; append instead.** Replacing
Claude Code's default discards the tool-use scaffolding that makes it a
coding agent. `SYSTEM_PROMPT_FLAG` is `--append-system-prompt` for this
reason.
8. **The four prompts in `prompt/` are one set — edit them together.**
`plan.cc.md` emits the structure `implement.oc.md` consumes and
`tireless_core::plan::validate` enforces. Changing one alone breaks the
handoff silently, as a bad pull request rather than an error. Bump
`contract-version:` in all four plus `SYSTEM_PROMPT_CONTRACT_VERSION`
together; `PromptSet::load` refuses a mismatched set.
9. **A plan is validated, not trusted.** Never enqueue implementation work from
a plan that has not passed `plan::validate`. The `Acceptance` (runnable
stopping condition) and `Out of scope` (boundary) requirements exist because
a small model needs them; do not relax them because a plan looks fine to a
human reader.
10. **Admission is inherited, never invented.** tireless may apply the opt-in
label only to an issue descended from one a human opted in.
`may_opt_in(JobKind::Discover)` is `false` and must stay false: it is the
only thing standing between "proposes work" and "generates its own work
indefinitely". See design.md §2.5. This is not a policy to relax once the
system is trusted — the failure it prevents is unbounded, not merely wrong.
11. **Every constraint above is reachable from a binary's startup path.** A
guard that exists as a tested function nobody calls is not a guard. If you
add one, wire it into `Config::validate` or `tireless_agent::preflight::run`
so it cannot be bypassed by a caller that forgot.
## Quality gate
Before considering a change complete:
```sh
cargo fmt --all
cargo clippy --all-targets --all-features -- -D warnings
cargo test --workspace
cd dashboard && npm run lint && npm run build
```
This exact block is what a plan targeting this repo should use as its runnable
`Acceptance` (design.md §10.3).
**Touching `crates/tireless-data/migrations/` or any SQL also means running the
database tests**, which the gate above deliberately skips — CI has no Postgres,
so they are `#[ignore]`d rather than silently passing:
```sh
podman run -d --name pg -e POSTGRES_PASSWORD=test -e POSTGRES_DB=tireless_test \
-p 55432:5432 docker.io/library/postgres:18-alpine
export TIRELESS_TEST_DATABASE_URL=postgres://postgres:test@127.0.0.1:55432/tireless_test
cargo test -p tireless-data -- --ignored
```
Queries are checked at runtime, not compile time — there is no `.sqlx` offline
metadata and no `DATABASE_URL` needed to build. `store.rs` says why. The
consequence is that these tests are the only thing standing between a malformed
query and production.
**Never edit an applied migration.** sqlx records a checksum per version, so an
edit makes every deployed database refuse to start. Add a new numbered file.
## Commits
Conventional Commits (`type(scope): subject`), imperative, under ~70 chars.
Commit autonomously when the work is a coherent, complete unit; hold off when
follow-ups on the same topic are likely. See `generic.md` §12.