openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
205 lines (173 loc) • 41.8 kB
Markdown
---
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.
Runner placement is separate from coverage selection. On automatic canonical hybrid first attempts, preflight may offload security, up to three selected Control UI unit rows, and only the browser-extension E2E row when the [complete hosted base has at most 40 rows](/ci/capacity#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. Narrow PR admission reserves the full lint/type row templates plus one hosted `check-plan` job. Later row reductions do not expand that admission, so optional offloads can remain on Blacksmith even when the final selected inventory would fit.
## Scope and routing
Main-tier CI is [hourly by default](/ci/scheduled-workflows#hourly-main-ci).
Main-push lane selection below describes the opt-in `OPENCLAW_CI_ON_PUSH=true`
path. Hourly runs select the complete main tier, including Android, without
changed-path filtering. Full manual and release validation additionally select
the release-only proofs described in the scheduled-workflow guide.
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.
### Published-driver update cell
`scripts/lib/ci-published-driver-update-plan.mts` selects the required
`published-driver-update` job on PRs only for the updater, activation, handoff,
and canary owners: `src/infra/update-*` (including
`src/infra/update-managed-service-handoff*`), `src/cli/update-cli/**`,
`src/cli/startup-trace.ts`, and `src/gateway/server-startup-trace.ts`. The cell
follows the shared candidate build, so selecting broad state, identity, plugin,
or tooling changes would add its full runtime to those PRs' critical path.
State leases, state database admission, generic SQLite identity, native-plugin
files, and updater or CI scripts therefore defer this proof to hourly main and
full release validation. Their ordinary owner tests still run on PRs.
An unavailable diff retains the cell. Current main-tier and ordinary
manual/release CI select it independently of changed paths, while frozen targets
predating the harness omit it. Execution also requires the selected checkout revision to equal
the caller's `github.sha`. Exact-head dispatch fallbacks and other target-ref
dispatches selecting a different revision skip this cell and record the reason
in the CI gate summary; they do not provide published-driver proof for that target.
The reusable workflow checks out only `github.sha`, with credentials disabled,
read-only contents permission, no inherited secrets, and dependency caching off. It cannot
accept a caller-selected checkout ref in a different cache scope. The selected
`build-artifacts` job packages its existing build with the canonical integrity
check and publishes one candidate tarball. The cell downloads that same-run
artifact by its immutable ID, verifies its SHA-256, and skips host dependency
installation and candidate compilation. Successful producer artifacts can be
reused when only the failed consumer job is rerun.
The checksum gate prints both digests and rejects mismatches before starting
the update. The download action's `digest-mismatch: error` input configures its
archive verification policy; that input line is not a reported mismatch.
The published driver version is resolved once from npm. Its installed prefix is
cached as an archive keyed by that version, runner platform, and the pinned
Docker recipe. PRs only restore this exact key and mount the seed read-only;
the update runs against a private extraction. Only canonical main push,
scheduled, and manual runs prepare and save missing seeds, before any candidate
executes. A missing seed retains the ordinary published npm install path.
The PR cell targets five minutes after the shared candidate build. PRs use the
serving published Gateway to initialize synthetic state; main and release runs
also retain the initial Doctor repair pass. Readiness is checked every 100 ms
with a 30-second overall and one-second per-request limit. The same managed
update, recorded-run, restart, running-build, and readiness assertions apply in
both modes.
The cell reserves termination and diagnostic time within its twenty-minute budget.
Its command deadline returns a failed step with the active phase recorded,
instead of cancelling the workflow through a job-level timeout. The shared bare
Docker runner installs the latest stable npm package as the driver and supplies
a disposable OS account for managed-service ownership. A private Docker volume
holds the disposable npm installation and retained runtime on one native
filesystem, avoiding OverlayFS's forced full-runtime copies. The runner removes
the volume after its container settles, including failed updates. It performs one
managed update with two synthetic agents and isolated state, requiring a
finished update, the running candidate version, HTTP 200 from `/readyz`, no
canary/identity/lease warnings, and no candidate-startup or authority-check
failure in the recorded run. This catches compatibility failures hidden by
tests that use the same source for both driver and candidate. The selected job
is required by `openclaw/ci-gate`, reserves one GitHub-hosted runner in the
existing capacity accounting, and has a twenty-minute cell budget.
The fixture uses `agents.entries` with explicit ownership so the published
driver accepts both agents before updating.
`OPENCLAW_PUBLISHED_DRIVER_LEGACY_SQLITE=1` also seeds a retained node-host launch
journal in the shared-state database using the published 2026.10.1 schema, without
the later lazy boot-identity table. The update must preserve that launch alongside
the existing session, transcript, search, and SQLite reclamation assertions. This
is persisted-state upgrade proof; it does not exercise a running remote node.
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 retain one required `ios-build (smoke)` phase and its Xcode, Swift, and Watch Rust tooling. Current smoke always runs Swift lint and compiles the app and test bundles once with `build-for-testing`. On ordinary PRs, `scripts/lib/ci-ios-smoke-plan.mjs` selects the existing voice/media/typography and Access/chat lifecycle simulator groups from their source owners. Both groups own the app's real hosted startup and linked runtime sources; the voice group also audits Watch and ActivityWidget typography. Their selected test files and fixtures select their respective group. Build/test configuration and unavailable path metadata retain both groups.
When neither simulator group is selected, smoke uses a generic simulator build destination and skips simulator selection, boot, and SimSlim preparation. Selected groups keep separate `test-without-building` invocations against the same built products. The preflight and iOS job summaries record each group's selection and reasons. Repository variable `OPENCLAW_CI_IOS_SIMULATOR_FULL=true` (or `1`) restores both groups for every selected PR job; it does not select an otherwise unneeded iOS job. Main, scheduled, manual, release-gate, and compatibility planning retain full group selection. Existing historical execution contracts remain unchanged.
Smoke still omits Rust tests and the broader app/notification lifecycle and Watch simulator suites. A failed, cancelled, or unexpectedly skipped smoke still fails `openclaw/ci-gate`.
Hourly main-tier CI retains `ios-build (tests)`, including Rust, voice cleanup, native Access, and every focused app/notification lifecycle test, including its Watch messaging and activation coverage. Managed attachment UI/export proof, Watch operation simulator suites, and Watch delivery UI proof belong to full-tier manual/release validation; hourly runs also exclude screenshot capture. Main-tier and smoke builds use the selected simulator's native architecture without compiler indexing; voice and lifecycle tests retain logs and xcresult bundles without Xcode's verbose diagnostic collection. Only a canceled selected scheduled `ios-build` on canonical `main` can pass the aggregate with a coalescing notice; this delegates proof to a later job rather than validating the canceled revision. Other failures, unexpected skips, and cancellations remain failures. See [scheduled iOS aggregation](/ci/scheduled-workflows#hourly-main-ci).
Screenshot-input changes on pull requests, admitted pushes, and their exact-head `release_gate` fallbacks select both device shards and the evidence reducer through `shouldRunIosScreenshots`: iOS UI/resources/UI tests and Fastlane configuration, linked Swift sources, and capture/build tooling. Native unit-test-only Swift edits do not select screenshots.
Full-tier manual CI (`workflow_dispatch` with `validation_tier=full`, `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 keeps universal simulator builds, failure diagnostics, Rust, voice cleanup, native Access, lifecycle, managed attachment UI/export, Watch operation, and Watch delivery UI tests. Every case and assertion moved out of hourly CI remains in this phase. 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. Provenance binds every shard to the reducer's run, target SHA, and workflow SHA, not its attempt: rerunning one failed shard reuses the other shard's evidence from its earlier successful attempt, while GitHub starts the reducer only after each shard's latest attempt succeeded. 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 the Node tests that own, directly import, or explicitly watch those inputs. Native-only inputs do not request the complete Node inventory. Static compiler and boundary checks retain conservative coverage independently of runtime selection.
- **Compact planner policy changes** select the planner, inventory, routing, and policy tests owned by those files, plus affected import consumers, protected regressions, and the fixed smoke set. They do not select the complete compact core or every extension. Hourly main and Full Release Validation still exercise the full inventories through their existing owners.
- **Control UI source selection** retains the complete ordinary UI unit family when its area changes. Control UI E2E on PRs uses the existing PR-exempt owner policy: a fixed smoke cohort plus tests whose source owners, imported dependencies, harness inputs, or test files changed. Unmapped tests run on hourly main and in release validation; PRs still select them when their own file or an imported dependency changes. Only the core E2E harness, UI bundle configuration, explicit global app-shell owners, or unavailable changed-path information select the full Control UI E2E inventory. Shared components, styles, and feature modules follow explicit route/component owners and direct runtime imports instead; test and fixture imports retain transitive selection. Shared shell/store cycles do not make a leaf change select every route. Hourly and release validation own the full transitive composition. Owner selections share one browser row per 30 files, bounded by the existing row cap; full validation and the kill switch retain their existing row counts. The preflight job summary lists every selected file and its reasons. Set the repository variable `OPENCLAW_CI_UI_E2E_FULL` to `true` or `1` to restore full coverage for admitted Control UI E2E jobs on PRs. Hourly main and manual/release validation select the full Control UI E2E inventory, including the mapped compositions formerly restricted to release validation. Real-Gateway and browser-extension families retain their independent owners, so a browser-extension change need not select Control UI jobs. Node excludes files delegated to a selected UI job.
- **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** combines bounded owner areas, transitive test import/re-export reachability through workspace and public SDK aliases, explicit policy watches, and protected regressions. Protected tests also follow runtime imports across owner areas. One-hop proximity alone misses observed catches. Unrelated global inputs do not request the full runtime repository; owner and protected-test policy supplies the retained coverage.
- **PR extension lint** reports directly changed plugin directories, the Telegram/Codex/Slack smoke cohort for shared source changes, and direct consumers of changed public SDK entries (including test imports). It shares package-selection policy with the boundary checker; transitive and ambient-type fan-out runs on hourly main and release validation. The planner records selected extensions and reasons in its summary; no selected extensions means no extension lint work. Selected stripes retain the existing full declaration preparation, native typed lint program, and diagnostics. Lint/type policy changes, directly changed loose extension sources, and unavailable public-entry inventories retain full coverage. The existing `OPENCLAW_CI_EXTENSION_LINT_FULL=true|1` switch restores full PR coverage; unset keeps the aggressive default. Historical targets retain their full coverage, and non-extension checks keep their existing owners.
- **Erased TypeScript modules** retain their owner tests, affected runtime test consumers, and declared source watches. Type-only changes still select every consuming compiler graph through the separate static-check owner. Ambiguous compiler ownership, ambient/declaration files, and missing history retain complete static checks without widening runtime tests.
- **Filesystem source scanners** retain their declared watched inputs alongside ordinary importers. A policy watch supplements each changed source's tests and never supplies a missing ordinary owner.
- **Global and hub Node inputs** select their bounded owner tests, transitive import consumers, and protected regressions plus the fixed PR smoke set. Shared test bootstrap/configuration, root compiler resolution, workspace/install settings, patches, dependency hubs, and packing policy no longer request the complete runtime plan. Static checks retain full fallback where ownership is ambiguous. Missing changed paths or an unavailable bounded selector fail preflight instead of admitting every runtime family. Global package, lockfile, compiler, and shared Vitest inputs do not select every platform/browser family merely because Node ownership is broad; related protected tests remain selected. UI and wizard locale catalogs and translation metadata retain their dedicated localization and compiler checks without PR Node or browser rows; 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 and any bounded owner-selection reason. Missing or unbounded PR plans fail preflight; they never become the full compact matrix. The logged changed-path manifest supplies replay inputs. Hourly main and ordinary manual/release validation retain complete inventories through their existing owners.
- **PR check families** select static checks and guards from their own changed-path owners, independently of whether Node test targeting finds a precise plan. Lint and formatting reuse the local changed-check owner; lint configuration changes retain full lint. Semantic lint runs changed files and their transitive import consumers, including type-only imports and workspace/path aliases, through the existing shard owner. Each row receives only its assigned file list; workers do not rediscover the graph. Affected root tests retain their central compiler-config admission. Deleted, ambient, unsupported, and oversized selections retain full lint. Hourly main and Full Release Validation continue running full repository lint. Hosted profiles keep selected files in their existing stripes, while scripts, root tests, formatting, and localization checks retain their central owner. Preflight remains dependency-free and owns admission, static-check selection, and the original row/resource templates. For admitted narrow PRs, the hosted `check-plan` job installs through the existing Node setup owner, then materializes selected-file lint and compiler facts. It validates compiler ownership and selects every graph that consumes a changed file, including erased type imports into UI, plugins, scripts, and tests. Its consumers require a successful plan; failure cannot silently select a full fallback, and `openclaw/ci-gate` requires the planner itself. Existing production, test, and stripe jobs retain their runner placement and compiler concurrency; only rows with selected graphs run. GitHub and hybrid keep changed core-test consumers in their canonical stripes; the central row owns the selected extensions, scripts, and root-test graphs. Markdown and UI styles do not widen a mixed TypeScript change. Deleted, ambient, configuration, and unclassified inputs retain all compiler graphs. Shared fixtures and scanner policy inputs retain conservative check families. Conflict-marker and wall-clock deprecation guards run after Node setup in the required planner job. Other guards retain their generated native inputs. SDK subpath exports, extension import boundaries, declaration compile/canary proof, protocol generation, schema drift, and Knip remain blocking. Plugin and channel runtime contract tests use the exact changed-owner Node plan, including their watched SDK documentation and packaged skills. Narrow code changes run coercion checks once in the guard job. Main, ordinary manual CI, and historical targets skip this additional planner and retain their full check families. Static full fallback never widens runtime families. Bundled config metadata follows the shared schema owner map. The bundled/protocol row always verifies protocol generation; its full bundled runtime suite runs only on hourly main and ordinary manual/release validation. Specialized Bun launcher proof opts in for its direct launcher and module-generation owners. Startup, plugin, and channel tests retain their canonical configs within the changed-owner plan.
- **Browser integration on PRs** uses the Control UI file selection above while retaining complete ordinary real-Gateway and browser-extension families when their existing owners change. Unrelated families remain omitted unless protected or affected tests select a precise subset. Named release-only real-Gateway compositions retain their existing opt-in policy. Existing row caps, worker limits, serial/parallel ownership, and runner routes stay unchanged; small Control UI selections omit empty rows. Empty real-Gateway phase groups are removed; a desktop-only carrier keeps its required desktop proof without invoking a test phase with no files. Hourly main and Full Release Validation retain the full Control UI E2E set; historical manual targets without the selector retain their existing full target-owned path.
- **Extension package boundary selection** uses the PR's own merge-base diff, independently of restored receipt age. The job reuses the extension-lint planner’s bounded base-fetch action. A depth-one PR merge is compared with its verified raw first-parent tree, without requiring a complete ancestry walk. It checks directly touched plugin packages. Core/SDK declaration and shared compiler/dependency changes add a fixed smoke set: Telegram, Codex and Slack, chosen to cover 119 distinct directly consumed public SDK entries. Changes to a public SDK entry file also select packages that directly import that entry, including type-only imports. Transitive declaration consumers and main-only drift are intentionally left to the complete hourly main and manual/release checks. Selected packages retain full compiler diagnostics and normal receipt validation. Declaration preparation follows their packaged entry points with the native compiler, emitting only consumed SDK roots and required plugin declaration producers, including transitive inputs and ambient declarations. Hourly and manual/release checks still prepare the complete declaration set. The negative boundary canary always runs. The job summary lists selected and skipped packages with reasons. Unset repository variable `OPENCLAW_CI_EXTENSION_BOUNDARY_FULL` enables this aggressive policy; `true`, `1` or `full` restores complete PR checks. An unavailable comparison or import inventory also retains full scope.
- **Shared SDK declarations** keep the existing boundary check as a standalone producer on supported hybrid PRs targeting the canonical repository that already select both the boundary row and the check planner. The job count and check name stay unchanged. That producer validates a full native SDK and publishes its declaration tree and checked receipt; extension lint waits for that artifact and validates it through the usual preparation owner. Other additional checks and test-type jobs retain their existing dependencies. A content-keyed main cache supplies the same SDK to other consumers, with receipt validation on every restore. Compiler directory lookups use sorted file and directory membership, so relocation can retain a valid receipt when filesystem enumeration order changes. Scheduled, manual, release, frozen, and unsupported targets retain their complete existing checks.
- **Extension selection** uses exact tests from the changed plugin owner, transitive test importers, protected regressions, and explicit policy watches. Global dependency, shared-runtime, SDK, and planner inputs do not append a whole-plugin fallback. The existing Plugin Prerelease workflow owns the complete extension runtime inventory hourly and in Full Release Validation; normal CI does not append a second partial inventory.
- **PR builds** select `build-artifacts` for a dist-dependent row, an affected build/package owner test, or an individually selected built-process proof. Pipeline ownership comes from the existing changed-target map for build, declaration, package-tarball, and dist-artifact tests; generic runtime changes do not request the full artifact job. Source boundary guards and affected channel tests keep their Node owners. An artifact build does not also select an unrelated dist boundary, channel family, or every process verifier.
- **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.
- **Selector integration proofs** retain the complete real-checkout owner scenarios in their canonical config. Direct edits select them through the same owner mechanism. Bounded synthetic cases protect selection and budget behavior without expanding the whole checkout for each assertion.
- **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, ordinary manual/release CI, and PRs selecting their existing test owner: a local model roundtrip and a real Gateway connection. The complete suite is defined in `test/vitest/vitest.tui-pty.config.ts`. Narrow pull-request plans omit the named PTY process proofs and run the source-only harness assertion test as an exact target under that same config, without requiring dist. Manual/release full plans retain the `core-runtime-tui-pty` descriptor; PRs have no broad fallback. 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 for its existing storage/session owner scope on PRs, exact-head PR fallbacks, and main; ordinary manual/release CI retains it. Owner selection covers 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; ordinary manual and release dispatches always select it when the target contains the proof.
- **CI routing and plugin contract helper edits** use the same changed-owner Node plan and static correctness gates as other PRs. Historical fast-only scope hints cannot bypass that plan or select whole contract families. Hourly main and ordinary manual/release validation retain their complete routing and contract inventories.
- **Release tooling fast lane** applies when a canonical pull request carries the `release-fast-lane` label and every changed path is release tooling (`.github/workflows/**`, `scripts/**`, `test/scripts/**`, `.agents/skills/release-*/**`, `docs/reference/RELEASING.md`) or independently checked documentation. The manifest keeps `security-fast`, `check-shard`, `check-docs`, and the changed Node rows, relaxes the compact packing-policy proof to those rows, and skips every other lane; the preflight step summary lists the admitted and skipped lanes under `Release fast lane`. A declined label (out-of-scope path, global Node input, fork, push, dispatch, docs-only) warns and keeps ordinary selection. Hourly full main CI still covers the merged result.
- **QA Smoke on PRs and `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. Exact-head PR fallbacks use the same owner map. Ordinary 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** uses `resolveDockerSeedLanes` to select exactly `published-upgrade-survivor` on every admitted canonical main run, hourly by default and independently of changed paths. It runs `legacy-operator-state` with `auto-auth` against an exact published predecessor. Frozen targets declaring that scenario in their inert catalog retain the same composition; historical targets without that declaration keep the `base` fallback, and invalid catalogs fail. Pull requests and exact-head `release_gate` fallbacks defer the complete survivor to hourly main and Full Release Validation. They retain selected Node and static correctness gates, plus the other Docker lanes through `resolveChangedDockerSeedLanes` and its owner map. No survivor case or assertion is removed. Main and selected PR Docker lanes prepare the complete runtime and public SDK types with `pnpm build:ci-artifacts`, then use the canonical package helper with `--skip-build` and its tarball integrity check. Full declaration generation remains in the hourly protected cache warmer and ordinary manual/release package builds. Ordinary manual CI, including Full Release Validation's `normal_ci` child and its `npm-beta`/`npm-stable` scopes, retains full package generation and all six Docker lanes. Targets must declare the Docker seed capability; older targets without the tier selector retain the main/release survivor fallback. 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` output. Production UI files, plugin browser entries, and the UI build/performance policies select it. Other modules select it when a runtime import path from a UI source or performance script reaches them; workspace aliases use the shared import-graph owner. Type-only edges remain covered by static checks. Unrelated inputs retain hourly main and ordinary manual/release validation. Test-only files remain with their changed-test owner. Historical targets retain their existing performance-script availability contract.
- **Windows Node checks** use the existing Windows-sensitive process/path/filesystem/service and test/planner owner map. A selected Windows owner retains the complete package-script-owned Windows inventory and all five existing measured rows, preserving their runner placement, packing, and estimates. Narrow Node target selection never clips that platform family. The fixed smoke set alone does not select Windows; unrelated PRs omit it. Hourly main and ordinary manual/release validation retain the complete inventory.
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 a later run selects it. Hourly main-tier CI covers its
retained lanes; release-only compositions require applicable full manual or
release validation unless a direct edit selects them. 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 admit the remaining Docker seed lanes, QA Smoke,
and built-process verification through their existing owner maps and selected
test targets. The intact published-upgrade survivor runs hourly and in full release validation.
Unrelated PRs defer the other jobs. Native-host, built Doctor, Discord
attachment, Gateway watch, SQLite lifecycle, and TUI canaries each have an
independent selection flag; choosing one never selects every process proof.
Selected built proofs reuse the one artifact build. Main and ordinary manual CI
retain their established full-proof behavior; Full Release Validation dispatches
that ordinary manual child.
The named `CI_PROOF_TEST_FILES` stay outside ordinary Node rows so their dedicated
artifact/workflow owners execute them. The pre-existing complete FRV and installer
process tours remain manual/release-only; this change does not move those tours
onto PRs. Their faster source/continuation guards retain changed-owner coverage.
Install Smoke predates this selection change as a nightly and manual/release
workflow. Its existing 03:17 schedule and Full Release Validation child are
unchanged; no new PR trigger is added. Installer-owned Node tests still opt in
through the changed-source and direct-test owners. This is an existing timing
exception to hourly main coverage, not a new hourly install proof.
`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 when their owners select them; YAML action-pinning checks retain their
original guard owner. The Node planner tests separately assert that hourly main
and Full Release Validation retain moved PR-exempt files, while the existing
named process-proof exclusions remain outside ordinary PR execution.