# Scrivener Integrity

> When the user wants to validate, repair, or back up a Scrivener project, or worries it might be damaged or sync-conflicted. Use when the user says "is this project OK", "validate my project", "check this .scriv", "back up first", "rebuild the search index", "fix checksums", "the checksums are mismatched", or "did a sync conflict happen". For versioning a single document, see scrivener-snapshots; for the writes these checks protect, see scrivener-edit; for the external-folder sync round-trip, see scrivener-sync.

- Skill: `donnfelker/scrivener-integrity` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add donnfelker/scrivener-integrity`
- Raw SKILL.md: https://api.skillmd.com/api/skills/donnfelker/scrivener-integrity/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: donnfelker (https://skillmd.com/u/donnfelker)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/donnfelker/scrivener-integrity

---


# Scrivener Integrity

You help the writer confirm a Scrivener 3 (`.scriv`) project is sound, repair its
**derived** data, and make a full backup before any risky work. The write skills call
this skill proactively — every mutating command auto-backups (zip) and auto-snapshots a
document before overwriting its text — but a writer can also run these checks directly.

The governing idea: a `.scriv` is a structured *package*, and most of what looks alarming
in it is normal. Absent content and "mismatched" checksums are **not** corruption. This
skill is calibrated to say "your project is fine" when it is, and to hard-fail only when
the project genuinely cannot be opened.

## When to use

- "Is this project OK / validate this / check this .scriv" → `verify`
- "Fix the checksums / rebuild the search index" → `repair [--what checksums|search-index|all]`
- "Back this up / back up first / make a safety copy" → `backup [--out <dir>]`
- "What snapshots does this document have" → `snapshots <id>` (then see scrivener-snapshots)
- "Is it safe to edit / is the project open" → `preflight` (auto-run before every write)
- "Did a sync conflict happen" → `preflight` flags conflict files & undownloaded iCloud today; a dedicated `conflicts` command is 🔜 planned

## Commands

JSON output by default; add `--format text` for a human-readable report. Always pass
`--project` pointing at the `.scriv` package (or its `.scrivx`).

### preflight — is it safe to write? (auto-run before every write)

```bash
python3 ${CLAUDE_PLUGIN_ROOT}/tools/scrivener/cli.py preflight --project "<path.scriv>"
```

Reports whether the project looks **open in Scrivener** or **mid cloud-sync**. Every mutating
command runs this automatically and **refuses to write** (exit 3) on a blocking signal — pass
`--force` to override. Blocking signals: a Scrivener **lock file** in the package, **Scrivener
holding files open** (via `lsof`, macOS/Linux), undownloaded **iCloud placeholders**
(`.*.icloud`), or **sync-conflict files**. Other open handles (Spotlight, cloud daemons) are
info, never blocking. This guard lives in the toolkit, so it protects Claude Code, Codex,
Cursor, and CI alike — a Claude Code PreToolUse hook would only cover Claude Code.

### verify — validate the package

```bash
python3 ${CLAUDE_PLUGIN_ROOT}/tools/scrivener/cli.py verify --project "<path.scriv>"
python3 ${CLAUDE_PLUGIN_ROOT}/tools/scrivener/cli.py verify --project "<path.scriv>" --format text
```

Parses the `.scrivx` binder, walks the binder tree, and reports findings as **errors**,
**warnings**, or **info**. It is deliberately conservative (see philosophy below).

### repair — recompute derived data

```bash
python3 ${CLAUDE_PLUGIN_ROOT}/tools/scrivener/cli.py repair --what checksums --project "<path.scriv>"
python3 ${CLAUDE_PLUGIN_ROOT}/tools/scrivener/cli.py repair --what search-index --project "<path.scriv>"
python3 ${CLAUDE_PLUGIN_ROOT}/tools/scrivener/cli.py repair --what all --project "<path.scriv>"
```

`repair` only ever rewrites data Scrivener can regenerate itself: it recomputes
`Files/Data/docs.checksum` (SHA-1 of `content.rtf`, `notes.rtf`, `synopsis.txt`) and/or
rebuilds `Files/search.indexes`. It never touches your text, snapshots, comments, or
metadata. `repair` is a mutating command, so it backs up first by default.

### backup — full timestamped zip

```bash
python3 ${CLAUDE_PLUGIN_ROOT}/tools/scrivener/cli.py backup --project "<path.scriv>"
python3 ${CLAUDE_PLUGIN_ROOT}/tools/scrivener/cli.py backup --project "<path.scriv>" --out "<dir>"
```

Zips the **entire** `.scriv` package (binder, all `Data/<UUID>` payloads, snapshots,
settings) to a timestamped archive. This is separate from Scrivener's own on-close
backup rotation; the toolkit never writes into Scrivener's backup folder. Run this before
any large restructuring or a global replace.

### snapshots \<id\> — list a document's snapshots

```bash
python3 ${CLAUDE_PLUGIN_ROOT}/tools/scrivener/cli.py snapshots <uuid-or-title> --project "<path.scriv>"
```

Lists the snapshots stored under a document's `Data/<UUID>/snapshots/` (title +
timestamp). Use this to confirm an auto-snapshot was captured before an edit, or to find a
version to read/diff/restore — those operations live in scrivener-snapshots.

## The advisory-checksum philosophy

Scrivener writes `docs.checksum` to detect *external/sync* edits — it is a change
**hint**, not an integrity assertion. Real, perfectly valid projects routinely carry stale
entries (about **32%** of entries were stale in the reference project, simply from normal
use). So `verify` treats checksum state as **advisory**:

- A **stale/mismatched** entry on a checksummed file → **info** (offer `repair --checksums`).
- An **absent** entry for a file Scrivener doesn't checksum → **info**, never a problem.
- A **missing `Data/<UUID>` directory or absent `content.rtf`** → **normal**. Special root
  folders, folders, and empty/never-written documents legitimately have no body. Image
  items store `content.<ext>` instead of `content.rtf`.

**Checksum mismatches are never reported as corruption and never block a write skill.**

## What `verify` does and doesn't flag

`verify` **hard-fails (error)** on only two things:

1. An **unparseable `.scrivx`** — the binder XML won't load.
2. An **explicitly referenced file that is missing** — e.g. a `docs.checksum` entry, an
   Image item's declared `content.<FileExtension>`, or a referenced
   `content.comments`/snapshot file that is named but gone from disk.

Everything else is a **warning** (worth noting, safe to proceed) or **info** (advisory,
e.g. stale checksums → suggest `repair`). Absent body content is never an error.

## Safety

- **The toolkit checks for you.** Every mutating command runs `preflight` first and
  **refuses to write if the project looks open in Scrivener or mid cloud-sync** (override with
  `--force`). Still close Scrivener and let Dropbox/iCloud finish — the check is a backstop,
  not a license to edit a live project. `verify`/`snapshots`/`preflight` are read-only;
  `repair`/`backup` mutate.
- Write skills auto-backup (zip) and auto-snapshot a document before overwriting its text.
- Titles are not unique — pass a **UUID** to `snapshots`/`repair` when a title is ambiguous.

## Reference

- `references/integrity-model.md` — `verify` semantics (errors vs warnings vs info), the
  advisory-checksum model and the 32% stale reality, what `backup` zips, what `repair`
  rewrites, and the planned `restore` / `backups list` / `conflicts` workflows.

## Toolkit support

| Command | Status |
|---------|--------|
| `preflight` (refuse writes when open/syncing; auto-run before every mutation; `--force` to override) | ✅ available now |
| `verify` (advisory checksums; hard-fail only on unparseable .scrivx or a missing *referenced* file) | ✅ available now |
| `repair --what checksums\|search-index\|all` | ✅ available now |
| `backup [--out <dir>]` | ✅ available now |
| `snapshots <id>` (list) | ✅ available now |
| `restore <zip>` (restore a backup) | 🔜 planned (use `backup` + unzip manually today) |
| `backups list` | 🔜 planned (list backup zips you've made; track them yourself for now) |
| `conflicts` (dedicated command) | 🔜 planned — `preflight` already flags sync-conflict files & undownloaded iCloud placeholders |

## Related skills

- **scrivener-snapshots** — take/list/read/diff/restore document versions; `snapshots <id>`
  here just lists what exists.
- **scrivener-edit** — the write/restructure operations that auto-backup and auto-snapshot;
  this skill is the safety net they rely on.
- **scrivener-sync** — Sync with External Folder; run `verify`/`backup` before and after a
  sync round-trip, and watch for conflicts.
- **scrivener-format** — the `.scriv` package format (where `docs.checksum`, `search.indexes`,
  `Data/<UUID>`, and snapshots live on disk).

