Audit the markdown file at $0.
Invocation Contexts
This skill is invoked in two distinct contexts:
- Standalone
/audit(default): user-triggered via the/audit <file.md>slash command on any existing markdown file. Uses inline mode — per-claim badges injected into the body plus a per-claim audit report at the end. Does not alteroutput/checkpoint.json. - Workflow Step 10 (automatic): invoked by the main orchestrator after Step 9 for Mode B/C/D or memo/opinion deliverables (see
CLAUDE.md§5 Step 10). Uses append mode — body preserved with only[Unverified]/[Partially Unverified]tags on failing claims, plus a## 부록: 검증 로그 (Citation Audit Log)appendix table at the end. The rendered output is audited draft content that the orchestrator passes to the final save path.
The only procedural difference between the two contexts is the --mode flag passed to render in step 15 below. Steps 1–14 are identical.
Procedure
Confirm
$0exists and is a markdown file. If it does not, stop and ask for a valid path.Run:
python3 -m citation_auditor chunk "$0" --max-tokens 3000Parse stdout as JSON with the schema
{ "chunks": [...] }.Run deterministic extraction first:
python3 -m citation_auditor extract "$0" --max-tokens 3000For each chunk, use the deterministic extraction output as the minimum claim set. If a chunk has sparse deterministic output, optionally add non-citation factual claims using structured output with this schema:
text: stringsentence_span: { start: integer, end: integer }claim_type: factual | citation | quantitative | temporal | othersuggested_verifier: string | null
Do not extract speculation, forecasts, rumors, advocacy, or soft prediction language such as:
전망이다예상된다업계 관계자에 따르면가능성이 있다Unless the sentence also contains a concrete verifiable factual assertion that stands on its own.
Keep claim offsets chunk-relative. Do not convert them to document offsets yourself.
Deduplicate deterministic and model-supplemented claims before routing.
Route each claim to verifier skills:
- If
suggested_verifieris set and exactly matches a loaded verifier skill name, use it. - Otherwise test the claim text against each specialized verifier skill frontmatter
patternsusing case-insensitive regex matching and use every match. - Set
verifier_name == "general-web"only when specialized matches count is zero and no validsuggested_verifierexists. - Do not run
general-webas a parallel verifier for a claim that already matchedkorean-law,eu-law,us-law,uk-law,scholarly, orwikipedia.
- If
For each
(claim, verifier)pair, use the Task tool to dispatch a subagent that loads that verifier skill and receives the claim JSON.Require each verifier subagent to return only this JSON:
{ "label": "...", "rationale": "...", "supporting_urls": ["..."], "authority": 0.0 }supporting_urlsmay contain either clickable source URLs or plain-language source references when no stable URL exists. Preserve them verbatim and do not invent clickable URLs for non-linkable sources such as precedent search-result IDs.Build aggregate input JSON locally. The exact top-level shape is
{ "verdicts": [ <bundle>, ... ] }where each<bundle>covers one(chunk, claim)pair and holds all verifier candidates for that claim. Do not invent other top-level keys.Exact schema (copy this structure):
{ "verdicts": [ { "chunk": { "index": 0, "text": "<full chunk text>", "segments": [ { "chunk_start": 0, "chunk_end": 44, "document_start": 0, "document_end": 44 } ] }, "claim": { "text": "<claim sentence>", "sentence_span": { "start": 64, "end": 268 }, "claim_type": "citation", "suggested_verifier": "us-law" }, "candidates": [ { "claim": { "text": "<same claim as above>", "sentence_span": { "start": 64, "end": 268 }, "claim_type": "citation", "suggested_verifier": "us-law" }, "verifier_name": "us-law", "authority": 0.9, "label": "contradicted", "rationale": "<Korean rationale from verifier>", "evidence": [ { "url": "https://..." } ] } ] } ] }Rules:
- One bundle per
(chunk, claim)pair. If the same claim has two verifiers, put both candidates inside the same bundle'scandidatesarray — do NOT create a second bundle for the same claim. - The top-level
claiminside each bundle is the canonical claim; each candidate'sclaimmust match it verbatim. evidenceitems require a non-emptyurlstring. If the verifier returnedsupporting_urls: [], emit"evidence": []— do not emit[{"url": ""}].labelmust be one ofverified/contradicted/unknown.authoritymust match the verifier skill's declared authority value.
- One bundle per
Write that JSON to a temp file and run:
python3 -m citation_auditor aggregate <tmpfile>Write the aggregate output to a temp file and run:
- Standalone
/audit:python3 -m citation_auditor render "$0" <aggfile>(inline mode is default). - Workflow Step 10:
python3 -m citation_auditor render "$0" <aggfile> --mode=append.
- Standalone
Return only the final annotated markdown unless the user explicitly asked for intermediate JSON. In the Step 10 context, the orchestrator treats this as audited draft content and then performs the final save according to the requested output format.
If model-supplemented claim extraction validation fails, retry once with a repair prompt. If it still fails, use deterministic extraction only and note it briefly.
If a verifier subagent returns invalid JSON, drop that candidate instead of inventing a verdict.
If a line was skipped because it is a forecast, opinion, rumor, or unattributed speculation, treat that as expected behavior rather than an extraction failure.
If the user asks why a forecast or opinion line was not audited, explain that this plugin audits verifiable factual claims and citations, not predictions or commentary.