Skip to content

fix(grade-codebase): grade liveness, not presence — close the false-A gap#43

Open
markhazlett wants to merge 3 commits into
mainfrom
claude/add-codebase-grading-system-JVIaS
Open

fix(grade-codebase): grade liveness, not presence — close the false-A gap#43
markhazlett wants to merge 3 commits into
mainfrom
claude/add-codebase-grading-system-JVIaS

Conversation

@markhazlett

@markhazlett markhazlett commented May 31, 2026

Copy link
Copy Markdown
Owner

Why

Running /grade-codebase on a real repo returned an A despite the repo having no skills and its agent context living in a global .ai/ folder that hadn't been touched in months (referenced from CLAUDE.md, with no feedback loop for learnings). That's a false positive the skill should never produce.

Root cause

Every D1 signal was a presence/format check (test -f, wc -l, grep for section headers, grep for placeholders). A stale-but-well-formatted context doc passes all of them. The rubric named the quality questions ("is AGENTS.md useful vs. a stub", "are flags used vs. installed", "is the canonical example actually canonical") in its §5 judgment column — but the skill never had to test them, and they were capped at 50%. So presence floated all the way to an A.

The same disease ran through the whole rubric: it graded configured, never enforced/current/honest.

Fix — separate "configured" from "enforced/current/honest", and force the skill to probe the latter

Rubric:

  • D1 — signals split into presence (necessary, fakeable) vs. liveness probes (required to score above C): verify 3+ doc claims against the code, freshness vs. code churn, feedback-loop mechanism. Grader must follow references into .ai/, docs/agent/, etc. and grade that content.
  • §4present-but-misleading scores below absent (a stale doc / empty-green suite / disabled gate is D–F, not C). A D1 of D/F caps the overall grade at C (compounding-context rule — bad onboarding poisons every other dimension, so averaging is wrong here).
  • §5 — judgment column reframed as required liveness probes spanning D1/D2/D4/D6/D7/D8/D10 (does the suite assert anything; is the gate a required check vs. continue-on-error; is the lockfile in sync; are flags/loggers used vs. installed; is the canonical example actually canonical).
  • §6 — new red flags feat(learn): anti-list defense + verify-before-recommend (G3) #15–18: stale/misleading context, gate configured-but-not-enforced, trivial test suite (all skip/todo/no-assert), lockfile drift.

Skill (SKILL.md + new liveness-probes.md):

  • New mandatory step 4 — Run liveness probes (the false-A guard). A probed dimension cannot exceed C until its probe runs; a probe you can't run is not verified, never a pass.
  • Honesty rule: never award A on a probed dimension without running its probe.
  • Body trimmed back under the 700-word ceiling (699), detail pushed into the sub-file per CONVENTIONS.

Template: per-dimension liveness-probe results block so the verification evidence is surfaced, separate from configuration presence.

Calibration fixtures (the §8 follow-up, now built)

Three deliberately-rigged negative-case mini-repos under .claude/skills/grade-codebase/fixtures/, each targeting one probe / red flag, each with an EXPECTED.md stating the grade it must receive:

Fixture Failure mode Must grade
stale-context/ CLAUDE.md (+ referenced .ai/) cites paths/commands the code contradicts D1 = D/F, overall ≤ C (§6 #15)
empty-suite/ vitest suite green but all skip/todo + expect(true), never calls the code under test D2 ≤ C (§6 #17)
disabled-gate/ CI lint/typecheck jobs neutered by continue-on-error / || true D4 ≤ C (§6 #16)

Two validation layers:

  • bin/test-grade-fixtures (new) mechanically guards that each fixture remains a valid negative — fails loud if an edit neuters one (verified: simulating an enforced gate makes it fail). This is the regression guard for the calibration set itself; it does not run the model.
  • A model-graded /grade-codebase full run against each fixture is the dogfood that confirms the skill marks it down.

VERSION 0.14.1 → 0.14.3.

Remaining open work (tracked in rubric §8)

  • Wire the model-graded expectation into bin/skill-eval as a trajectory eval, so a future rubric/skill edit that re-opens the false-A fails automatically (currently the model-graded layer is a manual dogfood).
  • The freshness probe isn't mechanically exercised by the fixtures (they're plain dirs, not git repos with backdated history).

Process note

This change was run back through /write-skill (the audit that surfaced the bloated body + missing discovery layer in an earlier commit). The skill is tier: flexible, so the rigid-skill requirements (subagent baseline, rationalization table, eval.yaml) don't apply.

Test plan

  • bin/test-frontmatter — 32/32 pass
  • bin/test-grade-fixtures — 3/3 fixtures valid negatives; fail-path verified
  • wc -w SKILL.md = 699 (< 700 hard ceiling)
  • All REQUIRED SUB-FILE references resolve to files on disk
  • Build the golden-fixture calibration repos (§8 follow-up)
  • Dogfood: run /grade-codebase full against each fixture; confirm the grade matches its EXPECTED.md
  • Wire fixtures into bin/skill-eval as an automated trajectory eval

https://claude.ai/code/session_011uDsxgUQ6b68bvRsbtSsvX

claude added 2 commits May 31, 2026 23:33
… gap

A repo with a stale .ai/ context folder (untouched for months, referenced
from CLAUDE.md) scored an A because every D1 signal was a presence/format
check. The rubric named the quality questions in its judgment column but
never forced the skill to test them, so presence floated to an A.

Systemic fix — separate 'configured' from 'enforced/current/honest':
- Rubric D1: split signals into presence vs. liveness probes (verify 3+
  doc claims against the code, freshness vs. code churn, feedback-loop
  mechanism); instruct the grader to follow references into .ai/ etc.
- Rubric section 4: present-but-misleading scores BELOW absent; a D1 of
  D/F caps the overall grade at C (compounding-context rule); presence
  alone caps at C without verified liveness.
- Rubric section 5: reframe the judgment column as required liveness
  probes spanning D1/D2/D4/D6/D7/D8/D10 (assert-anything, gate-enforced,
  lockfile-in-sync, used-not-installed, canonical-actually-canonical).
- Rubric section 6: add red flags 15-18 (stale/misleading context, gate
  configured-but-not-enforced, trivial test suite, lockfile drift).
- Skill: new mandatory step 4 'Run liveness probes' + liveness-probes.md
  sub-file; honesty rule against awarding A without running the probe.
- Template: liveness-probe results block per dimension.
- Section 8: document the remaining calibration gap (no golden fixture).

Bump VERSION 0.14.1 -> 0.14.2.
…lse-A guard

Closes the calibration gap documented in rubric §8: until now the liveness
probes were validated only by manual spot-check, so a regression that
re-opened the false-A would go unnoticed.

Adds three deliberately-rigged negative-case fixtures under
.claude/skills/grade-codebase/fixtures/, each targeting one liveness probe /
red flag:
- stale-context/  — CLAUDE.md (+ referenced .ai/) cites paths and commands
  the code contradicts (no services/, no Makefile, no src/db/client.ts).
  Must grade D1 = D/F, overall capped at C (§6 #15).
- empty-suite/    — vitest suite is green but all skip/todo + expect(true);
  never calls the functions under test. Must grade D2 <= C (§6 #17).
- disabled-gate/  — CI defines lint/typecheck jobs but continue-on-error
  and || true swallow the exit codes. Must grade D4 <= C (§6 #16).

Each fixture ships an EXPECTED.md stating the model-graded assertion.

Two validation layers:
- bin/test-grade-fixtures (new): mechanically guards that each fixture
  *remains a valid negative* — fails loud if an edit neuters one. This is
  the regression guard for the calibration set itself; it does not run the
  model.
- A model-graded /grade-codebase full run against each fixture is the
  dogfood that confirms the skill marks it down (documented in the fixtures
  README and each EXPECTED.md).

Remaining open work (noted in §8): wire the model-graded expectation into
bin/skill-eval as a trajectory eval, and exercise the freshness probe
(needs git history the plain-dir fixtures don't have).

Bump VERSION 0.14.2 -> 0.14.3.
@markhazlett markhazlett marked this pull request as ready for review June 2, 2026 15:59
Conflicts:
- VERSION: take main's 0.22.1 line, bump to 0.22.2 (this branch's content
  delta is small relative to main's 70+ commits since the divergence).
- Path conflicts: main moved skills/ to top-level (.claude/skills is now a
  symlink). All new files added on this branch (fixtures/, liveness-probes.md,
  bin/test-grade-fixtures) relocate to the canonical skills/ path; git
  tracks them as renames so history is preserved.

Adjustments made during the merge:
- bin/test-grade-fixtures: paths updated to skills/grade-codebase/fixtures/
  (the canonical location, not the symlink); the disabled-gate enforcement
  check is tightened to match `continue-on-error: true` only when it's a
  real YAML key and `|| true` only on a `run:` line — previously the same
  patterns inside a comment also counted, which is dishonest.
- bin/tests/test-grade-fixtures.test.sh: new wrapper so the regression test
  is picked up by bin/test-all (which main added).

Validators after merge:
- bin/test-frontmatter: 33/33 pass
- bin/test-grade-fixtures: 3/3 fixtures valid negatives
- bin/test-terminal-states: 4/4 pass
- bin/test-all: 13 passed / 8 failed — same as origin/main; the 8 failures
  are pre-existing on main and unrelated to this branch.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants