Code Foundry has one repository-owned control plane:
.github/code-foundry.yml. init creates it, sync renders the selected
baseline, and doctor checks local and GitHub-facing prerequisites.
npx code-foundry init
# edit .github/code-foundry.yml
npx code-foundry sync
npx code-foundry doctorThe generated file is deliberately explicit. Keep it under version control and
change it directly rather than passing one-off flags to sync.
repository manifests and source
|
v
.github/code-foundry.yml
|
+--> detected language and package-manager setup
+--> standard workflow callers
+--> runtime repository and version
+--> validation, release, license, and cache policy
toolchain: auto reuses an existing .mise.toml; otherwise it uses native
language tooling. Set toolchain: native to prohibit mise or toolchain: mise
to require an existing mise configuration.
| Key | Values | Notes |
|---|---|---|
version |
1 |
Configuration schema version. |
profile |
auto, application, monorepo, minimal |
Repository shape; auto detects it. |
languages |
comma-separated language names | Supported values are typescript, rust, python, and solidity. |
package_manager |
bun, pnpm, yarn, npm, none |
JavaScript package-manager policy. |
toolchain |
auto, native, mise |
Environment setup policy. |
runtime_repository |
OWNER/REPO |
Source of reusable workflows and runtime code. |
runtime_ref |
tag or commit | Runtime version used by generated callers. |
features |
all or a list |
See Feature selection. |
draft_protection |
true, false |
Skip generated runner-heavy gates for draft PRs when false. |
billing_paused |
true, false |
Disable Dependabot version updates while CI billing is paused. |
dependency_updater |
dependabot, renovate, none |
Which dependency-update bot sync manages; see Feature selection. |
codeql |
auto, true, false |
Enable CodeQL when the repository and GitHub plan support it. |
dependency_review |
auto, true, false |
Enable Dependency Review when supported. |
runner and *_runner |
GitHub runner labels | Override the default runner per workflow. |
For codeql: auto and dependency_review: auto, public repositories use the
available GitHub security checks and private repositories require the relevant
capability. Set either key to false when the check is unavailable or not
wanted. CodeQL is omitted from the generated validation caller; Dependency
Review remains a conditional step inside Security rather than a separate check.
Code Foundry runner-heavy validation, security, qualification, and Cloudflare
Deployment jobs protect draft pull requests by default. The draft_protection
configuration key defaults to true; set it to false only when the repository
intentionally runs generated gates for draft PRs. Cloudflare reusable-workflow
callers use their equivalent draft-protection input. These opt-outs affect
CI/deployment gates only and do not disable Draft Guard or draft-PR automation.
The CI_BILLING_PAUSED repository variable gates every generated workflow job,
but Dependabot's update runs execute under the dynamic event and never see
that variable. Set billing_paused: true alongside the variable and re-run
sync so the rendered dependabot.yml sets open-pull-requests-limit: 0 and
drops the update cadence to monthly for every ecosystem. Removing the flag and
syncing again restores the active template. npx code-foundry ci status reports
when a paused repository still has Dependabot updates active.
Use features: all or a comma/space-separated list. The canonical validation
feature is validation; the legacy names ci, test, security, and codeql
remain aliases for compatibility. Other selectable features are:
draft-pr— create or update development pull requests.release-pr— promotestagingintomainin thestaging-releasetopology.release— run Release Please and optional package publication.dependabot— install the language-aware Dependabot configuration.
The dependency_updater key selects which dependency-update bot sync
manages, and an explicit value wins over the dependabot feature (including
features: all):
dependabot(default) — render.github/dependabot.ymlwhenever thedependabotfeature is selected; existing repositories are unaffected.renovate— never render.github/dependabot.ymland remove an existing one; installrenovate.jsonfrom the runtime template only when the repository has none. A repository-ownedrenovate.jsonis never modified.none— never render.github/dependabot.yml, remove an existing one, and install nothing.
Installing renovate.json does not schedule update runs: a repository still
needs the Renovate GitHub App or a self-hosted runner. The billing_paused
Dependabot pause applies only while dependency_updater is dependabot.
The release-integrity and OpenCode Security callers are installed independently
of feature selection. OpenCode Security is disabled unless the repository or
organization variable OPENCODE_SECURITY is true and the
OPENCODE_API_KEY secret exists. opencode_security_model optionally replaces
the generated scanner model.
Merge queues are a separate opt-in because they need a stable runtime pin:
merge_queue: trueSee Merge queue validation before enabling it.
| Key | Values | Purpose |
|---|---|---|
performance |
auto, true, false |
Discover, require, or disable performance checks. |
performance_command |
JSON argv array or array of argv arrays | Ordered commands for a non-package performance harness. |
performance_profile |
empty or node-package |
Shared package import, memory, archive, and dependency audit. |
performance_budget_file |
repository-relative path | Budget file for node-package; defaults to performance-package-budgets.json. |
eval |
auto, true, false |
Discover, require, or disable the deterministic eval tier. |
eval_command |
JSON argv array | Explicit harness command when there is no eval package script. |
eval_report_file |
repository-relative path | Report validated against the eval contract; defaults to eval-results/result.json. |
eval_budget_file |
repository-relative path | Optional budget file; defaults to eval-budgets.json. |
eval_runner |
runner label | Runner for the Validation / Eval lane; defaults to the repository runner. Browser evals need a Chrome-capable runner such as ubuntu-latest. |
required_capabilities |
comma-separated task names | Fail closed when a required task or coverage evidence is unavailable. |
coverage_enforcement |
auto, required, off |
Shared coverage-report policy. |
coverage_minimum |
0–100 |
Minimum percentage; defaults to 80. |
coverage_metrics |
lines, functions, branches, statements |
Metrics checked by the coverage gate. |
coverage_report |
comma-separated repository paths | Istanbul JSON summary or LCOV evidence files. |
pre_commit_build |
true, false |
Run the project's build in the local pre-commit gate. Defaults to false; see Pre-commit gate. |
filter_<task> |
comma-separated repository path globs | Skip a lane when no changed path matches; see Lane path filters. |
rust_sccache |
true, false |
Install sccache and wrap rustc so compile artifacts survive manifest/lockfile churn. |
rust_nextest |
true, false |
Install cargo-nextest and prefer cargo nextest run over cargo test in Rust test lanes. |
e2e_shards |
1–8 |
Fan the Validation / Test / E2E job into one runner per shard. The repository's test:e2e/e2e script receives E2E_SHARD_INDEX and E2E_TOTAL_SHARDS and partitions itself (Playwright takes --shard=$E2E_SHARD_INDEX/$E2E_TOTAL_SHARDS); each shard owns a runner-local test database, so shards stay isolated. |
release_batching_schedule |
five-field numeric cron | Batch Release Please into scheduled windows. Ordinary pushes skip the producer; release-PR squashes and manual dispatches still run. Batched releases are created privately as drafts and published with prerelease visibility atomically. Manual dispatches publish stable hotfixes. Absent, releases publish per merge. Requires a single root Release Please package. |
release_batching_prerelease |
true, false |
Defaults to true with a schedule. Sync enables draft and force-tag-creation in Release Please; the producer publishes the draft with the selected visibility. false keeps scheduled releases stable-visible. |
release_batching_soak_hours |
1–336 |
Generate a daily promoter that promotes the newest prerelease when the oldest candidate since the last stable has soaked this many hours, stitching batch notes. Requires a batching schedule and prerelease publication. Set STABLE_PROMOTION_HELD=true in repository Actions variables to hold scheduled and manual promotion; force overrides the hold or soak, and version selects a plain semantic version. |
release_batching_qualification_workflow |
workflow filename (for example release-assets.yml) |
Require the latest release-event run of this workflow for the candidate tag’s exact source commit to succeed before stable promotion. Requires a batching soak. Running, missing, failed, or cancelled runs hold promotion, including named and forced promotions; force only bypasses hold and soak. The promoter receives read-only Actions access. |
Supported task capabilities are format, lint, type_check, build, unit,
integration, e2e, smoke, eval, and performance. coverage is a policy
capability that also requires unit tests. See Required capabilities and task
evidence. The eval tier runs the repository's own
harness against the shared report contract and optional budgets; see
Evals.
The shared performance job discovers performance:check, then perf:check,
in JavaScript repositories. Other repositories can provide one command or an
ordered list of argv arrays without shell interpolation:
performance: true
performance_command: '["python3", "scripts/performance_audit.py", "--check"]'
performance_runner: ubuntu-latestThe node-package profile supports cold-import, memory, package-size, file-count,
and production-dependency budgets. Supported budget names are
coldImportP50Ms, coldImportP95Ms, coldImportRssMaxBytes,
coldImportRelativeP50, packedBytes, unpackedBytes, packageFileCount,
packageMapFileCount, and productionDependencyCount. Reports are written under
performance-results/ and are uploaded when present.
performance: true makes the performance task required. Use performance: auto
to keep discovery optional. Product-quality profiles are repository-owned
manifests invoked by existing build or E2E commands; they are not activated by a
configuration key. See Product quality profiles.
code-foundry sync installs .githooks/pre-commit, which runs
code-foundry pre-commit when the repository depends on code-foundry. The
gate is change-aware and never runs work for code the commit cannot affect:
- With nothing staged it exits immediately.
git diff --cached --checkstill blocks whitespace errors in every staged change. - Oxfmt and Oxlint check only the staged files they support, honoring their
own ignore patterns. Staging
.oxfmtrc.json,.oxlintrc.json, or their*.config.*equivalents widens that tool to the whole repository through the repository'sformat:check/lintscript. A repository without Oxfmt or Oxlint keeps its own script, which runs only when a matching file is staged. - The project's
type-check/typecheck/type:checkscript (ortsc --noEmit) runs only when.ts,.tsx,.mts,.cts, ortsconfig*.jsonfiles are staged, and for JavaScript sources when the repository type-checks them with a rootjsconfig.jsonorcheckJs. - Ruff checks only staged Python files (
--force-exclude), or the whole repository whenpyproject.toml,ruff.toml, or a requirements file is staged. Staged Rust sources orCargo.tomlruncargo fmt --checkand Clippy. - The build does not run at commit time; CI and explicit commands own it. Set
pre_commit_build: trueto run thebuildscript (orcargo build) when code is staged.
Tools read working-tree contents, so a partially staged file is checked as it
exists on disk. Task scripts that declare dependencies of their own, such as a
Turborepo typecheck task with dependsOn: ["^build"], still run them; drop
that dependency to keep type checks build-free.
| Key | Values | Purpose |
|---|---|---|
release_type |
auto, node, python, rust, simple, none |
Select a release manifest; auto detects one. |
npm_publish |
true, false |
Opt into npm publication. |
license |
gpl-3.0-or-later, agpl-3.0-or-later, apache-2.0, mit, preserve, none |
License policy for initialized repositories. |
git_workflow |
direct, staging-release |
Choose the branch topology. |
merge_strategy |
squash or rebase |
Required topology-specific merge method. |
release_merge_strategy |
squash or rebase |
Required method for Release Please version PRs. |
staging_validation_mode |
fast, audit |
Validation tier for pull requests into staging; staging-release only. |
direct is the default: feature branches and release PRs target main, and
both merge with squash. staging-release sends feature branches to staging,
uses rebase for the staging → main promotion and Release Please PR, and
keeps feature PRs into staging on squash. sync, doctor, and release
automation reject a strategy that does not match the selected topology.
# Preview/staging environment
release_type: auto
git_workflow: staging-release
staging_validation_mode: fast
merge_strategy: rebase
release_merge_strategy: rebaseUse simple with version.txt when no package manifest exists. Use none to
skip automated releases. npm_publish affects generated consumer release
callers; Code Foundry's own repository uses the qualified publication path
described in Qualified publication. A self-sync of
the Code Foundry source does not advance its own runtime_ref; this prevents a
self-referencing runtime pin from creating a release loop.
| Key | Values | Purpose |
|---|---|---|
sync_mode |
overlay, strict |
Synchronization policy; overlay is the default. |
custom_workflows |
preserve |
Custom workflows are always preserved; other values are rejected. |
post_release |
true, auto, false |
Enable a post-release delivery hook. |
post_release_workflow |
workflow filename | Workflow dispatched by the post-release hook. |
post_release_mode |
auto, workflow-dispatch, release-event, disabled |
Select the hook delivery mechanism. |
sync_mode accepts overlay (the default) or strict; sync validates the
selected value before writing. Custom workflows remain preserved in either mode,
and custom_workflows must remain preserve. See Extension points.
The standard workflows use lockfile- and configuration-keyed caches. Their repository variables, rather than application source files, control cache behavior:
REPO_FOUNDRY_CACHE_PACKAGEScontrols package-store caching.REPO_FOUNDRY_CACHE_BUILDcontrols build-cache reuse.turbo_remote: auto,true, orfalsedeclares the remote-cache policy;doctor --githubwarns when enabled remote caching lacksTURBO_TOKENorTURBO_TEAM.TURBO_TOKENandTURBO_TEAMprovide the Turborepo remote-cache credentials.
Use these controls only after measuring a repeatable benefit. See Caching and remote caching.
Validation lanes run their full suite by default. Repositories with clearly separated areas — for example a Rust workspace next to a web app — can opt any task lane into path filtering so a change that cannot affect the lane skips its setup and execution steps:
filter_integration: 'src/**/*.py,tests/**,pyproject.toml,uv.lock'
filter_eval: 'eval/**,crates/**,src/**'
filter_e2e: 'apps/**,src/**'filter_<task> accepts the same task names as required_capabilities. Globs
are repo-relative: **/ matches any directory depth, * stays inside one
path segment, ? matches one character, and a trailing / is shorthand for
dir/**.
The gate is fail-open by design: a task without a configured filter, a diff
that cannot be resolved (shallow checkout edge, missing base ref), or an empty
change set all keep the lane running. Paths are evaluated against
base...head for pull requests and before..after for pushes; other events
always run. Because filters only decide whether a lane executes, required
checks stay green and the aggregate gate sees a normal success.
Diff resolution only runs for lanes that have a filter configured, so
repositories using a single filter pay one shallow fetch per change instead
of one per lane. An orchestrator that has already resolved the change set can
inject it for a lane through a newline-delimited CHANGED_PATHS environment
variable; an empty or missing value falls back to local resolution, so an
injection can never widen a skip.
Security lanes and CodeQL are never filterable: their coverage is structural, not change-scoped.
Rust CodeQL defaults to one full scan with one worker. Larger multi-crate repositories can opt into bounded parallelism:
codeql_rust_shards: '["crates/api", "crates/worker"]'
codeql_rust_threads: 2
codeql_rust_max_parallel: 2Each shard must contain tracked Rust source. Absolute paths, parent traversal,
duplicates, empty scopes, and more than eight shards are rejected. Use
["all"] when complete, non-overlapping source scopes are not available.
Every lane rendered from this configuration (pull-request validation, the default-branch scan, the scheduled audit, and merge queues) receives the same shard list, and the CodeQL Detect job fails closed when caller workflows drift apart. Keep them aligned: each shard uploads a SARIF category keyed by a hash of the shard scope, GitHub code scanning baselines every category it has seen on the default branch, and the code-scanning merge gate waits for results in every tracked category. Two practical consequences:
- Treat
codeql_rust_shardsas add-only while the gate tracks categories. Removing a shard orphans its category on the default branch, and pull requests then stall on "Code scanning is still expecting N results" until the category is covered again. - To retire a category for real, delete its code-scanning analyses (Code
security → Code scanning → analyses, or the code-scanning analysis
deletion API) after removing the shard, then re-run
sync. Covering retired categories in the shard list keeps the gate green but re-runs their analysis on every pull request and is not a substitute for cleanup.
On pull requests without a code-scanning merge gate, analyzers run only for
languages with changed files, and Rust shards run only when their scope (or a
workspace-wide Cargo manifest or toolchain file) changed; unchanged matrix
entries report Not applicable instead of analyzing. Repositories whose
rulesets enforce a code-scanning requirement are detected automatically and
analyze everything, because that merge gate waits for results in every tracked
category. Push, schedule, and merge-queue events always analyze the complete
set so the default branch keeps one full, comparable baseline.
Repositories that deploy to Cloudflare Workers can use the opt-in verified
delivery workflow with fixed Preview and Production environments,
candidate verification, and version-identity promotion. Pin both the reusable
workflow reference and runtime-ref to the same reviewed 40-character commit
SHA. Provide CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID in the consumer
repository and configure environment reviewers separately.
The legacy cloudflare-deploy.yml workflow remains available for direct
(unverified) deployments. It runs wrangler versions upload for previews and
wrangler deploy for production, records a GitHub deployment plus status, and
respects CI_BILLING_PAUSED. With deploy-tool: cf it instead deploys through
the Cloudflare cf CLI: production runs cf-wrangler build plus
cf deploy --prebuilt (consuming the project's cf Build Output), previews run
cf previews deploy named after the pull request or branch, and the cf/wrangler
CLIs must be devDependencies of the deployed package (the build step installs
them), so build-script is required. Deployment URLs and version IDs are
extracted from the same job outputs either way. Preview deployment records use the pull request
head SHA when called from a PR, which lets GitHub show the completed preview in
the PR's Deployments section; direct pushes use the workflow SHA. Its
legacy-compatible Wrangler default is latest; callers should prefer local or
provide an exact wrangler-version for reproducibility. Bun consumers may pass
build-script, install-working-directory, and bun-version; the runtime
installs the frozen lockfile and builds the Worker before invoking Wrangler.
Bun-backed callers invoke Wrangler through bunx so OpenNext's production
delegation resolves the workspace-local opennextjs-cloudflare binary; callers
without build-script retain the npm/npx path. See Verified Cloudflare
delivery.