Skip to main content

Temper-Skills — adversarial reviewers write a test suite for your agent skill's decision logic, then freeze it into deterministic Python

CI Coverage PyPI Python License

Your skill is silently making decisions. Temper-Skills finds them, gets adversarial reviewers to write a test suite for them, and freezes the logic into deterministic Python that must keep passing.

Before: a classic skill, its decisions buried in prose and re-derived on every call. The temper adversarial loop turns it into a light skill: a thin SKILL.md that delegates, a typed input contract you ratify, a deterministic assess_ankle.py with zero LLM calls, and a ratified test suite pinned in CI.

Does the review actually catch anything? Fed a first-aid skill giving outdated RICE advice, an audit-grade run corrected its own source — and the ratified suite pins that correction so no later run can regress it — see Step 3.

Quickstart

uvx temper-skills audit path/to/skill.md    # one skill: findings + a recommended fix
uvx temper-skills audit .claude/skills/     # your whole library, ranked (--report audit.md to share)

A live run on the flagship example — audit, then the adversarial temper loop converging round by round; real run, waits compressed

No config. Any one backend works — an ANTHROPIC_API_KEY or a logged-in claude/opencode CLI — and it tells you exactly what to do if none is found. Inside Claude Code there's nothing to install: /temper path/to/skill.md runs on your subscription. Bare temper-skills <path> does the right thing: a directory gets the library sweep, a file gets the guided tour.

skill.md ──audit──▶ findings + recommended fix
                      ├─ temper           → run the loop: test suite + deterministic tree
                      ├─ decompose         → it's a flow: split into N decisions, temper each
                      ├─ externalize_data  → flat lookup: emit a data file + matcher, not a tree
                      ├─ build_normalizer  → real logic on free-text input: pin the features first
                      └─ delegate_prose    → no decision here: improve it as prose elsewhere

Three steps — audit → (decompose) → temper — and you can stop after any of them.

Step 1 — audit: what is this skill deciding?

A health report for a skill's decision logic — worth reading even if you never temper. It names the decision, reports findings in plain terms (implicit decisions bundled together, free-text inputs whose answers drift call-to-call, lookup tails wearing a tree's clothes), and recommends a fix per finding — tempering is one of five possible fixes, not the premise. Temper owns the decision-freezing lane and delegates the rest (prose quality, generic evals) to tools that already do it well.

temper-skills audit skill.md --report audit.md   # findings + a shareable Markdown report

→ the four scored axes, the action table, and exit codes: docs/reference.md

Step 2 — decompose: a flow into its decisions

The loop freezes one decision at a time, so a multi-decision skill is split first into per-decision mini-schemas plus the generative steps left to the model. --temper-each runs the whole chain: it emits the schemas, stops for you to ratify them, then on re-run tempers each into a tree and writes a thin orchestrator skill that chains them.

temper-skills decompose skill.md --temper-each --out-dir out/   # emit + stop; re-run to compile

See examples/dog_day/ — a dog-care assistant split into three chained decisions + a note.

Step 3 — temper: freeze one decision into tests + a tree

This is the engine. An adversarial loop reviews the decision from several angles — a proposer drafts the tree, personas attack it, an independent arbiter rules on each critique — and converges when no round improves on the best.

The flagship example, examples/ankle_sprain/: the source skill advises the outdated RICE protocol. In the audit-grade run that produced these artifacts, the panel — drawing on medical literature the prompt never cited — corrected its own source and layered in the Ottawa fracture rules the prompt never mentioned (educational example, not clinical advice):

✓ 16-case test suite → test_assess_ankle_ratified.py  ·  16/16 ratified cases pass
✓ deterministic tree → assess_ankle.py  (zero LLM calls at inference)
✓ tempered skill → ankle-sprain/SKILL.md  (now advises PEACE & LOVE, not RICE)
# assess_ankle.py — generated by temper-skills (audit-grade) — excerpt
def assess_ankle(case: dict) -> str:
    ...
    # critic: the Ottawa Ankle Rule — 5 interacting criteria that rule fracture in/out;
    # this is the combinatorics a flat 'is it broken?' check misses
    if pain_malleolar_zone is True and (bone_tenderness_lateral_malleolus is True
            or bone_tenderness_medial_malleolus is True or can_bear_weight is False):
        return 'seek_imaging'
    ...
    # critic: OH-MERDE: the source skill said RICE + complete rest + heavy icing — that's
    # outdated since ~2012. Acute phase is PEACE: Protect, Elevate, Avoid prolonged ice
    # (it slows healing), Compress, Educate — early protected loading, NOT complete rest
    if hours_since_injury is not None and hours_since_injury <= 72:
        return 'police_acute'

The correction isn't prose in a chat window — it's a reviewed, versioned diff: the tree disagrees with its own source, says why in a provenance comment, and 16 ratified cases pin the corrected behavior in CI forever. That permanence is the point: an LLM re-derives this logic on every call, and whether it recalls that RICE is outdated is a dice roll — re-run the loop and it may converge back to RICE (the Ottawa gates reappear more reliably). A prompt catches the correction sometimes; the loop only has to catch it once — ratify it, and regressing to RICE stops being a bad sample and becomes a failing test.

Profile Max rounds Panel Per-round gate
quick ~8 1 attacker + critic no — draft output
standard ~20 2 attackers + 3 structural critics yes
audit-grade ~50 4 attackers + 3 structural critics yes

→ who the personas are and why the panel scales: docs/reference.md

Two ways to run it

1. On your Claude Code subscription — no install, no API key. The subagent-mode skill drives the loop with persona subagents:

/temper path/to/skill.md

2. As a CLI / library — any LiteLLM backend (Anthropic, OpenAI, Gemini, Vertex, local), for CI and headless use:

temper-skills ingest skill.md --backend auto     # api | claude | opencode | auto
tree = temper_skills.distill(
    sources=temper_skills.Sources(
        schema=AnkleInjury,
        constraints=[{"rule": "visible_deformity -> always urgent_care", "hard": True}],
    ),
    profile="audit-grade",
)
tree.export("assess_ankle.py")

→ backend selection, billing caveats, Vertex AI setup: docs/reference.md

The guarantees, and where they stop

  • The schema is a ratified contract. --propose-schema drafts it from the skill and stops for your review — the loop never runs on an unratified schema. → details
  • The test suite grows as the loop runs. Personas contribute labeled cases every round (deduped, written to disk with provenance); proposed labels never gate CI until a human ratifies them. temper-skills validate tree.py cases.json pins the tree in CI — a prompt can't be. → the full lifecycle
  • The original skill adopts the tree. A tempered skill.md is emitted that extracts features, calls the frozen function, and relays the verdict — instead of re-deriving the logic every call. → details
  • Trees evolve without recompiling. temper-skills incremental tree.json -c "<new rule>" re-crystallizes around the delta and shows a reviewable structural diff; untouched nodes keep their provenance.
  • Not a security scanner, not an extractor. "Adversarial" means decision robustness, not prompt injection; and turning raw text into the schema's features is a small normalizer you own, upstream of the guarantee. → scope

Where this fits

Not another skill eval harness — the step after one. Eval harnesses like skillgrade and agent-skills-eval measure the model wielding the skill: run the task N times with graders, or A/B the same prompts with and without the skill loaded. That's the right tool for prose, generation, and tool-flow quality — and it's measurement, not change: after the eval, every production call still re-derives the skill's decisions from prose, so the number you measured can drift with the next model bump or prompt tweak. Temper-Skills takes the decision subset of the skill out of that loop entirely — into Python that can't drift and ratified cases that gate it in CI. Use both: a harness for "does the model use this skill well?", temper for the classify/route/escalate calls that shouldn't be re-decided on every call.

Real sweep: the audit of Anthropic's 17 official skills — the audit says no most of the time (that's the point); 11 of 17 bundle 2–5 separable decisions in one prompt.

Examples

Example Why it's here Audit
ankle_sprain/ The flagship — start here. Outdated RICE advice corrected to PEACE & LOVE + the Ottawa rules, locked in by the ratified suite (educational, not clinical advice) TEMPER
ticket_routing/ The one to watch converge: the difficulty is the interactions (priority × tier × SLA × security) TEMPER
parking/ The everyday fit: zone × day × hour × holiday × permit, with the edges a flat reading misses TEMPER
license_compat/ The "moat" demo: OSS license compatibility, genuinely hard combinatorics TEMPER (audit-grade)
dog_food/ The cautionary contrast: an unbounded toxin list wants a data file, not a tree CAVEATS → externalize_data
dog_day/ The flow: three decisions + a note → three trees + a thin orchestrator DECOMPOSE FIRST

Honest scope

  • Built and tested: audit (single skill or library sweep), decompose, the adversarial temper loop, validate, incremental mode, the tempered-skill emitter.
  • The panel's insights are sampled, not guaranteed. Structural attacks (edge cases, interaction bugs) recur reliably across runs; literature-level corrections like the RICE fix are opportunistic — a re-run may not rediscover one. The ratified suite is what turns a good run into a permanent one.
  • audit-grade proposes a lot to ratify. One run on the flagship skill proposed 229 cases with 65 open disagreements — budget a real review pass, or start with standard.
  • Deferred features and roadmap: docs/reference.md

Development

pip install -e ".[dev]"
pytest -q                             # full suite, no network
git config core.hooksPath .githooks   # once per clone: block red commits locally

CI runs lint/format/types + the suite on Python 3.10–3.13, then temper-skills validate on the canonical examples — the tool gating itself with its own command. See CONTRIBUTING.md.

Origin

Mechanism validated in production on medical tooling — deterministic rule engines built by adversarial loop. Temper-Skills is the open-source generalization. Apache-2.0.

Metadata

Release files for temper-skills 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for temper-skills 0.1.1
File Size Uploaded
temper_skills-0.1.1.tar.gz 1.2 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for temper-skills 0.1.1
File Interpreter ABI Platform
temper_skills-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 1.3 MB

Release files / temper_skills-0.1.1.tar.gz

Download URL temper_skills-0.1.1.tar.gz
Size 1.2 MB
Tags Source
SHA-256 checksum
How to use checksums
66b25822e3631ef8f84cacfcc7711364720c8f52df1c9f13fd04279afc851fac
BLAKE2b-256 checksum
How to use checksums
dc15ab491a907bafcc543fbff79251c8f8b4addf10fbee3469334ae4a0299cb6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 9, 2026.

Transparency log

Release files / temper_skills-0.1.1-py3-none-any.whl

Download URL temper_skills-0.1.1-py3-none-any.whl
Size 91.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
adc045c60d158037d487482320eb174ec5eb955b099ea0532ba7c8a4cee75cb9
BLAKE2b-256 checksum
How to use checksums
db8bb55f30b06cee1959f7ae22fcd15e6a32867e028bc81e07896f4e45278243
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 9, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files

0.1.0

2 release files

0.0.3

2 release files

0.0.2

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page