kici: runs & approvals
kici run
Section titled “kici run”Execute workflows locally or remotely. A bare kici run [event] performs a real routed run with this machine as the ephemeral agent; the remote subcommand runs fixtures through an orchestrator.
kici run <event> --local
Section titled “kici run <event> --local”Run a workflow on this machine as a real routed dispatch. kici run <event> --local compiles your workflows, matches triggers against the specified event, expands matrices, and executes the matched jobs — this machine joins as an ephemeral agent through the warm local dev plane. No orchestrator deployment is required.
kici run local is retired, because every run is a real routed dispatch. Invoking kici run local <event> prints a hint pointing at kici run <event> --local and exits without running.
kici run [event] --local [options]Concurrency enforcement:
A local run is a real routed dispatch, so a workflow’s concurrency block is enforced by the local dev plane’s own orchestrator — the same machinery a deployed orchestrator uses:
- The
groupcallback is evaluated agent-side against the simulated event (the same{ branch, event }context the agent sees), and the resulting key is reported back to the plane’s orchestrator before steps execute. cancelInProgress: truesupersedes the older run in the group;falsequeues the newer run behind it.
Coordination is scoped to that plane, whose state lives under ~/.kici/local/ — so enforcement is per-machine and per-user, and running the same workflow on two different machines does not serialize across them. That requires a deployed orchestrator.
Execution isolation:
By default, kici run <event> --local executes steps inside an isolated tmp checkout rather than against your real working directory. Any file a step writes, builds, or deletes — and any git mutation a step performs — lands in that throwaway copy, so casual local runs never touch your tree.
What gets materialized into the isolated checkout has full parity with what kici run remote reconstructs: your current working tree minus gitignored files, with .kiciignore applied to local changes, over a real .git directory. Concretely, the checkout is built from a clone pinned to your current HEAD, with your local overlay (modified, staged, and untracked-but-not-ignored files) copied on top and locally-deleted files removed. Workflows that read git metadata work because the .git directory is present and pinned to your HEAD.
The checkout is a fresh temp directory named kici-local-run-<random> under the system temp directory (for example, /tmp/kici-local-run-ab12cd).
Cleanup policy:
- The isolated checkout is removed when the run finishes, whether it succeeded or failed.
- A hard process death (SIGKILL, OOM kill) skips that cleanup. Leftovers older than a day are swept by the agent’s own startup garbage collection, which every
kici run <event> --localinvocation triggers.
Set the KICI_TMPDIR environment variable to place the isolated checkout (and every other KiCI-created temp directory) under a base directory other than the system temp directory.
Secrets are always sourced from your real .kici/ directory, not from the isolated checkout. Gitignored secret files (such as .kici/.env.local and .kici/secrets.yaml) are never copied into the checkout, so a step that reads a secret still gets it from the original location.
Pass --in-place to run against the real working directory instead — useful when you explicitly want in-tree execution. Both modes need a git repository, because the run triggers on a commit. Outside a git work tree, the command fails and tells you to initialize a repository (git init).
The run’s event carries the branch you have checked out, in both modes, so a push({ branches }) filter matches as it would for a push to that branch. On a detached HEAD, the branch is kici-local (a kici run remote fixture uses main).
The run’s changed files are the commit it runs. In the default isolated mode, that commit holds your uncommitted changes, so paths filters and ctx.changedFiles rules see those files. With no uncommitted changes, or with --in-place, the run uses your HEAD commit, so they see the files HEAD changed.
If the trigger times out:
The command waits up to 60 seconds for the local dev plane to create the run. If nothing appears in that window it stops and names the cause it read back from the plane:
| Message | What it means |
|---|---|
plane is not leader yet (election grace period) | The plane had not finished electing itself. A single-machine plane elects within seconds; if it does not, read the plane log (kici local logs). |
no kici.lock.json at <commit> in <path> | The plane resolved no lock file for the commit the run packed, so nothing matched. Commit .kici/kici.lock.json, or make sure it is present in the working tree. |
the plane recorded delivery <id> as "<status>" | The plane processed the trigger and wrote down a terminal status for it without creating a run. The status names the stage that stopped. |
no run appeared for delivery <id> — see <log> | Neither the plane’s cluster state nor its delivery record explained the timeout. The named log is the next place to look. |
no run appeared for this trigger — the plane never accepted the webhook | Every webhook POST came back without a delivery id, so the plane never queued the trigger. Check the plane is up (kici local status), then read the named log. |
Examples:
# Run workflows matching a push event on this machinekici run push --local
# Run a pull-request-open workflow locallykici run pr:open --local
# Reuse the working tree instead of an isolated clonekici run push --local --in-place
# Force the throwaway/offline planekici run push --local --offline
# Environment variable overrideskici run push --local --env NODE_ENV=test --env CI=true
# Quiet mode (summary only, no streaming)kici run push --local --quietAn event that matches several workflows creates one run per workflow.
kici run --local prints a Run started: line for each run, follows all of
them, and names each job <workflow> / <job> in the summary table.
Exit codes:
| Code | Meaning |
|---|---|
| 0 | All workflows succeeded |
| 1 | One or more jobs failed |
kici run remote
Section titled “kici run remote”Execute fixtures remotely through the full CI pipeline. Fixtures are defined in .kici/tests/*.ts using the fixture() factory function. Without arguments, lists available fixtures.
Remote runs route through the Platform. Authenticate with a personal access token (kici login), then target an organization with kici org use <org> or the --org flag. The Platform relays the run to the org’s orchestrator, while your working-tree overlay uploads directly to object storage — see How the run is routed and The two planes below.
Like kici run <event> --local, kici run remote recompiles your workflows (.kici/workflows → kici.lock.json) before dispatching, so the orchestrator matches and dispatches against your current workflow definitions — a brand-new or edited workflow takes effect without a separate kici compile. A compile or validation error aborts the run before anything is uploaded.
The orchestrator must have cache storage configured (KICI_STORAGE_TYPE = s3 or filesystem) with a dev-reachable upload endpoint so the CLI’s direct upload succeeds; see the testing guide and Storage layout for setup.
kici run remote [fixture] [options]--approve-all works in --json / --quiet mode: the run still auto-approves each gate it holds on, and the auto-approve diagnostics are written to stderr so stdout stays a pure JSON (or summary-only) payload. Without --approve-all, a --json / --quiet run that hits a gate stays held and prints a one-line “run held; approve via the dashboard or kici approve <run-id>” notice to stderr per hold.
Examples:
# List available fixtureskici run remote
# Run a single fixture against the active orgkici run remote push-main
# Target a specific org for this runkici run remote push-main --org xyz789ghi012
# Target a specific orchestrator cluster within the orgkici run remote push-main --orchestrator us-east
# Run all push-related fixtureskici run remote 'push-*'
# Run everythingkici run remote --all
# Run a specific workflow directly (bypass trigger matching)kici run remote --workflow ci
# Send a fixture's event to one workflow only (bypass trigger matching)kici run remote push-main --workflow ci
# Quiet mode -- just pass/failkici run remote push-main --quiet
# JSON output for scriptingkici run remote push-main --json
# Fire and forgetkici run remote push-main --no-wait
# View recent test run historykici run remote --history
# Interactively pick which fixtures to run (multi-select)kici run remote --pick
# Narrow runsOnAll jobs to a subset of the host rosterkici run remote deploy --target role:web
# AND-combine repeated --target values (hosts must match every selector)kici run remote deploy --target role:web --target dc:eu
# Skip a runsOnAll job instead of failing it when the target matches no hostkici run remote deploy --target role:gpu --target-allow-emptyInteractive fixture selection (--pick / -p):
Pass --pick (or -p) to open an interactive checkbox menu of the available
fixtures. Toggle one or more with space, confirm with enter, and the selected
fixtures run through the normal remote pipeline (honoring --parallel,
--no-wait, and the other run flags). Notes:
--pickis mutually exclusive with a fixture argument,--all, and--workflow. Passing any together exits with code 2.- When
stdinis not a TTY,--pickprints the available fixtures and exits without running anything — pass a fixture name (or--all) in scripts.
Running one workflow (--workflow):
--workflow <name> runs only the named workflow and bypasses trigger matching.
- Without a fixture, the workflow gets a
manualevent. - With a fixture (or a glob, or
--all), each selected fixture’s event goes to that workflow only. The other workflows the event matches do not run. --workflowwins over a workflow the fixture names itself. The CLI prints a warning when the two differ.- A name that is not in the compiled lock file stops the run before anything is uploaded. The error lists the workflows the lock file contains.
--workflowcannot be combined with--pick.
Without --workflow, a fixture runs every workflow whose triggers match its
event. Each matched workflow gets its own run, as it does for a webhook, so two
workflows that define a job of the same name never share a run:
- The CLI prints one
Run started: <run-id> (<workflow>)line per run, follows every run at once, and prefixes each streamed log line with its workflow. - The summary table, the error blocks and the
--junitreport name each job<workflow> / <job>. Each failed job gets its own error block, which holds only that job’s output. - Ctrl-C cancels every run.
--no-waitprints every run id and returns. kici run remote --historyrecords each run on its own line.--jsonkeeps one entry per fixture, so itssummarycounts fixtures. The entry’sstatusis the combined result.runslists each run (runId,workflowName,status,jobs), and each job carriesworkflowName. The entry’s top-levelrunIdnames the first run and is deprecated: readrunsinstead.
A kici older than 0.21.0 follows a single run. For it, the orchestrator runs
the matched workflows as one shared run when their job names cannot collide.
When they can collide, it refuses the fixture. That happens when two workflows
define a job of the same name, or when one defines dynamic jobs. The refusal
names the shared job names and asks you to upgrade kici or pass --workflow.
Host narrowing with --target
Section titled “Host narrowing with --target”--target <selector> is a runtime narrowing for runsOnAll jobs, analogous to
Ansible’s --limit. A runsOnAll job normally fans out to every roster host
matching its predicate, one pinned execution per host. --target intersects that
matched roster with a label selector, so the effective host set is
runsOnAll ∩ target:
- Narrow-only.
--targetcan only remove hosts from the matched set, never add them. The widening dimension (OR across host groups) lives in the workflow’srunsOnAll;--targetonly subtracts. - Run-global,
runsOnAll-only. A single--targetapplies to everyrunsOnAlljob in the run. Jobs pinned to a single host withrunsOnare untouched. - Repeatable and AND-combined. Each
--targetvalue is its own selector; a host must satisfy all of them to survive the narrowing. Use a single value for an OR-style match within one selector and repeated values for AND. - Selector syntax matches
runsOn: an exact label (role:web), a glob (role:*), or a regex (/^box-0[1-3]$/).
When --target narrows a runsOnAll job to zero hosts, the default is to fail
the run (fail-loud — a typo in the selector shouldn’t silently skip work). Pass
--target-allow-empty to skip the zeroed job instead; the job records a
skipped status, and any downstream job that needs it with when: 'on-skip' (or
when: 'always') still runs. See Job dependencies
for the when gating model.
Exit codes:
| Code | Meaning |
|---|---|
| 0 | Every run of every matched workflow passed |
| 1 | One or more runs failed, or a run was refused |
How the run is routed
Section titled “How the run is routed”kici run remote sends a run either through the KiCI Platform or straight to an orchestrator. It picks the target in this order:
- The
--orchestrator-url <url>flag (see Direct mode). KICI_ORCHESTRATOR_TOKEN, withKICI_ORCHESTRATOR_URLor the orchestrator saved bykici connect.- The orchestrator saved by
kici connect. - Your Platform login, saved by
kici login.
Through the Platform, a remote run is dispatched to your active organization — the one set with kici org use <org>, or overridden per-run with --org <id>. The org is resolved in this order:
- The
--org <id>flag, if provided. - Otherwise the active org saved in your global config by
kici org use <org>. - If neither is set, the command errors and asks you to select an org with
kici org useor pass--org.
The orchestrator anchors the org without any manual webhook source: it auto-provisions a system-managed remote source (routing key remote:<orgId>) that maps to its bound organization, so even a zero-source org is immediately routable for remote runs. You never set a routing key for a remote run — selecting the org is enough.
When an org has more than one connected orchestrator cluster, the CLI picks the target cluster in this order:
- The
--orchestrator <name>flag, if provided. - Otherwise the per-org default cluster, set with
kici orchestrators use <name>. - If the org has exactly one connected orchestrator, it is auto-selected.
- Otherwise the run errors with the list of connected clusters, and you pass
--orchestrator <name>to choose one. Runkici orchestrators listto see the available cluster names.
The two planes
Section titled “The two planes”kici run remote uses two independent paths:
- Control plane — run initiation, trigger, status, log retrieval, and cancellation flow from your machine through the Platform, which relays them over a WebSocket connection to the org’s orchestrator. In direct mode they go to the orchestrator’s HTTP API instead, with the same requests and responses. The CLI polls for log chunks and run status until the run reaches a terminal state; there is no direct streaming socket to the orchestrator. The log position records how far each log file was read, so output from a step that starts later is never skipped.
- Data plane — your working-tree overlay tarball uploads directly from your machine to the orchestrator’s object store via a presigned PUT URL. The overlay never passes through the Platform. This is why the orchestrator’s object-store upload endpoint must be reachable from your machine; see Storage layout.
Direct mode
Section titled “Direct mode”In direct mode, kici run remote talks to an orchestrator’s own HTTP API (/api/v1/test) and skips the Platform. You need no KiCI Platform account. It works with an orchestrator in any mode: independent, hybrid, observed or platform.
Ask the orchestrator’s operator for a personal orchestrator token (kici-admin token create <label> --role admin --subject <you>), then save the target:
kici connect https://ci.example.com # prompts for the tokenecho "$TOKEN" | kici connect https://ci.example.com --token-stdinkici run remote push-main # runs on that orchestratorFor a single run, or in CI, pass the target without saving it:
KICI_ORCHESTRATOR_TOKEN=... kici run remote push-main --orchestrator-url https://ci.example.comKICI_ORCHESTRATOR_URL selects a target only together with KICI_ORCHESTRATOR_TOKEN. The agent and the orchestrator read KICI_ORCHESTRATOR_URL as their own socket address, so a machine that runs one often has it set. The URL alone never moves a run off the Platform.
What changes in direct mode:
- Who you are. The CLI first asks the orchestrator who the token belongs to, and prints the caller, its role and the orchestrator mode. An
auditortoken can follow runs but cannot start them, so the run stops before the upload. - Which organization. The orchestrator chooses it: its default organization when
independent, or the organization the Platform assigned it otherwise.--organd--orchestratorare ignored, with a warning. - Storage. The overlay still uploads straight to the object store. The orchestrator signs the upload address from
KICI_STORAGE_UPLOAD_ENDPOINT(thenKICI_STORAGE_EXTERNAL_ENDPOINT, thenKICI_STORAGE_ENDPOINT). If your machine cannot reach that address, the error names it and the setting. - Approval holds. On an
independentorchestrator you answer a hold inline (or with--approve-all) through the orchestrator’s held-run routes; the token needs theowneroradminrole. The orchestrator records an--approve-allapproval as an automatic approval (held_run.auto_approvein its access log) by the token’s subject, and still checks each hold’s eligibility. On a Platform-connected orchestrator, holds are answered through the Platform (the dashboard,kici approve, or a PR comment), and the CLI says so once.
Direct mode leaves runs routed through the Platform unchanged. Run kici disconnect to forget the saved orchestrator, and kici doctor to see which target is active.
For executing workflow steps on your own machine without an orchestrator (no scaler, agents, or environments), use kici run <event> --local.
Fresh repos (no GitHub remote)
Section titled “Fresh repos (no GitHub remote)”kici run remote works even if the repo has never been pushed to GitHub, because no run depends on a git remote. Every kici run remote run behaves the same way, with or without a remote:
- The entire working tree is uploaded, including the
.gitdirectory, so steps that run git commands work - The lock file is sent inline (no GitHub API fetch)
- The build cache is skipped: no
__build__job runs, and no cached source or dependencies are used - A bound context that does not set
allowLocalExecution: true(the default isfalse) is skipped for the run, and the run shows a warning that names it
Destination routing is unchanged for fresh repos: the run goes to the same target as any other run (your active org through the Platform, or the direct orchestrator).
For a detailed guide on writing fixtures, configuring secrets, and understanding the upload flow, see Testing guide.
kici orchestrators
Section titled “kici orchestrators”List the orchestrator clusters connected to an organization, and set the per-org default cluster used by kici run remote. Requires kici login and an active org (or pass --org).
kici orchestrators list [--org <id>]kici orchestrators use <clusterName> [--org <id>]kici orchestrators list prints the org’s connected orchestrator clusters, so you know what to pass to --orchestrator (or to kici orchestrators use).
kici orchestrators use <clusterName> sets the default orchestrator cluster for the org, stored per-org in your global config. Subsequent kici run remote invocations target that cluster unless overridden with --orchestrator.
Examples:
# List the active org's connected clusterskici orchestrators list
# List a specific org's clusterskici orchestrators list --org xyz789ghi012
# Set the default cluster for the active orgkici orchestrators use us-east
# Set the default cluster for a specific orgkici orchestrators use us-east --org xyz789ghi012kici runs
Section titled “kici runs”Inspect and manage execution runs from the terminal — the equivalent of the
dashboard Runs page. All kici runs subcommands read/write the same org-scoped
data as the dashboard, so they require kici login and an active org
(kici org use <name>).
kici runs list
Section titled “kici runs list”List runs with optional filters. Output is a table (run id, workflow, status, branch, trigger, started, duration); pagination is reported at the bottom.
A run can span several workflows: the shared run that a kici older than
0.21.0 gets for a fixture with no --workflow. Its workflow cell shows the
first workflow and a count of the others (hello-world +3). --workflow matches a run that spans
the named workflow. An archived run matches its first workflow only. In --json, each run carries workflowNames, every
workflow with the first one first, next to workflowName.
When no runs match, kici runs list checks whether any webhooks arrived
recently. If deliveries came in but nothing produced a run, it prints a
one-line summary (“3 webhooks received in the last hour, 0 matched”) and
suggests kici preview push to test your triggers locally — the fast way to
find a misconfigured trigger. When nothing arrived it prints “No runs found.”
kici runs list [options]kici runs listkici runs list --status runningkici runs list --workflow ci --branch mainkici runs list --json | jq '.runs[].runId'kici runs show
Section titled “kici runs show”Show a run’s summary header plus its jobs-and-steps tree (name, status,
duration, exit code). A run that never started a step also prints why: the
run-level failure, and per job the reason it did not run — a job a
context rejected names the context and the
rule. Any approval hold on the run is listed too, with its context, hold type,
status and reason. If the run id is not on the Platform but exists in your
local run history (from kici run <event> --local), the local record is shown instead.
kici runs show, kici runs logs, kici runs rerun and kici runs cancel take a
full run id or its first characters (at least 4), like a git short SHA. A prefix that matches several runs fails and
lists them, so type more characters. A prefix that matches none, or text that
cannot start a run id, fails and says so. A prefix also matches archived runs and
runs in your local run history. kici runs list shows full run ids. To cancel on
an orchestrator directly (--orchestrator-url), give the full run id: the
orchestrator does not look up a prefix.
Steps are listed in the order they ran: a job’s image build, then its init
commands, then its steps. An init command is named init[<n>], where <n> is
its position in the job’s init list. kici runs logs uses the same order and
names in its headings. An init command that an older orchestrator recorded with
no step type is listed and named the same way.
In --json, each step in detail.jobs[].steps carries phase: setup (the
job’s image build), init (an init command) or steps. An init command also
carries initIndex, its position in the job’s init list. stepIndex is the
stored index. For an init command it is a large internal number, so read phase
and initIndex instead. The steps are in the order they ran, the same as in the
text output. This is also true for a run that an older orchestrator recorded.
For a run that spans several workflows, the header shows workflows= with every
workflow name, and each job is named <workflow> / <job>. In --json, each
job in detail.jobs carries workflowName.
A failed job prints an error: line: the job’s own error message, or else the
step that failed and its exit code (error: step test exited with code 3). A
failed init command is named init[<n>] in this line too.
For a run started with kici run remote, the Platform reads the jobs and steps
from the orchestrator cluster that ran the run. When the Platform cannot get them
from that orchestrator, it shows the jobs it stored, without steps. The command
then prints a note on standard error that names the reason, for example:
step detail unavailable: the Platform could not read it from the orchestrator that ran this run (no orchestrator that can serve this run is connected)Standard output and the exit code do not change. See Troubleshooting for each reason.
kici runs show <run-id> [options]kici runs show abc123kici runs show abc123 --jsonkici runs logs
Section titled “kici runs logs”Print each job/step’s log lines in order, with headers. Each job starts with its
setup log, under a (setup) heading, when it wrote one: the clone, the .kici
dependency install and the workflow module load. The parentheses keep it apart
from a step named setup. When a job’s setup log has no lines, a note under
the heading tells the two cases apart: (no setup log recorded for this job) or
(the setup log for this job is empty). When the orchestrator does not report
the difference, no setup heading prints for that job. The --json output carries
no note.
--json prints an object that maps each key to an array of lines. A step’s key
is <job>/<step>. A job’s setup lines, when it wrote any, are under
setup:<job>, with % and / in the job name written as %25 and %2F. A
setup key never contains a / and a step key always does, so a step named
setup cannot collide with it:
{ "setup:build": ["[host-install] Dependencies installed"], "build/setup": ["step output"], "setup:deploy%2Fprod": ["[host-install] Dependencies installed"], "deploy/prod/apply": ["step output"]}kici runs logs prints the same step-detail note as kici runs show. When a
job’s setup log cannot be read, a note on standard error names the job and the
error. Standard output and the exit code do not change.
kici runs logs <run-id> [options]kici runs logs abc123kici runs logs abc123 --job buildkici runs logs abc123 --followkici runs rerun
Section titled “kici runs rerun”Re-trigger a completed run. The server enforces a short cooldown between reruns of the same run. The cooldown also starts when the Platform cannot confirm the start.
When you run the command again within 30 minutes of a rerun that was not
confirmed, the Platform sends the first request again. You get the run that the
first request started, and no second run starts. The command then prints
Rerun already queued by an earlier request: <id>, and --json output
contains "alreadyAccepted": true. This applies to the same user or API key
only.
The command prints the new run id as soon as the orchestrator accepts the rerun.
The run then starts. If it cannot start, it fails, and kici runs show <id>
shows why. If the Platform cannot confirm the start in time, the command says so
and exits with code 1. The run can still start, so look for it before you retry:
kici runs list --trigger rerun --since <time>. The command prints this command
with the time filled in.
The rerun builds the same commit as the original run and goes through the same
steps: jobs wait for their needs, contexts and their approval gates apply,
and jobs can use artifacts and the cache. It runs the workflow’s declared jobs;
jobs a generator produced are not run again. A rerun of a pull request from a
fork goes through the organization trust policy again, so it can be held for
approval as the first run was. When the policy now ignores it, the rerun is
refused.
A run cannot be rerun when the event it ran for is no longer stored: a webhook
delivery past the audit retention window, or an internal event that has
expired. A run started by a scaler event cannot be rerun either. Start a new run
instead.
kici runs rerun <run-id> [options]kici runs rerun abc123kici runs cancel
Section titled “kici runs cancel”Cancel a single run, or all in-progress runs on a branch.
kici runs cancel [run-id] [options]kici runs cancel abc123kici runs cancel abc123 --forcekici runs cancel --branch feature/wipTo cancel a run on an orchestrator directly, for example a run on the local dev
plane, pass --orchestrator-url <url> or set KICI_ORCHESTRATOR_TOKEN (with
KICI_ORCHESTRATOR_URL or a saved kici connect URL), the same way
kici run remote picks its target. The command then calls that orchestrator’s admin API, so
the token needs the run.cancel permission. A target saved with kici connect
is used on its own only when you are not logged in to the Platform. --branch
always goes through the Platform, and it cannot be combined with
--orchestrator-url.
KICI_ORCHESTRATOR_URL=http://127.0.0.1:4319 \KICI_ORCHESTRATOR_TOKEN="$(cat ~/.kici/local/admin-token)" \ kici runs cancel 0f1e2d3c-4b5a-4c6d-8e7f-0123456789ab --forcekici runs artifacts list
Section titled “kici runs artifacts list”List the artifacts a run uploaded — name, producing job,
size, content hash, and creation time. An artifact whose stored object can no
longer be reached is flagged unavailable.
kici runs artifacts list <run-id> [options]kici runs artifacts list abc123kici runs artifacts list abc123 --jsonkici runs artifacts download
Section titled “kici runs artifacts download”Download a run’s artifacts. Name one to fetch just that artifact; omit the name
to download every artifact of the run. Each artifact extracts into its own
<name>/ directory by default.
kici runs artifacts download <run-id> [name] [options]--archive— save the raw.tar.gzas<name>.tar.gzinstead of extracting.-o, --output <dir>— write into<dir>instead of the current directory.
kici runs artifacts download abc123 bundlekici runs artifacts download abc123 bundle -o ./outkici runs artifacts download abc123 --archivekici runs artifacts download abc123The download streams directly from object storage over a short-lived signed URL — the artifact bytes never pass through the KiCI Platform — and the content hash is verified end to end, so a corrupted or truncated transfer fails loudly rather than leaving a bad file on disk.
Artifacts expire after the orchestrator’s configured retention. When you name a single artifact whose stored object is already gone, the command fails. When you download the whole run, such an artifact is reported as a warning and skipped so the remaining artifacts still land — the command fails only if every artifact of the run was unreachable. Any other failure (a rejected signed URL, a content-hash mismatch) stops the command immediately rather than continuing with the rest.
Artifact names are case-sensitive, so a single run can hold both bundle and
Bundle. On a filesystem that ignores case in path lookups — macOS and Windows
default to this, Linux does not — both would land on the same path, so
downloading the whole run refuses before writing anything and names the pair.
Fetch them one at a time into separate directories instead:
kici runs artifacts download abc123 bundle -o ./bundle-lowerkici runs artifacts download abc123 Bundle -o ./bundle-upperNaming a single artifact is never affected, and on a case-sensitive filesystem downloading the whole run still writes both.
Artifacts are packed relative to two roots: paths inside your repository and
paths under the home directory. Repository-relative files land directly under
<name>/; home-relative files land under <name>/~home/, so the two can never
overwrite each other and nothing is ever written outside the output directory.
When --json is set on any of these commands, kici emits only the JSON
document on stdout — the kici v<version> banner is suppressed — so the output
is safe to pipe into jq or JSON.parse. The same holds for the other
--json commands (kici run remote --json, kici workflows list --json) and
for --quiet.
kici reject
Section titled “kici reject”Reject a held approval gate. A rejection fails the held element and the run. A reason is required.
kici reject <run-id> --reason <text> [options]Examples:
# Reject a held job with a reasonkici reject abc123 --job deploy-production --reason "Wrong release branch"kici approve
Section titled “kici approve”Approve a held approval gate so the run resumes. Identify the held element by run ID, optionally narrowed to a job and step.
kici approve <run-id> [options]Examples:
# Approve a workflow-level holdkici approve abc123
# Approve a held jobkici approve abc123 --job deploy-production
# Approve a held step (steps are addressed by index)kici approve abc123 --job migrate-and-deploy --step 1You must be eligible for at least one unsatisfied clause (a member of a named team, or a named user) and hold the permission that matches the hold’s type — ci_trust:write for a security hold, contexts:admin for a wait-timer hold, contexts:write for every other hold. The server enforces this, so the same rule applies to the dashboard and to an AI agent’s tools. The command reports whether the element was released, how many clauses remain, or that it was rejected.
Reference
Section titled “Reference”kici approve
Section titled “kici approve”Approve a held approval gate for a run
Synopsis: kici approve <run-id> [options]
Arguments
| Argument | Required | Variadic | Description |
|---|---|---|---|
run-id | yes | no | Run ID whose approval gate to approve |
Options
| Option | Default | Description |
|---|---|---|
--job <name> | Approve the hold for a specific job | |
--step <index> | Approve a step-scoped hold (requires —job) | |
--hold-type <type> | Approve the hold of this type (reviewer, timer, concurrency, security) — a job can carry two | |
--hold <id> | Approve one hold by its id, as listed when nothing else separates them |
kici reject
Section titled “kici reject”Reject a held approval gate for a run
Synopsis: kici reject <run-id> [options]
Arguments
| Argument | Required | Variadic | Description |
|---|---|---|---|
run-id | yes | no | Run ID whose approval gate to reject |
Options
| Option | Default | Description |
|---|---|---|
--job <name> | Reject the hold for a specific job | |
--step <index> | Reject a step-scoped hold (requires —job) | |
--hold-type <type> | Reject the hold of this type (reviewer, timer, concurrency, security) — a job can carry two | |
--hold <id> | Reject one hold by its id, as listed when nothing else separates them | |
--reason <text> | Reason for the rejection |
kici run
Section titled “kici run”Execute workflows locally or remotely
Synopsis: kici run [event] [options]
Arguments
| Argument | Required | Variadic | Description |
|---|---|---|---|
event | no | no | Event type for a routed local run (e.g. push, pr:open) |
Options
| Option | Default | Description |
|---|---|---|
--local | false | Route the run with this machine as the ephemeral agent |
--offline | false | Force the throwaway/independent plane (offline) |
--connected | false | Force the connected/hybrid plane (requires attachment) |
--in-place | false | Reuse the working tree directly instead of an isolated clone |
--trusted | false | Route to the trusted fleet agent profile: steps see the ambient host env (minus the agent identity). Alias: —no-sandbox |
--no-sandbox | Alias for —trusted (the bwrap sandbox is already off by default) | |
--env <KEY=VALUE> | Per-run secret (repeatable) | |
--payload <path> | Dispatch payload JSON { action?, client_payload? } for a routed dispatch run | |
--kici-dir <path> | .kici | Path to .kici directory |
--quiet | false | Suppress the banner + streaming output |
--debug | false | Verbose internals |
kici run remote
Section titled “kici run remote”Execute fixtures remotely via orchestrator
Synopsis: kici run remote [fixture] [options]
Arguments
| Argument | Required | Variadic | Description |
|---|---|---|---|
fixture | no | no | Fixture name or glob pattern (omit to list available) |
Options
| Option | Default | Description |
|---|---|---|
--workflow <name> | Run only this workflow, bypassing triggers (with a fixture: send its event to this workflow) | |
--all | false | Run all available fixtures |
-p, --pick | false | Interactively pick fixtures to run |
--parallel | false | Run matching fixtures concurrently |
--no-wait | Fire and forget (print runIds, don’t stream) | |
--quiet | false | Suppress output except final result |
--json | false | Output structured JSON result |
--junit <path> | Output JUnit XML result | |
--history | false | Show recent run history |
--routing-key <key> | Deprecated; has no effect (the orchestrator chooses the routing key) | |
--orchestrator-url <url> | Run on this orchestrator directly, skipping the Platform (token: KICI_ORCHESTRATOR_TOKEN or kici connect) | |
--org <id> | Target organization (overrides the active org) | |
--orchestrator <name> | Target orchestrator cluster (overrides the per-org default) | |
--debug | false | Verbose internals |
--kici-dir <path> | .kici | Path to .kici directory |
--context <ctx.key=value> | Inject a namespaced context secret, uploaded encrypted to the orchestrator (repeatable) | |
--env <KEY=VALUE> | Provide a per-run secret (repeatable); uploaded encrypted to the orchestrator | |
--check | false | Run in check mode: report drift, change nothing |
--fail-on-drift | false | In check mode, exit non-zero if any step reports drift |
--target <selector> | Narrow runsOnAll jobs to hosts matching this label selector (repeatable, AND-combined) | |
--target-allow-empty | false | A —target that narrows a runsOnAll job to zero hosts skips it instead of failing |
--input <KEY=VALUE> | Typed workflow-dispatch input (repeatable) | |
--yes, --approve-all | false | Auto-approve every approval gate this run holds on (run-scoped; eligibility still enforced) |
kici runs
Section titled “kici runs”Inspect and manage execution runs
Synopsis: kici runs
kici runs artifacts
Section titled “kici runs artifacts”List and download a run’s artifacts
Synopsis: kici runs artifacts
kici runs artifacts download
Section titled “kici runs artifacts download”Download one artifact, or all of them — extracts by default
Synopsis: kici runs artifacts download <run-id> [name] [options]
Arguments
| Argument | Required | Variadic | Description |
|---|---|---|---|
run-id | yes | no | Run ID whose artifacts to download |
name | no | no | Artifact name (omit to download every artifact of the run) |
Options
| Option | Default | Description |
|---|---|---|
--archive | false | Save the raw .tar.gz instead of extracting |
-o, --output <dir> | Output directory (default: current directory) |
kici runs artifacts list
Section titled “kici runs artifacts list”List the artifacts a run uploaded
Synopsis: kici runs artifacts list <run-id> [options]
Arguments
| Argument | Required | Variadic | Description |
|---|---|---|---|
run-id | yes | no | Run ID whose artifacts to list |
Options
| Option | Default | Description |
|---|---|---|
--json | false | Output raw JSON |
kici runs cancel
Section titled “kici runs cancel”Cancel a run, or all in-progress runs on a branch
Synopsis: kici runs cancel [run-id] [options]
Arguments
| Argument | Required | Variadic | Description |
|---|---|---|---|
run-id | no | no | Run ID to cancel, or its first 4 or more characters |
Options
| Option | Default | Description |
|---|---|---|
--force | false | Force cancel (kill immediately, skip hooks) |
--branch <name> | Cancel all in-progress runs on this branch | |
--orchestrator-url <url> | Cancel on this orchestrator directly (token from KICI_ORCHESTRATOR_TOKEN or kici connect) |
kici runs list
Section titled “kici runs list”List execution runs (mirrors the dashboard Runs page)
Synopsis: kici runs list [options]
Options
| Option | Default | Description |
|---|---|---|
--status <s> | Filter by status | |
--workflow <w> | Filter by workflow name | |
--branch <b> | Filter by branch/ref | |
--repo <r> | Filter by repository | |
--trigger <t> | Filter by trigger type | |
--source <routingKey> | Filter by source routing key, or by provider (e.g. github) | |
--since <ts> | Only runs since (ISO-8601 or epoch ms) | |
--cursor <cursor> | Keyset cursor for the next page (from a prior nextCursor) | |
--json | false | Output raw JSON |
kici runs logs
Section titled “kici runs logs”Print step logs for a run
Synopsis: kici runs logs <run-id> [options]
Arguments
| Argument | Required | Variadic | Description |
|---|---|---|---|
run-id | yes | no | Run ID, or its first 4 or more characters |
Options
| Option | Default | Description |
|---|---|---|
--job <name> | Only logs for this job | |
-f, --follow | false | Tail logs for a live run |
--json | false | Output raw JSON |
kici runs rerun
Section titled “kici runs rerun”Re-trigger a run
Synopsis: kici runs rerun <run-id> [options]
Arguments
| Argument | Required | Variadic | Description |
|---|---|---|---|
run-id | yes | no | Run ID to rerun, or its first 4 or more characters |
Options
| Option | Default | Description |
|---|---|---|
--json | false | Output raw JSON |
kici runs show
Section titled “kici runs show”Show a run summary with its jobs and steps, why a job did not run, and any approval hold
Synopsis: kici runs show <run-id> [options]
Arguments
| Argument | Required | Variadic | Description |
|---|---|---|---|
run-id | yes | no | Run ID to inspect, or its first 4 or more characters |
Options
| Option | Default | Description |
|---|---|---|
--json | false | Output raw JSON |