From a24f9cf7439b10de54ee059109fcce31d01d3165 Mon Sep 17 00:00:00 2001 From: Joe Petrucelli Date: Tue, 27 Oct 2026 10:35:13 -0400 Subject: [PATCH 1/3] chore(release): 2.3.0 Minor bump: the 51 unreleased fragments include added checks and findings (staged-copy-drift, workflow-maturity graduated-script-missing, npm publishing). Folds the fragments into CHANGELOG.md [2.3.0] and corrects the README status line (29 checks, 13 scored / 16 advisory; npm + GitHub). --- CHANGELOG.md | 69 +++++++++++++++++++++++++++++++++++++ README.md | 4 +-- changelog.d/374.added.md | 1 - changelog.d/375.docs.md | 1 - changelog.d/376.fixed.md | 1 - changelog.d/377.fixed.md | 1 - changelog.d/378.added.md | 1 - changelog.d/379.added.md | 1 - changelog.d/383.added.md | 1 - changelog.d/388.fixed.md | 1 - changelog.d/390.fixed.md | 1 - changelog.d/391.fixed.md | 1 - changelog.d/392.fixed.md | 1 - changelog.d/393.docs.md | 1 - changelog.d/394.added.md | 1 - changelog.d/396.fixed.md | 1 - changelog.d/397.docs.md | 1 - changelog.d/400.fixed.md | 1 - changelog.d/403.fixed.md | 1 - changelog.d/404.fixed.md | 1 - changelog.d/405.docs.md | 1 - changelog.d/411.changed.md | 1 - changelog.d/412.fixed.md | 4 --- changelog.d/413.changed.md | 1 - changelog.d/417.changed.md | 1 - changelog.d/420.fixed.md | 4 --- changelog.d/421.added.md | 1 - changelog.d/422.fixed.md | 1 - changelog.d/425.added.md | 1 - changelog.d/428.changed.md | 1 - changelog.d/429.added.md | 1 - changelog.d/430.fixed.md | 1 - changelog.d/431.changed.md | 1 - changelog.d/432.fixed.md | 1 - changelog.d/433.fixed.md | 1 - changelog.d/434.added.md | 1 - changelog.d/435.added.md | 1 - changelog.d/436.added.md | 1 - changelog.d/437.fixed.md | 1 - changelog.d/438.added.md | 1 - changelog.d/439.changed.md | 1 - changelog.d/441.fixed.md | 1 - changelog.d/442.fixed.md | 1 - changelog.d/443.added.md | 1 - changelog.d/444.docs.md | 1 - changelog.d/445.security.md | 1 - changelog.d/446.added.md | 1 - changelog.d/447.fixed.md | 1 - changelog.d/448.fixed.md | 1 - changelog.d/449.security.md | 1 - changelog.d/450.added.md | 1 - changelog.d/451.fixed.md | 1 - changelog.d/452.added.md | 1 - node_modules | 1 + package-lock.json | 4 +-- package.json | 2 +- 56 files changed, 75 insertions(+), 62 deletions(-) delete mode 100644 changelog.d/374.added.md delete mode 100644 changelog.d/375.docs.md delete mode 100644 changelog.d/376.fixed.md delete mode 100644 changelog.d/377.fixed.md delete mode 100644 changelog.d/378.added.md delete mode 100644 changelog.d/379.added.md delete mode 100644 changelog.d/383.added.md delete mode 100644 changelog.d/388.fixed.md delete mode 100644 changelog.d/390.fixed.md delete mode 100644 changelog.d/391.fixed.md delete mode 100644 changelog.d/392.fixed.md delete mode 100644 changelog.d/393.docs.md delete mode 100644 changelog.d/394.added.md delete mode 100644 changelog.d/396.fixed.md delete mode 100644 changelog.d/397.docs.md delete mode 100644 changelog.d/400.fixed.md delete mode 100644 changelog.d/403.fixed.md delete mode 100644 changelog.d/404.fixed.md delete mode 100644 changelog.d/405.docs.md delete mode 100644 changelog.d/411.changed.md delete mode 100644 changelog.d/412.fixed.md delete mode 100644 changelog.d/413.changed.md delete mode 100644 changelog.d/417.changed.md delete mode 100644 changelog.d/420.fixed.md delete mode 100644 changelog.d/421.added.md delete mode 100644 changelog.d/422.fixed.md delete mode 100644 changelog.d/425.added.md delete mode 100644 changelog.d/428.changed.md delete mode 100644 changelog.d/429.added.md delete mode 100644 changelog.d/430.fixed.md delete mode 100644 changelog.d/431.changed.md delete mode 100644 changelog.d/432.fixed.md delete mode 100644 changelog.d/433.fixed.md delete mode 100644 changelog.d/434.added.md delete mode 100644 changelog.d/435.added.md delete mode 100644 changelog.d/436.added.md delete mode 100644 changelog.d/437.fixed.md delete mode 100644 changelog.d/438.added.md delete mode 100644 changelog.d/439.changed.md delete mode 100644 changelog.d/441.fixed.md delete mode 100644 changelog.d/442.fixed.md delete mode 100644 changelog.d/443.added.md delete mode 100644 changelog.d/444.docs.md delete mode 100644 changelog.d/445.security.md delete mode 100644 changelog.d/446.added.md delete mode 100644 changelog.d/447.fixed.md delete mode 100644 changelog.d/448.fixed.md delete mode 100644 changelog.d/449.security.md delete mode 100644 changelog.d/450.added.md delete mode 100644 changelog.d/451.fixed.md delete mode 100644 changelog.d/452.added.md create mode 120000 node_modules diff --git a/CHANGELOG.md b/CHANGELOG.md index 270633f8..eb8dd8de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,75 @@ next release; the maintainer folds the fragments in at release time. ## [Unreleased] +## [2.3.0] - 2026-10-27 + +### Added +- `workflow-maturity` now flags graduated skills whose script is missing (`workflow-maturity/graduated-script-missing`, WARNING). A `SKILL.md` declaring `status: graduated-code` asserts it is a thin wrapper over a deterministic script; when that script isn't on disk the skill is non-functional (`exit 127` on every invocation) while its version bump advertises the opposite. Detection is deliberately conservative — only skills declaring `status: graduated-code` are considered (a v1.x LLM-driven skill legitimately has no script), `~` and `$HOME` expand against the scan homedir before the existence test, and placeholder/interpolated tokens (``, `${VAR}`, `$1`, `{{ }}`), comment lines, and relative paths are skipped rather than guessed at. +- The release workflow now sources GitHub release notes from the folded `## []` section of `CHANGELOG.md` (via `assemble-changelog --notes `), falling back to the raw git log only when that section is absent. +- **staged-copy-drift**: new advisory check (weight 0) that hashes each tracked staged copy against its deployed twin under the operator's home config dir and flags content drift. Opt-in via `--include-home-skills`; rows come from `.rigscorerc.json` `stagedCopies`. +- **memory-hygiene**: warn-only dangling/orphan `[[wikilink]]` report (advisory). +- **The README contract is enforced in this repo, not from outside it**: a test asserts README.md states its Purpose, Owner, Status and Exit Condition, and that each has a body. The workspace that consumes rigscore as a submodule cannot check this — no workflow passes `submodules:` to actions/checkout, so that directory is empty in its CI — which is how an exemption for this repo sat expired for a month unnoticed. +- MCP runtime tool-hash pins now carry freshness: a pin older than `mcpConfig.runtimeToolPinMaxAgeDays` (default 90) or with no valid timestamp reports `mcp-config/runtime-tool-pin-stale` instead of `runtime-tool-pin-recorded`; recorded findings gain `ageDays`. Info severity, so exit codes are unchanged. +- A failing `--fail-under` gate now writes one `rigscore: FAIL ...` line to stderr (score or failing-project count, threshold, lowest project in `--recursive`), and an unknown flag that is a near miss of a real one warns with `did you mean --flag?` (the flag is still ignored). Exit codes and stdout are unchanged. +- `sandbox-posture` now covers Windsurf Cascade: it reads `windsurf.cascadeCommandsAllowList` / `windsurf.cascadeCommandsDenyList` from the per-user `settings.json` and reports `sandbox-posture/windsurf-shell-allowlisted` (warning) when a bare shell, interpreter or `*` is allow-listed and not denied. Windsurf posture is `unrestricted` in that case, otherwise `partial`; a `settings.json` without the list keys is not a surface. +- **GitHub Action: Markdown job summary.** The action now writes a readable report to the run page (score, grade, checks needing attention, top findings with fixes, and the real reason a run failed), and the `check` input documents comma-separated ids. +- The generated facts snapshot now includes `nodeFloor` (minimum Node version from `engines.node`) and `clientCount` (size of the client registry). +- **Compliance report shows when each framework's mappings were last verified.** Every framework section now prints `Mappings last verified: YYYY-MM-DD` under its status, so an auditor can see how old the control-ID mapping is against the primary source. The date lives in `FRAMEWORKS` and is bumped only after a real re-check. +- **`--fail-on `** — exit 1 when any finding is at that severity or worse, regardless of score; `warning` includes `critical`. Respects suppressions and works with `--recursive`. An unknown level is an argument error (exit 2). +- `mcp-hash`, `mcp-pin` and `mcp-verify` accept an opt-in `--connect` flag that launches one named local stdio MCP server from the project config and reads its live `tools/list`, so runtime tool pinning no longer needs a manual pipe. A scan still never spawns a server. Each launch needs per-run consent (an interactive `[y/N]` or `--yes`; no terminal and no `--yes` refuses with exit `2`), prints the exact argv, working directory and env keys first, runs with a minimal environment, refuses relative commands or scripts that escape the project through a symlink, and kills the process tree after the list is read or after 15 s (`RIGSCORE_CONNECT_TIMEOUT_MS` overrides). A timeout or malformed reply exits `5`. The consent prompt prints each argv element as typed, so a Windows path shows its single backslashes rather than JSON-doubled ones, and control characters are shown as `<0xNN>`. +- The terminal report now ends with a one-line pointer to `--fix` when a fixer matches a finding, and `--json` output carries `rigscoreVersion` and `schemaVersion` keys (additive; existing keys unchanged). +- SARIF results now carry `region.startLine` (and CodeClimate `lines.begin`) when a finding supplies `context.file` and `context.line`, so GitHub code scanning annotates the right line instead of line 1. Each rule's `helpUri` defaults to that check's docs page. Findings that describe the home directory (shell-history secrets, `~/` skill files) get no repo location, and absolute, `..` and drive-letter paths are never emitted as locations. +- `release.yml` now publishes the package to npm on each release using npm trusted publishing (OIDC from GitHub Actions, no stored token), so `npx rigscore` and `npm i -g rigscore` work from the first release after this change. The step skips a version npm already has, so a re-run does not fail. The README, troubleshooting page and workflow headers no longer say the package is GitHub-only. + +### Changed +- The four release-time workflows (`release.yml`, `docker-publish.yml`, `prebuilt-binaries.yml`, `release-provenance.yml`) now run on the self-hosted runner. `ci.yml` stays GitHub-hosted: its `test` and `secret-scan` jobs are `pull_request`-triggered on a public repository, and its matrix covers `macos-latest` and `windows-latest` that no self-hosted runner here provides. +- `engines.node` floor raised to `>=20.0.0` (Node 18 is EOL as of 2025-04-30). The `18.17` CI matrix leg and its PR-time exclude entries are dropped, along with the dependabot `vitest`/`@vitest/coverage-v8` hold that existed only to dodge that leg's `styleText` failure. +- The four release-time workflows (`release.yml`, `docker-publish.yml`, `prebuilt-binaries.yml`, `release-provenance.yml`) now run on GitHub-hosted `ubuntu-latest`. The org self-hosted runners do not serve public repositories, so these jobs were never picked up (the 2026-09-10 and 2026-09-12 Release runs queued 24h and were cancelled). +- Terminal report: each finding now shows its id in square brackets with a one-time `rigscore explain ` hint, output wraps to the terminal width (80 when piped), and the check-name and project columns size to the longest entry instead of a fixed width. +- `skill-files/tag-chars` and `unicode-steganography/tag-chars` now share one pattern covering the whole Unicode Tags block (U+E0000-U+E007F), so both checks also flag U+E0000 and U+E0002-U+E001F. The duplicated `gitCheckIgnore` and `toRegex` helpers moved into `src/utils.js`. +- The terminal report now folds `governance-docs` missing-rule warnings into one warning that lists every missing rule, with one fix line and the member ids, instead of one near-identical warning per rule. Display only: JSON, SARIF, scoring and `--ignore` still see one finding per rule with its own id. + +### Fixed +- Windows CI no longer flakes the wall-clock perf gates. `test/performance.test.js` scales its tight `elapsed <` assertions (5s, 15s) by 3x on win32 — the same factor `vitest.config.js` applies to test/hook timeouts (#371 fixed those; the in-body perf assertions were the sibling it left untouched). A loaded windows runner (measured 1.6x slow) had pushed the 20-project recursive scan past its 15s ceiling on a now-blocking leg, reddening a PR that changed no scanner code. The gate still catches a genuine algorithmic regression, which blows past even a 3x-scaled ceiling. Linux behavior is unchanged (`perf()` is identity off-windows). +- The changelog fold is no longer blocked by its own tests. The `meta-ci-contract` RS-4 block pinned specific fragment files by id, but `assemble-changelog --release` deletes fragments as it folds them — so the assertions could only hold until the first real release, then failed forever, leaving the repo unable to cut a release with folded notes (fragments piled up unfolded and 2.2.0 shipped raw git-log notes). Replaced the file-pinning assertions with a pure-function check that `renderRelease()` folds the current `changelog.d` cleanly (version section stamped, empty Unreleased re-opened, every fragment body preserved) — robust by construction, no network or filesystem mutation. Folding into the release workflow remains a separate follow-up. +- A forced site re-post now reports the changes it actually shipped. `force_site_post` re-runs the release for a version whose tag already exists, and the previous-tag lookup (`git describe --tags --abbrev=0`) returned that same tag — so the range collapsed to `vX..vX`, the changelog came out empty, and the generated post was a 70-word stub with no change table that the release-post validator rejected. The lookup now excludes the version being released, which is a no-op on a normal release (the tag does not exist yet) and yields the real predecessor on a re-post. +- A forced site re-post can now run more than once. `force_site_post` re-runs a cross-repo site post that failed, but the job built the release branch fresh every time and pushed it plain — so once the first attempt had left `rigscore-release/v` on the site repo, every re-dispatch was non-fast-forward and died before publishing, and `gh pr create` would have failed on the duplicate PR immediately after. The branch is now reused and force-published (its content is wholly derived from the released tag and the facts generator, so the regenerated commit is authoritative), and the PR is opened only when one is not already open. +- `rigscore init --example` now refuses to scaffold into a directory that already holds a real project (`.git`, `package.json`, `pyproject.toml`, `go.mod`, `Cargo.toml`, `pom.xml`, `Gemfile`), printing what it found and how to proceed; `--force` still allows it deliberately. The example is intentionally vulnerable — a privileged `docker-compose.yml`, a root `Dockerfile`, an `.mcp.json` granting a filesystem MCP server access to `/`, and a prompt-injection skill — so a real source tree is the one place it must never land. Run once by mistake inside a live service directory, those files persist untracked, where neither review nor CI will surface them. +- `rigscore init --example [dir]` now scaffolds into the positional directory instead of always using the current one, matching how `init -- [dir]` already behaved. Previously the argument was accepted and ignored, so `rigscore init --example demo` wrote the deliberately-vulnerable demo project into the caller's own directory — which is exactly the command the new real-project refusal suggests running. +- **Plugin discovery works in a real `node` process.** `discoverPlugins()` imported the plugin package *directory*, which Node's ESM loader rejects outright (`ERR_UNSUPPORTED_DIR_IMPORT`) — so no `rigscore-check-*` plugin has ever loaded outside the test suite. It looked healthy because vite's resolver silently substituted the package entry point when tests ran, so every plugin assertion passed against behaviour Node does not have. rigscore now resolves the entry the package declares (`exports` `.` subpath honouring `import`/`node`/`default`, then `main`, then `index.js`) and imports that file. An entry that is missing, or that points outside the package directory, is reported to stderr and skipped rather than loaded. Regression tests run discovery in a real `node` subprocess so a bundler's resolver can never stand in for Node's again. +- **`workflow-maturity/skill-no-eval` required a directory, not coverage.** The check `stat()`ed `evals//` and passed on the strength of its existence, so `mkdir` alone turned it green — as did a `tests/test_.*` file of zero bytes. A repo running rigscore accumulated 17 eval directories holding nothing but a README that said "STUB — presence-check satisfier; replace with real fixture before claiming coverage", and the dashboard reported full eval coverage the whole time. An eval directory now counts only when it carries something runnable or gradeable at its top level — an executable `test_*.sh`, a `cases.jsonl` with at least one `{"prompt": …, "expected_assertions": …}` line, or a non-empty `acceptance-criteria.md` — and a `tests/test_.*` entry counts only when it has content. A placeholder that exists but covers nothing reports under a new id, `workflow-maturity/skill-eval-hollow` (info), so "created a stub" is distinguishable from "never created one" at a glance; `skill-no-eval` keeps its exact former meaning for the nothing-at-all case. +- The public release workflow no longer names or automates against the private site repo. `.github/workflows/release.yml`'s cross-repo facts-sync job (token clone + `gh pr create` into the private repo) is removed along with the `force_site_post` re-post input that served it; that sync now runs downstream, in the site repo, pulled from this repo's public releases instead of pushed by this repo. +- The `container` init pack's "no leaked identifiers" test no longer publishes the client project names it guards against — those move to the `FORBIDDEN_EXTRA` repo secret (comma-separated), with CI asserting the secret is set so the guard can't silently vanish; the generic tokens stay in the public test file for local runs. +- CI no longer fails every Dependabot pull request on the `FORBIDDEN_EXTRA` + presence check. Dependabot-triggered runs read GitHub's separate Dependabot + secret store, so `secrets.FORBIDDEN_EXTRA` is always empty there; the guard + now exempts exactly those runs and stays blocking everywhere else. +- `skill-files` no longer counts non-text files toward `data.filesScanned`. A + binary in a skill directory is reported in the new `data.filesNonText` count + and as `skill-files/non-text-file`, so it cannot raise scan coverage while + yielding a clean verdict. +- `infrastructure-security` and `mcp-config` no longer read the operator's `$HOME` unless `--include-home-skills` is set. Before, the deny-list check read `~/.claude/settings.json` first, and `mcp-config` scanned every home MCP config (`~/.claude.json`, `~/.cursor/mcp.json`, Zed, Codex, Goose, Cline …) and home `enableAllProjectMcpServers`. So the same project scored 65 on a bare CI runner and 81 on a laptop. Both checks now go through `src/lib/home-scope.js` like every other home-reading check, and a project's verdict no longer depends on who ran the scan. +- The Homebrew formula pin now follows each release automatically. The release workflow opens a pull request that points `Formula/rigscore.rb` at the new tag and its sha256 (and dispatches CI on it), warning loudly if it cannot open the PR; the formula is pinned to v2.2.0, and the pin test no longer hardcodes a version. (#430) +- **instruction-effectiveness:** dead-file-reference no longer flags `path::symbol` refs (the file is still checked), path-led commands with arguments, or scheme-less URLs such as `youtu.be/` and `github.com/org/repo`. A path containing a space is now read as a command and not checked. +- `rigscore init --help` now prints usage and writes nothing (it used to write a `.rigscorerc.json`). `rigscore explain /` now resolves a finding id from the check page's Triggers table and prints its severity, condition and fix, instead of falling back to the whole page; heading matches are exact, and an unknown id lists the documented ids. +- `infrastructure-security` now runs on macOS and Windows instead of returning N/A. The hooks path, git wrapper, deny list and sandbox-gate arms are plain file reads and run everywhere; only the `lsattr` immutable-flag arm and the default `/etc/profile.d` safety-guard lookup stay Linux-only (off Linux, declared `immutableDirs` report an info `cannot-check-immutability` finding naming the platform), and Windows skips the uid-0 and execute-bit rules. +- `rigscore init --` no longer writes through symlinks. A pack destination that is itself a symlink (including a dangling one, which previously read as absent) or whose parent directory resolves outside the target directory now refuses the whole pack with a named error before anything is written. Installing into a target directory that does not exist yet still works. +- **config:** `semantic.command` (the program `--semantic` spawns) is now read only from the home `~/.rigscorerc.json`. A value in a scanned project's `.rigscorerc.json`, or in a file it extends, is ignored with a one-line stderr warning, so a scanned repo can no longer choose what runs. +- `walkDirSafe` no longer follows a symlink that leaves the scan root (`link -> /`, `link -> ~/.aws`); it reports `outsideRootSkipped`, and `deep-secrets` discloses it (`deep-secrets/outside-root-skipped`) instead of printing "Deep scan clean". +- The release workflow now dispatches the prebuilt archives workflow on the released tag, so every release gets its archives attached; it passes release notes to `gh release create` as a file instead of a step output, so a commit subject can no longer forge outputs; coverage counts every `src` file; and Dependabot proposes Docker base-image bumps (major Node bumps excluded). +- Two skill-file tests no longer fail when the suite runs under `umask 000`: they pin the mode of the files they write to 644. Tests that passed `homedir: '/tmp'` now get a fresh temporary home, so they stop scanning whatever sits in the host's `/tmp`. Test-only change. + +### Security +- The CI and leak-scan workflows now declare read-only token permissions, the prebuilt-binaries release workflow passes the release tag through the environment and refuses anything that is not `vX.Y.Z[-prerelease]`, and CI audits production dependencies for high-severity advisories. +- **plugins:** a scan no longer loads plugin code that the scanned repo supplies. The paths in a project's `.rigscorerc.json` `plugins` and the `rigscore-check-*` packages in `/node_modules` now load only when your home `~/.rigscorerc.json` lists that repo's absolute root in `trustProjectPlugins`, for example `{ "trustProjectPlugins": ["~/code/my-repo"] }`. Existing users who rely on in-repo plugins opt in by adding that entry (with `--recursive`, one entry per subproject). A skipped plugin is named in one stderr line. Plugins installed beside rigscore itself still load, and `trustProjectPlugins` in a project config is ignored. + +### Docs +- `workflow-maturity`'s docs no longer overclaim for the new `graduated-script-missing` sub-check. The check declares `enforcementGrade: `keyword`` ("evadable by rewording; advisory only") and a weight rationale asserting every finding is a roadmap-dependent judgment call — both true of sub-checks 1-5 and both false of sub-check 0, which resolves a script path and asks the filesystem whether it exists. THREAT-MODEL.md tells auditors to discount `keyword` signal and treat `mechanical` findings as load-bearing, so the unqualified gloss pointed readers away from a finding they should trust. Grade and weight are declared per check, so the discrepancy is documented rather than mislabelled, including its consequence: a broken graduated skill currently costs zero points and is visibility only. Scoring it needs its own check or per-finding weights — an open decision, now recorded as one. +- **README carries its own governance record**: a closing "Project governance" section states rigscore's Purpose, Owner, Status, and Exit Condition, so the workspace README contract is satisfied by the file itself rather than by an exception entry that expired 2026-06-30. +- **Contact addresses point at the org account.** `SECURITY.md`'s vulnerability-report fallback and `CODE_OF_CONDUCT.md`'s conduct-report address named a personal inbox; both now name the monitored org address. README's Owner link points at the `Back-Road-Creative` org rather than a personal GitHub profile (the visible handle text is unchanged). Added a `.mailmap` so the four name/email spellings one person committed under resolve to a single contributor in `git shortlog`/`log`/`blame` — display only, no commit is rewritten, and bot identities stay distinct. +- **Public-facing docs and fixtures**: authoring-machine paths and private monorepo layout references genericized in docs, fixtures, and one remediation string. The `agent-output-schemas` check now points readers at rigscore's own `docs/checks/agent-output-schemas.md` instead of a file only the authoring workspace can open. +- **Docs gate covers README and CLAUDE.md**: `npm run verify:docs` now fails when the README lead drops "any AI client" or states a stale known-client count, and when `CLAUDE.md` has the wrong title or omits a check in `src/checks/`. README.md is link-checked too. CLAUDE.md and the README lead were brought current in the same change. + ## [2.2.0] - 2026-07-18 ### Added diff --git a/README.md b/README.md index 22a9037d..a8fee6f2 100755 --- a/README.md +++ b/README.md @@ -1310,8 +1310,8 @@ Back Road Creative ([@joepetjr](https://github.com/Back-Road-Creative)) ## Status -Active — v2.2.0. 28 checks ship (13 scored, 15 advisory), distributed via GitHub -only (`npx github:Back-Road-Creative/rigscore`; npm was intentionally dropped). +Active — v2.3.0. 29 checks ship (13 scored, 16 advisory), distributed on npm +(`npx rigscore`) and GitHub (`npx github:Back-Road-Creative/rigscore`). Default mode makes no network or LLM calls — `--online` opts into supply-chain lookups and `--semantic` shells out to `claude -p`. Known capability gaps are tracked in [`docs/ROADMAP.md`](docs/ROADMAP.md) and are candidates, not diff --git a/changelog.d/374.added.md b/changelog.d/374.added.md deleted file mode 100644 index e20726f9..00000000 --- a/changelog.d/374.added.md +++ /dev/null @@ -1 +0,0 @@ -- `workflow-maturity` now flags graduated skills whose script is missing (`workflow-maturity/graduated-script-missing`, WARNING). A `SKILL.md` declaring `status: graduated-code` asserts it is a thin wrapper over a deterministic script; when that script isn't on disk the skill is non-functional (`exit 127` on every invocation) while its version bump advertises the opposite. Detection is deliberately conservative — only skills declaring `status: graduated-code` are considered (a v1.x LLM-driven skill legitimately has no script), `~` and `$HOME` expand against the scan homedir before the existence test, and placeholder/interpolated tokens (``, `${VAR}`, `$1`, `{{ }}`), comment lines, and relative paths are skipped rather than guessed at. diff --git a/changelog.d/375.docs.md b/changelog.d/375.docs.md deleted file mode 100644 index 02cba558..00000000 --- a/changelog.d/375.docs.md +++ /dev/null @@ -1 +0,0 @@ -- `workflow-maturity`'s docs no longer overclaim for the new `graduated-script-missing` sub-check. The check declares `enforcementGrade: `keyword`` ("evadable by rewording; advisory only") and a weight rationale asserting every finding is a roadmap-dependent judgment call — both true of sub-checks 1-5 and both false of sub-check 0, which resolves a script path and asks the filesystem whether it exists. THREAT-MODEL.md tells auditors to discount `keyword` signal and treat `mechanical` findings as load-bearing, so the unqualified gloss pointed readers away from a finding they should trust. Grade and weight are declared per check, so the discrepancy is documented rather than mislabelled, including its consequence: a broken graduated skill currently costs zero points and is visibility only. Scoring it needs its own check or per-finding weights — an open decision, now recorded as one. diff --git a/changelog.d/376.fixed.md b/changelog.d/376.fixed.md deleted file mode 100644 index 6b8a2db1..00000000 --- a/changelog.d/376.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- Windows CI no longer flakes the wall-clock perf gates. `test/performance.test.js` scales its tight `elapsed <` assertions (5s, 15s) by 3x on win32 — the same factor `vitest.config.js` applies to test/hook timeouts (#371 fixed those; the in-body perf assertions were the sibling it left untouched). A loaded windows runner (measured 1.6x slow) had pushed the 20-project recursive scan past its 15s ceiling on a now-blocking leg, reddening a PR that changed no scanner code. The gate still catches a genuine algorithmic regression, which blows past even a 3x-scaled ceiling. Linux behavior is unchanged (`perf()` is identity off-windows). diff --git a/changelog.d/377.fixed.md b/changelog.d/377.fixed.md deleted file mode 100644 index cadad8c4..00000000 --- a/changelog.d/377.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- The changelog fold is no longer blocked by its own tests. The `meta-ci-contract` RS-4 block pinned specific fragment files by id, but `assemble-changelog --release` deletes fragments as it folds them — so the assertions could only hold until the first real release, then failed forever, leaving the repo unable to cut a release with folded notes (fragments piled up unfolded and 2.2.0 shipped raw git-log notes). Replaced the file-pinning assertions with a pure-function check that `renderRelease()` folds the current `changelog.d` cleanly (version section stamped, empty Unreleased re-opened, every fragment body preserved) — robust by construction, no network or filesystem mutation. Folding into the release workflow remains a separate follow-up. diff --git a/changelog.d/378.added.md b/changelog.d/378.added.md deleted file mode 100644 index 619c09bb..00000000 --- a/changelog.d/378.added.md +++ /dev/null @@ -1 +0,0 @@ -- The release workflow now sources GitHub release notes from the folded `## []` section of `CHANGELOG.md` (via `assemble-changelog --notes `), falling back to the raw git log only when that section is absent. diff --git a/changelog.d/379.added.md b/changelog.d/379.added.md deleted file mode 100644 index cfc14f53..00000000 --- a/changelog.d/379.added.md +++ /dev/null @@ -1 +0,0 @@ -- **staged-copy-drift**: new advisory check (weight 0) that hashes each tracked staged copy against its deployed twin under the operator's home config dir and flags content drift. Opt-in via `--include-home-skills`; rows come from `.rigscorerc.json` `stagedCopies`. diff --git a/changelog.d/383.added.md b/changelog.d/383.added.md deleted file mode 100644 index 58a157a2..00000000 --- a/changelog.d/383.added.md +++ /dev/null @@ -1 +0,0 @@ -- **memory-hygiene**: warn-only dangling/orphan `[[wikilink]]` report (advisory). diff --git a/changelog.d/388.fixed.md b/changelog.d/388.fixed.md deleted file mode 100644 index d40209b7..00000000 --- a/changelog.d/388.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- A forced site re-post now reports the changes it actually shipped. `force_site_post` re-runs the release for a version whose tag already exists, and the previous-tag lookup (`git describe --tags --abbrev=0`) returned that same tag — so the range collapsed to `vX..vX`, the changelog came out empty, and the generated post was a 70-word stub with no change table that the release-post validator rejected. The lookup now excludes the version being released, which is a no-op on a normal release (the tag does not exist yet) and yields the real predecessor on a re-post. diff --git a/changelog.d/390.fixed.md b/changelog.d/390.fixed.md deleted file mode 100644 index e2daaa8a..00000000 --- a/changelog.d/390.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- A forced site re-post can now run more than once. `force_site_post` re-runs a cross-repo site post that failed, but the job built the release branch fresh every time and pushed it plain — so once the first attempt had left `rigscore-release/v` on the site repo, every re-dispatch was non-fast-forward and died before publishing, and `gh pr create` would have failed on the duplicate PR immediately after. The branch is now reused and force-published (its content is wholly derived from the released tag and the facts generator, so the regenerated commit is authoritative), and the PR is opened only when one is not already open. diff --git a/changelog.d/391.fixed.md b/changelog.d/391.fixed.md deleted file mode 100644 index 1dae24b4..00000000 --- a/changelog.d/391.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- `rigscore init --example` now refuses to scaffold into a directory that already holds a real project (`.git`, `package.json`, `pyproject.toml`, `go.mod`, `Cargo.toml`, `pom.xml`, `Gemfile`), printing what it found and how to proceed; `--force` still allows it deliberately. The example is intentionally vulnerable — a privileged `docker-compose.yml`, a root `Dockerfile`, an `.mcp.json` granting a filesystem MCP server access to `/`, and a prompt-injection skill — so a real source tree is the one place it must never land. Run once by mistake inside a live service directory, those files persist untracked, where neither review nor CI will surface them. diff --git a/changelog.d/392.fixed.md b/changelog.d/392.fixed.md deleted file mode 100644 index 6d19220e..00000000 --- a/changelog.d/392.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- `rigscore init --example [dir]` now scaffolds into the positional directory instead of always using the current one, matching how `init -- [dir]` already behaved. Previously the argument was accepted and ignored, so `rigscore init --example demo` wrote the deliberately-vulnerable demo project into the caller's own directory — which is exactly the command the new real-project refusal suggests running. diff --git a/changelog.d/393.docs.md b/changelog.d/393.docs.md deleted file mode 100644 index 2e377bbd..00000000 --- a/changelog.d/393.docs.md +++ /dev/null @@ -1 +0,0 @@ -- **README carries its own governance record**: a closing "Project governance" section states rigscore's Purpose, Owner, Status, and Exit Condition, so the workspace README contract is satisfied by the file itself rather than by an exception entry that expired 2026-06-30. diff --git a/changelog.d/394.added.md b/changelog.d/394.added.md deleted file mode 100644 index 21832662..00000000 --- a/changelog.d/394.added.md +++ /dev/null @@ -1 +0,0 @@ -- **The README contract is enforced in this repo, not from outside it**: a test asserts README.md states its Purpose, Owner, Status and Exit Condition, and that each has a body. The workspace that consumes rigscore as a submodule cannot check this — no workflow passes `submodules:` to actions/checkout, so that directory is empty in its CI — which is how an exemption for this repo sat expired for a month unnoticed. diff --git a/changelog.d/396.fixed.md b/changelog.d/396.fixed.md deleted file mode 100644 index 5d0560b8..00000000 --- a/changelog.d/396.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- **Plugin discovery works in a real `node` process.** `discoverPlugins()` imported the plugin package *directory*, which Node's ESM loader rejects outright (`ERR_UNSUPPORTED_DIR_IMPORT`) — so no `rigscore-check-*` plugin has ever loaded outside the test suite. It looked healthy because vite's resolver silently substituted the package entry point when tests ran, so every plugin assertion passed against behaviour Node does not have. rigscore now resolves the entry the package declares (`exports` `.` subpath honouring `import`/`node`/`default`, then `main`, then `index.js`) and imports that file. An entry that is missing, or that points outside the package directory, is reported to stderr and skipped rather than loaded. Regression tests run discovery in a real `node` subprocess so a bundler's resolver can never stand in for Node's again. diff --git a/changelog.d/397.docs.md b/changelog.d/397.docs.md deleted file mode 100644 index b3ddd619..00000000 --- a/changelog.d/397.docs.md +++ /dev/null @@ -1 +0,0 @@ -- **Contact addresses point at the org account.** `SECURITY.md`'s vulnerability-report fallback and `CODE_OF_CONDUCT.md`'s conduct-report address named a personal inbox; both now name the monitored org address. README's Owner link points at the `Back-Road-Creative` org rather than a personal GitHub profile (the visible handle text is unchanged). Added a `.mailmap` so the four name/email spellings one person committed under resolve to a single contributor in `git shortlog`/`log`/`blame` — display only, no commit is rewritten, and bot identities stay distinct. diff --git a/changelog.d/400.fixed.md b/changelog.d/400.fixed.md deleted file mode 100644 index 857a2653..00000000 --- a/changelog.d/400.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- **`workflow-maturity/skill-no-eval` required a directory, not coverage.** The check `stat()`ed `evals//` and passed on the strength of its existence, so `mkdir` alone turned it green — as did a `tests/test_.*` file of zero bytes. A repo running rigscore accumulated 17 eval directories holding nothing but a README that said "STUB — presence-check satisfier; replace with real fixture before claiming coverage", and the dashboard reported full eval coverage the whole time. An eval directory now counts only when it carries something runnable or gradeable at its top level — an executable `test_*.sh`, a `cases.jsonl` with at least one `{"prompt": …, "expected_assertions": …}` line, or a non-empty `acceptance-criteria.md` — and a `tests/test_.*` entry counts only when it has content. A placeholder that exists but covers nothing reports under a new id, `workflow-maturity/skill-eval-hollow` (info), so "created a stub" is distinguishable from "never created one" at a glance; `skill-no-eval` keeps its exact former meaning for the nothing-at-all case. diff --git a/changelog.d/403.fixed.md b/changelog.d/403.fixed.md deleted file mode 100644 index 1b56a934..00000000 --- a/changelog.d/403.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- The public release workflow no longer names or automates against the private site repo. `.github/workflows/release.yml`'s cross-repo facts-sync job (token clone + `gh pr create` into the private repo) is removed along with the `force_site_post` re-post input that served it; that sync now runs downstream, in the site repo, pulled from this repo's public releases instead of pushed by this repo. diff --git a/changelog.d/404.fixed.md b/changelog.d/404.fixed.md deleted file mode 100644 index 96271e90..00000000 --- a/changelog.d/404.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- The `container` init pack's "no leaked identifiers" test no longer publishes the client project names it guards against — those move to the `FORBIDDEN_EXTRA` repo secret (comma-separated), with CI asserting the secret is set so the guard can't silently vanish; the generic tokens stay in the public test file for local runs. diff --git a/changelog.d/405.docs.md b/changelog.d/405.docs.md deleted file mode 100644 index 27a07472..00000000 --- a/changelog.d/405.docs.md +++ /dev/null @@ -1 +0,0 @@ -- **Public-facing docs and fixtures**: authoring-machine paths and private monorepo layout references genericized in docs, fixtures, and one remediation string. The `agent-output-schemas` check now points readers at rigscore's own `docs/checks/agent-output-schemas.md` instead of a file only the authoring workspace can open. diff --git a/changelog.d/411.changed.md b/changelog.d/411.changed.md deleted file mode 100644 index b5c83762..00000000 --- a/changelog.d/411.changed.md +++ /dev/null @@ -1 +0,0 @@ -- The four release-time workflows (`release.yml`, `docker-publish.yml`, `prebuilt-binaries.yml`, `release-provenance.yml`) now run on the self-hosted runner. `ci.yml` stays GitHub-hosted: its `test` and `secret-scan` jobs are `pull_request`-triggered on a public repository, and its matrix covers `macos-latest` and `windows-latest` that no self-hosted runner here provides. diff --git a/changelog.d/412.fixed.md b/changelog.d/412.fixed.md deleted file mode 100644 index 3aa5de2d..00000000 --- a/changelog.d/412.fixed.md +++ /dev/null @@ -1,4 +0,0 @@ -- CI no longer fails every Dependabot pull request on the `FORBIDDEN_EXTRA` - presence check. Dependabot-triggered runs read GitHub's separate Dependabot - secret store, so `secrets.FORBIDDEN_EXTRA` is always empty there; the guard - now exempts exactly those runs and stays blocking everywhere else. diff --git a/changelog.d/413.changed.md b/changelog.d/413.changed.md deleted file mode 100644 index 9cbf8f96..00000000 --- a/changelog.d/413.changed.md +++ /dev/null @@ -1 +0,0 @@ -- `engines.node` floor raised to `>=20.0.0` (Node 18 is EOL as of 2025-04-30). The `18.17` CI matrix leg and its PR-time exclude entries are dropped, along with the dependabot `vitest`/`@vitest/coverage-v8` hold that existed only to dodge that leg's `styleText` failure. diff --git a/changelog.d/417.changed.md b/changelog.d/417.changed.md deleted file mode 100644 index 02a5501f..00000000 --- a/changelog.d/417.changed.md +++ /dev/null @@ -1 +0,0 @@ -- The four release-time workflows (`release.yml`, `docker-publish.yml`, `prebuilt-binaries.yml`, `release-provenance.yml`) now run on GitHub-hosted `ubuntu-latest`. The org self-hosted runners do not serve public repositories, so these jobs were never picked up (the 2026-09-10 and 2026-09-12 Release runs queued 24h and were cancelled). diff --git a/changelog.d/420.fixed.md b/changelog.d/420.fixed.md deleted file mode 100644 index 2a2fa9f1..00000000 --- a/changelog.d/420.fixed.md +++ /dev/null @@ -1,4 +0,0 @@ -- `skill-files` no longer counts non-text files toward `data.filesScanned`. A - binary in a skill directory is reported in the new `data.filesNonText` count - and as `skill-files/non-text-file`, so it cannot raise scan coverage while - yielding a clean verdict. diff --git a/changelog.d/421.added.md b/changelog.d/421.added.md deleted file mode 100644 index dff08dc8..00000000 --- a/changelog.d/421.added.md +++ /dev/null @@ -1 +0,0 @@ -- MCP runtime tool-hash pins now carry freshness: a pin older than `mcpConfig.runtimeToolPinMaxAgeDays` (default 90) or with no valid timestamp reports `mcp-config/runtime-tool-pin-stale` instead of `runtime-tool-pin-recorded`; recorded findings gain `ageDays`. Info severity, so exit codes are unchanged. diff --git a/changelog.d/422.fixed.md b/changelog.d/422.fixed.md deleted file mode 100644 index 5677f1bd..00000000 --- a/changelog.d/422.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- `infrastructure-security` and `mcp-config` no longer read the operator's `$HOME` unless `--include-home-skills` is set. Before, the deny-list check read `~/.claude/settings.json` first, and `mcp-config` scanned every home MCP config (`~/.claude.json`, `~/.cursor/mcp.json`, Zed, Codex, Goose, Cline …) and home `enableAllProjectMcpServers`. So the same project scored 65 on a bare CI runner and 81 on a laptop. Both checks now go through `src/lib/home-scope.js` like every other home-reading check, and a project's verdict no longer depends on who ran the scan. diff --git a/changelog.d/425.added.md b/changelog.d/425.added.md deleted file mode 100644 index 5d16eac3..00000000 --- a/changelog.d/425.added.md +++ /dev/null @@ -1 +0,0 @@ -- A failing `--fail-under` gate now writes one `rigscore: FAIL ...` line to stderr (score or failing-project count, threshold, lowest project in `--recursive`), and an unknown flag that is a near miss of a real one warns with `did you mean --flag?` (the flag is still ignored). Exit codes and stdout are unchanged. diff --git a/changelog.d/428.changed.md b/changelog.d/428.changed.md deleted file mode 100644 index bf34e814..00000000 --- a/changelog.d/428.changed.md +++ /dev/null @@ -1 +0,0 @@ -- Terminal report: each finding now shows its id in square brackets with a one-time `rigscore explain ` hint, output wraps to the terminal width (80 when piped), and the check-name and project columns size to the longest entry instead of a fixed width. diff --git a/changelog.d/429.added.md b/changelog.d/429.added.md deleted file mode 100644 index d1857c92..00000000 --- a/changelog.d/429.added.md +++ /dev/null @@ -1 +0,0 @@ -- `sandbox-posture` now covers Windsurf Cascade: it reads `windsurf.cascadeCommandsAllowList` / `windsurf.cascadeCommandsDenyList` from the per-user `settings.json` and reports `sandbox-posture/windsurf-shell-allowlisted` (warning) when a bare shell, interpreter or `*` is allow-listed and not denied. Windsurf posture is `unrestricted` in that case, otherwise `partial`; a `settings.json` without the list keys is not a surface. diff --git a/changelog.d/430.fixed.md b/changelog.d/430.fixed.md deleted file mode 100644 index a26c6153..00000000 --- a/changelog.d/430.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- The Homebrew formula pin now follows each release automatically. The release workflow opens a pull request that points `Formula/rigscore.rb` at the new tag and its sha256 (and dispatches CI on it), warning loudly if it cannot open the PR; the formula is pinned to v2.2.0, and the pin test no longer hardcodes a version. (#430) diff --git a/changelog.d/431.changed.md b/changelog.d/431.changed.md deleted file mode 100644 index 2608674d..00000000 --- a/changelog.d/431.changed.md +++ /dev/null @@ -1 +0,0 @@ -- `skill-files/tag-chars` and `unicode-steganography/tag-chars` now share one pattern covering the whole Unicode Tags block (U+E0000-U+E007F), so both checks also flag U+E0000 and U+E0002-U+E001F. The duplicated `gitCheckIgnore` and `toRegex` helpers moved into `src/utils.js`. diff --git a/changelog.d/432.fixed.md b/changelog.d/432.fixed.md deleted file mode 100644 index 76aec55c..00000000 --- a/changelog.d/432.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- **instruction-effectiveness:** dead-file-reference no longer flags `path::symbol` refs (the file is still checked), path-led commands with arguments, or scheme-less URLs such as `youtu.be/` and `github.com/org/repo`. A path containing a space is now read as a command and not checked. diff --git a/changelog.d/433.fixed.md b/changelog.d/433.fixed.md deleted file mode 100644 index 839942dd..00000000 --- a/changelog.d/433.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- `rigscore init --help` now prints usage and writes nothing (it used to write a `.rigscorerc.json`). `rigscore explain /` now resolves a finding id from the check page's Triggers table and prints its severity, condition and fix, instead of falling back to the whole page; heading matches are exact, and an unknown id lists the documented ids. diff --git a/changelog.d/434.added.md b/changelog.d/434.added.md deleted file mode 100644 index eee4d1b7..00000000 --- a/changelog.d/434.added.md +++ /dev/null @@ -1 +0,0 @@ -- **GitHub Action: Markdown job summary.** The action now writes a readable report to the run page (score, grade, checks needing attention, top findings with fixes, and the real reason a run failed), and the `check` input documents comma-separated ids. diff --git a/changelog.d/435.added.md b/changelog.d/435.added.md deleted file mode 100644 index e3b26942..00000000 --- a/changelog.d/435.added.md +++ /dev/null @@ -1 +0,0 @@ -- The generated facts snapshot now includes `nodeFloor` (minimum Node version from `engines.node`) and `clientCount` (size of the client registry). diff --git a/changelog.d/436.added.md b/changelog.d/436.added.md deleted file mode 100644 index 53c9270f..00000000 --- a/changelog.d/436.added.md +++ /dev/null @@ -1 +0,0 @@ -- **Compliance report shows when each framework's mappings were last verified.** Every framework section now prints `Mappings last verified: YYYY-MM-DD` under its status, so an auditor can see how old the control-ID mapping is against the primary source. The date lives in `FRAMEWORKS` and is bumped only after a real re-check. diff --git a/changelog.d/437.fixed.md b/changelog.d/437.fixed.md deleted file mode 100644 index 97d11582..00000000 --- a/changelog.d/437.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- `infrastructure-security` now runs on macOS and Windows instead of returning N/A. The hooks path, git wrapper, deny list and sandbox-gate arms are plain file reads and run everywhere; only the `lsattr` immutable-flag arm and the default `/etc/profile.d` safety-guard lookup stay Linux-only (off Linux, declared `immutableDirs` report an info `cannot-check-immutability` finding naming the platform), and Windows skips the uid-0 and execute-bit rules. diff --git a/changelog.d/438.added.md b/changelog.d/438.added.md deleted file mode 100644 index 65556e7a..00000000 --- a/changelog.d/438.added.md +++ /dev/null @@ -1 +0,0 @@ -- **`--fail-on `** — exit 1 when any finding is at that severity or worse, regardless of score; `warning` includes `critical`. Respects suppressions and works with `--recursive`. An unknown level is an argument error (exit 2). diff --git a/changelog.d/439.changed.md b/changelog.d/439.changed.md deleted file mode 100644 index 8d7ba4c2..00000000 --- a/changelog.d/439.changed.md +++ /dev/null @@ -1 +0,0 @@ -- The terminal report now folds `governance-docs` missing-rule warnings into one warning that lists every missing rule, with one fix line and the member ids, instead of one near-identical warning per rule. Display only: JSON, SARIF, scoring and `--ignore` still see one finding per rule with its own id. diff --git a/changelog.d/441.fixed.md b/changelog.d/441.fixed.md deleted file mode 100644 index 18caab6c..00000000 --- a/changelog.d/441.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- `rigscore init --` no longer writes through symlinks. A pack destination that is itself a symlink (including a dangling one, which previously read as absent) or whose parent directory resolves outside the target directory now refuses the whole pack with a named error before anything is written. Installing into a target directory that does not exist yet still works. diff --git a/changelog.d/442.fixed.md b/changelog.d/442.fixed.md deleted file mode 100644 index f802b56b..00000000 --- a/changelog.d/442.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- **config:** `semantic.command` (the program `--semantic` spawns) is now read only from the home `~/.rigscorerc.json`. A value in a scanned project's `.rigscorerc.json`, or in a file it extends, is ignored with a one-line stderr warning, so a scanned repo can no longer choose what runs. diff --git a/changelog.d/443.added.md b/changelog.d/443.added.md deleted file mode 100644 index f7cfb263..00000000 --- a/changelog.d/443.added.md +++ /dev/null @@ -1 +0,0 @@ -- `mcp-hash`, `mcp-pin` and `mcp-verify` accept an opt-in `--connect` flag that launches one named local stdio MCP server from the project config and reads its live `tools/list`, so runtime tool pinning no longer needs a manual pipe. A scan still never spawns a server. Each launch needs per-run consent (an interactive `[y/N]` or `--yes`; no terminal and no `--yes` refuses with exit `2`), prints the exact argv, working directory and env keys first, runs with a minimal environment, refuses relative commands or scripts that escape the project through a symlink, and kills the process tree after the list is read or after 15 s (`RIGSCORE_CONNECT_TIMEOUT_MS` overrides). A timeout or malformed reply exits `5`. The consent prompt prints each argv element as typed, so a Windows path shows its single backslashes rather than JSON-doubled ones, and control characters are shown as `<0xNN>`. diff --git a/changelog.d/444.docs.md b/changelog.d/444.docs.md deleted file mode 100644 index 5f37b888..00000000 --- a/changelog.d/444.docs.md +++ /dev/null @@ -1 +0,0 @@ -- **Docs gate covers README and CLAUDE.md**: `npm run verify:docs` now fails when the README lead drops "any AI client" or states a stale known-client count, and when `CLAUDE.md` has the wrong title or omits a check in `src/checks/`. README.md is link-checked too. CLAUDE.md and the README lead were brought current in the same change. diff --git a/changelog.d/445.security.md b/changelog.d/445.security.md deleted file mode 100644 index e4e8c09f..00000000 --- a/changelog.d/445.security.md +++ /dev/null @@ -1 +0,0 @@ -- The CI and leak-scan workflows now declare read-only token permissions, the prebuilt-binaries release workflow passes the release tag through the environment and refuses anything that is not `vX.Y.Z[-prerelease]`, and CI audits production dependencies for high-severity advisories. diff --git a/changelog.d/446.added.md b/changelog.d/446.added.md deleted file mode 100644 index db192cdd..00000000 --- a/changelog.d/446.added.md +++ /dev/null @@ -1 +0,0 @@ -- The terminal report now ends with a one-line pointer to `--fix` when a fixer matches a finding, and `--json` output carries `rigscoreVersion` and `schemaVersion` keys (additive; existing keys unchanged). diff --git a/changelog.d/447.fixed.md b/changelog.d/447.fixed.md deleted file mode 100644 index ea507c20..00000000 --- a/changelog.d/447.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- `walkDirSafe` no longer follows a symlink that leaves the scan root (`link -> /`, `link -> ~/.aws`); it reports `outsideRootSkipped`, and `deep-secrets` discloses it (`deep-secrets/outside-root-skipped`) instead of printing "Deep scan clean". diff --git a/changelog.d/448.fixed.md b/changelog.d/448.fixed.md deleted file mode 100644 index 9596f4c3..00000000 --- a/changelog.d/448.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- The release workflow now dispatches the prebuilt archives workflow on the released tag, so every release gets its archives attached; it passes release notes to `gh release create` as a file instead of a step output, so a commit subject can no longer forge outputs; coverage counts every `src` file; and Dependabot proposes Docker base-image bumps (major Node bumps excluded). diff --git a/changelog.d/449.security.md b/changelog.d/449.security.md deleted file mode 100644 index 053be565..00000000 --- a/changelog.d/449.security.md +++ /dev/null @@ -1 +0,0 @@ -- **plugins:** a scan no longer loads plugin code that the scanned repo supplies. The paths in a project's `.rigscorerc.json` `plugins` and the `rigscore-check-*` packages in `/node_modules` now load only when your home `~/.rigscorerc.json` lists that repo's absolute root in `trustProjectPlugins`, for example `{ "trustProjectPlugins": ["~/code/my-repo"] }`. Existing users who rely on in-repo plugins opt in by adding that entry (with `--recursive`, one entry per subproject). A skipped plugin is named in one stderr line. Plugins installed beside rigscore itself still load, and `trustProjectPlugins` in a project config is ignored. diff --git a/changelog.d/450.added.md b/changelog.d/450.added.md deleted file mode 100644 index ae7ee98a..00000000 --- a/changelog.d/450.added.md +++ /dev/null @@ -1 +0,0 @@ -- SARIF results now carry `region.startLine` (and CodeClimate `lines.begin`) when a finding supplies `context.file` and `context.line`, so GitHub code scanning annotates the right line instead of line 1. Each rule's `helpUri` defaults to that check's docs page. Findings that describe the home directory (shell-history secrets, `~/` skill files) get no repo location, and absolute, `..` and drive-letter paths are never emitted as locations. diff --git a/changelog.d/451.fixed.md b/changelog.d/451.fixed.md deleted file mode 100644 index 61ded1cc..00000000 --- a/changelog.d/451.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- Two skill-file tests no longer fail when the suite runs under `umask 000`: they pin the mode of the files they write to 644. Tests that passed `homedir: '/tmp'` now get a fresh temporary home, so they stop scanning whatever sits in the host's `/tmp`. Test-only change. diff --git a/changelog.d/452.added.md b/changelog.d/452.added.md deleted file mode 100644 index 40dfe638..00000000 --- a/changelog.d/452.added.md +++ /dev/null @@ -1 +0,0 @@ -- `release.yml` now publishes the package to npm on each release using npm trusted publishing (OIDC from GitHub Actions, no stored token), so `npx rigscore` and `npm i -g rigscore` work from the first release after this change. The step skips a version npm already has, so a re-run does not fail. The README, troubleshooting page and workflow headers no longer say the package is GitHub-only. diff --git a/node_modules b/node_modules new file mode 120000 index 00000000..0025c89e --- /dev/null +++ b/node_modules @@ -0,0 +1 @@ +../rs-npm/node_modules \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 61dfdd6a..3448aa7b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "rigscore", - "version": "2.2.0", + "version": "2.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "rigscore", - "version": "2.2.0", + "version": "2.3.0", "license": "MIT", "dependencies": { "chalk": "5.6.2", diff --git a/package.json b/package.json index 0355b64e..132bcb6f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rigscore", - "version": "2.2.0", + "version": "2.3.0", "description": "Configuration hygiene checker for AI dev environments. Scans locally, no external calls.", "type": "module", "bin": { From d297a198164eb2ef3015a5c09c7e66de619ccbe5 Mon Sep 17 00:00:00 2001 From: Joe Petrucelli Date: Tue, 27 Oct 2026 10:40:32 -0400 Subject: [PATCH 2/3] ci: retrigger From 2a9f45bd6282f1e7c590cb46e3ec3be8c209c535 Mon Sep 17 00:00:00 2001 From: Joe Petrucelli Date: Tue, 27 Oct 2026 11:01:09 -0400 Subject: [PATCH 3/3] chore(release): fold #454 into the 2.3.0 changelog --- CHANGELOG.md | 1 + changelog.d/454.fixed.md | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 changelog.d/454.fixed.md diff --git a/CHANGELOG.md b/CHANGELOG.md index eb8dd8de..2924f7c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -69,6 +69,7 @@ next release; the maintainer folds the fragments in at release time. - `walkDirSafe` no longer follows a symlink that leaves the scan root (`link -> /`, `link -> ~/.aws`); it reports `outsideRootSkipped`, and `deep-secrets` discloses it (`deep-secrets/outside-root-skipped`) instead of printing "Deep scan clean". - The release workflow now dispatches the prebuilt archives workflow on the released tag, so every release gets its archives attached; it passes release notes to `gh release create` as a file instead of a step output, so a commit subject can no longer forge outputs; coverage counts every `src` file; and Dependabot proposes Docker base-image bumps (major Node bumps excluded). - Two skill-file tests no longer fail when the suite runs under `umask 000`: they pin the mode of the files they write to 644. Tests that passed `homedir: '/tmp'` now get a fresh temporary home, so they stop scanning whatever sits in the host's `/tmp`. Test-only change. +- Windows: skill files, rule files and Kubernetes manifests no longer drop out of a scan at random. The directory walker keyed visited folders on a file ID that loses precision above 2^53 (common on NTFS), so two sibling folders could look like a symlink loop and the second was skipped. It now reads file IDs at full precision. ### Security - The CI and leak-scan workflows now declare read-only token permissions, the prebuilt-binaries release workflow passes the release tag through the environment and refuses anything that is not `vX.Y.Z[-prerelease]`, and CI audits production dependencies for high-severity advisories. diff --git a/changelog.d/454.fixed.md b/changelog.d/454.fixed.md deleted file mode 100644 index 701d20c1..00000000 --- a/changelog.d/454.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- Windows: skill files, rule files and Kubernetes manifests no longer drop out of a scan at random. The directory walker keyed visited folders on a file ID that loses precision above 2^53 (common on NTFS), so two sibling folders could look like a symlink loop and the second was skipped. It now reads file IDs at full precision.