Passthrough is verified and pinned by regression tests that assert on what cortex forwarded upstream, so tireless can rely on it. The design no longer carries it as an open dependency. Two residual behaviours replace it, neither a passthrough defect: Multiple system messages are forwarded unmerged and in order, last one wins. OpenCode sends its own preamble alongside an agent's configured prompt, so the stage 5 question becomes "did implement.oc.md arrive last", not "did it arrive". That failure is silent -- an agent that reads as a generic coding assistant and ignores Out of scope -- and would present as a prompt-quality problem rather than a plumbing one, so stage 5 now asserts on the upstream request. helexa#223: /no_think is ignored on /v1/responses, where a thinking model can spend its whole output budget on reasoning and return "" with status incomplete. Added RunOutcome::OutputBudgetExhausted so that is retryable but blameless -- a mis-sized ceiling must not trip a circuit breaker on a lane with nothing wrong with it. Config pins the chat/completions surface. The qwen3_next gap is recorded as a gap, not a hole: the system slot is not arch-branched, so there is no family-specific path to fail. Fleet now offers Qwen3-Coder-Next, a better fit for executing a written spec, but cold and feasible only on beast where the pinned 27B lives. Recorded as an operator decision per generic.md §14 rather than taken here. Config also notes why tireless pins a model name and never a helexa/* capability alias. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DHhHtohxcdk1PL3tfnYJdH
30 KiB
tireless — design and staged implementation plan
Status: planning. Nothing in stages 1+ is built yet.
Conventions: ~/git/architecture —
generic.md is the baseline; deviations are flagged inline below and in readme.md.
1. What tireless is
A daemon that watches labelled issues on Gitea (and, for legacy repos, GitHub), claims them, and either plans them (decomposing an issue into an epic and child issues) or implements them (producing a branch and a pull request). A human reviews the output. Nothing merges itself.
The point is to move the operator's attention up a level: from writing implementation plans and implementations, to identifying what should be worked on and reviewing what came back.
What tireless is not
- Not a kanban board. vibe-kanban is the reference implementation for driving agents; its task/project/board model is deliberately absent here. The forge's issues are the work list.
- Not a merge robot. It opens PRs. Review and merge stay human.
- Not a model provider client. It never speaks to Anthropic or any inference API directly. This is load-bearing — see §3.
- Not a multi-tenant service. One operator, one subscription, one fleet. Sharing it with others would breach the Anthropic consumer terms (§3.4).
2. Operating model
2.1 The loop
poll ──▶ enqueue ──▶ claim ──▶ prepare clone ──▶ run agent ──▶ deliver ──▶ report
▲ │
└──────────────────────────── reconcile ◀──────────────────────────────────┘
Two systemd units, one binary:
| Unit | Role | Spends tokens? |
|---|---|---|
tireless-poller |
discovers opted-in issues, enqueues, mirrors labels | no |
tireless-runner |
claims jobs, prepares clones, drives agents, opens PRs | yes |
Splitting them means the discovery loop can run continuously while the
token-spending half is paused, throttled, or restarted independently. During an
incident the useful action is almost always "stop the runner, leave the poller
running" — which is a systemctl stop rather than a config change.
2.2 The label protocol
Labels are the human interface. An operator opts an issue in by labelling it; tireless reports back the same way.
| Label | Written by | Meaning |
|---|---|---|
tireless |
human | Opt-in. Without it tireless ignores the issue entirely, whatever else is present. |
tireless/plan |
human | Decompose into an epic and child issues. |
tireless/implement |
human | Implement and open a PR. |
tireless/agent:cc |
human | Force the Claude Code lane. |
tireless/agent:oc |
human | Force the OpenCode lane. |
tireless/claimed |
tireless | A job holds this issue. |
tireless/blocked |
tireless | Needs a human; tireless has stopped. |
tireless/done |
tireless | Delivered — children created, or PR opened. |
The opt-in label is separate from the mode labels on purpose. Removing one label
(tireless) disables the issue without destroying the operator's expressed
intent about how it should be handled, and a single unlabelled repo full of
tireless/implement leftovers cannot accidentally activate.
Labels are not the source of truth. They are a best-effort mirror of state held in Postgres, reconciled on every poll. Two reasons: a label edit is not atomic, so it cannot safely express a claim; and a forge outage must not lose job state.
2.3 Routing: which agent gets the work
Implemented in tireless-core::routing, with tests.
| Job | Lane | Why |
|---|---|---|
Plan |
Claude Code (Opus) | Decomposition is the high-judgement, low-volume task. Getting a plan wrong is expensive downstream; getting it right is worth the strong model. |
Implement, descended from a tireless plan |
OpenCode (helexa) | A tireless plan is a spec. Executing a written spec is what a local model on the GPU fleet does well, at no subscription cost. |
Implement, human-written issue |
Claude Code | No plan behind it means the issue needs interpretation before it needs code. |
any, with tireless/agent:* |
as labelled | An explicit operator override always wins. |
The general rule: Claude Code gets judgement, OpenCode gets specification.
This also produces a pleasing economic shape. The subscription is spent on the scarce thing (planning, and interpreting under-specified work), while the bulk of mechanical implementation runs on hardware already sitting in the office.
2.4 The plan contract
The handoff between the lanes is the load-bearing interface in this design: an Opus-authored plan must be specific enough for a 27B model to execute alone. That is not left to chance in either direction.
Both ends are shaped by system prompts. prompt/plan.cc.md tells Claude Code
it is writing for a literal, competent, absent reader that cannot ask questions
and will fill any gap with an invention. prompt/implement.oc.md tells OpenCode
to execute the specification faithfully, stop when acceptance passes, and report
rather than improvise. They are two halves of one contract and are versioned
together — see prompt/readme.md.
The surfaces are not symmetric, and the difference matters:
| Lane | Mechanism | Ownership |
|---|---|---|
| Claude Code | --append-system-prompt |
Anthropic owns the base prompt; tireless appends |
| OpenCode | AgentConfig.prompt |
tireless owns the whole prompt |
Claude Code also offers --system-prompt, which replaces its default outright.
tireless does not use it: the default carries the tool-use and repository
navigation scaffolding that makes Claude Code a coding agent, and discarding it
yields a less capable agent rather than a more obedient one.
The middle is validated, not trusted. A plan is parsed into
tireless_entities::ChildSpec and checked by tireless_core::plan::validate
before any implementation job is enqueued. Every child must carry five sections,
two of which exist specifically because a small model needs them and a human
reader does not:
- Acceptance must include at least one runnable command. Without a stopping condition a literal implementer does not know when it is finished, and keeps going — usually by rewriting adjacent code it was not asked to touch.
- Out of scope must be non-empty. Without a declared boundary nothing stops a capable model expanding the work.
Plans are also checked for dangling and cyclic dependencies, and
implementation_order derives the order in which children may be started.
This makes the risk cheap to discover. A plan that fails validation costs one
comment and a tireless/blocked label, seconds after the planning run. The same
plan unvalidated costs an OpenCode run, a branch, and an operator's review
attention before anyone notices the spec was unusable.
Dependency: helexa faithful passthrough — settled. The OpenCode system prompt reaches the model via OpenCode → cortex → neuron, relying on helexa's guarantee of no injection, no rewriting, no defaults (helexa/helexa#179, closed 2026-08-02).
Verified live on all three surfaces, streaming and non-streaming, with a negative control proving nothing is injected when no prompt is sent, and pinned by regression tests that assert on what cortex forwarded upstream rather than on the reply. tireless can rely on it.
Two residual behaviours shape stage 5, and neither is a passthrough defect:
- Several system messages are forwarded unmerged, in order, and the last one
wins. OpenCode sends its own preamble alongside an agent's configured
prompt, so ordering decides whetherimplement.oc.mdgoverns. The stage 5 question is therefore not did the prompt arrive but did it arrive last. The failure is silent: an agent that behaves like a generic coding assistant, ignoringOut of scope, with no error anywhere. - Thinking models on
/v1/responsescan return an empty string (helexa#223, open)./no_thinkis honoured on chat/completions but not on Responses, where a smallmax_output_tokensmay be spent entirely on the reasoning block, yielding""withstatus: "incomplete". tireless must treat that as a distinct outcome rather than an empty success, or it will burn a retry on a budget artifact.
Model choice for this lane is an operator decision (generic.md §14 —
placement of load on shared infrastructure). The fleet now offers, via cortex:
| Model | Alias | State | System prompt |
|---|---|---|---|
Qwen/Qwen3.6-27B |
helexa/large |
warm, pinned on beast | verified |
Qwen/Qwen3-Coder-Next |
— | cold; feasible only on beast | shared code path, not live-tested |
Qwen/Qwen3-Next-80B-A3B-Thinking |
— | cold; feasible only on beast | shared code path, not live-tested |
A coder-specialised model is the obvious fit for executing a written spec, but
adopting one means displacing the pinned 27B that currently serves
helexa/large. That trade is not tireless's to make; it is recorded here and
revisited with stage 5 evidence.
tireless pins a model name, not an alias, for the same reason it pins agent package versions: an alias that silently starts resolving to a different model would change implementation behaviour between one job and the next with no deploy and no signal.
3. Constraints
These are the reasons the architecture looks the way it does. Each is encoded in code or config, not merely written down here — comments rot, failing assertions do not.
3.1 Both agents are spawned as vendor binaries
tireless spawns @anthropic-ai/claude-code and opencode-ai as subprocesses and
lets each authenticate itself. It never constructs a request to a model provider.
This is what makes subscription-backed operation legitimate. Anthropic's
enforced line is credential extraction — taking the subscription OAuth token and
using it in your own API client, which is what got OpenClaw, OpenCode, Roo Code
and Goose blocked in January 2026 ("This credential is only authorized for use with Claude Code."). Running the first-party binary is the permitted side of
that line.
Two invariants follow, and neither may be optimised away:
- tireless never reads or forwards agent credentials. It stats
~/.claude.jsonto check a login exists (tireless-agent::claude::has_credentials) and does nothing else with it. - tireless never sets
ANTHROPIC_API_KEY. The variable reaches Claude Code only if an operator placed it in the unit environment.
3.2 Automated use of a subscription is explicitly permitted
Anthropic's consumer terms §3 prohibit automated access "Except when you are accessing our Services via an Anthropic API Key or where we otherwise explicitly permit it". The help centre article Use the Claude Agent SDK with your Claude plan is that explicit permission, naming three covered categories:
- Claude Agent SDK usage in your own projects
claude -p(non-interactive mode)- third-party applications authenticating through your subscription
The third is tireless. Its current banner: "We're pausing the changes to Claude
Agent SDK usage described below. For now, nothing has changed: Claude Agent SDK,
claude -p, and third-party app usage still draw from your subscription's usage
limits."
This is the constraint most likely to change. The June 15 2026 split into a
separate "Agent SDK credit" pool ($20 Pro / $100 Max 5x / $200 Max 20x) was
paused, not cancelled, with advance notice promised. tireless therefore treats
the auth mode as a config switch, not an architecture: dropping
ANTHROPIC_API_KEY into /etc/tireless/tireless.env moves the whole Claude Code
lane to pay-as-you-go with no code change. Billing mode is recorded per run
(AgentRun::billing, read from Claude Code's own apiKeySource) so the
dashboard reports what actually happened rather than what was intended.
3.3 The OpenCode lane is never Anthropic
OpenCode is a third-party harness with its own provider clients. Driving an Anthropic subscription through it is precisely the blocked pattern. Anthropic work goes through the Claude Code lane; OpenCode goes to helexa cortex.
Encoded as a startup assertion — tireless_agent::opencode::assert_not_anthropic
— checked against both the provider id and the base URL host, with tests. A
config edit that points the lane at Anthropic fails the service, loudly, at
start. Note that cortex presents an Anthropic-compatible API surface; that is
fine and explicitly tested for, because it is local inference with no
subscription involved.
3.4 Single operator
The consumer terms §2 forbid sharing account credentials or making the account
available to others. tireless runs as one operator's agent against their own
repos. If a second person's request could trigger a run on this subscription,
that boundary is crossed — which is why the dashboard is mesh-only behind
tireless.internal and has no multi-user model.
3.5 Concurrency guardrails move
Claude Code capped concurrent subagents at 20 and now defaults nested spawns to depth 3 (changed twice in July 2026). tireless bounds its own concurrency (§5) rather than discovering the vendor's limits by hitting them.
4. State and claiming
4.1 Postgres is the authority
House cluster, magrathea.kosherinata.internal:5432, mTLS and passwordless
(generic.md §5). Role tireless_rw, ident-mapped from the deploy host's cert
CN — installed on both magrathea and frankie, since a failover to a server
missing the mapping locks tireless out.
4.2 Claiming
SELECT … FOR UPDATE SKIP LOCKED (generic.md §3). The claim is a row
transition, which makes it atomic across any number of runners. Claims carry a
lease (claim_expires_at); a timer returns expired claims to the pool so a
runner that died mid-job does not strand its issue.
The forge label tireless/claimed is written after the database claim
succeeds, and is treated as advisory on read. If a poll finds an issue labelled
claimed with no live job behind it, the label is stale and gets cleaned up —
this is the normal path after a database restore or a hard crash.
4.3 Job states
Pending ──▶ Claimed ──▶ Running ──┬──▶ Delivered (PR opened / children created)
▲ ├──▶ Blocked (needs a human)
│ └──▶ Failed (past the retry budget)
└───── lease expiry ───────────┘
Abandoned (opt-in removed, or issue closed)
Delivered, Blocked, Failed and Abandoned are terminal —
JobState::is_terminal. A terminal job is never re-claimed; re-running requires
an operator (tireless job run <id>) or a fresh label cycle.
4.4 Idempotency
Every stage assumes it may be interrupted and re-run:
- enqueue is an upsert keyed on
(forge, owner, repo, number); - a job whose branch already exists on the remote reuses it rather than failing;
- a job whose PR already exists reports it rather than opening a second;
- clone directories are addressed by job id, so a retry cannot collide with a previous attempt's tree.
5. Respecting provider limits
Nothing external stops an unattended driver from asking for work. Four brakes,
implemented in tireless-core::budget with tests:
-
Concurrency cap per lane. Claude Code defaults to 1. A subscription is one person's allowance; parallel sessions are the fastest way to exhaust it. OpenCode defaults to 2, bounded by the GPU fleet rather than a bill.
-
Window budget. A hard ceiling on runs started per rolling window (default 12 per 5h for Claude Code). Not advisory: when spent, the lane stops until the window rolls. Defaults are deliberately low — raising a ceiling after watching real usage is easy; discovering you burned a month's allowance overnight is not.
-
Provider signal. Claude Code emits
rate_limit_eventmessages in its stream. vibe-kanban parses them and discards them — the match arm atcrates/executors/src/executors/claude.rs:1954is empty. tireless consumes them (tireless_agent::claude::parse_limit_signal) and holds the lane until the reported reset. This is the most valuable signal available to an unattended driver, because it reports what the provider thinks rather than what we guessed, and it is checked first, ahead of our own optimism. -
Circuit breaker. N consecutive failures (default 3) stop the lane entirely until an operator intervenes. Repeated failure usually means something retrying will not fix, and every retry still costs tokens.
Forge politeness is separate and equally deliberate: a floor on poll interval
(120s, config), conditional requests using the stored ETag, per-repo jitter so
N repos do not fire together, and backoff with jitter on 429/5xx.
An optional quiet window suspends both polling and claiming.
6. Architecture
6.1 Crates
Per generic.md §1, with one addition noted below.
| Crate | Role |
|---|---|
tireless-entities |
domain types, no I/O. Exports TS bindings for the dashboard via ts-rs. |
tireless-core |
routing, budgets, job lifecycle. Declares ports; depends on no adapter. |
tireless-data |
Postgres + forge clients (Gitea, GitHub). |
tireless-agent |
addition — spawns and drives Claude Code and OpenCode. |
tireless-api |
binary: Axum REST/JSON on /v1. |
tireless-worker |
binary: poll and run roles. |
tireless-cli |
binary: operator CLI (tireless). |
tireless-agent is a deviation worth stating: process orchestration is not data
access, and it is shared by the runner and the CLI (which can dry-run a single
job), so §1's "extract when the second consumer appears" test is met.
6.2 Deployment
| Concern | Value |
|---|---|
| Host | bob.hanzalova.internal |
| API port | 23296 — derived per port-allocations.md §3, registry updated |
| Ingress | tireless.internal on the hanzalova proxy, mesh-only, per-service internal cert |
| Dashboard | static, /var/www/tireless, served by nginx |
| Database | magrathea.kosherinata.internal:5432, mTLS |
| Deploy | Gitea Actions, build-and-rsync, musl static |
bob was chosen because it already hosts vibe-kanban and helexa-bench, and sits on
the same site as cortex (hanzalova.internal:31313) — so the highest-volume
path, OpenCode implementation runs, stays local rather than crossing the
WireGuard mesh.
6.3 Checkouts
No worktrees. Worktrees share one object store, which is the right trade for many cheap branches of a repo you already have, and the wrong one here: jobs must not be able to reach each other's state.
/var/lib/tireless/mirror/<forge>/<owner>/<repo>.git # bare, refreshed before use
/var/lib/tireless/job/<job-id>/<repo>/ # clone of the mirror
Cloning from a local path hardlinks objects rather than copying them, so a job
clone is fast and near-free on disk regardless of repo size — git never mutates
an existing object, so the hardlinks are safe. origin is then repointed at the
real remote, because the mirror is a cache, not the truth.
Branches are namespaced tireless/<issue>-<slug> so forge branch protection can
permit the bot there and nowhere else.
6.4 Identity
A dedicated tireless Gitea account, not the operator's. Its token is scoped to
issue and PR write; branch protection on each repo's default branch denies it
push. Three benefits: the audit trail distinguishes agent work from human work;
you can meaningfully review a PR you did not author; and revoking the agent does
not touch your own credentials.
6.5 systemd hardening
Full hardening set from generic.md §8 on all three units, with one documented
relaxation on tireless-runner: MemoryDenyWriteExecute=false. Both agents
are Node programs and V8's JIT requires write-then-execute pages; with it enabled
the agent aborts at startup. Per §8 only the one setting that breaks the service
is relaxed — the API and poller keep it.
7. Staged implementation plan
Each stage is independently deployable and independently verifiable, per
generic.md §14. The ordering is deliberate: everything that can be proven
without spending tokens is proven first, and the first token-spending
capability produces text (issues), not code.
Stage 0 — Foundations (scaffolded)
Workspace, entities, routing and budget logic with tests, binaries that start and stop cleanly, dashboard shell, deployment assets, CI.
Done when: tireless-api answers /v1/ready on bob:23296, the dashboard loads
at tireless.internal, all three units are active, and the deploy workflow is
green end to end.
Stage 1 — Forge ingestion (read-only)
Gitea client with conditional requests; poll loop with interval floor and jitter; Postgres schema and migrations; repo CRUD through the API and dashboard.
Reads issues, writes nothing to the forge. No claiming, no agents.
Done when: labelled issues in a real repo appear in the dashboard within one poll interval, and a repo's schedule can be changed from the dashboard without a redeploy.
Why first: proves the poll loop, rate discipline and repo configuration while the blast radius is still zero.
Stage 2 — Claiming and lifecycle (still no agents)
Job state machine, FOR UPDATE SKIP LOCKED claiming, lease expiry, label
mirroring, issue comments, reconciliation of stale labels. A dry-run executor
that posts what it would do instead of running an agent.
Done when: labelling an issue causes tireless to claim it, comment its intended plan of action, and release it on lease expiry — with the full external protocol exercised and not one token spent.
Why here: the claim protocol is the part most likely to have subtle bugs, and this is the last stage where those bugs are free.
Stage 3 — Claude Code executor
Spawn the pinned CLI, read stream-json, capture session id for --resume,
record apiKeySource as billing mode, consume rate_limit_event into the
governor, enforce budgets and the circuit breaker.
Apply prompt/plan.cc.md via --append-system-prompt; parse the result into
PlanSpec and gate it through plan::validate before creating any issue.
First real capability: tireless/plan on a real issue produces an epic and child
issues.
Done when: a planning run completes against a real issue, the plan passes validation, the children are sensible when read as an implementer would read them — cold, with no other context — the journal shows the billing mode, and an artificially lowered window budget demonstrably holds the lane.
This is also where plan quality is judged, while the only cost of a bad plan is a
comment thread. Iterate on prompt/plan.cc.md here, not in stage 5.
Why planning first: the output is issues, not code. A bad plan is a comment thread; a bad implementation is a branch. Start where mistakes are cheapest.
Stage 4 — Git and PR pipeline
Mirror cache, per-job clone, branch, commit, push, open PR. Wire the Claude Code implementation path. Idempotent re-runs against existing branches and PRs.
Done when: an issue labelled tireless/implement yields a reviewable PR from a
protected-branch-respecting bot account, and re-running the job updates rather
than duplicates.
Stage 5 — OpenCode executor
Spawn opencode serve on loopback with a per-spawn password, drive it over HTTP,
target helexa cortex. Enforce assert_not_anthropic from config. Register a
custom OpenCode agent carrying prompt/implement.oc.md as its system prompt.
Route plan-descended implementation jobs here.
Start with a precedence probe. cortex passthrough is settled (§2.4), so the
open question is ordering: OpenCode sends its own preamble alongside the agent's
configured prompt, and the last system message wins. Capture what OpenCode
actually sends upstream and assert implement.oc.md arrives last — asserting on
model behaviour instead would not distinguish "prompt ignored" from "prompt
honoured but model disagreed".
If it does not arrive last, that is a stage 5 work item, not a blocker: options are an OpenCode agent that suppresses the built-in preamble, or folding the instructions into the user turn where ordering is under tireless's control.
Handle status: "incomplete" explicitly (helexa#223): an empty output from a
thinking model on the Responses surface is a token-budget artifact, not a failed
run, and must not consume a retry or trip the circuit breaker.
Done when: a child issue created by a stage-3 planning run is implemented
end-to-end by OpenCode on the GPU fleet, with zero subscription usage; the run
demonstrably respected its Out of scope section; and a captured upstream
request shows implement.oc.md in the winning position.
Stage 6 — Scheduling and dashboard control
Schedule editing, repo add/remove, lane pause/resume, budget and limit-signal display, run history with per-run billing mode.
Done when: the operator can add a repo, change its cadence, and pause the Claude Code lane without touching a shell.
Stage 7 — Hardening
Dead-letter semantics for repeatedly failing jobs, Prometheus metrics, alerting on tripped breakers, retention and cleanup of job directories, and the optional container isolation backend behind the existing executor interface.
8. What is lifted from vibe-kanban
vibe-kanban is the reference implementation for driving these two agents. It is not a dependency — tireless reimplements the parts it needs — but these are the files worth reading before writing the corresponding stage.
| Concern | vibe-kanban reference |
|---|---|
| Executor interface | crates/executors/src/executors/mod.rs:222 (StandardCodingAgentExecutor) |
| Claude Code spawn + control protocol | crates/executors/src/executors/claude.rs:619 |
| Pinned agent package | claude.rs:61, opencode.rs:92 |
| Session resume | claude.rs:370 (--resume, --resume-session-at) |
| Session id extraction | claude.rs:891 |
apiKeySource / billing detection |
claude.rs:911 |
rate_limit_event (parsed, then dropped) |
claude.rs:1954 — tireless does not drop it |
| OpenCode loopback server | opencode.rs:92, opencode/sdk.rs:405 (basic auth) |
Process-group kill for orphaned npx children |
opencode.rs:75 (Drop impl) |
That last one is worth pre-empting rather than rediscovering: vk's comment notes
that kill_on_drop proved unreliable and leaked orphaned processes, which is why
it kills the whole process group explicitly. An unattended service accumulating
orphaned Node processes would be a slow, confusing failure.
9. Risks and open questions
The subscription arrangement can be withdrawn. Accepted, explicitly. The mitigation is that the API-key fallback is a config switch (§3.2), so the failure mode is a billing change rather than a rewrite.
A headless subscription login is a manual step. The OAuth flow must be
completed interactively as the tireless service account on bob. It is scripted
as far as it can be and documented in script/infra-setup.sh. If the token ever
requires reauthentication, the runner fails its preflight rather than silently
falling back to an API key.
Unattended agents with commit rights are a real exposure. Bounded by: a bot account that cannot push to any default branch; hardened units; per-job clones; and human review before merge. Stage 7's container backend tightens this further, and the executor interface is shaped so it can drop in without touching agent-driving code.
Plan quality is unproven. The whole economic argument — Opus plans, local models implement — rests on tireless-authored plans being specific enough for a 27B model to execute. Stage 5 is where that assumption meets evidence. If it fails, the fallback is routing more implementation to Claude Code, which costs subscription budget but not a redesign.
The plan contract (§2.4) narrows this considerably: paired system prompts shape both ends, and structural validation rejects a plan lacking a runnable stopping condition or a declared boundary before any implementation job is enqueued. What remains genuinely unknown is semantic quality — whether a plan that satisfies the schema is also correct and specific enough in substance. No validator catches a well-formed plan that is simply wrong about the codebase, and there is no unit test for whether a prompt produces good plans. That is measured on real issues in stage 3, before stage 5 spends anything on acting on them.
System prompt precedence in the OpenCode lane is unverified. Passthrough is
settled (helexa#179 closed), but OpenCode's own preamble and
implement.oc.md both arrive as system messages and the last wins (§2.4). If
tireless loses that ordering, the symptom is not an error but a generic-feeling
agent that ignores Out of scope — the exact failure the prompt exists to
prevent, presenting as a prompt-quality problem rather than a plumbing one.
Checked first in stage 5, by asserting on the upstream request rather than on
behaviour.
Adding a qwen3_next model to the lane would re-open a verified assumption.
The system slot is not arch-branched — rendering goes through helexa's shared
chat_template.rs using each model's own tokenizer_config — so there is no
family-specific code to fail, and template tests cover the shared path. But
Qwen3-Coder-Next and Qwen3-Next-80B-A3B-Thinking have not been live-tested,
because both are feasible only on beast where the pinned 27B is resident. If the
operator decides the coder model is worth the displacement, probe it while warm
rather than forcing an eviction to find out.
Not yet decided: whether a failed implementation should automatically open a
tireless/blocked issue describing what it could not do, or simply comment on
the original. Deferred to stage 4, when there is real failure data to look at.