Repository navigation
Releases: emersonjoe/trilha-runner
Release list
trilha-runner 0.7.0
Proportional changes: a reviewer should not get 400 lines of AI changes for something 5 lines
would fix.
Added
- Every implement run is told to keep the change small. The prompt gets a "Keep the change
small" section: the smallest change that meets the acceptance criteria and passes the checks;
no refactoring, renaming, reformatting or files the task does not need; no new abstraction or
dependency unless the task asks; a larger change is explained withtrilha-runner note, or
asked first withtrilha-runner askwhen it changes the task's scope. Reviews and hunts do not
get it. - The AI review checks proportion. A review compares the diff since the merge base with what
the task asks and reports every change the task does not need (refactors, renames,
reformatting, new abstractions or dependencies, unrelated files, dead code) as amedium
finding titledUnneeded change: …with its path and lines; a diff much larger than the task
needs gets the verdictchanges. - Tests prove
diff_statscounts the task branch's real size against the commit it was cut from:
a three-line addition is 3 lines, a reformat of a 40-line file is 40 in and 40 out, and what
the maintainer commits on the checkout afterwards is not counted. make claude-key,make claude-key-status,make claude-key-forgetandmake e2e-claude
(scripts/anthropic-key.sh): the Anthropic API key for the opt-in live Claude case lives in
the macOS Keychain, the Linux Secret Service or a 0600 file, is checked with the API before
it is stored, never goes on a command line and reaches only the test process.make e2e-ollama
runs the Ollama live cases.- The live Claude case uses
ANTHROPIC_API_KEYwhen it is set, handed to the agent through the
run's AI config like a control plane's credential, and fails when the key appears in any file
the runs leave in the repository or anything the control plane received.
Fixed
claude-coderuns reach the control plane with their model, tokens and cost. The preset
asks Claude Code for--output-format jsonand reads its result line: the final message is
the report, the model it resolved, input tokens (cache writes and reads included), output
tokens andtotal_cost_usd. The reported cost wins over the per-token estimate. Before,
everyclaude-coderun showed no model, tokens or cost. An older Claude Code that prints
plain text keeps working, with nothing invented.
trilha-runner 0.6.0
Protocol 0.4 (trilha-spec v0.4.0, spec 016) on the runner side: phase A of the dev-3.0 plan
(trilha-cloud/docs/PLANO-DEV3.md, DV-01 to DV-04 and DV-12) and roadmap RM-012/RM-013; phase B
(DV-05b, DV-06, DV-07, DV-08, DV-13a–c and report artifacts).
Added
-
Review runs (
kind: review, DV-06).review_mode: report(the default) reviews the task's
branch read-only in a detached checkout of its own;fixworks on the branch, commits and runs
the task's checks. The prompt points at the merge base with the default branch; the result
carriesreview {verdict, findings}(verdictchangeswhen there are findings and the agent
gave none,passotherwise). -
Bug hunts (
kind: hunt, DV-07): read-only, pointed athunt_seed's area and style, scoped
to the files the branch changed since its merge base; the result carriesfindings[]stamped
with the area and the style. -
Variants (
variant_index, DV-05b) work ontrilha/task-nnn-v<N>in.trilha/runs/TASK-NNN-vN/,
prove the branch they created increated-branch, never adopt one they cannot prove
(-vN-2,-vN-3…), and read nothing a sibling left in the context pack. -
Review comments (
review_comments[], DV-08) become a<reviews>block built on the runner
from the worktree's lines, XML-escaped and quoted as data; the result carriesreview_replies[]. -
Agent commands
trilha-runner finding,verdict,replyandartifact, and the same tools in
theaidriver. A read-onlyairun gets neitherwrite_filenorask. -
Artifacts:
trilha-runner artifact <file>publishes.html,.md,.pngor.jpgreports
(≤ 2 MiB, ≤ 5 per run), copied to.trilha/runs/<run>/artifacts/; the worker uploads them with
PUT /api/runs/{id}/artifacts/{name}before the result and lists only the accepted ones; 403
ARTIFACTS_DISABLEDdrops them all. -
trilha-runner doctor --worktrees(DV-13a) classifies.trilha/runs/*as live,
finished-kept, orphaned or metadata-only, with age buckets and sizes; deletes only with
--prune-orphansor--prune-older-than, never unmerged or uncommitted work without
--force-unmerged, a branch only whencreated-branchproves the runner made it; exit code
12 when something was skipped or failed. -
Opt-in live hunt against Ollama on a planted off-by-one.
-
The agent can ask.
trilha-runner ask "<question>", on the agent's PATH during a run,
ends the runneeds_inputwith the question when the agent ends its turn: the work so far is
committed, no check runs, no attempt failed, nothing is retried, and the task isblocked
locally. Theaidriver has anasktool that does the same. The context pack tells every
agent to ask one objective question and stop when information is missing. -
Answers continue the work. A claim with
continuationresumes the same claude-code session
(--resume <session_id>) with the answer, quoted, as the prompt; any other case starts a fresh
session with the question and the answer quoted in the context pack. claude-code sessions are
named up front (--session-id <uuid>) and the result reportssession. -
Agent state.
trilha-runner hook claudeturns Claude Code'sPreToolUse,PostToolUse,
StopandStopFailurehooks — configured in.trilha/runs/TASK-NNN/claude-settings.json,
beside the worktree and passed with--settings— intoagent_stateprogress reports
(working,idle,asked,stopped_error…), sent only on change and at most once every 2 s;
theaidriver reports the same from its loop.trilha-runner attention "<reason>"and
--clearraise and clear the attention badge. The agent commands talk to the run over a Unix
socket (TRILHA_RUN_SOCKET,TRILHA_RUN_ID) and always exit 0 within a second. -
Task memory.
trilha-runner noteandtrilha-runner overview(andnote/overviewtools
in theaidriver) putnotes(sourceai) andoverviewin the result; the claim's
contextis quoted into the context pack withexecution.QuoteContext, labelled as
information, never instructions. -
Diff counts. Results carry
diff_stats(files, insertions, deletions) from
git diff --numstat <checkout HEAD>...HEAD; a binary file counts as a file with 0/0. -
Heartbeat during a run. While any run is active the worker sends
running n/Nevery 30 s
(RM-013). -
Result outbox. The result is written to
.trilha/runs/<run-id>/result.jsonbefore
POST /result, retried with backoff instead of stopping the worker, set aside as
result.rejected.jsonon a final 4xx, and resent at the next start (RM-012, runner side). -
Opt-in live tests against a real model:
TRILHA_E2E_OLLAMA=1andTRILHA_E2E_CLAUDE=1.
Changed
- The wire types follow protocol 0.4 (
github.com/emersonjoe/trilha-specat9cb760d): the claim
decodeskind,continuationandcontext; progress carriesagent_stateandattention;
the result carriesquestion,session,overview,notes,diff_statsand may have status
needs_input.make conformancealso compares theneeds_inputfixture. - A task left
runningby a runner that died (no live process behind
.trilha/runs/TASK-NNN/runner.pid) goes back toreadywith a note instead of blocking forever
(RM-012). - A failed
POST /resultno longer stops the worker (see the outbox above). worktree cleanfirst stops the processes whose current directory is inside the worktree —
SIGTERM, 1.5 s, SIGKILL;/procon Linux,lsofon macOS — then removes it (DV-13b). It also
accepts a run directory (TASK-001-v2).- The runner records
created-branchwhen it cuts a task's branch, andcheckpoint.jsonand
agent.loglive in the run's own directory (.trilha/runs/<task or run key>/).
Fixed
- The agent's environment now carries
USERandLOGNAME: Claude Code on macOS finds a local
login in the keychain by user and answered "Not logged in" without them.
Security
- Questions, notes, overviews and attention reasons are size-limited (the protocol's limits) and
secret-scanned before they leave the machine — the run's credential and credential-shaped
strings (sk-,ghp_,github_pat_,xox?-,AKIA, private keys); a question with a secret
becomes a fixed notice and its words stay inquestion.txt(mode 0600). The agent socket is
owner-only; a session id reaches argv only as a UUID; the claude-code settings allow exactly the
agent's own runner commands through Bash. - A read-only run is verified, not trusted: a dirty worktree, a moved
HEADor a moved branch
fails it withfailure_class: policyand discards its findings and artifacts; Docker mounts the
worktree read-only for it. Findings and replies are validated and secret-redacted; a text
artifact holding a secret is refused, an image must start with its format's bytes, and a
published file must come from the worktree or$TRILHA_ARTIFACT_DIR. Review comment bodies,
authors and paths are XML-escaped, quoted as data, and read only inside the worktree (symbolic
links included). - Foreign code (
foreign_code: true, DV-13c) runs only in the Docker sandbox (policy failure
otherwise), gets no secret beyond the AI credential a command-line driver needs (none in the
aidriver's environment; its checks run in the sandbox), readsproject.verifyfrom the
default branch, and is never pushed.
trilha-runner 0.5.0
Fixed
driver_versionson the claim and the heartbeat repeated the runner's own version for every
driver, so the fleet panel could not tell whichclaudeorcodexa worker had. The worker
now runsclaude --versionandcodex --versiononce at startup, with a 3-second timeout,
and reports their first line (unknownwhen the binary is absent or silent);echoandai
keep the runner's version (roadmap RM-008).- A tool the checks need but the worker does not have burned every attempt of the model
(acervorun-000007spent three becausepythonwas missing; issues #13, #14). Before the
first attempt the runner now resolves the program of every check —exec.LookPathon the
host,command -vthrough the sandbox prefix when there is one — and a missing one ends the
run at once: one attempt, no driver call,failure_class: environment,
error_code: environment_missing_tool, arunrecord naming the tool and the check. Exit
status 127, "command not found" and "executable file not found" during execution or
verification get the same class, and the retry policy never retries anenvironmentfailure
(roadmap RM-014). - The Docker sandbox case
TestDockerPostgresAndTeardownfailed onmain(2026-10-15) with
db:5432 - no responseafter readiness had passed:pg_isready -U postgresinside the service
container answers on the Unix socket while the postgres image still runsinitdb's temporary
server, which listens on no TCP port, and the agent reached the service in the gap before the
real server listened. The readiness example in the README, the test and the catalog now probe
over TCP (pg_isready -h localhost -U postgres), the way the agent reaches the service; twenty
consecutive runs pass (roadmap RM-007). - A run that could not start because the project is not a git repository, or because the
agent manifest names an unknown driver, returned an error and left the taskreadywith no
evidence — against principle III and what the README promised. The task now goes
ready → running → failedwith arunrecord of stageworktreeordriver, like a
missingexeccommand already did. A task that is notreadyor is blocked by a dependency
is still left untouched. The note no longer repeats the stage (worktree: worktree: …), and
run/nextprint no emptybranchline when no worktree was created. - A worker with
--pushcould publish a branch only once. Its checkout is a single-branch
clone, so a push left no remote-tracking ref, and the next--force-with-leaseon the same
branch expected it not to exist and was refused as stale: the second task of a round failed
to publish the spec branch, and a rerun failed to publish its task branch. The checkout now
tracksrefs/heads/trilha/*, so each push records what the runner published and the next
one is refused only when someone else moved the branch. A checkout cloned by an earlier
version adopts the branches already on the remote the first time it is reused.
Added
.trilha/tells the truth about what shipped: spec 002 (Cloud bundles in real repositories,
0.2.0) isdone; TASK-007 "Container sandbox" (0.3.0) isdonewith an evidence note pointing at
the 0.3.0 changelog; TASK-006 (open a PR after review) stays an idea and names roadmap RM-030
(roadmap RM-004).- The execution contract is conformant again:
testdata/contracts/execution-v1.jsonand
execution-v1.schema.jsonare byte for byte trilha-spec's canonical fixture and schema, and
queue.Attempt/runner.Attemptcarry an attempt's cost under the protocol's names
(provider,tokens_in,tokens_out,cost,currency), filled from what the driver
reports and proven by the contract test to survive a round trip.error_codenow travels
on the result and on each attempt. AconformanceCI job checks outemersonjoe/trilha-spec
(main) as a sibling and runsmake conformance, so the fixtures cannot drift silently
(roadmap RM-003). .github/workflows/release.yml: a tagvX.Y.Zbuilds static binaries for linux/amd64,
linux/arm64, darwin/amd64 and darwin/arm64 with-trimpath -ldflags "-s -w -X main.version=X.Y.Z",
publishes them withsha256sums.txtand the CHANGELOG section of that version as the release
notes. Nothing is built beforeTestVersionMatchesTagproves the tag is the version in
cmd/trilha-runner/main.go(versionis now avar, overridable at link time) andmake test
is green; actions are pinned by SHA likeci.yml. The README shows the install path without Go
(roadmap RM-002).- IBM's seven end-to-end testing practices, applied (
docs/testing.md): every case in the
journey catalog states resources, preconditions, data, steps and expected;internal/catalog
fails the suite when a case is incomplete, cites a missing test, a test belongs to no case,
the locales differ, or a fixture holds a real-looking e-mail, token or host.make report
(internal/testreport) summarizes passed/failed/skipped, skip reasons, the slowest tests
and coverage per package, and fails belowtestdata/coverage-floor.txt; CI shows it in the
job summary. CI requires Docker on GitHub-hosted runners (TRILHA_REQUIRE_DOCKER), runs the
binary and git packages on macOS (make host), andnightly.ymlrunsmake flakyevery
night. A pull request template asks for journey, cases, tests per level, numbers, what was
not tested and the acceptance in business words. - Tests for the gaps the journey catalog listed: the Cloud client for bundles, progress and
deployments; the worker's cross-project dependency check; the bundle checkout, divergent
files and branch publishing; loading delivery profiles (including the shipped example); and
the attempt loop retrying a transient provider failure throughRunner.Run. - A testing standard (constitution principle VII,
docs/testing.md) and a catalog of user
journeys with their test cases (docs/journeys.md);make ci,make flaky,make e2eand
make cover. Tests forworktree,internal/taskcompat, cross-repository dependency rules,
the retry policy and a failing agent through the CLI binary.
v0.3.1
This release publishes v0.3.0 and v0.3.1 together — v0.3.0 was tagged but never had a GitHub release.
0.3.1 — 2026-09-19
Fixed
- The
claude-codepreset is selected forclaude-code-oauthas well (the provider name the control plane sends when the credential is an OAuth token); before, an OAuth project fell through to the manifest'sexecdriver and the credential was never injected. - The preset's fixed argv is now
claude -p - --permission-mode acceptEdits, plus--model <model>from the claim when the model id is well-formed; print mode has nobody to answer a permission prompt, so withoutacceptEditsthe agent could not write a file.
Security
- The project's credential no longer reaches a command line. The run's environment enters the
Docker sandbox once through a0600file atdocker run, anddocker execcarries no
KEY=VALUE: an argument ofdocker execis an argument of a host process, andpsshowed it
to every user on the machine. - The sandboxed agent drops every capability (
--cap-drop ALL) and runs as the user that owns
the worktree rather than as root. The two go together: withoutCAP_DAC_OVERRIDEa root agent
cannot write a worktree the operator owns, so running as the owner is what keeps the one
writable path writable.
Changed
sandbox.Sandbox.Preparetakes the run's environment, so a sandbox can carry a credential
into the container instead of each command carrying it.- A container that does not stay up is reported with its own output, and a service that has
exited is caught before its readiness command is waited on rather than after the timeout. Prepareanswers a no-op release rather than nil on failure, so deferring it cannot panic.
0.3.0 — 2026-09-18
Added
- Worker capability advertisements (
labels,capacity, active runs and component versions) with concurrent execution up to the declared capacity. - Per-project AI provider credentials, host allow-lists and the fixed
claude-codedriver preset. - Numeric
evalevidence parsed from check output, including threshold gates and hashed dataset manifests. - Ordered delivery steps, migration gates, per-service health checks and migration-aware rollback reporting.
- Cross-repository dependency resolution through repeatable
next --repo alias=pathmappings and the remote dependency endpoint. - Docker execution sandboxes with private networks, declared services, fixed resource limits and guaranteed teardown.
Security
- Exec drivers now receive an allow-listed host environment; project credentials are injected only into the claimed process and redacted from output.
- Docker sandboxes never mount the Docker socket, use read-only roots and mount only the task worktree writable.
Changed
- Requires Trilha Spec protocol 0.3 (
trilha-spec@56ddc07):evalevidence now carriesmetric,value,threshold,comparatoranddatasetas first-class fields, read throughtask.ParseMetricLineand settled bytask.Eval; the>,<and=comparators are no longer accepted as gates.
v0.2.0
Cloud work bundles are materialized as Trilha Spec files in isolated Git checkouts. The runner can publish specification and task branches and execute runner-local allow-listed deployment profiles with secret redaction. Includes hardened eoslab systemd artifacts and self-hosted CI support.