Repository context. Gather first
Collect these with individual Bash calls, one command per call, never combined into a single
invocation:
- Current branch,
git branch --show-current
- Uncommitted .md files (empty = none matched or the probe returned nothing),
git status --porcelain | grep -E '\.md"?$' | head -10
The pipe is the bound and belongs in the command. A read-time cap ("read only the first 10 entries")
bounds nothing: the Bash tool returns the command's complete output into context before there is
anything to decide about.
Treat a failure (not a repository, git unavailable) as an unknown value and carry on. Keep these as
separate body Bash calls rather than pre-compute lines: the harness runs a skill's whole pre-compute
block as one shell invocation, and a worktree-isolated session refuses a compound command that
contains git. The dated record for that composition claim is the source-control plugin's
worktree/reference/gather-block.md,
"The pre-compute block runs as one shell invocation".
Purpose
Markdown in docs/, README files, onboarding docs, third-party pasted prose, and drifted skill bodies accumulates FLAVOR. Filler ("just", "really", "basically"), hedging ("perhaps", "might"), articles, pleasantries. context/flavor-vs-content-matrix.md defines FLAVOR (safe to cut) vs CONTENT (never cut). The batch fan-out path (Phase A LATITUDE) is a word-level trimmer: mechanical drops + passive→active + nominalization only. No sentence-level restatement deletion. The single-file in-session Edit fallback may apply the full matrix taxonomy (including redundant restatement of bold rule names) behind the same semantic-diff net. Always-loaded instruction files (.claude/rules/**, AGENTS.md, CLAUDE.md, **/SKILL.md) bound empirically at 2-3% yield (see ## Sources). Likely 5-15% yield on author-time-undisciplined content when the Edit fallback's broader latitude applies; batch fan-out yields are correspondingly smaller.
Methodology: snapshot original → backend mechanical compression (the caveman plugin via /caveman:compress, OR in-session Edit fallback) → spawn semantic-diff subagent comparing original vs condensed (output: SEMANTIC LOSS / AMBIGUITY / FALSE POSITIVE per finding with verbatim citations) → revert every SEMANTIC LOSS + AMBIGUITY → run markdownlint-cli2 → ship or revert.
Backend selection
Default-action Step B picks the mechanical-compression backend: the caveman plugin (marketplace caveman, invoked as /caveman:compress via the Skill tool) when present, otherwise the in-session Edit-based fallback. Caveman performs the mechanical flavor cuts (articles, fillers, hedging, verbose-verb collapses) as the compression backend. It is NOT the verification gate. Fallback policy is graceful: the in-session Edit-based path substitutes whenever caveman is absent or unwanted. Subsequent steps (semantic-diff dispatch, revert pass, markdownlint) wrap the output regardless of backend choice.
Note the distinction inside that plugin: /caveman:compress is a function-call skill (this skill's backend); /caveman:caveman is a session-wide response formatter. Unrelated to this skill.
Step A. Detect caveman plugin: bash "${CLAUDE_SKILL_DIR}/scripts/detect-caveman.sh"
Tri-state: available → prefer caveman; absent OR unknown → treat as absent and use the Edit fallback (unknown means claude/jq missing from PATH: fail open to Edit, not a hard error).
Step B, caveman backend (preferred when available): cross-tool-call steps (Bash state does not persist across tool calls, no trap … EXIT, no relying on $tempdir in a later call):
Bash call 1. Create a temp copy and echo its absolute path (no EXIT trap):
tempdir=$(mktemp -d)
cp "$target" "$tempdir/$(basename "$target")"
printf '%s\n' "$tempdir/$(basename "$target")"
Skill call. Skill(caveman:compress, args="<absolute-path-from-step-1>") on that temp copy. Caveman may write <file>.original.md beside the copy inside the tempdir.
Bash call 2. On caveman success, copy the compressed file back and remove the tempdir explicitly:
cp "<absolute-path-from-step-1>" "$target"
rm -rf "$(dirname "<absolute-path-from-step-1>")"
On caveman failure, skip the cp and still rm -rf the tempdir so the real target is untouched.
Tempdir wrapper contains caveman's hardcoded <file>.original.md backup write. Real-path file replaced only on success. Consumers may add a defensive **/*.original.md entry to their .gitignore as belt-and-suspenders against cleanup races or future caveman backup-path-convention changes.
Step B fallback. In-session Edit (caveman absent, unknown, or unwanted):
Agent applies Edit ops directly on $target per the context/flavor-vs-content-matrix.md taxonomy (full matrix, including restatement deletion). Same flavor-vs-content rules; no backend indirection.
Step C+ unchanged: semantic-diff dispatch (mandatory hard rule), revert pass for SEMANTIC LOSS / AMBIGUITY / UNCERTAIN findings, markdownlint-cli2, summary.
Action router
| Action |
Args |
Behavior |
<target> (default, no action keyword) |
empty → uncommitted .md from git status; file path → single-file; dir path → batch |
snapshot → backend → dispatch → revert-pass → markdownlint verify → summary |
audit [target] |
same target rules |
read-only dry-run; run scripts/audit-scan.sh (six-signal heuristic in context/target-types.md); classify SKIP/COMPRESS/UNCERTAIN |
Flags (apply to both actions):
--force. Proceed even when the default <3% AND 0 semantic-loss → REVERT rule would trip. User owns the sub-3% diff
--keep-snapshot. Persist the original to ${CLAUDE_PLUGIN_DATA}/snapshots/<ISO-basic>Z-<basename>.orig.md (the plugin data directory survives plugin updates)
Auto-detect default
Shared clean-tree / no-scope shape: ../../context/clean-tree-fallback.md. The rules below are what this skill runs; open the shared file only when editing them, for the sibling divergences it owns.
- Empty arg AND clean tree → interactive session: repo-wide interview fallback (next section); non-interactive context (subagent, headless/CI): friendly no-op exit 0 ("No uncommitted .md files. Pass file/dir target.")
- Empty arg AND uncommitted
.md files → batch default action over those files
- Single file path → single-file default action
- Directory path → batch default action (filenames sorted lexically for deterministic output)
- First positional ==
audit → audit action on rest (same clean-tree offer as rule 1 when the rest is empty. Report-only corpus audit, no Edit)
Repo-wide interview fallback (empty arg, clean tree, interactive)
Instead of dead-ending, offer a repo-wide run, confirmation-gated at every step; declining at any step exits with the friendly no-op message. Bare /docs-hygiene:compress audit on a clean tree uses steps 1–2 only (free audit + report; no compression interview).
- Offer (AskUserQuestion): run against all tracked eligible
.md files? Decline → no-op exit.
- Audit first (free, mechanical scan, no subagents): run the audit action over every tracked eligible
.md. Present INLINE only aggregate counts per class, a dispatch-cost estimate (2 subagent requests per compressed file), and a top-20 excerpt of COMPRESS rows selected deterministically: expected-yield band descending, then word count descending, then lexical path (band strings tie; the two tie-breaks keep the excerpt stable run-to-run). Write the full per-file table to a file: destination ${CLAUDE_PLUGIN_DATA}/audit/<branch-or-scope>-audit.md when that dir is writable, otherwise a temp path echoed to the user, lexically sorted per the "Summary output deterministic" hard rule, and point at it. Never render every row inline. On a large repo the full table can run to hundreds of KB and truncate the confirmation prompt it feeds. Stop here when the invocation was the audit action (report-only).
- Interview with prescribed defaults (AskUserQuestion, recommended option listed first), default (mutating) action only:
- Scope. Default: top-10 COMPRESS-classified files, highest expected yield first (report-only / decline remains available); alternates: top-N (user picks N), all COMPRESS, include UNCERTAIN, stop after audit (report only). A misclassified cohort burns two dispatches per file before the circuit breaker stops it, so the default caps exposure at ten. See
context/fan-out-orchestration.md circuit breaker.
- Concurrency. Default: 2 concurrent subagents per wave (rate-limit-conservative); alternates: 1 (sequential), 3-5 (
context/fan-out-orchestration.md default).
- Always-loaded files. Default: excluded (SKIP per the 2-3% empirical baseline); including them requires the same explicit opt-in as
--force.
- Confirm and run: batch default action over the confirmed set, waves per
context/fan-out-orchestration.md. Every per-file hard rule, semantic-diff dispatch, revert pass, markdownlint, <3% AND 0 semantic-loss → REVERT, applies unchanged.
Non-interactive contexts never interview; they take the no-op branch. The fallback adds an entry path only. It changes no compression, verification, or revert semantics.
Hard rules
- Semantic-diff dispatch is mandatory for default action. Audit is read-only. No dispatch.
- Post-edit
markdownlint-cli2 MUST pass (using the consuming repository's markdownlint config when present). Non-zero exit blocks ship; revert and surface failures. markdownlint-cli2 is required for correctness (it is the ship gate): if the binary is absent (neither on PATH nor as the repo's node_modules/.bin/markdownlint-cli2), STOP at the entry point before compressing anything and surface the remediation. Install it explicitly (npm install --save-dev markdownlint-cli2 or a global install); never treat absence as a lint failure and never ship unverified output.
- Default
<3% AND 0 semantic-loss → REVERT. Proven safe in the authoring repo's empirical baseline (always-loaded instruction files: 3/3 attempts reverted). --force bypasses.
- Summary output deterministic. No timestamps; filenames sort lexically.
- Snapshot default = ephemeral (
mktemp -d, deleted post-dispatch). --keep-snapshot persists to ${CLAUDE_PLUGIN_DATA}/snapshots/ instead.
- Always-loaded instruction-file policy: SOFT-BLOCK. Default reverts <3%/0SL on ANY file including
.claude/rules/** / AGENTS.md / CLAUDE.md / **/SKILL.md. --force bypasses on ANY file. User owns the result. audit heuristic emits informational SKIP recommendation on always-loaded paths citing the 2-3% empirical baseline; not a structural gate.
- Subagent dispatch follows
context/semantic-diff-prompt.md template. Findings must carry verifiable citations; training-recall citation tokens are forbidden. [known] / [from memory] / [context] / [obvious] / [standard] / [usual].
- Backend choice does NOT bypass semantic-diff dispatch. When the caveman backend is absent or unwanted,
/docs-hygiene:compress falls back to in-session Edit-based compression. Backend selection determines only the mechanical-compression path; semantic-diff + revert pass + markdownlint hard rules apply regardless. LLM-compression fabrication risk (caveman backend OR in-session Edit) caught structurally by the revert pass.
Output schema (default action, per target)
<basename>: <action_taken> (compression_pct=N.N%, semantic_loss=K, ambiguity=M, false_positive=P, markdownlint=PASS|FAIL)
action_taken ∈ {compressed, reverted, skipped}. Aggregate at end of batch.
Audit action output: table with target, expected_yield_pct, classify (SKIP/COMPRESS/UNCERTAIN), reason.
Gotchas
- Self-audit drifts toward EXPANSION. The semantic-diff dispatch must run as a SEPARATE fresh-context audit, never a self-audit by the model that produced the edits. Self-audit re-adds words just removed ("preserve clarity"; an observed failure, see ## Sources). Prefer a cross-vendor advisor for that audit when one is installed and set up, e.g. the OpenAI Codex plugin, when its documented surface can take this artifact, invoked per its own docs, with the fresh-context same-vendor subagent as the stated fallback, never a route to a command that may not resolve (per
docs/PLUGIN-PHILOSOPHY.md "Fresh-eyes checkpoints" in the marketplace repository). Subagents cannot reliably spawn the verifier themselves (nested subagent support is version-dependent, and a fresh-context verifier beats self-critique regardless), so for batch fan-out follow context/fan-out-orchestration.md: the main session dispatches separate compress + audit subagents, reconciling per finding.
- Sub-3% diffs auto-revert unless
--force. Default <3% AND 0 semantic-loss → REVERT; always-loaded instruction files bound at 2-3% yield (see ## Sources). Pass --force only when a targeted sub-3% diff is intentional, the user owns the result.
- Caveman writes a
<file>.original.md backup beside the target. The caveman backend hardcodes this backup path; running it against the real file litters the repo. Backend Step B wraps caveman in a mktemp -d tempdir so the backup lands there and Bash call 2 removes it explicitly; a gitignore entry for **/*.original.md in the consuming repo is optional belt-and-suspenders.
When NOT to use
- Code files (
.cs, .py, .ts, .sh, etc.). Methodology is markdown-specific. Code-comment compression is out of scope
- Binary files
- Author-time-disciplined instruction files (
.claude/rules/**, AGENTS.md, CLAUDE.md, **/SKILL.md). audit will SKIP-recommend; sub-3% revert default applies (Gotchas "Sub-3% diffs auto-revert unless --force")
- Conversation summarization or session compaction. That's the built-in
/compact, different semantic
- Subagent context invoking
/docs-hygiene:compress for batch fan-out. See Gotchas "Self-audit drifts toward EXPANSION"; follow context/fan-out-orchestration.md
What this skill is NOT
- Not an orchestrator surface.
/docs-hygiene:compress prints a human-readable summary; no structured/--json output
- Not a lint front-end.
markdownlint-cli2 is the post-edit verifier, not the primary purpose
- Not a code-comment compressor. Out of scope
- Not a
/code-review / /simplify shadow. The bundled /code-review and /simplify skills review code changes; /docs-hygiene:compress rewrites markdown prose. Different concerns
- Not
/docs-hygiene:audit-noise. /docs-hygiene:compress owns FLAVOR (filler, hedging, articles, redundant restatement). /docs-hygiene:audit-noise owns NOISE classification (historical citations, ghost refs, "Why this file exists" preambles, hard-coupled enumerated consumer lists) per its own taxonomy. Different concerns; both may apply to the same target iteratively
- Not a content-relocation / cite-don't-recap tool. When an inline passage recaps detail that already lives in a cited single source of truth (another doc or rule), condensing it is content RELOCATION, not flavor removal, the mandatory semantic-diff net sees the words gone from THIS file and reverts them as SEMANTIC LOSS, blind to the SSOT. Apply "reference, don't duplicate" as a MANUAL editorial pass (verify the cited SSOT actually holds the detail first, an unread pointer is an unverified claim); route the duplicated cluster to
/docs-hygiene:extract-ssot at any multiplicity (it rosters rule-of-one / -two / -three buckets; only extraction into a NEW artifact waits for 3+ files)
Sources
- Always-loaded instruction-file yield bound (2-3%): authoring-repo baseline, 3/3 compression attempts reverted, all flavor-only, 0 semantic loss
- Self-audit expansion drift: authoring-repo batch compression wave, 2026-05-23. 4/4 reverse-direction edits from self-audit (
context/fan-out-orchestration.md ## History)
Reference index. Load on demand
| File |
Load when |
context/semantic-diff-prompt.md |
Dispatching the semantic-diff subagent; it is the prompt body and the return contract. |
context/flavor-vs-content-matrix.md |
Judging whether a specific span is FLAVOR or CONTENT, and during the revert pass. |
context/target-types.md |
Resolving what an argument points at, or classifying SKIP / COMPRESS / UNCERTAIN in audit. |
context/fan-out-orchestration.md |
Compressing N files across parallel subagents, so the semantic diff stays in a separate context. |
context/integration.md |
Another skill, lint job, or gate wants to call this one, and you need the invocation form it may use. |
1---2name: compress3description: Compress (tighten, shorten, trim) markdown files by dropping flavor, filler, hedging, articles, while preserving all content (directives, qualifiers, thresholds, examples), with a mandatory semantic-diff subagent that reverts any SEMANTIC LOSS or AMBIGUITY. Use when: 'compress this doc', 'tighten markdown', 'cut prose', 'shorten without losing meaning', 'trim onboarding doc', or verbose prose in docs/, READMEs, rule bodies, skill bodies, or third-party pasted text. Actions: default (snapshot → backend → semantic-diff subagent → revert-pass → markdownlint) and audit (read-only dry-run classifying SKIP/COMPRESS/UNCERTAIN per file); empty target + clean tree in an interactive session offers a confirmation-gated repo-wide run (audit-first interview with prescribed defaults) instead of the no-op; flags: --force (bypass <3% revert rule), --keep-snapshot; not for: session compaction (/compact), markdown noise classification (/audit-noise), code-comment trimming, or content relocation/SSOT consolidation (/extract-sso4---56## Repository context. Gather first78Collect these with **individual** Bash calls, one command per call, never combined into a single9invocation:1011- Current branch, `git branch --show-current`12- Uncommitted .md files (empty = none matched or the probe returned nothing), `git status --porcelain | grep -E '\.md"?$' | head -10`1314The pipe is the bound and belongs in the command. A read-time cap ("read only the first 10 entries")15bounds nothing: the Bash tool returns the command's complete output into context before there is16anything to decide about.1718Treat a failure (not a repository, git unavailable) as an unknown value and carry on. Keep these as19separate body Bash calls rather than pre-compute lines: the harness runs a skill's whole pre-compute20block as one shell invocation, and a worktree-isolated session refuses a compound command that21contains git. The dated record for that composition claim is the `source-control` plugin's22[worktree/reference/gather-block.md](https://raw.githubusercontent.com/melodic-software/claude-code-plugins/main/plugins/source-control/skills/worktree/reference/gather-block.md),23"The pre-compute block runs as one shell invocation".2425## Purpose2627Markdown in `docs/`, README files, onboarding docs, third-party pasted prose, and drifted skill bodies accumulates FLAVOR. Filler ("just", "really", "basically"), hedging ("perhaps", "might"), articles, pleasantries. `context/flavor-vs-content-matrix.md` defines FLAVOR (safe to cut) vs CONTENT (never cut). The **batch fan-out path** (Phase A LATITUDE) is a word-level trimmer: mechanical drops + passive→active + nominalization only. No sentence-level restatement deletion. The **single-file in-session Edit fallback** may apply the full matrix taxonomy (including redundant restatement of bold rule names) behind the same semantic-diff net. Always-loaded instruction files (`.claude/rules/**`, `AGENTS.md`, `CLAUDE.md`, `**/SKILL.md`) bound empirically at 2-3% yield (see ## Sources). Likely 5-15% yield on author-time-undisciplined content when the Edit fallback's broader latitude applies; batch fan-out yields are correspondingly smaller.2829Methodology: snapshot original → backend mechanical compression (the `caveman` plugin via `/caveman:compress`, OR in-session Edit fallback) → spawn semantic-diff subagent comparing original vs condensed (output: SEMANTIC LOSS / AMBIGUITY / FALSE POSITIVE per finding with verbatim citations) → revert every SEMANTIC LOSS + AMBIGUITY → run `markdownlint-cli2` → ship or revert.3031## Backend selection3233Default-action Step B picks the mechanical-compression backend: the `caveman` plugin (marketplace `caveman`, invoked as `/caveman:compress` via the Skill tool) when present, otherwise the in-session Edit-based fallback. Caveman performs the mechanical flavor cuts (articles, fillers, hedging, verbose-verb collapses) as the compression backend. It is NOT the verification gate. Fallback policy is graceful: the in-session Edit-based path substitutes whenever caveman is absent or unwanted. Subsequent steps (semantic-diff dispatch, revert pass, markdownlint) wrap the output regardless of backend choice.3435Note the distinction inside that plugin: `/caveman:compress` is a function-call skill (this skill's backend); `/caveman:caveman` is a session-wide response formatter. Unrelated to this skill.3637**Step A. Detect caveman plugin:** `bash "${CLAUDE_SKILL_DIR}/scripts/detect-caveman.sh"`38Tri-state: `available` → prefer caveman; `absent` OR `unknown` → treat as absent and use the Edit fallback (`unknown` means `claude`/`jq` missing from PATH: fail open to Edit, not a hard error).3940**Step B, caveman backend (preferred when available):** cross-tool-call steps (Bash state does not persist across tool calls, no `trap … EXIT`, no relying on `$tempdir` in a later call):41421. **Bash call 1**. Create a temp copy and echo its absolute path (no EXIT trap):4344 ```bash45 tempdir=$(mktemp -d)46 cp "$target" "$tempdir/$(basename "$target")"47 printf '%s\n' "$tempdir/$(basename "$target")"48 ```49502. **Skill call**. `Skill(caveman:compress, args="<absolute-path-from-step-1>")` on that temp copy. Caveman may write `<file>.original.md` beside the copy inside the tempdir.513. **Bash call 2**. On caveman success, copy the compressed file back and remove the tempdir explicitly:5253 ```bash54 cp "<absolute-path-from-step-1>" "$target"55 rm -rf "$(dirname "<absolute-path-from-step-1>")"56 ```5758 On caveman failure, skip the `cp` and still `rm -rf` the tempdir so the real target is untouched.5960Tempdir wrapper contains caveman's hardcoded `<file>.original.md` backup write. Real-path file replaced only on success. Consumers may add a defensive `**/*.original.md` entry to their `.gitignore` as belt-and-suspenders against cleanup races or future caveman backup-path-convention changes.6162**Step B fallback. In-session Edit (caveman absent, unknown, or unwanted):**6364Agent applies Edit ops directly on `$target` per the `context/flavor-vs-content-matrix.md` taxonomy (full matrix, including restatement deletion). Same flavor-vs-content rules; no backend indirection.6566**Step C+ unchanged:** semantic-diff dispatch (mandatory hard rule), revert pass for SEMANTIC LOSS / AMBIGUITY / UNCERTAIN findings, markdownlint-cli2, summary.6768## Action router6970| Action | Args | Behavior |71|---|---|---|72| `<target>` (default, no action keyword) | empty → uncommitted `.md` from `git status`; file path → single-file; dir path → batch | snapshot → backend → dispatch → revert-pass → markdownlint verify → summary |73| `audit [target]` | same target rules | read-only dry-run; run `scripts/audit-scan.sh` (six-signal heuristic in `context/target-types.md`); classify SKIP/COMPRESS/UNCERTAIN |7475Flags (apply to both actions):7677- `--force`. Proceed even when the default `<3% AND 0 semantic-loss → REVERT` rule would trip. User owns the sub-3% diff78- `--keep-snapshot`. Persist the original to `${CLAUDE_PLUGIN_DATA}/snapshots/<ISO-basic>Z-<basename>.orig.md` (the plugin data directory survives plugin updates)7980## Auto-detect default8182Shared clean-tree / no-scope shape: [`../../context/clean-tree-fallback.md`](../../context/clean-tree-fallback.md). The rules below are what this skill runs; open the shared file only when editing them, for the sibling divergences it owns.83841. Empty arg AND clean tree → interactive session: repo-wide interview fallback (next section); non-interactive context (subagent, headless/CI): friendly no-op exit 0 ("No uncommitted .md files. Pass file/dir target.")852. Empty arg AND uncommitted `.md` files → batch default action over those files863. Single file path → single-file default action874. Directory path → batch default action (filenames sorted lexically for deterministic output)885. First positional == `audit` → audit action on rest (same clean-tree offer as rule 1 when the rest is empty. Report-only corpus audit, no Edit)8990## Repo-wide interview fallback (empty arg, clean tree, interactive)9192Instead of dead-ending, offer a repo-wide run, confirmation-gated at every step; declining at any step exits with the friendly no-op message. Bare `/docs-hygiene:compress audit` on a clean tree uses steps 1–2 only (free audit + report; no compression interview).93941. **Offer** (AskUserQuestion): run against all tracked eligible `.md` files? Decline → no-op exit.952. **Audit first** (free, mechanical scan, no subagents): run the audit action over every tracked eligible `.md`. Present INLINE only aggregate counts per class, a dispatch-cost estimate (2 subagent requests per compressed file), and a top-20 excerpt of COMPRESS rows selected deterministically: expected-yield band descending, then word count descending, then lexical path (band strings tie; the two tie-breaks keep the excerpt stable run-to-run). Write the full per-file table to a file: destination `${CLAUDE_PLUGIN_DATA}/audit/<branch-or-scope>-audit.md` when that dir is writable, otherwise a temp path echoed to the user, lexically sorted per the "Summary output deterministic" hard rule, and point at it. Never render every row inline. On a large repo the full table can run to hundreds of KB and truncate the confirmation prompt it feeds. **Stop here when the invocation was the audit action** (report-only).963. **Interview with prescribed defaults** (AskUserQuestion, recommended option listed first), default (mutating) action only:97 - **Scope**. Default: **top-10** COMPRESS-classified files, highest expected yield first (report-only / decline remains available); alternates: top-N (user picks N), all COMPRESS, include UNCERTAIN, stop after audit (report only). A misclassified cohort burns two dispatches per file before the circuit breaker stops it, so the default caps exposure at ten. See `context/fan-out-orchestration.md` circuit breaker.98 - **Concurrency**. Default: 2 concurrent subagents per wave (rate-limit-conservative); alternates: 1 (sequential), 3-5 (`context/fan-out-orchestration.md` default).99 - **Always-loaded files**. Default: excluded (SKIP per the 2-3% empirical baseline); including them requires the same explicit opt-in as `--force`.1004. **Confirm and run**: batch default action over the confirmed set, waves per `context/fan-out-orchestration.md`. Every per-file hard rule, semantic-diff dispatch, revert pass, markdownlint, `<3% AND 0 semantic-loss → REVERT`, applies unchanged.101102Non-interactive contexts never interview; they take the no-op branch. The fallback adds an entry path only. It changes no compression, verification, or revert semantics.103104## Hard rules105106- **Semantic-diff dispatch is mandatory for default action.** Audit is read-only. No dispatch.107- **Post-edit `markdownlint-cli2` MUST pass** (using the consuming repository's markdownlint config when present). Non-zero exit blocks ship; revert and surface failures. `markdownlint-cli2` is **required for correctness** (it is the ship gate): if the binary is absent (neither on `PATH` nor as the repo's `node_modules/.bin/markdownlint-cli2`), STOP at the entry point before compressing anything and surface the remediation. Install it explicitly (`npm install --save-dev markdownlint-cli2` or a global install); never treat absence as a lint failure and never ship unverified output.108- **Default `<3% AND 0 semantic-loss → REVERT`.** Proven safe in the authoring repo's empirical baseline (always-loaded instruction files: 3/3 attempts reverted). `--force` bypasses.109- **Summary output deterministic.** No timestamps; filenames sort lexically.110- **Snapshot default = ephemeral** (`mktemp -d`, deleted post-dispatch). `--keep-snapshot` persists to `${CLAUDE_PLUGIN_DATA}/snapshots/` instead.111- **Always-loaded instruction-file policy: SOFT-BLOCK.** Default reverts <3%/0SL on ANY file including `.claude/rules/**` / `AGENTS.md` / `CLAUDE.md` / `**/SKILL.md`. `--force` bypasses on ANY file. User owns the result. `audit` heuristic emits informational SKIP recommendation on always-loaded paths citing the 2-3% empirical baseline; not a structural gate.112- **Subagent dispatch follows `context/semantic-diff-prompt.md` template.** Findings must carry verifiable citations; training-recall citation tokens are forbidden. `[known]` / `[from memory]` / `[context]` / `[obvious]` / `[standard]` / `[usual]`.113- **Backend choice does NOT bypass semantic-diff dispatch.** When the caveman backend is absent or unwanted, `/docs-hygiene:compress` falls back to in-session Edit-based compression. Backend selection determines only the mechanical-compression path; semantic-diff + revert pass + markdownlint hard rules apply regardless. LLM-compression fabrication risk (caveman backend OR in-session Edit) caught structurally by the revert pass.114115## Output schema (default action, per target)116117```text118<basename>: <action_taken> (compression_pct=N.N%, semantic_loss=K, ambiguity=M, false_positive=P, markdownlint=PASS|FAIL)119```120121`action_taken` ∈ {`compressed`, `reverted`, `skipped`}. Aggregate at end of batch.122123Audit action output: table with `target`, `expected_yield_pct`, `classify` (SKIP/COMPRESS/UNCERTAIN), `reason`.124125## Gotchas126127- **Self-audit drifts toward EXPANSION.** The semantic-diff dispatch must run as a SEPARATE fresh-context audit, never a self-audit by the model that produced the edits. Self-audit re-adds words just removed ("preserve clarity"; an observed failure, see ## Sources). Prefer a cross-vendor advisor for that audit **when one is installed and set up**, e.g. the OpenAI Codex plugin, when its documented surface can take this artifact, invoked per its own docs, with the fresh-context same-vendor subagent as the stated fallback, never a route to a command that may not resolve (per `docs/PLUGIN-PHILOSOPHY.md` "Fresh-eyes checkpoints" in the marketplace repository). Subagents cannot reliably spawn the verifier themselves (nested subagent support is version-dependent, and a fresh-context verifier beats self-critique regardless), so for batch fan-out follow `context/fan-out-orchestration.md`: the main session dispatches separate compress + audit subagents, reconciling per finding.128- **Sub-3% diffs auto-revert unless `--force`.** Default `<3% AND 0 semantic-loss → REVERT`; always-loaded instruction files bound at 2-3% yield (see ## Sources). Pass `--force` only when a targeted sub-3% diff is intentional, the user owns the result.129- **Caveman writes a `<file>.original.md` backup beside the target.** The caveman backend hardcodes this backup path; running it against the real file litters the repo. Backend Step B wraps caveman in a `mktemp -d` tempdir so the backup lands there and Bash call 2 removes it explicitly; a gitignore entry for `**/*.original.md` in the consuming repo is optional belt-and-suspenders.130131## When NOT to use132133- Code files (`.cs`, `.py`, `.ts`, `.sh`, etc.). Methodology is markdown-specific. Code-comment compression is out of scope134- Binary files135- Author-time-disciplined instruction files (`.claude/rules/**`, `AGENTS.md`, `CLAUDE.md`, `**/SKILL.md`). `audit` will SKIP-recommend; sub-3% revert default applies (Gotchas "Sub-3% diffs auto-revert unless `--force`")136- Conversation summarization or session compaction. That's the built-in `/compact`, different semantic137- **Subagent context invoking `/docs-hygiene:compress` for batch fan-out**. See Gotchas "Self-audit drifts toward EXPANSION"; follow `context/fan-out-orchestration.md`138139## What this skill is NOT140141- **Not an orchestrator surface.** `/docs-hygiene:compress` prints a human-readable summary; no structured/`--json` output142- **Not a lint front-end.** `markdownlint-cli2` is the post-edit verifier, not the primary purpose143- **Not a code-comment compressor.** Out of scope144- **Not a `/code-review` / `/simplify` shadow.** The bundled `/code-review` and `/simplify` skills review code changes; `/docs-hygiene:compress` rewrites markdown prose. Different concerns145- **Not `/docs-hygiene:audit-noise`.** `/docs-hygiene:compress` owns FLAVOR (filler, hedging, articles, redundant restatement). `/docs-hygiene:audit-noise` owns NOISE classification (historical citations, ghost refs, "Why this file exists" preambles, hard-coupled enumerated consumer lists) per its own taxonomy. Different concerns; both may apply to the same target iteratively146- **Not a content-relocation / cite-don't-recap tool.** When an inline passage recaps detail that already lives in a cited single source of truth (another doc or rule), condensing it is content RELOCATION, not flavor removal, the mandatory semantic-diff net sees the words gone from THIS file and reverts them as SEMANTIC LOSS, blind to the SSOT. Apply "reference, don't duplicate" as a MANUAL editorial pass (verify the cited SSOT actually holds the detail first, an unread pointer is an unverified claim); route the duplicated cluster to `/docs-hygiene:extract-ssot` at any multiplicity (it rosters rule-of-one / -two / -three buckets; only extraction into a NEW artifact waits for 3+ files)147148## Sources149150- Always-loaded instruction-file yield bound (2-3%): authoring-repo baseline, 3/3 compression attempts reverted, all flavor-only, 0 semantic loss151- Self-audit expansion drift: authoring-repo batch compression wave, 2026-05-23. 4/4 reverse-direction edits from self-audit (`context/fan-out-orchestration.md` ## History)152153## Reference index. Load on demand154155| File | Load when |156|---|---|157| `context/semantic-diff-prompt.md` | Dispatching the semantic-diff subagent; it is the prompt body and the return contract. |158| `context/flavor-vs-content-matrix.md` | Judging whether a specific span is FLAVOR or CONTENT, and during the revert pass. |159| `context/target-types.md` | Resolving what an argument points at, or classifying SKIP / COMPRESS / UNCERTAIN in `audit`. |160| `context/fan-out-orchestration.md` | Compressing N files across parallel subagents, so the semantic diff stays in a separate context. |161| `context/integration.md` | Another skill, lint job, or gate wants to call this one, and you need the invocation form it may use. |