> ## Documentation Index
> Fetch the complete documentation index at: https://docs2.openclaw.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Scope selection

Changed-scope detection, native lane selection, and the per-area routing rules that decide which lanes a diff selects. Part of the [CI scope and routing](https://docs2.openclaw.ai/ci/scope-and-routing.md) index.

Runner placement is separate from coverage selection. On automatic canonical hybrid first attempts, preflight may offload security, the three Control UI unit rows, and only the browser-extension E2E row when the [complete hosted base has at most 40 rows](https://docs2.openclaw.ai/ci/capacity.md#bounded-hybrid-hosted-offload), keeping optional additions within 45 total. Base counts above 40 keep those rows on Blacksmith with their original tests and workers; an eligible base above 45 also emits a warning while retaining the complete base manifest.

## Scope and routing

Full main CI is [hourly by default](https://docs2.openclaw.ai/ci/scheduled-workflows.md#hourly-main-ci).
Main-push lane selection below describes the opt-in `OPENCLAW_CI_ON_PUSH=true`
path. Hourly runs use ordinary full manual coverage, including Android, without
changed-path filtering.

Scope logic lives in `scripts/ci-changed-scope.mjs` and is covered by unit tests in `src/scripts/ci-changed-scope.test.ts`. Ordinary manual dispatch skips changed-scope detection and makes the preflight manifest act as if every scoped area changed. The exact-head `release_gate` exception evaluates the fetched pull request merge tree and retains its macOS, iOS-build, and generated-native-locale decisions while still verifying native sources.

Labeler skips PR edits without title or base-branch changes. These ignored edits use isolated per-run concurrency groups so they cannot cancel running labeling or replace useful pending work. Opened, reopened, synchronize, and title/base-edit events retain the shared per-PR group and supersede older labeling runs. Issue labeling and manual backfills retain their existing non-cancelling ref group.

Affected pull requests, `main` pushes, and exact-head `release_gate` fallbacks run one required `ios-build (smoke)` phase: the existing `pnpm ios:build` command and Swift lint, with their Xcode, Swift, and Watch Rust tooling. They do not run Rust tests, lifecycle or Watch simulator tests, or screenshot capture. A failed, cancelled, or unexpectedly skipped smoke still fails `openclaw/ci-gate`.

Ordinary manual CI (`workflow_dispatch` with `release_gate=false` and `release_scope=full`), including full-scope release validation, retains separate Release device and Debug/native-test phases plus the full iPhone, iPad, and Watch screenshot matrix. The test phase runs the Rust engine, lifecycle, and Watch operation tests. The two device shards build their own simulator apps independently of `ios-build`; scenarios stay serial within each device, with Watch evidence in the iPad shard. A hosted reducer verifies the exact evidence union and provenance before publishing the canonical screenshot artifact. The final gate requires both build phases, both screenshot shards, and the reducer. Frozen compatibility targets retain their existing Debug-only full-manual contract and screenshot exclusion; `npm-beta` and `npm-stable` still defer native qualification. All iOS build phases and screenshot shards use Xcode 27 on GitHub-hosted `xcode-27`, the preview macOS 27 image. A pure iOS app change does not select macOS jobs by itself.

The iPad shard captures Watch from the Watch app compiled by its own fresh screenshot build, after all four iPad captures pass. It does not rebuild Watch or transfer simulator products between jobs. The standalone `watch_screenshot` lane still prepares and builds a fresh Watch app. A missing or invalid product fails capture without a rebuild fallback; completed iOS screenshots and result bundles remain available in the failure artifact.

Separate iOS and macOS Periphery workflows enforce a zero-findings dead-code policy. Each runs only when a non-draft pull request touches its native scan scope, or when manually dispatched.

The shared PR commenter reads each producer's fixed run title to distinguish report admission, explicit `converted_to_draft` cleanup, passive draft events, and manual runs. Reports require a live open PR at the same repository and head with draft status off; draft cleanup requires draft status on. Passive runs cannot publish or supersede reports. Newer eligible runs and attempts supersede older results, including while pending. A report-admitted run that successfully detects no scan scope can clear an existing comment; draft cleanup names draft status instead of claiming scope loss. The commenter rechecks PR state, repository, head, and draft status immediately before writing, but separate REST calls are not atomic.

Runs without recognized admission metadata are logged no-ops: they neither publish nor supersede. After rollout, a new pull-request source event is needed to produce an eligible run; rerunning an old unmarked run does not recover its original admission intent.

The iOS, macOS, and both shared OpenClawKit Periphery scans use Xcode 27 on GitHub-hosted `xcode-27`. The toolchain change preserves all four hosted scan jobs, their scope, workloads, timeouts, artifacts, and rerun behavior; it changes no Blacksmith placement or registration counts. Periphery remains pinned to 3.8.0, with native compatibility required across all four consumers when its toolchain changes.

* **Max-lines baseline maintenance** uses the existing required `checks-fast-baseline-ratchets` guard on pull requests. When that guard is selected, changing only `config/max-lines-baseline.txt` does not add unrelated tooling tests; mixed changes retain their owning tests and boundary checks. The guard still verifies the exact tested merge tree against its prepared base and rejects baseline expansion or stale entries. Changes to the ratchet implementation, CI routing, or other config files retain their existing test selection.
* **CI workflow edits** validate the Node CI graph, workflow linting, and the Windows lane (`ci.yml` executes it), but do not force iOS, Android, or macOS native builds by themselves; those platform lanes stay scoped to platform source changes.
* **Native inputs beside Node changes** retain every selected macOS, iOS, or Android check and any Node tests that import or read those inputs. A native-only input with no Node test owner does not widen the Node plan when all of its native checks are admitted. Missing native coverage and unresolved tooling inputs retain the conservative fallback.
* **Compact planner policy changes**, including edits to `test/scripts/ci-node-test-plan.test.ts`, select the complete compact core plan on Blacksmith and hybrid PR runs. The GitHub runner profile retains precise changed-test targeting. This exercises inventory and worker-policy assertions in the packed jobs they protect.
* **Control UI source fallback** retains complete compact rows for the Node UI suites and their host-side import consumers, plus tests that read the changed UI files. Unrelated core changes still resolve independently. This partial fallback requires the dedicated browser and E2E lanes; it preserves each retained row's test inventory, process settings, and timing floor. Missing sources and unresolvable owners retain a diagnostic fallback; public SDK and workspace aliases follow the shared import graph.
* **Documentation beside code** keeps all `docs/` content, including navigation JSON, generated metadata, and assets, plus README files, `AGENTS.md`, and skill Markdown from widening the code's Node plan. Packaged workspace templates and test fixtures retain their runtime owners. Docs-only routing still selects no Node rows; unrelated runtime Markdown outside `docs/` retains its owner.
* **PR dependency and helper selection** follows import/re-export reachability through workspace package and public SDK source aliases. Helpers select their transitive consumers, including tests imported by other tests. Package and lockfile changes compare resolved direct dependency closures against the exact merge-tree base, including transitive versions, peer resolutions, integrity, and removals; only consumers of changed resolutions enter the Node plan. Descriptive metadata and unchanged resolved ranges add no Node tests. Plugin-owned `openclaw` package metadata selects its plugin and eligible import readers; the existing release-only host plugin suite keeps its tier. Main retains its complete automatic Node tier.
* **Erased TypeScript modules** can avoid broad Node fallback for an unresolved source self-target only when the native parser proves that both the exact diff-base version and the candidate emit no runtime code. A new file requires a successful base-tree lookup proving its absence. Production checks, every consuming test compiler graph, and graph boundary checks remain scheduled; ambiguous compiler ownership keeps the full compiler plan. Actual import consumers, file-reader tests, sibling tests, and policy watches stay selected. Ambient and declaration files, removal of runtime code, parse failures, and unavailable history retain the conservative fallback.
* **Filesystem source scanners** retain their declared watched inputs alongside ordinary importers. `src/tasks/task-boundaries.test.ts` scans `.ts` files throughout `src/`, except `.test.ts`, `.test-harness.ts`, `.test-utils.ts`, and `.e2e-harness.ts`; changes outside `src/tasks/` can affect it. Its policy watch supplements each changed source's tests and never supplies a missing ordinary owner.
* **Global Node inputs** retain broad coverage: shared test bootstrap/configuration, root compiler resolution, workspace/install settings, patches, shared Vitest/Vite/tsx updates, package execution/resolution fields, and compact packing policy. Missing or unverifiable source/dependency ownership fails safe with a separate reason. UI and wizard locale catalogs and translation metadata select no PR Node or browser test rows. TypeScript catalogs retain the ordinary compiler and check gates independently of that test suppression. UI and native catalogs retain their dedicated localization verifiers; wizard catalog parity remains in `src/wizard/i18n/index.test.ts` on main and manual runs. Localization generators and runtime modules retain tests.
* **Generated Control UI boot metadata** selects its chunking and Vite tests through the shared test-target map. The existing UI performance lane still builds and checks the generated chunks; the metadata alone does not require unrelated core suites.
* **Preflight diagnostics** log `Node test plan changed-set` with the selected row count or `Node test plan broad fallback` with the first blocking reason. Reasons name global execution/resolution inputs, packing-policy proof, unavailable dependency history, the first path with an unresolved owner, and missing canonical execution metadata. Merely reaching a public SDK entry, changing a helper, or exceeding 96 test targets does not request the full plan. The logged changed-path manifest supplies the inputs for replay; a skipped preflight has no observed plan.
* **PR wrapper extraction** selects `pr-worktree-provision.test.ts` when the wrapper, its library, or a file in `scripts/pr-lib/wrapper-components.txt` changes. This manifest-derived policy watch supplements ordinary source tests because filesystem copying is invisible to the import graph. Manifest-only changes also run provisioning, including its duplicate-inventory and eager runtime import-closure checks.
* **Tooling test changes** under `test/scripts/**/*.test.ts` also select `test-projects.test.ts`, whose workflow-routing expectations depend on the test inventory. The changed tests retain their own coverage, and newly added workflow guards check routing expectations before merge.
* **Messaging test-root changes** under `src/auto-reply/` and `src/infra/outbound/`, plus core-test tsconfig inputs, select `tsgo-core-test-shards.test.ts`. This filesystem-inventory guard verifies exactly-once ownership; shard size against the advisory 720-root limit is reported as a warning only and never blocks CI. It supplements the changed tests' ordinary owners; other test-root paths retain their existing selection.
* **Codex app-server test changes** under `extensions/codex/src/app-server/**/*.test.ts` also select `test/vitest-projects-config.test.ts`, which verifies complete and unique full-suite test ownership. The changed tests retain their existing extension suite coverage.
* **Git-owner changes** to its action, base-commit policy, projection generator, lifecycle tests and support, or named owner-adopting workflows such as Workflow Sanity, QA Profile Evidence, Mantis ref validation/installers/worktrees, Docs Sync Publish Repo, OpenClaw Performance, the Linux/macOS/npm-placeholder release admission jobs, and plugin ClawHub/npm publication select the existing `macos-node` and Windows lanes. These run native checkout ownership proof without selecting Swift, iOS, or Android jobs; Mac app and shared-native changes retain their existing Mac lanes.
* **macOS Swift runner budgets** are 30 minutes per worker on GitHub-hosted `xcode-27` with Xcode 27, including automatic first attempts. Regular PR/main CI and PR `release_gate` dispatches run `tests` for Swift lint, schema checks, and app coverage build/tests, alongside `packages` for the Talk opt-out build, shared package suite, and standalone Swabble suite for current targets. Ordinary full-scope manual validation adds the independent `release` app build, moves lint/schema ownership to that phase, and retains health renders in `tests`. Every selected phase must pass the existing CI gate. At most two workers run concurrently; a failed phase does not cancel the other phases' diagnostics. The preview macOS 27 image changes neither coverage nor the Swift 6.3 source-language minimum; complete native proof is required on the new toolchain.
* **macOS fixture support** changes select the existing Mac Node gate so the shared managed-command and concurrency owner receives Darwin proof independently of Swift/app changes.
* **macOS Swift build caches** retain the original nanosecond timestamps and content hashes of their source inputs inside `apps/macos/.build`. The restore helper replays timestamps only for byte-identical regular files with matching permissions in the current input inventory; changed, missing, linked, or invalid entries keep their checkout metadata and invalidate through SwiftPM normally. The v6 archive keys include the phase, helper, toolchain, package graph, and source identities, with same-phase, same-graph prefix reuse. Each phase starts with a cold seed instead of restoring the former combined build archive, then records metadata immediately before its own trusted save. Both phases may restore the shared SwiftPM dependency cache; its sole eligible writer is `tests` in regular CI or `release` in full validation. Candidate cache trust is unchanged: cache-off validation compiles every selected phase cold. Historical targets retain their target-owned build commands.
* **Workflow Sanity** runs `actionlint`, `zizmor` over all workflow YAML files, the composite-action interpolation guard, and the conflict-marker guard. Actionlint configuration stays with this owner and does not widen Node test selection. The PR-scoped `security-fast` job also runs `zizmor` over changed workflow files so workflow security findings fail early in the main CI graph.
* **Docs on opt-in `main` pushes** are checked by the standalone `Docs` workflow with the same ClawHub docs mirror used by CI, so mixed code+docs pushes do not also queue the CI `check-docs` shard. Pull requests and manual CI still run `check-docs` from CI when docs changed.
* **TUI PTY** runs two built-CLI artifact canaries in `build-artifacts` on main and ordinary manual/release CI: a local model roundtrip and a real Gateway connection. The complete suite is defined in `test/vitest/vitest.tui-pty.config.ts`; canonical pull-request fallbacks and manual/release full plans retain its `core-runtime-tui-pty` descriptor. CI consumes that descriptor only through the built-artifact selection flag, so the full suite has no executing matrix row; manual and release CI also run only the canaries. Canonical `main` push compaction omits the full descriptor while keeping the canaries.
* **SQLite session lifecycle** runs on main and ordinary manual/release CI. Main selects the built-CLI migration, restart, compaction, cleanup, and session RPC proof only when the diff touches its direct storage/session owners or a reachable session path in the embedded runner. The `build-artifacts` verifier wave runs it against the runtime already built in that job, after the isolated startup-memory measurement. It overlaps independent readers on Blacksmith and stays serial on hosted runners; manual and release dispatches always select it when the target contains the proof.
* **CI routing-only edits, the small set of core-test fixtures the fast task runs directly, and narrow plugin contract helper edits** use a fast Node-only manifest path: `preflight`, `security-fast`, and only the fast lanes the change touches — a single `checks-fast-core` CI-routing task, the plugin contract job, or both. That path skips build artifacts, Node 24 minimum compatibility, channel contracts, full core shards, bundled-plugin shards, and additional guard matrices.
* **QA Smoke on `main`** runs when the diff touches the qa-lab harness, `qa/` scenario data, the matrix/telegram channels the smoke profile drives, Docker packaging scripts, or the gate's orchestration. Generic runtime, UI, workspace-package, and dependency changes do not select smoke by themselves. Manual CI and Full Release Validation retain the complete smoke profile; missing changed paths or an older planner without the selector retain coverage when the target supports the smoke harness.
* **Docker seed on canonical `main` pushes** uses `resolveDockerSeedLanes` to select exactly `published-upgrade-survivor` on every admitted run, independently of changed paths. It runs `legacy-operator-state` with `auto-auth` against an exact published predecessor. Main prepares the complete runtime and public SDK types with `pnpm build:ci-artifacts`, then uses the canonical package helper with `--skip-build` and its unchanged tarball integrity check. Ordinary manual CI, including Full Release Validation's `normal_ci` child and its `npm-beta`/`npm-stable` scopes, retains full package generation and also selects `cron-mcp-cleanup`, `fleet-cache`, `mcp-channels`, `mcp-code-mode-gateway`, and `update-channel-switch`. Targets must declare the Docker seed capability; older targets without the tier selector retain the survivor fallback. Pull requests and exact-head `release_gate` fallbacks omit these Docker proofs and retain selector, scheduler, update, Doctor, and state unit/boundary coverage. Docs-only main pushes remain excluded at the workflow trigger. Expanded release history stays in Package Acceptance and the weekly Update Migration workflow.
* **Control UI performance** uses the dedicated `run_control_ui_performance` manifest output. Production UI files, plugin browser entries, workspace packages, dependency and build inputs, performance policies, and relative imports reached by the UI or performance tooling select it. All workspace packages remain conservative owners because the relative import graph does not resolve workspace package aliases. Test-only files and generic runtime changes outside that graph do not select it. Manual dispatches, unknown changed paths, and older planners without the selector retain coverage; historical targets keep their existing performance-script availability contract.
* **Windows Node checks** are scoped to Windows-specific process/path wrappers, npm/pnpm/UI runner helpers, package manager config, and the CI workflow surfaces that execute that lane; unrelated source, plugin, install-smoke, and test-only changes stay on the Linux Node lanes. Test-only changes to any explicit target in `test:windows:ci:1` or `test:windows:ci:2` also select the existing Windows lane; these package scripts own its test inventory. The Windows planner balances their union into four or five whole-file rows (five with the current measured inventory); changing that planner also selects Windows.

Owner-selected main proof gates can leave regressions outside their selected
paths undetected until manual or release validation. PR unit/boundary checks do
not replace their process proofs. Push gates compare the triggering event's
`before` commit with its head, covering
every commit in that push. They do not accumulate changes from earlier pushes
whose pending runs were coalesced away (cancelled). If a coalesced or otherwise
cancelled main run never executes a selected owner-path proof, that proof can
remain unexecuted until hourly full CI, a later non-cancelled opt-in main run
selecting the same lane, or applicable manual/release validation runs. The published-upgrade survivor is
selected on every admitted canonical main run, so it does not depend on a later
owner-path match. Ordinary manual CI and Full Release Validation also select it
independently of changed paths, subject to the target's Docker seed capability.

## Process proof tier

Pull requests and exact-head `release_gate` fallbacks omit Docker seed, QA Smoke,
real-Gateway UI, and the named browser-host, Doctor, Discord, SQLite, Gateway
watch, and TUI built-process verifiers. Build artifacts, unit suites, native
Windows boundaries, and mocked-Gateway browser projects remain selected by
their existing owners. Main and ordinary manual CI retain the process proofs;
Full Release Validation dispatches that ordinary manual CI child.

`scripts/lib/ci-proof-test-inventory.mts` owns the complete files omitted from PR
Node plans, including Doctor refusal and Codex process replacement proofs.
Both compact and precise changed-target plans apply this inventory after owner
resolution; proof-only changes retain boundary coverage. Main and release
plans retain the complete files and all assertions. This explicit inventory
does not exclude E2E-named package-contract tests or the ordinary mixed TUI
suite when targeted directly.

`test/scripts/ci-workflow-planning.test.ts` checks proof-tier selection for PR,
main-push, ordinary manual, and PR-fallback events. The Docker scheduler guard in
`test/scripts/ci-workflow-guards.test.ts`
asserts that the selected `docker_seed_lanes` inventory reaches
`pnpm test:docker:all` through `OPENCLAW_DOCKER_ALL_LANES`, and its release-child
guard verifies that `normal_ci` dispatches `ci.yml` against the exact target.
`test/scripts/ci-workflow-evidence.test.ts` owns QA protocol, evidence-reader,
and maturity handoff checks. Fast CI-routing and cache warming include all three
files; YAML action-pinning checks retain their original guard owner.
The Node planner tests separately assert that main retains the complete Doctor
refusal and Codex recovery files while PR and PR-fallback plans omit them.
