The changes job runs Turborepo 2.9.14's affectedTasks query before installing application dependencies or provisioning services. turbo.json records package dependency propagation; apps/sim/turbo.json partitions the app into shared runtime, server, workspace UI, and acceptance tasks. .github/ci-suites.json turns those tasks into job matrices. The uploaded ci-plan artifact names changed files, selected tasks, attribution reasons, and any fallback.
A PR compares its merge base with the checked-out merge commit. Main/staging pushes execute all checks; the parent ci workflow skips checks on dev pushes, while PRs targeting dev still run checks. A manual checks run executes all checks unless supplied a full base commit SHA to reproduce a scoped plan. Missing history, an empty diff, unknown files outside the workspace graph, root script/CI changes, or a planner error run every check. Missing selection outputs fail the aggregate gate. The gate also rejects an omitted dependency, any failed/cancelled job, and any skip that its explicit selection flag did not authorize.
Dedicated docs verification uses the same planner, comparing pushes with their previous commit and PRs with their merge base. Site acceptance follows docs#build and its transitive package dependencies. The app's docs-consumer task depends on server runtime and that docs build: backend-only changes run the platform consumer against the docs app, while site changes also run corpus and browser checks. Independent SDK and unit-only edits skip those production jobs. The required docs HTTP gate verifies explicit site/platform selections before accepting any skip.
Docs production builds run once and publish an artifact to isolated HTTP, corpus, Chromium, and WebKit jobs. Source checks keep their own checkout because generator verification mutates inputs. Content caches use separate lint/source/build namespaces; generation validates every restore, repairs missing declarations, and removes obsolete modules. The Next build cache keeps its supported incremental behavior. Content and Next caches have size limits, and Next cache files expire after seven days; successful content eviction happens after all consumers finish.
| Change | Expensive checks selected |
|---|---|
| Independent SDK | That workspace's tests and type-check; no app build, services, or browsers |
| App unit test | App test shards and test type check |
| Integration test | Both provision paths; no HTTP/browser job merely for the test edit |
| Workspace UI | Database integration, mobile, SEO and live Electron, plus app tests/build |
| Landing content | SEO, app tests/build; no database integration or Electron |
| Shared runtime or transitive dependency | All dependent boundaries |
| One E2E harness | Its acceptance group and the shared production build |
The server boundary deliberately includes all API routes and backend domains. Shared modules and unclassified app source remain broad inputs: this is safer than pretending a route's static imports describe database, dynamic registry, or background-worker dependencies. Files read outside a package graph need explicit inputs: CLI acceptance imports packages/sim-cli/src/embed.ts, app tests read docs content, and live Electron and realtime acceptance cross app boundaries. When adding such a dependency, update the corresponding task inputs/dependencies and the real Git/Turbo selector scenarios.
Integration also depends on workspace UI because the desktop executor integration suite runs the production workspace stream dispatcher against PostgreSQL and Redis. Its explicit harness inputs include the database and realtime Vitest configs and integration setup files, which runtime tasks exclude.
SEO depends on workspace UI and server code: landing pages reuse workspace pricing and preview components, provider models/pricing, and the block registry. These directory boundaries are not isolated application dependency graphs.
affectedTasks already uses task inputs. Do not enable futureFlags.affectedUsingTaskInputs just for this planner: that flag also changes pruning, which can pull CI-only cross-app task dependencies into production Docker contexts.
Affected type-checks follow the package graph too; repository audits and lint run whenever the checks workflow runs. Workspace-only unit jobs use a free GitHub runner and do not start the app test shards.
Database integration uses 4-vCPU Blacksmith runners with sticky dependency disks, following CI_PROVIDER and falling back to standard GitHub runners with matching setup. A prior same-source comparison of 16 jobs per provider (32 total) had a slowest shard of 6m 04s on 2-vCPU Blacksmith versus 6m 55s on GitHub, with identical coverage. This is one paired sample, not a long-run percentile estimate. Evaluate runner changes against complete coverage and the overall CI gate, since a faster shard does not shorten a run when another job finishes later. Lightweight HTTP suites retain free standard GitHub runners and do not follow CI_PROVIDER.
GitHub runners install dependencies directly rather than transferring Bun and node_modules archives. In a same-source paired comparison, those restores took 26.2 seconds before an 82-millisecond install, while a clean install took 12.42 seconds. This is one paired observation; retain the policy only while repeated CI timings support it. Blacksmith keeps its sticky dependency disks. GitHub Turbo output caches remain enabled for jobs that request them, with a commit-specific key and a task/event/fork-scoped restore prefix, so a rerun can reuse an exact entry without creating another archive. Push jobs never restore pull-request entries.
Closing a same-repository pull request deletes only its GitHub merge-ref caches after verifying that it remains closed or merged. The regular pull_request workflow skips forks and Dependabot because their tokens cannot delete caches; those entries retain GitHub's normal cache eviction. Cleanup checks out no code and never selects a branch-wide or default-branch cache. Blacksmith sticky disks retain their separate lifecycle.
Memory-heavy builds and browser jobs retain appropriately sized Blacksmith runners. Electron uses 4-vCPU Blacksmith: both shards passed within 14 seconds of the 8-vCPU control; the free GitHub rest shard was slower and its lease run stalled.
Every HTTP and browser group consumes the same production build in an isolated job/database. Electron also uses that build and skips its development-route warm-up. Local HTTP and Electron development runs retain their dev-server behavior. Build inputs exclude test-only files so a harness edit can reuse an unchanged production build.
Desktop inbox protocol, assistant actions UI, and organization search UI run as three parallel HTTP jobs with isolated databases, apps, and reports. Each harness owns its acceptance task, so an edit selects its own job. The inbox protocol task depends on server and realtime code; the browser tasks also depend on workspace UI. Desktop inbox needs no browser. The UI jobs and browser extension install Chromium while their app starts, waiting for both before executing assertions; browser-install failures still fail the job and preserve the install log. No acceptance assertions are removed by the split.
Live Electron acceptance tracks its live-app suite, fixtures, and Playwright configuration separately from shell-only smoke tests. Changing a shell-only smoke case runs the dedicated desktop workflow without starting the app's production build and live lease checks.
Desktop smoke weights also stay outside the desktop runtime task. Editing weights reruns desktop partition coverage without selecting live-app acceptance.
Browser extension acceptance runs native interaction and access-revalidation groups in parallel jobs with isolated databases, apps, browser profiles, and reports. Both retain the same task boundary, so an affected extension harness selects both groups. Native streaming and first-open capture checks retain real timers. Access checks install Playwright's clock before the app mounts: advancing production polling timers preserves real HTTP and database authorization checks without waiting through each polling interval. Capture timestamps originate in Chromium's isolated world, so the harness restores wall time after each advance. The local browser-extension group still runs the complete suite; live-provider runs require this complete mode and retain native timing throughout.
Integration shard weights come from successful reports for both push and migration provisioning paths. Refresh them with scripts/update-integration-durations.ts, then check the new partition against a separate complete run before adopting it. Weights only change scheduling; discovery still owns coverage. The current refresh uses runs 41348786822 and 41377145857, with 41377810718 held out for validation.
Desktop pickup checks overlap their real grace periods using distinct calls and waiters under fixed authorization and parent-run fixtures. Recovery exhaustion checks keep worker responses, injected faults, and observed requests scoped to each stream before overlapping their real backoff schedules. Their immediate-takeover checks remain serial. File isolation and serial execution between integration files remain unchanged; these suites still verify actual timeout, lease, retry-floor, and late-claim behavior against PostgreSQL and Redis.
SCIM's production app uses a sim.ai fixture hostname mapped to loopback, since NEXT_PUBLIC_FORCE_HOSTED is ignored in production. Readiness, HTTP tests, and internal API requests still connect to loopback. The SCIM suite verifies that ending an Enterprise subscription rejects both owner administration and an existing bearer token, and that restoring it restores access.
- SEO is plain HTTP, independent of browser and viewport. Its complete suite runs once in the SEO job, instead of repeating in each of four mobile shards.
- The MCP suite's homepage-returns-200 control duplicated SEO's stronger real-homepage Markdown/content-negotiation check. SEO owns that positive control; MCP retains host isolation, ownership challenge, discovery, API catalogs, unknown-page/API behavior, and refusal of Markdown.
- CLI CI uses one cold/warm-up iteration and one measured iteration instead of three identical measured repetitions. Both retain run completion, selected output, file-reference size, trace, and omitted-workflow-snapshot assertions. The standalone benchmark keeps its configurable sample size.
- A precompiled Electron app needs no route-compilation warm-up, which repeated read/import/chat-switch/Stop flows before each shard. The actual acceptance tests still exercise those behaviors.
The long-import and lease-expiration checks remain: they prove distinct survival/uncertain-outcome behavior across real process failure. Likewise, the two integration provision paths remain because migrations create constraints/triggers that schema push does not.
Local planner validation is bun run test:scripts scripts/ci-changes.test.ts scripts/ci-gate.test.ts. It executes real Turbo queries in small Git fixtures; no application build or database is required. Full builds, services, browser suites, and Electron checks run on hosted CI.
The separate desktop-e2e workflow retains its desktop-specific path filters. Its macOS shell smoke suite runs whole files across four isolated runners, with one worker per runner. The matrix owns the partition count; job names and runner arguments use strategy.job-total. scripts/desktop-e2e.ts asks Playwright to discover the complete suite, then assigns longer files first to the currently lightest shard using apps/desktop/e2e-durations.json. Exact escaped file filters preserve serial fixtures. Unmeasured files receive the median measured weight, deleted files cannot select tests, and invalid or empty selections fail. Normal local runs, packaging smoke and the weekly Electron canary keep their existing commands.
The current weights are median test durations in seconds from runs 41496623284, 41497346251, and 41502340540. Refresh them from complete desktop-smoke-reports-* artifacts when the workload changes: sum tests[].results[].duration by spec.file in each run, take the median across runs, and divide milliseconds by 1,000. The weights steer scheduling only; Playwright discovery owns coverage. Before adopting new weights, compare repeated paired runs and the complete case/status multiset, including skipped cases and retries.
Desktop native outputs are keyed by source, build recipes, compiler, SDK, OS, architecture, and Node header bytes. Every build still validates restored checksums, executable modes, universal architectures, and the helper signature; invalid or incomplete outputs rebuild. Native-input pushes to main/staging seed trusted Node 22 and 24 caches without repeating the PR smoke suite. PR cache entries never populate trusted branch caches.
Each shard uploads its coverage/timing report, the complete discovery report, and its deterministic shard plan. bun run test:scripts scripts/duration-shards.test.ts scripts/desktop-e2e.test.ts checks partition coverage, unknown files, deterministic ordering, and balancing. The CLI regression executes tiny browser-free Playwright fixtures to prove that imported test factories and paths containing regex characters are selected correctly; it launches no Electron or browser.
Local pushes run lint:check and type-check through Husky before Git sends commits. These gates reuse Turborepo's task cache; a failure stops the push. The pre-commit hook still formats staged files, and hosted CI owns tests, audits, builds, and acceptance checks.