Browse documentation
CI
Selective CI
Declare monorepo dependencies and verify which workflows a landing candidate needs.
On this page
Run only affected workflows
Selective candidate CI reduces unnecessary work in a monorepo: a web-only change can run the web workflow without rerunning an unrelated API workflow. Shared code changes can still run every dependent suite. This can reduce runner time and queue validation time; the benefit depends on which workflows your changes affect.
Availability: candidate selection is an organization-scoped rollout and is off by default. Your platform administrator must enable it on a deployment that supports candidate impact plans and complete backend diffs. Adding a manifest alone does not enable selection. The initial optimized scope is ordinary Changes in a native, Composal-primary repository using the top-only merge queue. Do not assume the same optimization applies to GitHub-primary, federated, virtual-repository, or per-PR queues.
Composal selects whole workflows, not individual unit tests. It evaluates the complete diff from the pinned target revision to the candidate that would land, including queued predecessors, renamed and deleted files, and executable-bit changes. This avoids skipping checks for a predecessor merely because the last Change did not touch its files.
Without a suite manifest, candidate selection uses applicable workflow triggers and path filters. Include shared libraries, generators, lockfiles, and toolchain inputs in those filters whenever they affect the workflow. For a monorepo with dependencies across directories, use the explicit graph below instead of maintaining overlapping path lists in each workflow.
Split checks into separate workflow files when they have different inputs. For example, application tests may depend on web/**, shared packages, lockfiles, and the runner image; an image-contract check may depend only on image sources and toolchain configuration. Keeping both jobs in one workflow schedules both whenever that workflow is selected. Separate workflows let an application-only Change omit the independent image check while an image change still runs both. Preserve dependencies between checks when one consumes another's output.
Measure the resulting job count and runner time, including setup and retries. Removing a parallel job can reduce runner usage without reducing landing time if another required job still takes longer. Compare equivalent Changes and retain failed or superseded candidate attempts in the measurement.
Declare monorepo dependencies
Commit .vex/ci-suites.json to declare file ownership and dependencies. This example assumes exactly two workflow files, .vex/workflows/api.yml and .vex/workflows/web.yml, configured to run for Change events:
{
"version": 1,
"global_inputs": ["pnpm-lock.yaml", "mise.toml"],
"ignored_inputs": [],
"components": {
"shared": { "inputs": ["shared/**"], "depends_on": [] },
"api": { "inputs": ["api/**"], "depends_on": ["shared"] },
"web": { "inputs": ["web/**"], "depends_on": ["shared"] }
},
"suites": {
"api": { "workflow": ".vex/workflows/api.yml", "depends_on": ["api"] },
"web": { "workflow": ".vex/workflows/web.yml", "depends_on": ["web"] }
}
}
Adapt the paths and dependencies to your repository:
componentsown repository-relative input patterns.depends_onnames components that this component consumes. If the web app consumes the API contract, addapito the web component's dependencies.suitesmap stable names to whole workflow files and the components they validate. Map every workflow file exactly once, including workflows whose event triggers make them inapplicable to a particular candidate. The graph does not override event or branch eligibility.global_inputsbroaden execution to all applicable workflows. Include shared dependency locks, toolchain configuration, build configuration, and other inputs that can affect everything.ignored_inputsexplicitly identify inputs that affect no suite. Start with an empty list. Only ignore documentation when no build, link checker, generated output, or test consumes it.
With this example, a change under web/ selects web, api/ selects API, and shared/ selects both. A lockfile change runs all applicable workflows. A new, unmapped directory also runs all applicable workflows until you declare its ownership. Dependencies expand transitively to consumers; Composal does not infer missing dependencies from imports.
All fields shown are required. Component and suite names begin with a lowercase letter and use lowercase letters, digits, underscores, or hyphens, up to 64 characters. Patterns are relative to the repository root; use paths such as api/**, not absolute paths or negated patterns. Dependency references must exist and the graph must be acyclic. The manifest is limited to 64 KiB, 256 components, 256 suites, and 2,048 input patterns.
Adopt selection safely
- Start with separate workflows for independently testable areas. Record full-CI duration and runner time before enabling selection.
- Review the graph with the owners of each component. Include code generation, shared schemas, build scripts, lockfiles, and toolchain inputs. An omitted real dependency cannot be detected just because the JSON is valid.
- Land the manifest through normal review. Composal trusts the pinned target's policy: adding, editing, or removing the manifest runs the conservative plan for that candidate. Workflow configuration changes also cannot authorize their own skips.
- Have your platform administrator begin with shadow selection. Shadow mode records which workflows would be selected while still executing all applicable workflows. Compare web-only, shared-code, dependency, rename, deletion, and unmapped-file changes before enabling execution of the selected set.
- Inspect the queue's CI impact details or
com queue statusafter enabling selection. Compare selected workflows with scheduled execution and any fallback reason. Use full CI when investigating a missing dependency or unexpected selection.
Unknown, unavailable, oversized, or unproven diffs run all applicable workflows. Invalid, unreadable, or changed manifests, cycles, missing component references, global inputs, and unmapped files also broaden execution. A fallback is a conservative validation result, not evidence that selection saved work.
Impact receipts are limited to 1 MiB of serialized JSON. If path or suite explanations exceed that budget, Composal records receipt_budget_exceeded and runs all applicable workflows. If the required workflow identities alone exceed the budget, candidate planning fails visibly; it never drops required workflows to fit. Reduce oversized workflow configuration before retrying.
A candidate with no applicable selected workflows can have no workflow jobs. That does not waive separately required external checks, approvals, queue ordering, or target freshness. Automatic landing batches and reusable passing test results across different candidates are not provided by this feature.
Verify your configuration
Use small reviewed Changes to check the manifest before relying on skipped workflows. For the two-workflow example above, compare the queue's impact explanation with these expectations:
| Change | Expected selection | What to check |
|---|---|---|
web/ only | Web | API is unrelated under the declared graph. |
api/ only | API | Add a dependency if web consumes the API contract. |
shared/ | API and web | Both consumers are included. |
pnpm-lock.yaml or mise.toml | All applicable workflows | Global inputs broaden execution. |
| A new, unmapped directory | All applicable workflows | Declare ownership before expecting savings. |
Rename from api/ to web/ | API and web | Both old and new paths contribute. |
Delete a file under shared/ | API and web | Deletion still affects consumers. |
| Change the suite manifest | Conservative execution | The Change cannot approve its own narrower policy. |
In shadow mode, these are the workflows Composal would select; all applicable workflows still run. After selection is enabled, compare the scheduled workflows with the plan. A docs-only Change in this example is unmapped and therefore runs all applicable workflows. To omit documentation work, first establish that no suite consumes those files, then explicitly add the relevant pattern to ignored_inputs through review.
Read the explanation for the complete landing candidate. If an earlier queued Change modifies shared/, a later web-only Change can correctly validate both workflows. Native Composal candidates and ancestry follow JJ commit identities and parent relationships.
If a workflow you expected is missing, request Run Full CI, have your platform administrator disable selection for the affected scope, and correct the dependency graph. If everything runs unexpectedly, check whether shadow mode is active, a global or unmapped path changed, the policy changed, or the diff could not be proven complete. Do not remove real dependencies merely to reduce the selected count.
Disabling selection also prevents unpublished selective candidates from using their earlier omission decisions to land. The queue rebuilds their validation under the current mode. Historical explanations remain available; full and shadow receipts remain usable because they execute all applicable workflows.
Next: request full CI when investigating selection, or add caching for workflows that still run.