Find and run test lanes
Human verified
A human reviewed this page, but it may be out of date. Later edits may not have been reviewed.
A lane is a named set of test files and setup requirements. Its manifest lists test files, selectors, prerequisites, and any expected-case checks. The suite index defines lane groups. Workflows without test suites build, publish, scan, or dispatch; see the workflow index. Files that share a runner start longest first, by measured file times. A file without a row starts first; refresh rows when a lane's longest files change.
Find test coverage
Every *.test.mjs and *.test.ts file under tests/integration/ must belong to
exactly one lane. The same audit covers tests/conformance/, tests/browser/,
and tests/docs/. Check registration without running tests or provisioning resources:
node scripts/ci/run-tests.mjs auditThe audit fails for unmapped files, duplicate ownership, missing files, or invalid lane/group definitions. Registration does not mean a test runs on every PR: the check pages distinguish ordinary CI, manual/advisory workflows, and lanes with no GitHub workflow. Each manifest is the exact file and selector inventory.
To find the owning lane for a file name or path fragment, run from the repository root:
node --input-type=module - local-first-agent <<'JS'import { loadTestSuites } from './scripts/ci/test-suites.mjs'; const { lanes } = loadTestSuites('scripts/ci/test-suites.json'); for (const [lane, { files }] of Object.entries(lanes)) { for (const { path } of files) { if (path.includes(process.argv[2])) { console.log(`${lane}\t${path}`); } }}JSReplace local-first-agent with the file name you need. When adding a test,
register it in its owning manifest and check that the intended workflow selects
that lane. A passing inventory audit does not prove that a credentialed or
manually prepared lane has run.
The runner removes inherited test selectors unless the manifest declares them.
requiredEnv fails when an input is missing or empty; optionalEnv forwards an
input only when supplied. Both may be declared on a lane or a file. A lane's
focusedEnv extends optionalEnv only after --file selects an exact file
in that lane.
File-specific test selectors do not reach sibling files, and optional inputs do
not select a file or change its required-case inventory.
Shared runtime image in ordinary CI
The four image lanes and First Agent Smoke use one runtime image built from the workflow's checked-out commit. On pull requests, that is the tested merge commit. The producer uploads an OCI image artifact; each selected consumer verifies its source, workflow run, archive checksum and platform before loading it. Packaging still builds its controller, and each lane owns its other preparation and tests. First Agent Smoke adds runtime trust and reuses its WebSocket package for the provider loaded into the job's Docker engine.
Full Integration also shares this runtime with Docker Model Runtime, Gateway Routing and Production TUI. When Gateway Routing or Production TUI selects the current runtime, both full and focused runs wait for the producer. Their job budgets cover import, controller preparation and tests. Focused Gateway runs with explicit Gateway/Agent images skip the producer; full runs still wait for it because other lanes need it. Local TUI upgrade checks retain their selected baseline and candidate images.
The shared action passes the downloaded directory in
OCC_CI_RUNTIME_ARTIFACT_DIRECTORY and the producer outputs in
OCC_CI_RUNTIME_ARTIFACT_SHA256, OCC_CI_RUNTIME_ARTIFACT_SOURCE_SHA,
OCC_CI_RUNTIME_ARTIFACT_RUN_ID and OCC_CI_RUNTIME_ARTIFACT_ATTEMPT.
These are CI preparation inputs, not product configuration. Supply the complete
selection or omit all five; a partial selection fails.
An invalid or missing selected artifact fails preparation; it does not start a replacement source build. A partial rerun may reuse the successful producer's artifact from the same workflow run and source. If that artifact has expired, rerun the producer as well. Local runs without an artifact selection keep their source-building path.
The producer's build has a provisional 180-minute limit within a 240-minute job. These limits allow measurement of cold builds; they are not expected build times. The CI runtime image timing notice measures source argument preparation, the Buildx build and OCI export. Local image verification follows the notice. Inspect producer build/export and consumer download/load timings separately from test durations. Ordinary artifacts stay within their workflow and do not replace native container verification or authorize publication.
Find a retained runtime before building locally
Before a cold local build, check CI runs triggered by pushes to main for the
exact source revision. This example does not search manual, pull-request or
merge-group runs; an empty result rules out only matching main-push runs.
Discovery uses GitHub CLI access to Actions and jq; it does not load an image
or qualify a release. Use the full commit SHA, not a branch name:
SOURCE_SHA='<full-40-character-commit>'REPOSITORY='openclaw/openclaw-enterprise'gh api --method GET --paginate \ "repos/$REPOSITORY/actions/workflows/ci.yml/runs" \ -f head_sha="$SOURCE_SHA" -f event=push -f branch=main -f per_page=100 \ --jq '.workflow_runs[] | {id, run_attempt, head_sha, status, conclusion, html_url}'Select a run whose head_sha matches. Inspect its runtime producer in the
selected attempt; a green unrelated job is insufficient:
RUN_ID='<run-id>'PRODUCER_ATTEMPT='<attempt-number>'gh api --paginate \ "repos/$REPOSITORY/actions/runs/$RUN_ID/attempts/$PRODUCER_ATTEMPT/jobs?per_page=100" \ --jq '.jobs[] | select(.name == "Shared Runtime Image / Build ordinary CI runtime") | {id, run_id, run_attempt, head_sha, status, conclusion, html_url}'Require one matching producer with the selected source, run and attempt,
status: completed, and conclusion: success. After a partial rerun, the
successful producer can belong to an earlier attempt; inspect that attempt
instead of assuming the latest run attempt produced the image.
List the image and separate small provenance receipt for that producer:
gh api --paginate "repos/$REPOSITORY/actions/runs/$RUN_ID/artifacts?per_page=100" | jq --arg image "ci-runtime-$RUN_ID-$PRODUCER_ATTEMPT" \ --arg receipt "ci-runtime-receipt-$RUN_ID-$PRODUCER_ATTEMPT" \ '.artifacts[] | select(.name == $image or .name == $receipt) | {id, name, expired, expires_at, digest, size_in_bytes, workflow_run}'Require one image artifact and, for the small download below, one receipt artifact,
with expired: false, a future expires_at, and
workflow_run.id / workflow_run.head_sha matching the selected run and source.
Record both artifact IDs and digests. These artifacts expire after one day;
an absent or expired artifact cannot satisfy this lookup. The API digest
identifies the downloaded artifact ZIP, not the image manifest. If the separate
receipt upload is missing, the image archive also contains receipt.json; inspect
it during a separately verified image transfer.
Download only the small receipt into a fresh directory to inspect the platform and image identities before transferring the image:
RECEIPT_DIR="$(mktemp -d)"gh run download "$RUN_ID" --repo "$REPOSITORY" \ --name "ci-runtime-receipt-$RUN_ID-$PRODUCER_ATTEMPT" --dir "$RECEIPT_DIR"jq '{sourceSha, runId, producerAttempt, platform, archiveSha256, indexDigest, platformManifestDigest, configDigest}' "$RECEIPT_DIR/receipt.json"Compare sourceSha with the intended Enterprise commit, runId with RUN_ID,
producerAttempt with the successful PRODUCER_ATTEMPT, and platform with the
local target platform. Its archiveSha256 covers runtime.tar. Discovery and a matching receipt
alone do not verify downloaded image bytes, native behavior or installed
acceptance. The existing runtime-artifact helper accepts only same-workflow
consumers; there is no supported local import command. Do not set GITHUB_*
variables to bypass that check. Retain this evidence when planning separately
verified local reuse or a source build. For release-qualified image transfer,
follow native image checks.
Results and cleanup
A lane fails if required cases are missing, skipped, marked TODO, or failed, or if cleanup fails. This includes skipped suites and Go tests. Results prove only selected coverage: inspect outcomes, source, cleanup and image digests. Retries have attempt-specific artifacts and replace the current result. Kubernetes preparation failures retain sanitized diagnostics when collection succeeds. Missing state or empty inventory does not prove cleanup.
Shared k3d runtime preparation adds runtimeProvenance to lane results and
Kubernetes diagnostics for the selected Gateway and Agent images. Each record
lists the observed local image ID and imported platform manifest digest.
An available record confirms their config digests match and includes the image-reported Codex version, binary SHA-256,
platform and upstream openclawCommit; unavailable means the metadata or
image binding could not be verified. This is diagnostic evidence, not release
qualification or an additional readiness check. The result's sourceSha names
the tested checkout, not an image build revision. Source builds may still have
no OCE build revision, and the upstream OpenClaw commit is a separate identity.
No image configuration, credentials or arbitrary provenance fields are uploaded.
Collection bounds the archive before accepting a single regular metadata file;
directories, oversized files and unsupported archive records remain unavailable.
Provenance reads and diagnostic merges require the same lane and preparation.
The public preparation identity hashes the private resource prefix; stateless
runs receive a fresh invocation identity. Validated current-run evidence
survives state cleanup.
For a failed matrix job, inspect each lane's result artifact to find the failing
lane. The aggregate reports the parent failure once and requires every selected
lane's same-source evidence, even when other lanes passed. If the artifact is
missing, check the CI lane first failure annotation when present. It reports
the first failed or cancelled action step, or unavailable when unknown.
Run a complete local attempt
On Linux, give the runner one fresh private attempt directory to prepare, run and clean up a lane with one command. Its parent directory must already exist, belong to the runner's user, and deny group and other write access:
node scripts/ci/run-tests.mjs run checks-baseline-1 --lifecycle complete \ --attempt "$HOME/oce-profile-wire-attempt-1" \ --file tests/conformance/openshell-provider-profile-wire.test.tsChoose an unused attempt name. This example runs one existing wire-conformance
file without preparing services. Omit --file to run the whole selected lane.
The command validates the selection before preparation and retains the lane's
required inputs. A file-only run requires all of the file’s expected cases.
It does not select images, provide missing credentials, or enable optional
wire tests. Follow the selected lane's setup requirements first.
To select one top-level standalone Node.js case, keep --file and add
--test-name. With Docker Compose available, this example prepares PostgreSQL,
runs the idle-backend regression, and cleans up. Run it from the root of the
checkout you want to test. The pnpm prefix checks its installed dependencies
before the runner starts Docker. If pnpm reports a stale installation, run
corepack pnpm install --frozen-lockfile in that checkout and retry. The
runner’s --root option does not change the directory pnpm checks.
corepack pnpm exec node scripts/ci/run-tests.mjs run postgres-platform --lifecycle complete \ --attempt "$HOME/oce-postgres-idle-attempt-1" \ --file tests/integration/postgres-connection-auth.test.mjs \ --test-name 'the pool survives PostgreSQL terminating an idle backend'The value is a nonempty literal name of at most 256 UTF-8 bytes. Missing --file,
names outside that range, <root>, and the selected file's absolute path fail
before preparation. For names beginning with --, use
--test-name='--dry-run preserves state'. Both forms select literal names;
the runner rejects a second selector before preparation or attempt creation.
A bare --test-name still needs a value.
The runner rejects --test-name-pattern.
The name must exactly match one required case in the selected file’s manifest.
Exactly one standalone top-level case with that name must pass. Missing, extra
or duplicate cases, suites, nested tests, skips, TODOs and failures fail the run.
Use a file-only run for suites or nested tests. Other registered cases may be
omitted. Results record the literal testName and scope: focused, including
when preparation fails; aggregation rejects them as full-lane evidence.
Complete mode uses only <root>/scripts/ci/test-suites.json, where --root
selects the checkout and defaults to the runner's checkout. You can omit
--manifest or explicitly supply that canonical path. Relative manifest paths
resolve from the selected root; normalized relative and absolute paths must equal
the canonical pathname. Symlink aliases are rejected. A different manifest fails
before the runner creates an attempt or starts any phase, so selection,
preparation and execution use the same definitions. Ordinary externally managed
run --state ... --results ... still accepts alternate manifests.
Complete mode rejects --state and --results. It creates the attempt directory
exclusively with mode 0700, then owns state.json, run.json and result.json
inside it. Do not write into an active attempt or reuse its directory. A competing
invocation that cannot claim the directory starts no phase and cleans nothing.
The final result.json includes separate lifecycle.phases.prepare, run and
cleanup outcomes. It is written after cleanup, or after the command determines
that cleanup cannot start safely. Passing tests alone do not make the attempt
pass. The ordinary test result, when available, remains in run.json even if later
cleanup fails. Phase exceptions use closed failure codes; command arguments and
output are not copied into phase errors.
SIGINT, SIGTERM and SIGHUP request deferred cancellation. The command reports the
request immediately, waits for the current phase, then cleans up without starting
the next test phase. Cancellation still fails the attempt. Preparation and tests
share a 30-minute deadline unless CI_RUNNER_DEADLINE_MS supplies an absolute Unix
millisecond deadline. Cleanup has its own CI_RUNNER_CLEANUP_TIMEOUT_MS allowance
(default 120 seconds). This mode does not promise immediate interruption.
A timeout, lost phase result or unconfirmed process termination produces
reconciliation-required and prevents a later cleanup phase. Command owners stop
their process groups when their phase loses its parent. Retain the attempt,
confirm its processes have stopped, then use the existing cleanup command below
with its state.json. Do not remove another attempt's resources. SIGKILL or host
failure can prevent a final result; a missing result is not cleanup evidence.
The runner starts each phase through a private IPC channel. Preparation and cleanup report their owned command groups. The runner checks that these groups have stopped before it starts cleanup.
This mode covers resources recorded by the selected preparation and test owners. It does not add installed-image qualification, verify unrelated resource absence, or restore host settings changed outside those owners.
Keep an owned service between file runs
From the checkout root on a disposable host, prepare the lane and keep its private state file until cleanup completes.
corepack pnpm exec node scripts/ci/prepare.mjs --lane <lane> --state <private-state-file>node scripts/ci/run-tests.mjs run <lane> --state <private-state-file> --results <results-file>node scripts/ci/cleanup.mjs --state <private-state-file>Run cleanup after preparation or test failures too. See local browser diagnostics and the CI flow for failure evidence.
For focused local proof, first validate the exact manifest path with
node scripts/ci/run-tests.mjs check-selection <lane> --file <manifest-path>.
Then prepare the lane and pass the same --file argument to run. The run
command also accepts --test-name for the top-level cases described
above; check-selection validates only the file. Omit both selection arguments
for the complete lane. The composite action validates focused selection before
tool setup or preparation; an unknown path, path alias, or another lane's file fails.
Manual tests
See Manual tests for lanes without GitHub workflows, their setup requirements, and the disabled installed repository-credentials lane.
