How it works
What happens between ciresk ci check and the exit code: the manifest, the tree, the cell, the worker and the summary.
The path of one check
The contract
Every surface, the CLI, the MCP endpoint and the API, is built from the agent's side:
| Need | What you get |
|---|---|
| A check you can run | ciresk ci check; exit 0 means pass, 1 means fail, everything else is a reason. See Exit codes. |
| Evidence you can show | The job id and the summary: verdict, up to 20 failing tests with file:line and the first 5 message lines, the last 50 lines of the first failed task, cache hits, cost. |
| A bounded result | Human output under 120 lines; summary under 2,000 tokens (hard cap 8,000); logs failed-only, 200 lines, 64 KiB by default, 256 KiB at most per call; every truncation stated in band. |
| The next step | The next line (last line of human output, next field in --json) is the exact command for the obvious drill-down. |
| No double spending | Identical requests from two agents are one job (deduplicated: true); a retry after a timeout returns the same job. |
| A parseable form | --json on every command: {schema_version, command, request_id, data, budget, next, truncated}, one document on stdout, progress on stderr. |
| An instant start | With ciresk ci watch running for the tree, ciresk ci check uploads nothing and is one request to queued. |
Reading a summary
ciresk ci check --json | jq '.data | {verdict, first_failed_task, failing_tests_total, failing_tests: .failing_tests[:3], next: .next}'| Field | Content |
|---|---|
verdict | succeeded, failed, cancelled, timed_out, infra_error, running |
failing_tests | Up to 20 of {name, file, line, status, message, task, shard}; failing_tests_total is the full count |
first_failed_task, tail, tail_task | The first task that failed and its last 50 lines |
cache | {hits, misses, writes} |
tasks | The task table with state, exit code, duration, cache outcome, cache_note (why a task was neither answered from the cache nor recorded in it) and worker |
next | The exact follow-up command |
Rules of thumb:
| Situation | Do |
|---|---|
| A test failed | ciresk ci tests <job> before any log; it lists failing tests first with file:line. |
| The tail is not enough | ciresk ci logs <job> --task <task> --grep '<first failing assertion>' -C 5. |
| More than 20 failing tests | Fix the first few, re-run; the next summary is smaller. |
| A task you expected cached ran | ciresk ci explain-miss <job>: the reason (--no-cache, a run that changed a tracked file, a first run, a failure) or the key items and input files that differ from the last recorded run. |
The summary says deduplicated: true | Another agent or person asked the same question; the result is theirs too. |
| The run exceeds a tool's timeout | ciresk ci run test --json, note data.id, then ciresk ci wait <job> in a later call. |
Do not claim success without a succeeded verdict. Quote the job id so a person can open it.
Deduplication
A job is keyed by the organization, the repository, the base commit, the diff digest, the tree root, the kind, the targets, full and the toolchains. An identical key already queued or running returns the existing job with deduplicated: true, and the budget is charged once, to the first submitter. Separately, every submission carries an idempotency key, so a retry after a timeout returns the same job rather than a second one. --no-dedup opts out of the first; nothing opts out of the second.
The MCP endpoint
Agents use the same surface through MCP at https://ci.eu.ciresk.com/v1/ci/mcp (Streamable HTTP, OAuth for people, bearer keys for machines). The endpoint publishes nine tools; their descriptions refer to each other with a ci__ prefix (ci__job_summary is the tool job_summary):
| Tool | When to call | Returns |
|---|---|---|
run | Start check, test, build, lint or a named task on a tree already uploaded (tree_root: the data.tree.root that ciresk ci run <kind> --json prints) or on a ref the cell can fetch without credentials; wait_s (at most 55) returns the summary once the job is terminal | Job or Summary |
job_summary | First call after any job | Summary; response_format: concise or detailed |
job_logs | Only when the summary tail is not enough: failed-only, tail, grep with context, byte paging | LogSlice with next_byte_offset and truncated |
job_tests | Failing tests first, cursor paged | {tests, total, failed_total, next_cursor} |
job_warnings | The warnings a job's tasks printed, as data | {warnings, total, unique, by_kind, next_cursor} |
job_artifacts | Names, sizes, time-limited URLs, an optional text excerpt | {artifacts, excerpt} |
job_wait | Block until a terminal state, 45 s by default and 55 s at most per call | Summary or {state, poll_after_ms} |
job_cancel | Cooperative cancel; marked destructive, asks for confirmation | {id, state, cancelled} |
jobs_list | Recent jobs for a repository, ref, actor or state; compact rows, never logs | {jobs, next_cursor} |
Every result carries _meta["ciresk/budget"] and _meta["ciresk/principal"]. Errors are isError: true text that names the fix. Read-only MCP is not a security boundary when the agent also has a shell; the token's scopes are. The CLI is the fallback surface whenever MCP is unreachable, with the same contract. Client set-up is in Claude Code, Codex and Cursor.
Budgets
| Limit | Scope | When exceeded |
|---|---|---|
| Soft budget (cost units) | Per actor, per session and per day | The job still runs, demoted to the lowest priority. |
| Hard cap (cost units) | Per actor, per session and per day | budget_exceeded, exit 5, the reset time in the message. |
| Concurrency | Per actor and per organization | concurrency_limited until a job finishes. |
--budget <units> | One job | The server stops the job when it passes the ceiling. |
A cost unit is one vCPU-second of the executing worker. A task is charged for the time it ran, at most the time its lease was held, and nothing when it ends infra_error (a fault on Ciresk's side) or is a cache audit the cell started itself. Every response carries budget: {session, day, concurrent, resets_at}; the CLI prints it as one stderr line and ciresk ci budget shows the whole picture. Deduplication charges the first submitter once. An agent whose remaining is 0 should stop and report rather than retry.
Attribution is automatic: in a Claude Code shell the job carries actor: agent:claude-code and the session id; in Codex agent:codex; in GitHub Actions ci:github-actions:<actor>; --actor and CIRESK_ACTOR override. Tokens, prompts and transcripts are not sent.