Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ When you change… | Update…
A CLI flag or subcommand | `man/oss-spec.md`, `docs/agent/help-agent.txt`, `agent_help::COMMANDS_TABLE`, `agent_help::COMMAND_SPECS`, `README.md` Usage table
A template file | `templates/_common/` (or overlay) — and re-run `oss-spec validate` against a generated demo
A §19 rule | `src/validate.rs`, `OSS_SPEC.md`, this `## Documentation sync points` table
A toolchain version bump (Rust / Python / Node / Go) | the repo-root pin file (`rust-toolchain.toml`, `.python-version`, `.nvmrc`, or `go.mod`'s `toolchain` directive), its `templates/<lang>/` counterpart, `templates/_common/.github/workflows/ci.yml.tmpl`, and `MIN_TOOLCHAIN_VERSIONS` in `src/validate.rs` (§10.5 local/CI parity)
The list of supported languages | `manifest::Language`, `templates/<lang>/`, `Makefile.tmpl`, `ci.yml.tmpl`, `dependabot.yml.tmpl`
`OSS_SPEC.md` | Bump the `version` field in its YAML front matter (semver — `feat!`/breaking bumps major, `feat` or new mandate bumps minor, pure clarifications bump patch). Also update `README.md`, `docs/`, `templates/_common/AGENTS.md.tmpl`, and this file as needed. The spec is mirrored into generated projects via the symlink `templates/_common/OSS_SPEC.md -> ../../OSS_SPEC.md`, so there is only one source of truth.

Expand Down
46 changes: 44 additions & 2 deletions OSS_SPEC.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: Open Source Project Bootstrap Specification
description: A prescriptive, language-agnostic specification for bootstrapping a new open source project with the licensing, documentation, automation, governance, and release plumbing that users and contributors expect from a well-run OSS codebase.
version: 2.0.2
version: 2.1.0
---

# Open Source Project Bootstrap Specification
Expand Down Expand Up @@ -340,7 +340,8 @@ Every push to a branch and every pull request must run:

1. Checkout with full history (required for changelog generation).
2. Toolchain setup (pinned minimum version — see §10.3 for the
per-language floor versions).
per-language floor versions, and §10.5 for pinning the **exact**
local-developer version that CI resolves against).
3. Dependency cache restore.
4. `make build`
5. `make test`
Expand Down Expand Up @@ -660,6 +661,47 @@ The `pages` workflow is independent of the release pipeline: a
release does not wait for Pages, and a Pages deploy does not wait for
a release. Each delivers its own artifact to its own audience.

### 10.5 Local/CI environment parity

Every project must pin its language toolchain in a **repository-root
pin file** that both the local developer's toolchain manager and the
CI workflow read. CI's toolchain step must resolve to that same file
(or to a literal that matches it exactly). A lint, test, or build
that succeeds locally must not fail on CI solely because the two
environments booted different toolchain versions.

Why this matters:

- Linters and compilers gain, remove, and reword diagnostics between
minor versions; an unpinned local toolchain produces noise that
only shows up on CI (the canonical failure mode: `cargo clippy`
passes on the contributor's Rust 1.90 install, then fails on CI's
pinned 1.88.0 because a new lint fired).
- A single pin file prevents the version string from being duplicated
in CI YAML, where it silently drifts.
- Contributors running `rustup show` / `pyenv install` / `nvm use` /
`go build` in a fresh clone pick up the correct version without
reading the CI config.

Per-language pin file (`must`):

| Language | Pin file | Example contents | CI reads it via |
|---|---|---|---|
| Rust | `rust-toolchain.toml` | `[toolchain]`<br>`channel = "1.88.0"`<br>`components = ["clippy", "rustfmt"]`<br>`profile = "minimal"` | `dtolnay/rust-toolchain@<channel>` matching the pin, or `rustup show` (auto-reads the file) |
| Python | `.python-version` | `3.12` | `actions/setup-python@v5` with `python-version-file: .python-version` |
| Node | `.nvmrc` (+ `"engines": { "node": ">=24" }` in `package.json`) | `24` | `actions/setup-node@v4` with `node-version-file: .nvmrc` |
| Go | `go.mod` with a `toolchain` directive | `go 1.22`<br>`toolchain go1.22.6` | `actions/setup-go@v5` with `go-version-file: go.mod` |
| Generic / polyglot | `.tool-versions` (asdf / mise) or a devcontainer | `rust 1.88.0`<br>`python 3.12.5` | Matching `asdf install` / devcontainer setup step |

Floating specifiers (`stable`, `latest`, `lts`, `lts/*`, `*`) are
**not permitted** in the pin file, same as in CI (§10.3).

Enforcement: `oss-spec validate` detects the project's languages from
their root manifest (`Cargo.toml`, `pyproject.toml`, `package.json`,
`go.mod`) and requires the corresponding pin file for each one. It
also cross-checks the pin-file version against the version referenced
by `ci.yml` and reports a violation if they disagree.

## 11. Documentation and website

Projects expose their surface in three complementary layers:
Expand Down
4 changes: 4 additions & 0 deletions rust-toolchain.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[toolchain]
channel = "1.88.0"
components = ["clippy", "rustfmt"]
profile = "minimal"
Loading