Skip to content
Closed
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
483 changes: 483 additions & 0 deletions docs/plans/2026-10-05-2151-feat-ratstack-scorecard-plan.md

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions evals/ratstack-scorecard/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.cache/
30 changes: 30 additions & 0 deletions evals/ratstack-scorecard/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# rat-stack scorecard

The scorecard measures the starter against [rat-stack](https://github.com/joelhooks/rat-stack) on one or more metrics per rat-stack bin and surface, and publishes which bins the starter beats. Both sides are run; no number comes from reading a README.

## Ownership

`evals/ratstack-scorecard/**` and `.github/workflows/scorecard.yml` are an Evaluator surface owned by the verifier session (`starter-verify`). Builders never edit them: a builder who changes the instrument that grades their work reports the score they chose (CONST-E9). A metric that looks wrong goes to Kiro as a finding.

## What it measures

`src/metrics/registry.ts` lists every row: its id (`M1`-`M29`, with sub-rows such as `M3.lcp`), the rat-stack bin it grades, its unit, which direction is better, whether it is a `count` (three runs that must agree exactly) or a `measurement` (N runs), and the family that measures it. The plan's Metric Catalog (`docs/plans/2026-10-05-2151-feat-ratstack-scorecard-plan.md`) explains each row.

## How a verdict is reached

- A row is **beaten** only when the starter's worst run is strictly better than rat-stack's best run. Overlapping ranges are **not beaten**; identical ranges are a **tie**.
- A count that does not reproduce exactly, or a side that produced the wrong number of runs, is an **instrument error**.
- An invariant rat-stack lacks is **unsupported** with a `file:line` citation. The citation is re-checked at the pin; if the cited lines no longer say it, the row is an instrument error.
- The ratchet compares a PR with the latest `main` artifact. It fails on any instrument error, on a row beaten on `main` that the PR no longer beats, and on a starter value worse than `main`'s worst run. A missing secret or a fork PR without a preview is neutral. A changed metric definition is re-baselined.

## Running it

Everything that loads third-party code runs inside the sandbox launcher from `systemfsoftware/pnpm-release-management` (`packages.<system>.sandbox`), with this directory as the sandbox project. The instrument's own flake (`flake.nix`) pins nixpkgs (pnpm 12.9.0, Node 24) and the launcher, and builds the tools' pnpm store from `pnpm-lock.yaml` as a fixed-output derivation (`tools-store`). The install is offline from that store; the sandbox gets no network at all.

```sh
cd evals/ratstack-scorecard
nix develop --command sh -c 'SANDBOX_PROJECT=$PWD sandbox --pnpm-store "$SANDBOX_PNPM_STORE" -- pnpm install --frozen-lockfile'
nix develop --command sh -c 'SANDBOX_PROJECT=$PWD sandbox -- pnpm vitest run'
```

The decision modules (`src/model/*.workflow.ts`) and the orchestrator import only Deno APIs, `node:` builtins and each other, so `deno check src/` type-checks them without any third-party code.
9 changes: 9 additions & 0 deletions evals/ratstack-scorecard/deno.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"compilerOptions": {
"strict": true,
"exactOptionalPropertyTypes": true,
"noUncheckedIndexedAccess": true
},
"exclude": ["node_modules/", "**/*.test.ts", "**/*.arbitrary.ts", "vitest.config.ts"],
"lock": false
}
94 changes: 94 additions & 0 deletions evals/ratstack-scorecard/flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

42 changes: 42 additions & 0 deletions evals/ratstack-scorecard/flake.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
description = "rat-stack scorecard: the verifier-owned instrument that measures the starter against rat-stack";

inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/494ce7fd23ff6a5dff39e1fb11e9b6f2ac74bf25";
pnpm-release-management = {
url = "github:systemfsoftware/pnpm-release-management/180122866dd537fa728b5563fb1820fbd2af88cc";
inputs.nixpkgs.follows = "nixpkgs";
};
};

outputs = { self, nixpkgs, pnpm-release-management }:
let
systems = [ "x86_64-linux" "aarch64-linux" "aarch64-darwin" ];
forEachSystem = fn: nixpkgs.lib.genAttrs systems (system: fn nixpkgs.legacyPackages.${system});
in
{
packages = forEachSystem (pkgs:
let
system = pkgs.stdenv.hostPlatform.system;
sandbox = pnpm-release-management.packages.${system}.sandbox;
tools-store = (pnpm-release-management.lib.mkPnpmWorkspacePackages {
inherit pkgs;
src = self;
pname = "ratstack-scorecard";
pnpm = pkgs.pnpm_12;
hash = "sha256-TylxLEQflTlKx6QDk9mFlBOEInUvvBBeOVFUYCURmYo=";
}).pnpm-store;
in
{
inherit sandbox tools-store;
});

devShells = forEachSystem (pkgs:
let system = pkgs.stdenv.hostPlatform.system; in {
default = pkgs.mkShell {
packages = [ pkgs.nodejs_24 pkgs.pnpm_12 pkgs.deno self.packages.${system}.sandbox ];
SANDBOX_PNPM_STORE = self.packages.${system}.tools-store;
};
});
};
}
11 changes: 11 additions & 0 deletions evals/ratstack-scorecard/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "@starter/ratstack-scorecard",
"private": true,
"type": "module",
"devDependencies": {
"@fast-check/vitest": "0.5.0",
"ajv": "8.20.0",
"fast-check": "4.10.2",
"vitest": "5.0.3"
}
}
Loading
Loading