Keel

Grounding report.

Every verification edge in commander-js, classified by who produces the signal and whether the actor being verified can write to that producer.

targetcommander-js
revisionba6d13ddb4243e5913367734f8c159089ffe7834
generated2026-07-25T03:48:59.993Z
1.00

anchored / (anchored + self_referential + unknown) = 12 / 12

anchored 12 self_referential 0 unknown 0 not_a_check 9 excluded from the denominator

Coverage (judged)

node kindjudged
script11
ci_step10

Nodes by kind, over the 21 node(s) carried in this report. Surfaces the gatherer cannot read are absent here rather than counted as unknown, which makes non-coverage Keel's own shoppable class — so it is stated, not implied.

ε-audit — not run

No verdict in this report carries a readable audit block, so nothing was re-decided and there is no agreement rate to report. This is not agreement, and it is not full agreement either: a rate over zero comparisons would state a measurement that was never taken. (“Readable” is load-bearing: a block can be present and state no comparison, and this state covers that too — see below if so.)

This run carries 4 probe-decided verdict(s), none of them contributing a readable comparison. (“Readable” rather than “audited”, because a block can arrive and state nothing — if any did, they are counted below.) Audit coverage is itself shoppable — audit none of the library and no disagreement can ever be found — so the absence is printed here rather than left to be inferred from a missing section.

Scope. Keel measures the shape of verification, not its quality. A repo can be 100% anchored with terrible tests. Anchoring says the signal comes from outside; it does not say the signal is sufficient.

Node graph — 21 node(s), in gather order

anchored 12 not_a_check 9

No mark is ringed: no drawn verdict is anchored below confidence 0.6. Hover a mark for its node.

anchored — 12

The producer sits outside the write boundary of the actor being verified.

check
anchored
package.json · script

This is an aggregate, so I descended rather than classifying the wrapper: it expands to check:type (tsc -p tsconfig.js.json, then tsd + tsc -p tsconfig.ts.json), check:lint (eslint .) and check:format (prettier --check .). Every leaf is a program computing a verdict from the source text, and the shell's && short-circuit means any non-zero leaf becomes this target's exit code — an author cannot make a type error or a lint violation report clean without editing the file or the pinned config. The CI job runs this exact target at .github/workflows/tests.yml:30 on push and pull_request, so the exit code is on the merge path. DOCUMENTED LIMIT: the rule sets (eslint.config.js, tsconfig*.json, .prettierrc.js) live inside the write boundary, so what is anchored is the enforcement, not the choice of what gets enforced.

producer the shell's && chain propagating the exit codes of tsc, eslint and prettier --check, each executing over the committed tree — actor can write to it: no

  • package.json:12 ("check": "npm run check:type && npm run check:lint && npm run check:format")
  • package.json:13-16 (check:format, check:lint, check:type leaves)
  • .github/workflows/tests.yml:30-31 (run: npm run check)

decided by agent · confidence 0.85

check:format
anchored
package.json · script

`--check` is the verify mode, not the rewrite mode: prettier prints the offending paths and exits 1 without touching the tree, so the signal is a genuine falsifiable comparison performed by the formatter binary against the committed bytes. An author cannot make an unformatted file pass without reformatting it or editing .prettierrc.js / .prettierignore. It reaches the merge path through `npm run check` at .github/workflows/tests.yml:30. DOCUMENTED LIMIT: what this asserts is formatting agreement only — it says nothing about whether the code is correct, and the style rules themselves are in-repo.

producer the prettier process's exit code, computed by re-formatting each committed file in memory and comparing the result to the bytes on disk — actor can write to it: no

  • package.json:13 ("check:format": "prettier --check .")
  • .prettierrc.js
  • .github/workflows/tests.yml:30-31

decided by agent · confidence 0.85

check:lint
anchored
package.json · script

The step shells out to a static analyser (ruff check, eslint, mypy, pyright, flake8, pylint, golangci-lint, cargo clippy, biome check, staticcheck, tsc --noEmit) over the committed tree. The verdict is computed by that program from the source text, so an author cannot make a violating file report clean without changing the file or changing the pinned analyser configuration — the signal's producer is the interpreter/binary, not any claim the repository makes about itself. Documented limit: the RULE SET is author-controlled (select/ignore/exclude lists live in the repo), so this is anchored on the execution axis while the scope of what gets enforced sits inside the write boundary.

producer the static-analysis tool's process exit code — a linter or type checker parsing the checked-out source and returning non-zero on a violation — actor can write to it: no

  • .github/workflows/lint.yml:31

decided by probe (static-analysis-check-invocation) · confidence 0.85

check:type
anchored
package.json · script

Descending: this expands to check:type:js (`tsc -p tsconfig.js.json`) and check:type:ts (`tsd && tsc -p tsconfig.ts.json`). tsconfig.json sets allowJs+checkJs+noEmit and skipLibCheck:false, so tsc actually type-checks lib/**/*.js and the hand-written typings/index.d.ts; the verdict is computed by the compiler from the source and cannot be talked into passing. Chained with &&, so either compiler failure becomes this target's exit code, and CI runs it via `npm run check`. DOCUMENTED LIMIT: strictness knobs (noImplicitAny:false, strictNullChecks:false in the shared tsconfig.json) are author-controlled, so the compiler decides honestly but the repo decides how much it is allowed to see.

producer the TypeScript compiler's exit code, from two tsc invocations (one over the JavaScript sources with checkJs, one over the TypeScript declarations) plus tsd — actor can write to it: no

  • package.json:15 ("check:type": "npm run check:type:js && npm run check:type:ts")
  • tsconfig.json (allowJs, checkJs, noEmit, skipLibCheck:false)
  • .github/workflows/tests.yml:30-31

decided by agent · confidence 0.85

check:type:ts
anchored
package.json · script

`tsd` compiles typings/index.test-d.ts and fails when an `expectType<...>` assertion does not hold or when a `@ts-expect-error` line stops erroring; `tsc -p tsconfig.ts.json` then re-checks the .ts/.mts files under the stricter override (noImplicitAny, strictNullChecks, useUnknownInCatchVariables all back on). Both verdicts are computed by the TypeScript type system from the committed declarations, so a broken public type signature cannot report clean without changing the declarations. This target is on the merge path twice — directly inside `npm test` (package.json:20) and transitively through `npm run check`. DOCUMENTED LIMIT: the ORACLE is in-repo — index.test-d.ts is written by the same maintainers as index.d.ts, so a green run proves the declarations match the assertions, not that the assertions describe the right API.

producer tsd's type-assertion runner and the tsc compiler, both executing over typings/index.d.ts and typings/index.test-d.ts — actor can write to it: no

  • package.json:16 ("check:type:ts": "tsd && tsc -p tsconfig.ts.json")
  • typings/index.test-d.ts:1-30 (expectType assertions, @ts-expect-error)
  • tsconfig.ts.json (stricter overrides)

decided by agent · confidence 0.85

check:type:js
anchored
package.json · script

tsconfig.js.json extends tsconfig.json, which turns on allowJs/checkJs and noEmit, so tsc parses the JSDoc and the JavaScript in lib/ and returns non-zero on an inconsistency. The producer is the compiler executing over the source text; an author cannot make a mismatched JSDoc annotation report clean without editing the source or the tsconfig. Reaches CI through `npm run check` (.github/workflows/tests.yml:30). DOCUMENTED LIMIT: the shared tsconfig deliberately relaxes noImplicitAny and strictNullChecks for the JS files, so the compiler's honesty is bounded by an in-repo setting.

producer the tsc process's exit code from type-checking the committed JavaScript (index.js, lib/**/*.js) with checkJs and noEmit — actor can write to it: no

  • package.json:17 ("check:type:js": "tsc -p tsconfig.js.json")
  • tsconfig.js.json (include: *.js, lib/**/*.js)
  • tsconfig.json (checkJs: true, noEmit: true)

decided by agent · confidence 0.85

test
anchored
package.json · script

`node --test` discovers and runs tests/*.test.{js,cjs} in child processes; the tests import ../index.js and assert with node:assert/strict, so a failed assertion, a thrown exception or an import error becomes a non-zero exit from the runtime — nobody can persuade a crashing parse into reporting green. This is the exact target CI runs at .github/workflows/tests.yml:28-29, across 3 Node versions x 3 operating systems, on push and pull_request, so the exit code is on the merge path. DOCUMENTED LIMIT: the EXECUTION is anchored, the ORACLE is not — tests/ and lib/ are authored by the same maintainers, so a green suite proves the code does what these tests say, not that the tests say the right thing.

producer the Node.js runtime's exit code from `node --test` executing 112 test files, followed by the TypeScript compiler and tsd — actor can write to it: no

  • package.json:21 ("test": "node --test && npm run check:type:ts")
  • tests/command.action.test.js:1-20 (node:test + node:assert/strict against ../index.js)
  • .github/workflows/tests.yml:28-29 (run: npm test)
  • .github/workflows/tests.yml:13-16 (matrix: node 22/24/26 x ubuntu/windows/macos)

decided by agent · confidence 0.9

test-all
anchored
package.json · script

Same execution path as `test`: the runtime runs the committed suite and the analysers run over the committed source, so no author-authored claim sits between the artefact and the verdict. NOTED, because it changes what this edge is worth: no workflow invokes `test-all` — .github/workflows/tests.yml runs `npm test` and `npm run check` as two separate steps (a comment there says this is deliberate, for correct status handling on Windows) — so this target is a local convenience alias and gates no merge by itself. It is anchored on the execution axis and redundant on the gating axis. DOCUMENTED LIMIT: the test oracle is co-authored with the implementation, as with `test`.

producer the Node.js runtime's exit code from `node --test`, chained with the tsc/eslint/prettier exit codes behind `npm run check` — actor can write to it: no

  • package.json:22 ("test-all": "node --test && npm run check")
  • .github/workflows/tests.yml:25-31 (npm test and npm run check invoked separately; test-all never invoked)

decided by agent · confidence 0.75

Perform CodeQL Analysis
anchored
.github/workflows/codeql-analysis.yml:57 · ci_step

The findings are computed by a scanner the repository does not ship and cannot edit: the action is pinned to github/codeql-action@7211b7c8077ea37d8641b6271f6a365a22a5fbfa, and with no `queries:` or config-file override in this workflow the rule set is GitHub's default suite, so unlike eslint (whose rules live in eslint.config.js) neither the engine nor the queries sit inside the write boundary. That is what makes the fork point traceable and external. LOW CONFIDENCE, and here is why: with the default configuration `analyze` uploads alerts to code scanning and exits non-zero on an analysis error, not on a finding — whether an alert actually blocks a merge depends on branch-protection settings that do not live in the clone. So the producer is anchored while the strength of the gate it feeds is not visible from the repository.

producer the CodeQL engine executing GitHub's default javascript query pack over an extracted database, on GitHub-hosted infrastructure, from an action pinned to a full commit SHA — actor can write to it: no

  • .github/workflows/codeql-analysis.yml:57-58 (uses: github/codeql-action/analyze@7211b7c8077ea37d8641b6271f6a365a22a5fbfa)
  • .github/workflows/codeql-analysis.yml:32-40 (no `queries:` override — default suite)
  • .github/workflows/codeql-analysis.yml:14-18 (security-events: write; alerts go to code scanning)

decided by agent · confidence 0.6

npm install
anchored
.github/workflows/tests.yml:26 · ci_step

`npm ci` is the frozen form of install, not the ordinary one: it will not refresh a stale lock, it errors out when package.json and package-lock.json are out of sync, and it verifies each downloaded tarball against the integrity hash recorded in the lock. So the step is a falsifiable assertion that two committed files agree and that the registry still serves the exact artefacts they name — an author who bumps a dependency and forgets to regenerate the lock cannot make it pass without changing the tree. Its exit code gates the rest of the job: `npm test` and `npm run check` (lines 28-31) only run if it succeeds. DOCUMENTED LIMIT: what is anchored is manifest/lock agreement and registry reachability, nothing about whether the code works.

producer npm's installer refusing to proceed when the committed package-lock.json and package.json disagree, plus registry tarball integrity hashes verified against the lockfile — actor can write to it: no

  • .github/workflows/tests.yml:26-27 (run: npm ci)
  • package-lock.json (the committed lockfile npm ci is pinned to)
  • .github/workflows/tests.yml:28-31 (subsequent steps depend on this exit code)

decided by agent · confidence 0.7

npm test
anchored
.github/workflows/tests.yml:28 · ci_step

I descended into the script rather than trusting the step name: `npm test` is package.json:21, `node --test && npm run check:type:ts`. Node's test runner executes the suites in tests/ as real processes and reports failures through the process exit code, which becomes this step's status; the workflow triggers on push and pull_request, so this is the edge that actually blocks a merge, and it runs nine times (node 22/24/26 x ubuntu/windows/macos) with fail-fast disabled. DOCUMENTED LIMIT: execution is anchored, the oracle is not — the assertions in tests/ and the implementation in lib/ are written by the same maintainers, so green means the code matches these tests, not that these tests are the right specification.

producer the Node.js runtime's exit code from `node --test` over 112 committed test files, then tsd and tsc — actor can write to it: no

  • .github/workflows/tests.yml:28-29 (run: npm test)
  • package.json:21 ("test": "node --test && npm run check:type:ts")
  • .github/workflows/tests.yml:3 (on: [push, pull_request, workflow_dispatch])
  • tests/ (112 *.test.js files using node:test + node:assert/strict)

decided by agent · confidence 0.9

npm run check
anchored
.github/workflows/tests.yml:30 · ci_step

Descending into package.json:12, this step runs check:type (tsc over both the JS with checkJs and the TS declarations, plus tsd), check:lint (eslint .) and check:format (prettier --check .), chained with && so any leaf failure becomes the step's status. Every leaf verdict is computed by an analyser from the source text, not asserted by any file in the repo, and the workflow's push/pull_request triggers put this status on the merge path. DOCUMENTED LIMIT: the rule sets — eslint.config.js, tsconfig*.json's relaxed strict flags, .prettierrc.js and .prettierignore — are all in-repo, so the enforcement is anchored while its scope is author-controlled.

producer the exit codes of tsc, tsd, eslint and prettier --check, each computed by a program reading the committed source — actor can write to it: no

  • .github/workflows/tests.yml:30-31 (run: npm run check)
  • package.json:12-17 (check and its leaves)
  • eslint.config.js, .prettierrc.js, tsconfig.json (author-controlled rule sets)

decided by agent · confidence 0.85

not_a_check — 9

Asserts nothing about correctness, so it is excluded from the denominator. This is the one shoppable class: mis-filing a real check here shrinks the denominator and inflates the score, so each row carries the same burden of argument as any other verdict.

fix
not_a_check
package.json · script

This expands to fix:lint (`eslint --fix .`) and fix:format (`prettier --write .`), both of which mutate the files rather than report on them; the target exists so a developer can make the check:* targets pass, and no workflow invokes it (.github/workflows/tests.yml runs `npm test` and `npm run check` only). Nothing consumes its exit code and it leaves no signal behind, so counting it in either direction would be a lie about what gates this repo. The falsifiable form of the same rules is check:lint / check:format, which are counted.

producer nothing that yields a verdict — the target's whole effect is eslint --fix and prettier --write rewriting the working tree — actor can write to it: not established

  • package.json:18 ("fix": "npm run fix:lint && npm run fix:format")
  • package.json:19-20 (fix:format = prettier --write, fix:lint = eslint --fix)
  • .github/workflows/tests.yml:26-31 (only npm ci / npm test / npm run check are invoked)

decided by agent · confidence 0.8

fix:format
not_a_check
package.json · script

`prettier --write .` rewrites every file it can parse and reports success; it is the remediation counterpart of check:format, not an assertion. There is no state in which it says 'this tree is wrong' — it makes the tree conform instead — so it is structurally incapable of being a check, and no workflow calls it. The assertion about formatting is made by `prettier --check .` in the CI-invoked `npm run check`.

producer no verdict producer — prettier in --write mode edits the files and exits 0 — actor can write to it: not established

  • package.json:19 ("fix:format": "prettier --write .")
  • package.json:13 (the --check counterpart)

decided by agent · confidence 0.85

fix:lint
not_a_check
package.json · script

The target's contract is to mutate the working tree until auto-fixable violations disappear, and nothing in the pipeline reads its exit status: .github/workflows/tests.yml runs `npm test` and `npm run check`, never `npm run fix`. Its one residual signal — a non-zero exit on a rule eslint cannot auto-fix — is already made, gatingly, by `eslint .` in check:lint. Filing the rewriting sibling here does not shrink the enforcement surface, because the asserting sibling is counted.

producer no verdict producer for gating purposes — eslint in --fix mode edits the files it can repair before reporting — actor can write to it: not established

  • package.json:20 ("fix:lint": "eslint --fix .")
  • package.json:14 ("check:lint": "eslint ." — the asserting counterpart, counted)
  • .github/workflows/tests.yml:26-31

decided by agent · confidence 0.75

Checkout repository
not_a_check
.github/workflows/codeql-analysis.yml:28 · ci_step

The step's whole effect is to put files or a toolchain on the runner so later steps have something to act on. It reads nothing about the repository's correctness and emits no pass/fail verdict about the artefact — a failure here means the runner could not fetch or install, not that the code is wrong. The check in such a job is whatever executes afterwards, so this edge must not enter the ratio in either direction.

producer a GitHub Actions provisioning action (repository checkout, toolchain setup, or dependency cache restore) — actor can write to it: not established

  • the step body is a bare `uses:` of a checkout/setup/cache action with no command of its own

decided by probe (ci-provisioning-action) · confidence 0.9

Initialize CodeQL
not_a_check
.github/workflows/codeql-analysis.yml:32 · ci_step

This step provisions the scanner: it fetches the CodeQL CLI and sets up the extractor for `matrix.language: javascript`, and emits no verdict about the repository. A failure here means the runner could not download or configure the tooling, not that the code is wrong. The signal in this job is produced by the `analyze` step at line 57, which is the edge classified on its merits; counting the setup step would double-count that one job.

producer the codeql-action/init step, which downloads the CodeQL bundle and creates an empty analysis database on the runner — actor can write to it: not established

  • .github/workflows/codeql-analysis.yml:32-33 (uses: github/codeql-action/init@7211b7c... with languages only)
  • .github/workflows/codeql-analysis.yml:57-58 (the analysing step)

decided by agent · confidence 0.85

Autobuild
not_a_check
.github/workflows/codeql-analysis.yml:43 · ci_step

The matrix pins `language: ['javascript']` (line 23), and JavaScript is an interpreted language CodeQL extracts directly from source — autobuild has no compiler to invoke here, so it asserts nothing about the artefact and would only fail on a tooling error. The in-file comment says as much ('Autobuild attempts to build any compiled languages (C/C++, C#, or Java)'). For a compiled-language matrix this same step WOULD be a real compile gate; that is not this instance.

producer the codeql-action/autobuild step, whose job is to produce a build for compiled languages so the extractor has something to trace — actor can write to it: not established

  • .github/workflows/codeql-analysis.yml:43-44 (uses: github/codeql-action/autobuild@7211b7c...)
  • .github/workflows/codeql-analysis.yml:21-23 (matrix: language: ['javascript'])
  • .github/workflows/codeql-analysis.yml:41-42 (comment: attempts to build any compiled languages)

decided by agent · confidence 0.7

actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
not_a_check
.github/workflows/tests.yml:19 · ci_step

The step's whole effect is to put files or a toolchain on the runner so later steps have something to act on. It reads nothing about the repository's correctness and emits no pass/fail verdict about the artefact — a failure here means the runner could not fetch or install, not that the code is wrong. The check in such a job is whatever executes afterwards, so this edge must not enter the ratio in either direction.

producer a GitHub Actions provisioning action (repository checkout, toolchain setup, or dependency cache restore) — actor can write to it: not established

  • the step body is a bare `uses:` of a checkout/setup/cache action with no command of its own

decided by probe (ci-provisioning-action) · confidence 0.9

Use Node.js ${{ matrix.node-version }}
not_a_check
.github/workflows/tests.yml:20 · ci_step

The step's whole effect is to put files or a toolchain on the runner so later steps have something to act on. It reads nothing about the repository's correctness and emits no pass/fail verdict about the artefact — a failure here means the runner could not fetch or install, not that the code is wrong. The check in such a job is whatever executes afterwards, so this edge must not enter the ratio in either direction.

producer a GitHub Actions provisioning action (repository checkout, toolchain setup, or dependency cache restore) — actor can write to it: not established

  • the step body is a bare `uses:` of a checkout/setup/cache action with no command of its own

decided by probe (ci-provisioning-action) · confidence 0.9

dependency automation (dependabot.yml)
not_a_check
.github/dependabot.yml · ci_step

Both entries are `package-ecosystem` version-update schedules (npm monthly, github-actions weekly). Dependabot's output here is a pull request, not a verdict: there is no exit code, nothing reads it, and no merge is blocked by it. The freshness of dependencies is proposed, and the proposal is then judged by the ordinary CI edges (tests.yml). A security-alert gate would be a different surface — branch protection or Dependabot alerts — and none is declared in this file.

producer nothing that emits a pass/fail — the file subscribes the repo to scheduled Dependabot version-update pull requests against the develop branch — actor can write to it: not established

  • .github/dependabot.yml:1-14 (version: 2; updates: two package-ecosystem schedule entries, no security or gating configuration)

decided by agent · confidence 0.8

Crystallization curve

Keel crystallization curve Crystallization curve over 15 runs from <repo>/reports. falls — total fitted change -66.5% of the mean across 15 runs (R^2 0.28, so the line explains less than half the variance — read the raw squares) CRYSTALLIZATION CURVE 15 sequential runs · 345 nodes judged of 1838 gathered · 124 anchored Keel corpus — 15 repositories, 2026-07-24 — measured corpus (declared on the command line) estimated tokens per node estimated tokens / judged node 0 300 600 run 0 · keel · estimated tokens per node = 498 · judged 25 of 32 gathered run 1 · anthropic-sdk-python · estimated tokens per node = 369 · judged 25 of 41 gathered run 2 · openai-python · estimated tokens per node = 398 · judged 25 of 95 gathered run 3 · vercel-ai · estimated tokens per node = 410 · judged 25 of 1014 gathered run 4 · aider · estimated tokens per node = 204 · judged 25 of 62 gathered run 5 · browser-use · estimated tokens per node = 378 · judged 25 of 104 gathered run 6 · mcp-python-sdk · estimated tokens per node = 592 · judged 25 of 121 gathered run 7 · simonw-llm · estimated tokens per node = 189 · judged 25 of 38 gathered run 8 · tiktoken · estimated tokens per node = 240 run 9 · requests · estimated tokens per node = 290 · judged 25 of 106 gathered run 10 · flask · estimated tokens per node = 408 · judged 25 of 59 gathered run 11 · sinatra · estimated tokens per node = 265 · judged 25 of 59 gathered run 12 · commander-js · estimated tokens per node = 316 run 13 · anthropic-quickstarts · estimated tokens per node = 310 · judged 25 of 62 gathered run 14 · tiktoken · estimated tokens per node = 53 0* 1* 2* 3* 4* 5* 6* 7* 8 9* 10* 11* 12 13* 14 seconds per node measured s / judged node 0.0 60.0 120.0 run 0 · keel · seconds per node = 0.1 · judged 25 of 32 gathered run 1 · anthropic-sdk-python · seconds per node = 15.8 · judged 25 of 41 gathered run 2 · openai-python · seconds per node = 15.4 · judged 25 of 95 gathered run 3 · vercel-ai · seconds per node = 105.9 · judged 25 of 1014 gathered run 4 · aider · seconds per node = 21.3 · judged 25 of 62 gathered run 5 · browser-use · seconds per node = 21.7 · judged 25 of 104 gathered run 6 · mcp-python-sdk · seconds per node = 20.1 · judged 25 of 121 gathered run 7 · simonw-llm · seconds per node = 11.8 · judged 25 of 38 gathered run 8 · tiktoken · seconds per node = 23.7 run 9 · requests · seconds per node = 16.8 · judged 25 of 106 gathered run 10 · flask · seconds per node = 20.5 · judged 25 of 59 gathered run 11 · sinatra · seconds per node = 16.9 · judged 25 of 59 gathered run 12 · commander-js · seconds per node = 18.7 run 13 · anthropic-quickstarts · seconds per node = 20.4 · judged 25 of 62 gathered run 14 · tiktoken · seconds per node = 0.1 0* 1* 2* 3* 4* 5* 6* 7* 8 9* 10* 11* 12 13* 14 probe-decided share measured share of decided nodes 0.00 0.50 1.00 run 0 · keel · probe-decided share = 0.00 · judged 25 of 32 gathered run 1 · anthropic-sdk-python · probe-decided share = 0.00 · judged 25 of 41 gathered run 2 · openai-python · probe-decided share = 0.16 · judged 25 of 95 gathered run 3 · vercel-ai · probe-decided share = 0.08 · judged 25 of 1014 gathered run 4 · aider · probe-decided share = 0.56 · judged 25 of 62 gathered run 5 · browser-use · probe-decided share = 0.32 · judged 25 of 104 gathered run 6 · mcp-python-sdk · probe-decided share = 0.00 · judged 25 of 121 gathered run 7 · simonw-llm · probe-decided share = 0.56 · judged 25 of 38 gathered run 8 · tiktoken · probe-decided share = 0.58 run 9 · requests · probe-decided share = 0.28 · judged 25 of 106 gathered run 10 · flask · probe-decided share = 0.00 · judged 25 of 59 gathered run 11 · sinatra · probe-decided share = 0.28 · judged 25 of 59 gathered run 12 · commander-js · probe-decided share = 0.19 run 13 · anthropic-quickstarts · probe-decided share = 0.24 · judged 25 of 62 gathered run 14 · tiktoken · probe-decided share = 1.00 0* 1* 2* 3* 4* 5* 6* 7* 8 9* 10* 11* 12 13* 14 probe library size measured probes in library 0 15 30 run 0 · keel · probe library size = 0 · judged 25 of 32 gathered run 1 · anthropic-sdk-python · probe library size = 3 · judged 25 of 41 gathered run 2 · openai-python · probe library size = 5 · judged 25 of 95 gathered run 3 · vercel-ai · probe library size = 8 · judged 25 of 1014 gathered run 4 · aider · probe library size = 10 · judged 25 of 62 gathered run 5 · browser-use · probe library size = 12 · judged 25 of 104 gathered run 6 · mcp-python-sdk · probe library size = 13 · judged 25 of 121 gathered run 7 · simonw-llm · probe library size = 15 · judged 25 of 38 gathered run 8 · tiktoken · probe library size = 17 run 9 · requests · probe library size = 19 · judged 25 of 106 gathered run 10 · flask · probe library size = 22 · judged 25 of 59 gathered run 11 · sinatra · probe library size = 23 · judged 25 of 59 gathered run 12 · commander-js · probe library size = 26 run 13 · anthropic-quickstarts · probe library size = 29 · judged 25 of 62 gathered run 14 · tiktoken · probe library size = 29 0* 1* 2* 3* 4* 5* 6* 7* 8 9* 10* 11* 12 13* 14 x axis: run index in the recorded corpus order (listed below). Squares are the raw per-run values; the dashed line is an ordinary-least-squares fit and is never shown without them. The fit is clipped to the panel, and withheld entirely (with the panel saying so) where a straight line would predict values the points cannot take — R^2 for every fit is in the trend notes below. * on run 0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 13 = judged fewer nodes than gathered; per-node values are per JUDGED node. Coverage (judged) ci_step 224 · script 64 · test_target 46 · review_gate 10 · deploy_gate 1 Trend estimated tokens per node: falls — total fitted change -66.5% of the mean across 15 runs (R^2 0.28, so the line explains less than half the variance — read the raw squares). raw first 498 -> last 53. seconds per node: falls — total fitted change -72.9% of the mean across 15 runs (R^2 0.04, so the line explains less than half the variance — read the raw squares). raw first 0.1 -> last 0.1. probe-decided share: rises — total fitted change 144.9% of the mean across 15 runs (R^2 0.21, so the line explains less than half the variance — read the raw squares). raw first 0.00 -> last 1.00. probe library size: rises — total fitted change 187.7% of the mean across 15 runs (R^2 0.99). raw first 0 -> last 29. Run order run order derived from Report.generatedAt (ascending, filename as tiebreak) — no order.json manifest present 0 keel -> 1 anthropic-sdk-python -> 2 openai-python -> 3 vercel-ai -> 4 aider -> 5 browser-use -> 6 mcp-python-sdk -> 7 simonw-llm -> 8 tiktoken -> 9 requests -> 10 flask -> 11 sinatra -> 12 commander-js -> 13 anthropic-quickstarts -> 14 tiktoken Shuffle check shuffle check INCOMPLETE — the curve direction ran, the ratio direction did not Permutation (400 draws, seed 20260724): median |delta normalized slope| 0.633, 92% of draws move it by >= 0.15. A permutation reorders ALREADY-RECORDED runs; it cannot reproduce what a genuine re-run in a different order would have cost, because the probe library would have accumulated differently. It is a sanity signal on order-dependence, never a substitute for the empirical re-run. Ratio stability under shuffle was NOT checked: no re-run supplied (--shuffled) and none declared in corpus.meta.json. Permuting recorded runs cannot move a per-target ratio, so the permutation result below says nothing about it. Supply a re-run with --shuffled <dir>. Disclosures - Skipped 3 file(s) that are not usable run reports: corpus-summary.json (no string "target"); curve.json (no string "target"); keel.bindings.json (no "nodes" array). - Run 0 (keel) judged 25 of 32 gathered nodes — a cap. Per-node cost below is per JUDGED node. - Run 1 (anthropic-sdk-python) judged 25 of 41 gathered nodes — a cap. Per-node cost below is per JUDGED node. - Run 2 (openai-python) judged 25 of 95 gathered nodes — a cap. Per-node cost below is per JUDGED node. - Run 3 (vercel-ai) judged 25 of 1014 gathered nodes — a cap. Per-node cost below is per JUDGED node. - Run 4 (aider) judged 25 of 62 gathered nodes — a cap. Per-node cost below is per JUDGED node. - Run 5 (browser-use) judged 25 of 104 gathered nodes — a cap. Per-node cost below is per JUDGED node. - Run 6 (mcp-python-sdk) judged 25 of 121 gathered nodes — a cap. Per-node cost below is per JUDGED node. - Run 7 (simonw-llm) judged 25 of 38 gathered nodes — a cap. Per-node cost below is per JUDGED node. - Run 9 (requests) judged 25 of 106 gathered nodes — a cap. Per-node cost below is per JUDGED node. - Run 10 (flask) judged 25 of 59 gathered nodes — a cap. Per-node cost below is per JUDGED node. - Run 11 (sinatra) judged 25 of 59 gathered nodes — a cap. Per-node cost below is per JUDGED node. - Run 13 (anthropic-quickstarts) judged 25 of 62 gathered nodes — a cap. Per-node cost below is per JUDGED node. - Run 14 (tiktoken): decidedByProbe + decidedByAgent = 10 but nodesSampled = 12. Probe-decided share uses the decided total (10). - Provenance "measured" was declared on the COMMAND LINE (--provenance), not in <repo>/reports/corpus.meta.json. The declaration therefore lives in the invocation and is only as trustworthy as the run sheet that records it. - Token counts are ESTIMATES in 15 of 15 run(s) (RunEconomics.tokensEstimated). No API exposes session token usage to a skill, so the token axis reads "estimated tokens" and nothing here claims a measured token count. Scope. Keel measures the shape of verification, not its quality. A repo can be 100% anchored with terrible tests. Anchoring says the signal comes from outside; it does not say the signal is sufficient.

Run economics

4 decided by probe
17 decided by agent
19% probe-decided share
21 / 21 nodes judged / gathered
1,506 estimated tokens in
5,131 estimated tokens out
6m 33s wall clock
3 probes minted
26 probe library size

Token counts are estimated, and labelled so: a skill running inside an agent session has no API for its own usage, so the figure is ceil(chars/4) over the judgment payloads and responses. Wall clock and probe-decided share are measured directly.