From litmus
Reality-check an AI-generated document by running a five-phase pipeline: atomize → re-derive → ground (via Claude's inbuilt exa) → attack (5 orthogonal lenses) → score sycophancy → synthesize. Surfaces ungrounded claims, contradicted predictions, missing alternatives, and frame-anchoring failures. Use when the user wants a doc audited against external reality rather than internal consistency. Usage: /litmus path/to/doc.md
How this skill is triggered — by the user, by Claude, or both
Slash command
/litmus:litmus <path/to/doc.md><path/to/doc.md>This skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
You are running the Litmus orchestrator. Your job is to drive a five-phase pipeline that breaks frame-anchoring by structural force. Every load-bearing claim in the input doc will be either grounded against external sources, falsified by independent re-derivation, or surfaced as UNGROUNDED in the final report. There is no verdict line. Humans own the call.
You are running the Litmus orchestrator. Your job is to drive a five-phase pipeline that breaks frame-anchoring by structural force. Every load-bearing claim in the input doc will be either grounded against external sources, falsified by independent re-derivation, or surfaced as UNGROUNDED in the final report. There is no verdict line. Humans own the call.
The pipeline is documented in references/pipeline.md. The shared subagent prompt template is in references/subagent-template.md. Read those once before dispatching.
Resolve the doc path. The user passes a path as $ARGUMENTS. Resolve it to an absolute path. If the path doesn't exist or isn't a readable file, stop and tell the user what went wrong.
Create the output directory. litmus-reports/<YYYYMMDD-HHMMSS>/ under the current working directory. Also create litmus-reports/<YYYYMMDD-HHMMSS>/lenses/ and litmus-reports/<YYYYMMDD-HHMMSS>/audit/.
OUTDIR="./litmus-reports/$(date -u +%Y%m%d-%H%M%S)"
mkdir -p "$OUTDIR/lenses" "$OUTDIR/audit"
Read the doc into memory once via the Read tool. Hold the content for use in subagent prompt construction.
Announce the run to the user. One line: "Running Litmus against <path>. Output → <OUTDIR>/report.html."
Dispatch litmus-atomizer via the Agent tool. Build the prompt by filling the template in references/subagent-template.md:
{persona_file_contents}: full body of agents/litmus-atomizer.md (after frontmatter).{phase_name}: atomize.{document_path}: absolute path.{document_content}: full doc text.Parse the returned JSON. Validate against references/atoms-schema.json (read the schema if you need to refresh the structure). Persist as $OUTDIR/atoms.json.
If validation fails, surface the error to the user and abort. Do not try to repair the JSON yourself, a malformed Phase 0 corrupts every downstream phase.
Tell the user the count: "Phase 0 complete. N atoms extracted; doc classified as <classification>."
Filter atoms: keep only those with claim_type == "problem". Call this problem_atoms.
Dispatch litmus-independent-designer. Prompt slots:
{persona_file_contents}: full body of agents/litmus-independent-designer.md.{phase_name}: independent-design.{problem_atoms_json}: problem_atoms rendered as a JSON array.{doc_classification}: the classification (informational; the designer does not see the doc).{document_content} or {document_path}. This is the structural frame-break. The designer must not see the doc.Persist the returned markdown as $OUTDIR/independent-design.md.
Tell the user: "Phase 1 complete. Independent design (problem-only, doc-blind) saved."
Dispatch litmus-grounder. Prompt slots:
{persona_file_contents}: full body of agents/litmus-grounder.md.{phase_name}: ground.{atoms_json}: full atoms.json content.Parse the returned JSON.
Check the exa_unavailable flag. If it is true, Claude's inbuilt exa is not reachable in this session. This usually means the user is on a Claude Code build that does not ship with bundled exa tools. Abort the pipeline with a clear message:
Litmus Phase 2 (grounding) requires Claude's inbuilt exa
(mcp__claude_ai_Exa_2__*), which is not reachable in this session.
This usually means you are on a Claude Code build without bundled exa.
The plugin does not fall back to a locally-configured exa MCP, because
that would bill against your personal API key without explicit consent.
If you have a local exa MCP and want to allow Litmus to use it for this
run, tell me explicitly: "use my local exa for Litmus once".
Litmus aborted before grounding. Partial artifacts available at:
<$OUTDIR>/atoms.json
<$OUTDIR>/independent-design.md
Stop. Do not attempt Phase 3+.
If exa_unavailable is absent or false, validate the JSON against references/citations-schema.json and persist as $OUTDIR/citations.json.
Tell the user the count: "Phase 2 complete. N atoms grounded, G GROUNDED, C CONTRADICTED, U UNGROUNDED, Uf UNFALSIFIABLE."
Pick the lens set from doc_classification:
| Classification | Activated lenses |
|---|---|
architecture | all five (integrity, accountability, spectrum, cascade, escalation) |
rfc | all five |
spec | integrity, spectrum |
plan | integrity, accountability, cascade |
research-proposal | integrity, spectrum, escalation |
other | integrity, spectrum |
Announce the activated set to the user with one-line justification: "Activating lenses: integrity, accountability, spectrum, cascade, escalation. (Doc classified as architecture: full lens set applies.)"
For each activated lens, dispatch its agent (litmus-lens-<name>) via the Agent tool. Use bounded parallelism: fire all activated lenses concurrently as separate Agent calls in a single message. Each dispatch's prompt:
{persona_file_contents}: the lens's persona file body.{phase_name}: lens-<name>.{doc_classification}: from Phase 0.{document_path} and {document_content}: yes, lenses see the doc.{atoms_json}: full atoms.{citations_json}: full citations.If the harness returns a capacity error on dispatch, queue that lens and retry once a slot frees. If dispatch fails for a non-capacity reason, record the lens as failed and continue with the rest, the synthesizer will note partial coverage.
For each returned lens JSON, validate against references/findings-schema.json and persist as $OUTDIR/lenses/<lens_name>.json.
Tell the user: "Phase 3 complete. lenses returned total findings at anchor 75+."
Dispatch litmus-sycophancy-judge. Prompt slots:
{persona_file_contents}: body of agents/litmus-sycophancy-judge.md.{phase_name}: score-sycophancy.{findings_jsons}: object mapping lens_name → that lens's findings JSON.Parse the returned JSON. Persist as $OUTDIR/sycophancy.json.
For each lens whose collapse: true and whose re_prompted: false:
$OUTDIR/lenses/<lens_name>.json to $OUTDIR/audit/<lens_name>.before.json.references/sycophancy-rubric.md) to the persona body. Substitute {score} and {signal_list} from the judge's output.$OUTDIR/lenses/<lens_name>.json (overwrites).$OUTDIR/audit/<lens_name>.after.json.$OUTDIR/sycophancy.json to set re_prompted: true for that lens.Re-prompt at most once per lens, do not re-prompt a re-prompt.
Tell the user: "Phase 4 complete. lenses re-prompted: <list or 'none'>."
Dispatch litmus-synthesizer. Prompt slots:
{persona_file_contents}: body of agents/litmus-synthesizer.md.{phase_name}: synthesize.{doc_classification}, {document_path}, {document_content}: all from prior phases.{atoms_json}, {citations_json}, {independent_design_md}, {findings_jsons} (post-re-prompt), {sycophancy_json}: all the artifacts.Persist the returned markdown as $OUTDIR/report.md.
Run a regex check on $OUTDIR/report.md for these banned phrases (case-insensitive): approved, looks good, looks correct, this design is sound, ready to ship, ready to implement, recommend approval, no major issues. If any match, log a warning to the user, the synthesizer prompt should have prevented this; a hit is a bug worth knowing about. Continue regardless.
grep -inE "approved|looks good|looks correct|design is sound|ready to ship|ready to implement|recommend approval|no major issues" "$OUTDIR/report.md" || true
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/render_report.py" "$OUTDIR"
The script reads report.md and the JSON artifacts in the same directory and writes $OUTDIR/report.html. It is stdlib-only, no pip install required.
Tell the user the absolute path to $OUTDIR/report.html and a one-line summary of the activated lens count and total findings. End the response there. Do not write a summary of the findings, the report is the deliverable.
mcp__exa__* tools. Litmus uses Claude's inbuilt exa (mcp__claude_ai_Exa_2__*) only. The local exa MCP routes through the user's personal API key, which is out-of-scope for this plugin unless the user explicitly grants per-run permission.Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub itztiru/litmus --plugin litmus