Skip to main content

Deterministic quality scorer for AI agent instruction files. Multi-format (SKILL.md, CLAUDE.md, .cursorrules, AGENTS.md), 8-dimension scoring with security, anti-gaming detection, zero dependencies.

Project description

Schliff — deterministic quality scores for AGENTS.md

The Ruff for AGENTS.md — deterministic quality scores for the instruction files that drive your AI. Same input, same score, on every machine.

PyPI Python Tests License: MIT AGENTS.md quality

That last badge is Schliff scoring this repo's own AGENTS.md — live, right now: 91.6 · A.

Your AI instruction files silently degrade — and nothing catches it. AGENTS.md is read by Cursor, Codex, Copilot, and Claude Code — one rotting file now quietly degrades four tools. A trigger phrase rots, an edge case slips, the file balloons past its token budget. No error, no red test — just agents that quietly get worse.

Schliff scores AGENTS.md — and the rest of the family (SKILL.md, CLAUDE.md, .cursorrules, system prompts) — against an explicit, versioned rubric. No LLM judge in the critical path. No network. No randomness. A rule engine you can read, pin, and gate CI on.

pip install schliff
schliff score AGENTS.md   # or any SKILL.md / CLAUDE.md / .cursorrules
schliff demo              # no instruction file handy? score a built-in bad one

This is the real, current output of schliff score AGENTS.md on this repo's own AGENTS.md — clone and run it yourself:

schliff v8.6.0

  structure             █████████░   90/100  great
  operational_coverage  ██████████  100/100  perfect
  efficiency            ████████░░   78/100  good
  composability         ████░░░░░░   45/100  poor
  clarity               ██████████  100/100  perfect

  Structural Score  ██████████████████░░  91.6/100  [A]

  Tokens: 837 / 3,000 (ok)
  Format: agents.md (normalized)

No model produced that number. Run it on another laptop and you get 91.6 again. A score you can't reproduce isn't a measurement — it's a vibe.

Every number in this README comes from released schliff==8.6.0 (pip install schliff==8.6.0 to reproduce byte-for-byte). No install? Paste your file into the playground — same engine, with AGENTS.md and SKILL.md tabs.


A real catch

The SKILL.md for ShieldClaw — a real prompt-injection-defense skill, now archived — is Schliff's reproducible before/after. The fixtures ship in docs/case-studies/shieldclaw/; every number below is the current engine's output, reproducible with schliff score. (For the 27.9 row, score a copy of SKILL-before.md outside that directory — in place, the engine auto-discovers the sibling eval suite.)

Score Grade Dimensions measured
Before, scored in isolation 27.9 F 4/7 — no eval suite; explicit ceiling warning
Before, with its eval suite 83.7 B 7/7
After fixes 93.8 A 7/7

Two separate effects, and Schliff refuses to conflate them. Adding the eval suite lifted the measurement ceiling (27.9 → 83.7) — that is coverage, not quality. The quality delta is 83.7 → 93.8, driven by composability 20 → 86 and efficiency 60 → 83 after adding scope boundaries, an I/O contract, and handoffs — structural gaps a linter can't see, caught as a number that was too low.

A second field run, against an external repo (hydra, measured on released schliff==8.4.0, 2026-07-03): 71.0 [C] → 76.5 [B] — edges 82→100, composability 56→81, fix merged upstream (Zandereins/hydra#34). Efficiency deliberately stayed at 47: a ~14k-token file against a 1,000-token budget was an informed decline, not a blind chase of the number. (External repo — not re-runnable from these fixtures.)


Why deterministic?

Most "AI quality" tools ask another LLM how good your prompt feels — a different answer every run. That makes the score non-reproducible (re-run it, get a different number), un-auditable (the rubric lives in a hidden prompt), and trivially gameable (write for the judge, not the user). You can't gate a release on a number that drifts. Schliff computes how good the file measurably is — the same answer every run.

Deterministic means reproducible and auditable — it does not automatically mean the number is right. Schliff's claim is narrower and checkable: the rubric is open source, every scorer is readable, the weights are a dict, and the case studies above show the score moving with real fixes. If you disagree with the rubric, you can read it and file an issue — you can't do that with a judge prompt.

Config linters tell you whether the file is valid — a list of pass/fail rules. Schliff tells you how good it is — one graded 0–100 score you can gate, diff across commits, and rank.

  • Reproducible. The headline composite is computed from a canonical, versioned weight registry. Calibration is off by default, so verify, badge, and the leaderboard return the same score on your laptop and in CI.
  • Auditable. Every dimension is a readable scorer in scripts/scoring/. The weights are a dict you can open. There is no hidden judge prompt.
  • Anti-gaming, precisely scoped. A dedicated guard layer (guards.py) detects and floors padding, junk fences, platitude farms, and keyword stuffing — worthless text cannot outrank operational text. A plausible lie about your repo is out of reach of any static scorer (see What the score does not measure).
  • Zero core dependencies. Core Schliff is stdlib-only and runs on Python ≥ 3.10. (Optional [evolve] / [judge] extras pull in LLM clients for an opt-in smoke-test only — never for scoring.)

Because the number is stable, it does real work: gate pull requests on it in CI, or diff and compare it across commits with the CLI.

An optional LLM judge exists for exploratory work, but it is never part of the deterministic score. The number you gate on is rule-based, end to end.


The scoring model

Full methodology: docs/SCORING.md.

For the SKILL.md family, Schliff runs 8 scorers per file. 7 of them form the headline composite; security (always computed) and runtime (opt-in) are reported as separate signals so a security warning never silently inflates or deflates your quality grade.

Dimension Weight In headline?
structure 0.15
triggers 0.20
quality 0.20
edges 0.15
efficiency 0.10
composability 0.10
clarity 0.05
security 0.05 Separate signal (gate threshold 70)
runtime Separate signal (no profile weight)

The seven headline weights are renormalized to sum to 1.0 — that is the canonical basis.

[!NOTE] security is a side signal for the SKILL.md / CLAUDE.md / .cursorrules / AGENTS.md family, but a core 0.15 headline dimension for the system_prompt format, which uses its own scorer set. Only runtime is excluded everywhere.

The composite: a full-denominator model

Schliff does not quietly renormalize across whatever you happened to measure. Unmeasured dimensions contribute 0 and stay in the denominator — so coverage gaps lower your ceiling instead of quietly disappearing. Your score ceiling equals your measurement coverage. Measure 4 of the 7 headline dimensions and your maximum possible score is capped accordingly, with an explicit warning:

ℹ Scored 4/7 dimensions — the score can't exceed 42% until the rest
  are measured. Run /schliff:init to add an eval suite and score:
  triggers, quality, edges.

[!IMPORTANT] This is deliberate. A partial measurement is an honest partial score, never a flattering one. Unmeasured work is missing points, not invisible. To lift the ceiling, measure more — don't hide the gap.

[!NOTE] Structural score = the composite renormalized over the dimensions Schliff can measure deterministically without an eval suite (structure, efficiency, composability, clarity). The full 7-dimension composite additionally folds in triggers, quality, and edges — which require an eval suite, generated with the /schliff:init Claude Code slash command. AGENTS.md needs no eval suite: its full 3-dimension headline (structure, operational_coverage, efficiency) is always measurable.

[!NOTE] Calibration is strictly opt-in: ambient auto-calibrated weights apply only when SCHLIFF_CALIBRATED_WEIGHTS is set and only for the interactive score command, and Schliff emits a weight_source=calibrated warning flagging that such scores are not comparable to the canonical scale. Everything that gates a release stays canonical.

Grade scale

S ≥ 95 · A ≥ 85 · B ≥ 75 · C ≥ 65 · D ≥ 50 · E ≥ 35 · F < 35


What the score does not measure

  • Structure, not truth. Schliff cannot verify that a documented command exists or runs. A syntactically plausible fabrication — invented-but-real-looking commands in well-formed sections — scores in the S range. This is a documented, test-pinned limit (test_known_limit_plausible_fabrication_scores_high, spec §11).
  • Not agent behavior. A high score doesn't prove your agent gets better — validity evidence today is case-study-level (the two dated before/afters above), not benchmark-level.
  • Token counts are estimates. stdlib len//4, not a tokenizer.
  • Coverage is on you. triggers/quality/edges need an eval suite (/schliff:init) or the ceiling warning caps the score. AGENTS.md has no such gap.
  • Informed declines are valid. A low dimension can be a deliberate tradeoff (hydra left efficiency at 47 rather than gut a 14k-token file).

A high Schliff score is necessary, not sufficient.


Multi-format support

One engine, five instruction-file formats — each with its own token budget and scorer set:

Format Token budget Scorers
SKILL.md 1,000 shared 8-scorer registry
CLAUDE.md 2,000 shared 8-scorer registry
.cursorrules 500 shared 8-scorer registry
AGENTS.md 3,000 shared 8 scorers + operational_coverage (own 3-dim headline)
system prompts 1,500 dedicated set (structure_prompt, output_contract, efficiency, clarity, security, composability, completeness)

Format is auto-detected; override with --format (skill, claude, cursor, agents, system-prompt).


Use it in CI

Published on the GitHub Marketplace as AGENTS.md Lint (Schliff).

GitHub Action

Gate pull requests on instruction-file quality. The action defaults to your repo-root AGENTS.md and posts a scored comment on every PR:

# .github/workflows/agents-lint.yml
name: AGENTS.md Lint
on: [pull_request]
jobs:
  score:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: Zandereins/schliff@v1
        with:
          minimum-score: '75'   # optional: fail the PR below this score

By default it scores AGENTS.md at the repo root; set skill-path: to lint a SKILL.md, CLAUDE.md, or .cursorrules instead. One caveat: the Action installs the latest released engine from PyPI, so after a release its scores can lead an older pinned install; pin it with schliff-version: '8.6.0' in the with: block if you need byte-stable gates.

CI gate without the Action

Prefer not to depend on a third-party action? The dependency-light equivalent:

      - run: pip install schliff
      - run: schliff verify AGENTS.md --min-score 75

schliff verify exits non-zero when the score falls short and works for every supported format — the minimum is scaled by measurement coverage, so SKILL.md files without an eval suite aren't auto-failed. Requires schliff ≥ 8.6.0 for AGENTS.md: older engines scored it under the SKILL profile (#101).

README badge

Show your repo's AGENTS.md quality — no setup, no CI, no account:

![AGENTS.md quality](https://img.shields.io/endpoint?url=https%3A%2F%2Fschliff-playground.vercel.app%2Fapi%2Fbadge%3Frepo%3DOWNER%2FREPO)

Replace OWNER/REPO with your repository. The badge is scored live from your AGENTS.md at HEAD; GitHub's image cache (camo) may delay refreshes. Public repos only.

pre-commit

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/Zandereins/schliff
    rev: v8.6.0
    hooks:
      - id: schliff-verify
        args: ['--min-score', '75']

The hook fires on SKILL.md files (its files filter); gate AGENTS.md with the Action or schliff verify AGENTS.md in CI.


CLI

schliff <command> [path] [options]
Command What it does
score Score a file and print the grade bar
verify CI gate — exit 0/1 based on a minimum score
check-commands CI gate — flag setup/build/test commands that don't exist in the repo (exit 1 if dangling)
doctor Scan and grade every installed skill
badge Generate a Markdown score badge
diff Explain score changes between two git commits
compare Compare two files side by side
suggest Rank fixes by estimated score impact
report Generate a Markdown score report
demo Score a built-in bad skill to see Schliff in action
evolve Improve an instruction file's score
version Print the version

Optional: closing the loop

Beyond grading, Schliff can apply fixes. The improvement engine measures first, then fixes (not the other way around):

  1. Score the file across all dimensions.
  2. Generate deterministic patch gradients for the weakest dimensions.
  3. Apply the safe, rule-based patches automatically — ~32% of suggested fixes apply deterministically through the apply gate (confidence=high, single-edit), as measured by the canonical script measure_patch_ratio.py — re-run it to check. The rest are handed to an optional LLM.
  4. Re-score and keep the change only if the score improved — otherwise revert.
  5. Stop on plateau detection or when the target is reached.

It also carries cross-session episodic memory (episodic_store.py), so improvement runs learn from prior attempts instead of repeating them. Drive it from Claude Code with /schliff:auto, or use schliff evolve directly. This is an optional convenience layer — the deterministic score is the product.

pip install "schliff[evolve,judge]"  # optional LLM extras for this layer only

LLM extras power this optional layer only; they are never used for scoring.

Install Pulls in When you need it
schliff stdlib only Scoring, verify, badge, CI — everything that gates a release
schliff[judge] LLM client Opt-in exploratory LLM-judge smoke-test (never scoring)
schliff[evolve] LLM client Opt-in autonomous-improvement extras

Under the hood

The full methodology — scorer internals, the full-denominator composite, the anti-gaming guards, and the calibration model — lives in docs/SCORING.md.

scripts/
├── cli.py                  # CLI entrypoint
├── scoring/
│   ├── registry.py         # canonical weights, scorer lists, headline exclusions
│   ├── composite.py        # full-denominator composite model
│   ├── formats.py          # format detection + token budgets
│   ├── guards.py           # anti-gaming detection
│   └── structure.py · triggers.py · quality.py · edges.py · …
├── text_gradient.py        # deterministic patch gradients (apply gate)
├── episodic_store.py       # cross-session episodic memory
└── measure_patch_ratio.py  # canonical source for the patch-ratio claim

Links & docs

License

MIT © Franz Paul

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

schliff-8.6.0.tar.gz (247.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

schliff-8.6.0-py3-none-any.whl (285.1 kB view details)

Uploaded Python 3

File details

Details for the file schliff-8.6.0.tar.gz.

File metadata

  • Download URL: schliff-8.6.0.tar.gz
  • Upload date:
  • Size: 247.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for schliff-8.6.0.tar.gz
Algorithm Hash digest
SHA256 9442288ca0f1f0b69ea3f147ae4d256e4c9e6e7971024ed3e55bfa5f726483a1
MD5 68a1e3635677a1fcf5089e1363828522
BLAKE2b-256 5d0ee6d595a9b90f65c36d5c923d7e394dc78bbb3e9c397f69a80b916d9c79a1

See more details on using hashes here.

Provenance

The following attestation bundles were made for schliff-8.6.0.tar.gz:

Publisher: publish.yml on Zandereins/schliff

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file schliff-8.6.0-py3-none-any.whl.

File metadata

  • Download URL: schliff-8.6.0-py3-none-any.whl
  • Upload date:
  • Size: 285.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for schliff-8.6.0-py3-none-any.whl
Algorithm Hash digest
SHA256 8b33efa9715875940a8efc48f7b771665b9708d9a19465026ce9f5a4e054574c
MD5 8cb19e7cb57d2b1a6459bd92ad466069
BLAKE2b-256 5d0bfa6027c6b4df38e9ff49cfdf161a2a0d7032ca7ae860de6d338e01548a73

See more details on using hashes here.

Provenance

The following attestation bundles were made for schliff-8.6.0-py3-none-any.whl:

Publisher: publish.yml on Zandereins/schliff

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page