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
14 changes: 14 additions & 0 deletions .changeset/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Changesets

This directory holds change-intent files consumed by pnpm-native workspace
versioning (`pnpm version -r`). One file per change, authored with:

```bash
pnpm change --bump <none|patch|minor|major> --summary "<changelog entry>" [<pkg>...]
```

- A PR that changes a publishable package (`npm/packages/comment-checker`) MUST ship with
an intent here.
- `--bump none` records a change that needs no release (e.g. devDependency or script touch).
- Intents are consumed (deleted) by `pnpm version -r` in the release workflow.
- Standard pnpm 11 native changesets format.
57 changes: 27 additions & 30 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ jobs:
steps:
- uses: actions/checkout@v4

# Toolchain comes from the repo flake (nix develop): cargo, clippy,
# rustfmt, gcc — same versions as every local dev shell.
- uses: DeterminateSystems/nix-installer-action@v22

# Stock rust-lang toolchain through rustup with clippy and rustfmt components
- uses: actions-rust-lang/setup-rust-toolchain@0267444136ce4919088f5eae0461f736f21356de # v1
with:
toolchain: stable
components: clippy, rustfmt
- name: Cache cargo build
uses: actions/cache@v4
with:
Expand All @@ -25,18 +26,14 @@ jobs:
key: ${{ runner.os }}-cargo-${{ hashFiles('Cargo.lock') }}
restore-keys: ${{ runner.os }}-cargo-

# No `bash -l`: login shells re-source runner profiles which re-prepend
# ~/.cargo/bin ahead of the dev shell, and the rustup shim then hijacks
# the toolchain (observed: rustup updated stable mid-build, removed
# rust-std, E0463). `nix develop -c` already exports the full PATH.
- name: Format check
run: nix develop -c bash -c 'CARGO_BUILD_JOBS=4 cargo fmt --check'
run: cargo fmt --check

- name: Clippy (deny warnings)
run: nix develop -c bash -c 'CARGO_BUILD_JOBS=4 cargo clippy --all-targets -- -D warnings'
run: cargo clippy --all-targets -- -D warnings

- name: Test (unit + property + composition + F1)
run: nix develop -c bash -c 'cargo test --all-targets -- --test-threads=4'
run: cargo test --all-targets -- --test-threads=4

npm:
runs-on: ubuntu-latest
Expand Down Expand Up @@ -73,41 +70,41 @@ jobs:
steps:
- uses: actions/checkout@v4

# Toolchain + cargo-mutants from the repo flake — no cargo install.
- uses: DeterminateSystems/nix-installer-action@v22
- uses: actions-rust-lang/setup-rust-toolchain@0267444136ce4919088f5eae0461f736f21356de # v1
with:
toolchain: stable

- uses: pnpm/action-setup@v4
with:
version: 11.21.0

- uses: actions/setup-node@v4
with:
node-version: 24

- name: Install cargo-mutants subcommand
run: |
cargo install cargo-mutants
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"

# Turbo verdict cache. The path must mirror turbo.json's `cacheDir`.
# Keyed on the mutation gate's input set plus the files turbo
# self-hashes (turbo.json, the npm package.json carrying the gate
# script): unchanged inputs restore an exact key, which also
# suppresses the post-job save; turbo then replays the cached verdict
# (FULL TURBO) instead of re-running the mutant loop. The restore-key
# prefix picks up the newest previous bag after any input change.
- name: Cache turbo verdicts
uses: actions/cache@v4
with:
path: .turbo/cache
key: turbo-${{ runner.os }}-${{ hashFiles('package.json', 'Cargo.toml', 'Cargo.lock', 'flake.lock', 'flake.nix', '.cargo/config.toml', 'crates/comment-checker/Cargo.toml', 'crates/comment-checker/src/**', 'crates/comment-checker/tests/**', 'turbo.json', 'npm/packages/comment-checker/package.json') }}
key: turbo-${{ runner.os }}-${{ hashFiles('package.json', 'Cargo.toml', 'Cargo.lock', '.cargo/config.toml', 'crates/comment-checker/Cargo.toml', 'crates/comment-checker/src/**', 'crates/comment-checker/tests/**', 'turbo.json', 'npm/packages/comment-checker/package.json') }}
restore-keys: |
turbo-${{ runner.os }}-

# Cargo build state for the turbo-miss path (any input change
# recompiles). Keyed on Cargo.lock + flake.lock (deps + toolchain);
# the shared `cargo-` restore prefix also picks up bags the gate
# job saved under the same namespace.
- name: Cache cargo build (mutants miss path)
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-mut-${{ hashFiles('Cargo.lock', 'flake.lock') }}
key: ${{ runner.os }}-cargo-mut-${{ hashFiles('Cargo.lock') }}
restore-keys: ${{ runner.os }}-cargo-

# pnpm store: `pnpm install` runs before turbo can replay anything
# (turbo itself comes from node_modules), so the store is cached to
# keep that install a fast relink instead of a cold fetch.
- name: Cache pnpm store
uses: actions/cache@v4
with:
Expand All @@ -117,4 +114,4 @@ jobs:
pnpm-store-${{ runner.os }}-

- name: Mutation gate via turbo (core classifier, 100%)
run: nix develop -c bash -c 'pnpm install --frozen-lockfile && CARGO_BUILD_JOBS=4 pnpm mutants'
run: pnpm install --frozen-lockfile && pnpm mutants
15 changes: 7 additions & 8 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,20 +63,19 @@ These are the checks that must pass. The one-shot gate below runs them as phases
- Core classifier mutation (when changing `crates/comment-checker/src/classify.rs`): `cargo mutants --file crates/comment-checker/src/classify.rs --timeout 90`

```bash
# One-shot verification command (with explicit caps)
CARGO_BUILD_JOBS=4 cargo fmt --check && CARGO_BUILD_JOBS=4 cargo clippy --all-targets -- -D warnings && cargo test -- --test-threads=4
# One-shot verification command
cargo fmt --check && cargo clippy --all-targets -- -D warnings && cargo test --all-targets
```

**For classifier changes**, the mutants command above must be run.

**For classifier changes**, the mutants command above must be run:
```bash
# One-shot verification command (with explicit caps)
CARGO_BUILD_JOBS=4 cargo fmt --check && CARGO_BUILD_JOBS=4 cargo clippy --all-targets -- -D warnings && cargo test -- --test-threads=4
cargo mutants --file crates/comment-checker/src/classify.rs --timeout 90
```
**Rust/Cargo specifics for manifest resolution**: Commands are direct `cargo` invocations (Cargo.toml serves as manifest; no `[scripts]` like package.json). The gate resolves via the Cargo toolchain present in PATH. The instructions surface names these as the verifiable entrypoints.

Keep the gate bounded when changing it. Concurrency multiplies: the runner's task cap times each task's own worker pool. For Cargo, use `CARGO_BUILD_JOBS=4` (or CI equivalent) or `-j` where supported to cap; defaults are safe for this small crate but the gate documents the cap. Prefer the runner's CPU-relative cap. Add a phase only when the check gating it is far cheaper than the phase behind it; never chain independent same-cost checks with `&&`, and never fan out uncapped.
**Rust/Cargo specifics for manifest resolution**: Commands are direct `cargo` invocations (Cargo.toml serves as manifest; no `[scripts]` like package.json). The gate resolves via the Cargo toolchain present in PATH. The instructions surface names these as the verifiable entrypoints.

Run checks using full system concurrency (`cargo` and `cargo-test` use host CPU defaults).
Add a phase only when the check gating it is far cheaper than the phase behind it; never chain independent same-cost checks with `&&`.
### Anti-Bypass Rules
- Run the full one-shot command, not individual tests in isolation.
- Evidence comes from the current run — never an old CI result or prior session; any failure blocks done, even unrelated-looking ones.
Expand Down
27 changes: 15 additions & 12 deletions scripts/tools/sync-root-version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,33 +11,36 @@ import {

const VERSION_RE = /^\d+\.\d+\.\d+(-[A-Za-z0-9.-]+)?$/

// Validated before any write. `$` matches before a trailing newline, so reject
// one explicitly.
const version = Deno.env.get('VERSION') ?? ''
if (!VERSION_RE.test(version) || version.includes('\n')) {
console.error(`sync-root-version: invalid VERSION: ${JSON.stringify(version)}`)
Deno.exit(1)
}

const flags = parseCliArgs({
alias: { 'dry-run': 'dryRun', 'manifest-path': 'manifestPath' },
boolean: ['dry-run'],
string: ['manifest-path'],
string: ['manifest-path', 'version'],
})
const dryRun = flags.dryRun === true
const manifestPath = typeof flags.manifestPath === 'string'
? resolve(flags.manifestPath)
: LAUNCHER_MANIFEST_PATH

const original = await Deno.readTextFile(manifestPath)
const manifest: LauncherManifest = JSON.parse(original)

// Version priority: --version flag -> VERSION env var -> existing manifest.version (bumped by pnpm version)
const rawVersion = typeof flags.version === 'string'
? flags.version
: (Deno.env.get('VERSION') ?? manifest.version ?? '')

if (!VERSION_RE.test(rawVersion) || rawVersion.includes('\n')) {
console.error(`sync-root-version: invalid version: ${JSON.stringify(rawVersion)}`)
Deno.exit(1)
}
const version = rawVersion

const targets: Target[] = JSON.parse(await Deno.readTextFile(TARGETS_PATH))
if (!Array.isArray(targets) || targets.length !== 5) {
console.error('sync-root-version: targets.json must declare exactly five platform targets')
Deno.exit(1)
}

const original = await Deno.readTextFile(manifestPath)
const manifest: LauncherManifest = JSON.parse(original)
manifest.version = version
// The committed manifest carries no optionalDependencies — pnpm cannot lock
// unpublished platform packages — so inject the pins at publish time, when they exist.
manifest.optionalDependencies = Object.fromEntries(
Expand Down
3 changes: 0 additions & 3 deletions turbo.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,6 @@
"$TURBO_ROOT$/crates/comment-checker/tests/**"
],
"outputs": [],
"env": [
"CARGO_BUILD_JOBS"
],
"cache": true
}
}
Expand Down
Loading