---
summary: "Changed-scope detection and the per-area rules that select CI lanes"
read_when:
  - You need to know why a lane was or was not selected for a diff
  - You are changing changed-scope detection
title: "Scope selection"
sidebarTitle: "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](/ci/scope-and-routing) index.

## Scope and routing

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.

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 GitHub-hosted `macos-26`. 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 always use GitHub-hosted `macos-26`. This transfers four existing scan registrations from `blacksmith-12vcpu-macos-26` to hosted capacity for eligible same-repository first attempts while preserving their scope, workloads, timeouts, artifacts, and rerun behavior.

- **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.
- **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 `macos-26`, including automatic first attempts. Regular PR/main CI and PR `release_gate` dispatches run only `tests`: Swift lint, schema checks, the Talk opt-out build, shared package suite, standalone Swabble suite for current targets, and app coverage build/tests. 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 phase's diagnostics.
- **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. 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 `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`: 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 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 pull requests** runs only when the diff touches a QA-owned surface: the qa-lab harness, `qa/` scenario data, the matrix/telegram channels the smoke profile drives, the docker packaging scripts, or the gate's own orchestration. Broad runtime changes (src, ui, packages, dependency manifests) no longer select the six-part smoke matrix per PR; the full profile set still runs for release validation and every canonical `main` push admitted by CI, so runtime regressions surface one push after merge instead of taxing every PR with roughly five extra hosted-runner minutes.
- **Published upgrades on pull requests** run the latest-release `legacy-operator-state` survivor inside `docker-seed-e2e` when update, doctor, state/schema, or survivor owners change. Source-tree tests and test helpers alone do not select this PR lane; survivor fixtures still do. Both schema-version constants are covered even when no updater source changes. Every canonical `main` push admitted by CI selects this lane independently of the diff. Docs-only pushes remain excluded by the workflow trigger; mixed docs and code pushes are admitted. Expanded release history stays in Package Acceptance and the weekly Update Migration workflow.
- **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.
