Skip to content
Back to skills

Audit

ASecurity

Check thesis chapters for consistency before submission — contradictory numbers, terminology drift, and broken cross-references — and read every rewritten or proposed sentence against the one it replaces before anyone sees the rewrite.

  • 42 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 3, 2026
ai-agentspythonrustgoshellbashnodeawsgit

Security analysis

A100/100

Scanned September 29, 2026

npx -y skills add yha9806/academic-writing-toolkit --skill audit --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Audit?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Audit
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/yha9806-audit/badge)](https://www.skillsdirectory.com/skills/yha9806-audit)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: audit
description: Check thesis chapters for consistency before submission — contradictory numbers, terminology drift, and broken cross-references — and read every rewritten or proposed sentence against the one it replaces before anyone sees the rewrite.
allowed-tools: Read, Glob, Grep, Bash
---

# /audit — Thesis Consistency Audit Skill

## Running Python helpers

Choose the interpreter before running the examples. For a globally installed
copy, use the private runtime recorded by its installer. In a checkout or linked
workspace, use `AWT_PYTHON` when set, otherwise the toolkit's `.venv` (follow the
skill directory link back to the toolkit): `Scripts/python.exe` on Windows,
`bin/python` on macOS/Linux. Without that environment, check that `python`
(Windows) or `python3` (macOS/Linux) actually runs and has the helper's dependencies.
Replace the example's `python3` with that executable. In PowerShell, prefix a
quoted executable with `&`; keep commands on one line and quote file paths.

## A manuscript registered with the writing loop

Run `loop coverage <workspace> --run` first (`experimental/writing-loop/bin/loop`). It
derives every check's inputs from the workspace (the tracked draft files at HEAD,
the bibliography, the ledgers, the target venue's corpus) instead of the thesis
defaults below, runs the ones the latest edits made due, and lists the checks
that cannot run on this manuscript and why. Its table covers the script checks
only: categories A, B and C below are read by the model, leave no run record, and
are listed under the table as such. Report any check it shows as not current,
missing a prerequisite, not applicable or failed, and never report a check it did
not run as clean.

## Purpose

Scan all thesis chapters for internal data consistency issues: contradictory numbers, inconsistent terminology, broken cross-references, and arithmetic errors. This is a pre-submission quality check.

## Trigger Words

This skill activates on: `audit`, `consistency check`, `check numbers`, `/audit`.

## Workflow

1. **Scan all chapter files** in the `chapters/` directory using Glob. Read each file to extract quantitative claims, terminology, and cross-references.

2. **Check the following categories:**

   **A. Numerical consistency** — read by the model, not checked by a script,
   except for what audit H binds. The three bullets below are what to look for;
   nothing verifies that you looked.
   - The same statistic (e.g., accuracy, sample size, p-value) cited in multiple chapters must have the same value.
   - Percentages in a distribution must sum to 100% (with tolerance of +/-1% for rounding).
   - Counts (e.g., "42 models") must match between chapters.

   For numbers that matter, use audit H instead: it binds the printed value to
   the artifact it came from, so the check survives the next edit.

   **B. Terminological consistency**
   - The same concept must use the same term throughout. Flag cases where synonyms are used inconsistently (e.g., "structured review" vs "systematic review" for the same concept).
   - Abbreviations must be defined on first use in each chapter.

   **C. Cross-reference validity**
   - References to other sections (e.g., "as discussed in Section 3.2") must point to sections that exist.
   - References to tables and figures must match actual table/figure numbers.
   - Forward references ("Chapter 6 will show...") must be fulfilled.

   **D. Citation checks — disabled in this release (disclosed gap)**

   The deterministic citation tiers previously run here are disabled: measured
   against realistic thesis text they produced false high-severity "phantom
   citation" findings on ordinary parentheticals, missed multi-word
   institutional authors, and flagged the comma form that Cite Them Right
   Harvard mandates. Until the checker meets a measured, disclosed
   false-positive rate, do not run it and do not present citation
   consistency as audited. Reference integrity is still covered by
   `/verify-refs` (BibTeX records) and by the notes-file contract lint.

   **E. Claim positioning (deterministic, runs before F and G — positioning
   is not repairable after review; style is)**

   ```
   python3 .claude/skills/audit/scripts/audit-claim-positioning.py --base-dir chapters --bib references.bib --json
   ```

   (omit `--bib` when the project has no bibliography file). Report every
   issue it returns: `unsourced-keyword` and `bare-novelty` as **High** — a
   field's vocabulary in use without its literature, or a novelty claim in a
   paragraph that shows no search — `uncited-method` and `dangling-entry` as
   **Medium**.

   Measured precision, one manuscript, 2026-09-20: the checker returned 11
   issues and 9 were false, all from three mechanisms now fixed — a
   `\keywords{}` block wrapped across source lines so a term matched only its
   own declaration; the bare word "novelty" inside the manuscript's own method
   name, and inside two sentences that *refuse* the claim; and a natbib
   optional argument (`\citep[p.~12]{key}`) that made the citation invisible,
   so a method cited in its own sentence was reported as uncited. After the
   fixes it returns the 2 that reading had already confirmed. That is n=1 and
   not a rate — but it is the reason to read every issue against the source
   before writing it into a report at High, exactly as category D's disabled
   tiers required.

   For a requested claim-scope or contribution review, consult
   `references/argument-licence/argument-level-lock.md`. Separate the field
   gap, delivered contribution, observed finding and extrapolation; report
   the six-line claim licence and any unsupported transition with a text or
   evidence anchor. This is an Advisory reading task. Do not create standing
   CSV ledgers or infer scientific validity from a checker result. Existing
   legacy packets can be interpreted with the schema and checks linked in
   `references/argument-licence/README.md`.

   **H. Number ledger — is this the artifact's number, and is it scoped?**

```
python3 .claude/skills/audit/scripts/audit-number-ledger.py --base-dir . --ledger numbers.tsv --json
```

A row binds one printed value to the file it came from:
`printed`, `in_artifact`, `scope`, `artifact`, `locator`. Two value columns
because they differ in practice — a figure writing `0.635` against prose
printing `63.5\%` — and the relation is recorded, never inferred. Report
`locator-not-in-artifact`, `value-not-in-locator`, `printed-artifact-mismatch`,
`number-not-in-manuscript` and `scope-missing` as **Critical**;
`unledgered-number` is a coverage list, not a finding. An empty ledger exits 2.

`scope` is matched literally: pick a token the correct sentences contain, or
`-`. On a real manuscript the first token tried flagged two sentences that carry
the scope in other words; `technique` passed both.

**I. Method ledger — is each sentence about what was done still bound to where it was done?**

```
python3 .claude/skills/audit/scripts/audit-method-ledger.py --base-dir . --ledger method-ledger.tsv \
    --repo e0=~/dev/experiments@<commit> --full sections/03_data.tex --git . --state <ids.json> --json
```

For sentences with no claim, number or citation: methods, data, design, what a
figure shows. A row (`id loc sentence claim_type pointer evidence verdict note
checked`) points at code, config or output in a repository pinned at a commit.
Pointers are read out of the cell's text: `path`, `path:12-20` (pinned code
only), `path#a.b[*].c` (resolved in the JSON or YAML), `path@"fragment"`,
`\label{x}`, `commit:<sha>`. Report `sentence-changed`, `pointer-missing`,
`pointer-by-line`, `no-pointer`, `open-verdict`, `retired-but-present` and
`row-dropped` as **High**; `note-contradicts-verdict` and `unledgered` as
**Medium**. `partial` rows are pending, neither pass nor fail. A deleted
sentence's row is kept as `retired <commit>`, never removed. `--migrate-headings`
prints a diff that moves run-in `\paragraph` headings out of sentence cells and
writes nothing. Nothing to examine exits 2.

**G. Claim ledger — does a LaTeX manuscript's claim match its archived source?**

   ```
   python3 .claude/skills/audit/scripts/audit-claim-ledger.py --base-dir . --ledger ledger.tsv --json
   ```

   For manuscripts kept as `.tex` (which audit F does not read). The ledger is a
   TSV with the columns `claim, cite_key, snippet, source_file, level`: one row
   binds one manuscript sentence to one verbatim snippet in one archived source.
   Report `snippet-not-in-source`, `claim-not-in-manuscript`,
   `key-not-in-claim-sentence`, `source-file-missing` and
   `negative-claim-without-fulltext` as **High**; `unledgered-assertion` as
   **Medium**; `qualifier-dropped` and `unledgered-credit` are prompts, not
   findings. Exit 2 means no ledger row was checked at all — report that as
   **not audited**, never as clean. `--pairs` prints the claim/snippet pairs so
   a reader can judge what the audit does not: whether a claim says more than
   its snippet.

   **F. Citation fidelity — does the citing sentence match its source?**

   ```
   node .claude/skills/audit/scripts/audit-citation-fidelity.mjs --base-dir . --json
   ```

   Exit 2 with `nothing_checked: true` means the audit found no citation under
   `chapters/**/*.md` — report it as **not audited**, never as clean. For a LaTeX
   manuscript, `not_covered` counts the `\cite` commands, distinct keys and
   files this audit does not read — under the whole `--base-dir`, so point it
   at the manuscript directory rather than at a repository that also holds
   archived copies, or the count will be a multiple of the truth.
   `--allow-empty` accepts an empty workspace on purpose. An empty `findings`
   list is only a result when `corpus_files` and `sentences_checked` say
   something was read.
   Report `quote-not-in-source` and `page-mismatch` as **High** — a quoted
   span that is not verbatim in the source's notes or PDF, or a page that the
   source contradicts — and `notes-missing` as **Medium**. `low-overlap` is
   **experimental**: list it under Measurements as a prompt to re-read, never
   as an issue; no false-positive rate has been measured for it yet. State
   the tool's own limit in the report verbatim: it does **not** detect a
   sentence that inverts its source in the source's own words — the failure
   that mattered most on a real manuscript — and that still requires
   reading. Every finding here is a proxy; a finding is a reason to open the
   source, not a verdict.

   **G. Prose fingerprint (measurement only)**

   With no baseline corpus, report G as **not audited** and say which corpus is
   missing. Do not leave it out of the report: an absent section reads as a
   pass. The bibliography baseline needs the project's *own* reference PDFs
   (`literature/`, twenty or more, the author's own papers excluded):

   ```
   python3 .claude/skills/audit/scripts/audit-prose-fingerprint.py --target chapters --baseline literature --exclude '<author-surname>*'
   ```

   The tool now checks that precondition itself rather than trusting the
   flag: it measures how much of the target's 4-gram vocabulary each baseline
   document contains, and a document that looks like a draft or a copy of the
   target is named in `baseline_suspect`. When one is found it **withholds
   every percentile and exits 2** — not 1, which means "measured, something is
   out of range". Exclude the file, or pass `--allow-overlap` if the overlap
   is intended; the waiver restores the percentiles but still reports
   `preconditions_checked: false`, because a waiver is not a met precondition.

   Two further fields have to be read before the percentiles mean anything.
   `baseline_too_short` names every document that loaded but fell under the
   1500-word floor, so `baseline_documents` plus the skipped, too-short and
   excluded lists account for every candidate in the directory; a count that
   does not close means the corpus is not what you think it is.
   `pipeline_mismatch` is true when the target is read as stripped markup and
   the baseline as printed PDF, which is what the documented invocation above
   does: a percentile then compares two readings, not two documents. When the
   target's own build sits beside it the tool measures both readings and puts
   them in `pipeline_cross_check`. On one real manuscript the word count --
   the denominator of every per-1k rate -- differed by 40% between them and
   sentence-length lag-1 changed sign, so quote the cross-check rather than
   treating the percentiles as exact.

   **Two baselines, and a percentile that does not name its own is not a
   result.** The corpus above is the project's bibliography: it answers whether
   the prose sits inside the literature the manuscript argues with. Build the
   other one before drafting, not at polish time:

   ```
   python3 .claude/skills/audit/scripts/build-venue-baseline.py --venue "<journal>" --from-year 2021 --out <corpus-dir> --manifest <repo>/venue_manifest.json
   ```

   It admits a record only when its DOI resolves to the venue's registered
   `container-title`, writes every candidate's disposition so the manifest's
   arithmetic closes, and exits 2 with `VENUE_CORPUS_TOO_SMALL` rather than
   returning a short corpus that reads like a complete one. Run the fingerprint
   a second time against it, with `--target` pointing at the manuscript's built
   **PDF** — against a venue corpus both sides go through the same extraction,
   which is the one case where `pipeline_mismatch` can be false. Read it once,
   to answer whether the manuscript reads like the venue's genre. Do not edit
   to move a venue percentile; the stop rule is unchanged.

   Report the distributions under **Measurements**, never as issues: this is
   Advisory by nature. Out-of-range is the hard signal, a percentile is a
   soft one, and clustering matters more than count. Always report
   `preconditions_checked` beside them: `outliers: []` on an unverified
   baseline says nothing, and reading it as a pass is the failure this scan
   was added for. Method and stop rules: `references/prose-polish-method.md`.

   **G2. Rewritten sentences, one at a time (before anyone reads a proposal)**

   The fingerprint and structure audits measure whole documents, so a handful
   of rewrites cannot move them, and neither reads a proposal that has not been
   applied. Every proposed rewrite, and every sentence a correction adds, goes
   through this first, as an `id, old, new` TSV (leave `old` empty for an
   added sentence; the file is read without quoting):

   ```
   python3 .claude/skills/audit/scripts/audit-sentence-changes.py --pairs <rewrites.tsv> --baseline <venue-corpus-dir>
   ```

   For a rewrite it names what was added: three or more words, a comma (when
   punctuation as a whole grew), a colon, semicolon, dash or parenthesis, a
   subordinate clause (counted as gained, so trading "because" for "which"
   counts), an adverb or a modifier (counted net, so a term swap does not),
   two prepositional phrases, a subordinate opener, or a merge of sentences.
   A split is judged as the old sentence against all its pieces. An added
   sentence is flagged for any colon, semicolon, dash or worded parenthesis
   and for density above the venue's 75th percentile. Modifiers are a
   stand-in for a part-of-speech tagger; the report's `limits` line says what
   it cannot see. A correction that stays faithful to its source by piling
   qualifiers onto the old sentence is the usual cause; split it or restructure
   it, and re-run until nothing is flagged or each remaining flag is one you
   can defend. Show the author the rewrites only after that.

   Keep the file after the author has read it, with two more columns:
   `verdict` (accepted, or rejected; revised counts as rejected; empty while
   not judged) and `reason`. Run the audit on it again and it sets the flags
   against the verdicts and names the script by its hash. The thresholds were
   fitted on one round; a later round's verdicts, judged by the version frozen
   before that round, are their only test.

   Once applied, the writing loop runs the same audit on each commit against
   the last commit at which it flagged nothing, so a round of several commits
   is read as a whole; the run record and the summary name that base. A
   flagged sentence keeps the base where it was until the sentence is fixed,
   or until the author accepts it by moving `draft.base_ref` forward.

   **G3. Topic and contribution type against the venue (before choosing it)**

   The venue corpus of `build-venue-baseline.py` answers whether the
   manuscript reads like the venue. It is built from preprints, which skew to
   the subfields whose authors post them, so it cannot say whether the venue
   publishes this topic or this kind of contribution. That takes every
   article the venue published in a window, from the registrar:

   ```
   python3 .claude/skills/audit/scripts/venue-topic-fit.py fetch --issn <ISSN> --from 2024-01-01 --out <corpus.json>
   python3 .claude/skills/audit/scripts/venue-topic-fit.py neighbors --corpus <corpus.json> --title "<title>" --abstract <abstract.txt>
   python3 .claude/skills/audit/scripts/venue-topic-fit.py sample --corpus <corpus.json> --n 100 --seed <int> --out <sheet.tsv>
   python3 .claude/skills/audit/scripts/venue-topic-fit.py tally --sheet <sheet.tsv>
   ```

   `neighbors` gives the nearest articles and where the manuscript's own
   nearest-neighbour similarity falls among the articles': a low percentile
   means its wording sits at the edge of what the venue publishes. Read the
   nearest articles before drawing anything from the number. `sample` draws
   titles to code by contribution type (M method, E evaluation or analysis of
   existing systems, D dataset or benchmark, S study of people or science,
   R review; mark a doubtful code with `?`); `tally` gives shares with 95%
   intervals. The coder is whoever codes the sheet, and the report should
   name them. Three limits go into any report of it: coding from titles is
   coarse; TF-IDF measures shared words, not fit; and every article in the
   corpus was accepted, so nothing here is an acceptance rate.

3. **Output the audit report** using the format below.

## Output Format

```
## Audit Report -- {YYYY-MM-DD}

### Summary

- **Critical**: {N} issues (contradictory data)
- **High**: {N} issues (broken references, missing definitions)
- **Medium**: {N} issues (terminology inconsistency, minor arithmetic)

### Issues

| # | Severity | Category | Location | Issue | Current | Expected |
|---|----------|----------|----------|-------|---------|----------|
| 1 | Critical | Numerical | Ch3 s3.2, Ch5 s5.4 | Sample size differs | 120 (Ch3) vs 125 (Ch5) | Should be consistent |
| 2 | High | Cross-ref | Ch4 s4.1 | Ref to "Section 3.7" | Section 3.7 | Section does not exist |

### Measurements (category G when a baseline exists; category F's experimental low-overlap prompts)

{Per metric: rate, clustering (gap CV), longest gap — with the baseline's
range and where the manuscript sits. Numbers, not verdicts.}

### Recommendations

{Grouped by severity, brief notes on how to resolve each issue.}
```

## Severity Levels

- **Critical**: The same quantitative claim has different values in different chapters. This directly undermines thesis credibility.
- **High**: Broken cross-references, undefined abbreviations on first use, missing table/figure numbers.
- **Medium**: Inconsistent terminology that does not cause factual error, minor rounding discrepancies within tolerance.

## Constraints

1. **Never auto-fix.** List all issues for the user to review and decide. The user may choose to fix selectively.
2. **No emoji** in output.
3. **Report all instances**, not just the first occurrence. If a statistic appears in 4 chapters with 2 different values, list all 4 locations.
4. **Be specific** about locations. Provide chapter number, section number, and surrounding context so the user can find the issue quickly.
5. **Do not flag stylistic issues.** This skill checks data consistency, not prose quality.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.