agentleFS
jackneiljackneil/claude-jacked/.claude/skills/docs-sync/SKILL.md

docs-sync

Skill2 starsChanged 9 days agoMITView on GitHub

Sync docs with code changes: diffs branch, maps to affected docs, spawns parallel update agents. (repo)

46 other projects use a version of this.

Adopted by

Most recently taken up by

Used with

Projects with it also use tdd, code-review and 1 more.

Other files people use alongside this one.

Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
  • GoogleSkill

    tdd

    Test-driven development. Use when the user wants to build features or fix bugs test-first, mentions "red-green-refactor", or wants integration tests.

    Also in 4 projects that use this

  • GoogleSkill

    code-review

    Adversarial systems, concurrency, memory-model, and architecture code review protocol. Use this skill whenever asked to review code, PRs, diffs, or branches in Filament.

    Also in 5 projects that use this

  • MicrosoftSkill

    handoff

    Compact the current conversation into a handoff document for another agent to pick up.

    Also in 4 projects that use this

  • Other versions46 projects use a version of this.

    Word for word · 0

    None found.

    Edited · 0

    None found.

    Wrote their own version · 46

    1. openai/openai-agents-python★ 31ksame name
    2. redis/node-redis★ 18ksame name
    3. redis/ioredis★ 15ksame name
    4. fossasia/visdom★ 10ksame name
    5. Kiln-AI/Kiln★ 5.3ksame name
    6. danielmeppiel/apm★ 4.2ksame name
    7. microsoft/apm★ 4.2ksame name
    8. openai/openai-agents-js★ 4ksame name
    9. pnp/powershell★ 912same name
    10. strands-agents/box★ 503same name

    36 more not listed

    How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.

  • In this repository6 other files it gives its agents.

The file

Show its rules and text

We split this skill into the rules it gives an agent

17 rules. 0 are common, 10 are distinctive, and 7 are very specific to this project.

  • 0

    Common

    Most of the 36 edited versions we read keep these.

  • 10

    Distinctive

    This author's own choices. Worth a look.

  • 7

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Before using the config above, run a lightweight drift check (advisory only.

    line 52
  2. Very specific:

    If EITHER differs, count the intervening commits (git rev-list --count <generated-commit>..HEAD 2>/dev/null) and surface one line, then …

    line 55
  3. Distinctive:

    If the generated commit is unreachable (history rewritten) or the manifest set changed, surface the nudge regardless of commit count.

    line 57
  4. Very specific:

    If this command was invoked via a local config wrapper (you see a ## Repo Config section earlier in the prompt), use that config to …

    line 64
  5. Very specific:

    If the config overlay date is more than 90 days old, mention: "Your /docs-sync config is over 90 days old — consider running /jacked-setup …

    line 70
  6. Very specific:

    If no ## Repo Config section is present, run all discovery steps normally.

    line 72
  7. Distinctive:

    Run these commands to gather context (all safe, read-only):

    line 78
  8. Distinctive:

    If Repo Config or the user targets an alias, redirect the dispatch to the canonical target.

    line 121
  9. Distinctive:

    If CLAUDE.md and AGENTS.md exist as two separate, non-symlinked files, audit both independently AND diff their content — flag any …

    line 122
  10. Distinctive:

    If there are no changes (empty diff), do not stop — proceed to Step 2.5 in case stale docs need auditing.

    line 148
  11. Very specific:

    Use git's last-commit timestamp throughout (filesystem mtime is unreliable — it changes on checkout, with a stat fallback for untracked …

    line 166
  12. Very specific:

    Use the SYMBOL-DRIFT output two ways:

    line 319
  13. Very specific:

    If ## Change-to-Doc Map exists in Repo Config: Use the table to determine which doc files are affected.

    line 339
  14. Distinctive:

    If no map exists (runtime discovery): Use this default mapping:

    line 341
  15. Distinctive:

    If the combined set (branch-affected ∪ audit queue) is empty, say: "Changes don't affect any docs, and no stale or symbol-drifted docs …

    line 355
  16. Distinctive:

    After agents complete, check if the changes introduced anything with no existing doc coverage:

    line 533
  17. Distinctive:

    Test-only changes (unless they change how to run tests)

    line 604

“Common” is measured: kept by at least 6 in 10 of the 36 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Generated by /jacked-setup - 2026-11-03 | Template v2 | Engine: jacked v0.110.2

Standalone - no dependencies. Commit this file. To update: uv tool install --upgrade claude-jacked && jacked install && /jacked-setup docs-sync

Repo Config

  • Project: claude-jacked
  • Base Branch: master
  • Stack: Python (FastAPI CLI + background service); vanilla JS/HTML/CSS dashboard under jacked/data/web/
  • Doc style: README, SECURITY.md and other user-facing docs use ASD-STE100 Simplified Technical English (active voice, one instruction per sentence, short sentences) and carry no em-dashes. Docstrings follow the same STE rules. Internal design docs and plans are exempt from the em-dash ban.

Doc Inventory

  • README.md (sections: What you get, Quick start, Your dashboard, Table of Contents, What's Included, Web Dashboard (Remote Access, Account switching, Plans and usage windows), Background Service and Tray Icon, Upgrading (the upgrade is a transaction), /recover, /whats-next ambition, Built-in Reviewers and Commands (Skills, Skill Packs, Skill Listing Budget, DCR Review Engine, Memory Vault, Open work at session start, Hook and settings integrity, Statusline, QA Browser Testing, Asana in /whats-next), Sound Notifications, FAQ, Uninstall / Troubleshooting, Version History, Advanced / Technical Reference)
  • CLAUDE.md (Project Rules, Environment: the uv-only + test-running rules)
  • JACKED_GUARDRAILS.md (Quality Gates, Size Limits, Structure, Error Handling, Testing, Security, Hook and Memory Integrity (MemoryTrap): case study, posture rules, what jacked does, limit of the threat model; Naming, Git, Python-Specific)
  • SECURITY.md (Report a vulnerability, Supported versions, What jacked protects, How releases are built)
  • lessons.md (recurring pitfalls; a Claude-instruction file, stays Markdown)
  • docs/architecture/ (oauth-and-credential-flows.md, auto-swap-system.md, work-ledger.html: where rules, issues and PRs each belong)
  • docs/ (design/plans/research/lockdown: internal design notes, not user-facing; touch only when a plan they describe actually changes)
  • jacked/data/commands/goal-maker.md ## Ledger contract and jacked/data/skills/whats-next/SKILL.md (shipped skill text that defines the ledger; tests pin it: tests/unit/test_long_run_ledger_contract.py)
  • x-article.md (launch post draft; marketing copy, not a reference doc)
  • No _wiki/; no separate CHANGELOG.md (the changelog lives in README's "Version History" table)
  • Doc dirs: docs/ (md + html, depth 3 incl. architecture/, design/, plans/, lockdown/, research/, superpowers/), design/ (md + html, incl. memory-vault and skill-diet design docs), .claude/research/ (local html research reports, gitignored)

Change-to-Doc Map

Change CategoryAffected Docs
Pipeline/Architecturedocs/architecture/*; README.md (Web Dashboard / Background Service)
SessionStart hook steps (jacked/data/hooks/session_start.py, git_reality.py, issue_queue.py, load budget)README.md (Open work at session start, Memory Vault, Hook and settings integrity); docs/architecture/work-ledger.html
Integrity tripwire / quarantine (jacked/integrity*.py, jacked/memory/quarantine.py, injection_shapes.py)README.md (Hook and settings integrity); JACKED_GUARDRAILS.md (MemoryTrap section); SECURITY.md (What jacked protects)
Git guard / untrusted-checkout reads (jacked/git_guard.py)SECURITY.md (What jacked protects); JACKED_GUARDRAILS.md (Security)
Issue queue / work ledger (jacked/service/issue_queue_refresh.py, jacked/issues_cli.py, goal-maker Ledger contract, whats-next)docs/architecture/work-ledger.html; README.md (Open work at session start, /whats-next ambition)
Configuration / Env / InstallREADME.md (Quick start, Your dashboard, Upgrading); CLAUDE.md (Environment)
Commands/CLI (jacked/cli.py, integrity_cli.py, issues_cli.py)README.md (What's Included, Quick start, Commands, Hook and settings integrity)
DependenciesREADME.md (Quick start install lines); pyproject.toml; uv.lock
CI / release workflows (.github/workflows/)SECURITY.md (How releases are built); README.md if user-visible
UI/Frontend (jacked/data/web/)README.md (Web Dashboard, Your dashboard)
Models/Schemas (jacked/web/database.py)docs/architecture/*; README.md if user-visible
Memory Vault (jacked/memory/, jacked/data/hooks/memory_*.py)README.md (Memory Vault section); design/memory-vault-design.html + design/memory-vault-implementation-plan.html if the design itself changed
Guardrails / size limits (tests/unit/test_module_size_ratchet.py)JACKED_GUARDRAILS.md (Size Limits)
TestsCLAUDE.md (test-running rules)
Release / version bumpREADME.md (Version History)
<!-- Staleness fingerprint - the engine compares these against the live repo on each run -->
  • Generated at commit: 68e95e00cc2ee874418c3fa016919e13152d3f39
  • Detected stack: pyproject.toml

Config Staleness Check

Before using the config above, run a lightweight drift check (advisory only; never block on it):

  1. git rev-parse HEAD and compare to Generated at commit above.
  2. ls package.json pyproject.toml go.mod Cargo.toml setup.py Gemfile pom.xml build.gradle composer.json mix.exs 2>/dev/null and compare the manifest set to Detected stack above.
  3. If EITHER differs, count the intervening commits (git rev-list --count <generated-commit>..HEAD 2>/dev/null) and surface one line, then continue:

    ⚠️ This /docs-sync config was generated N commits ago and the stack/paths may have drifted. Re-run /jacked-setup docs-sync to refresh. If the generated commit is unreachable (history rewritten) or the manifest set changed, surface the nudge regardless of commit count. Never stop or skip the run on a staleness mismatch.

<!-- ENGINE - DO NOT EDIT BELOW THIS LINE -->

Config Override

If this command was invoked via a local config wrapper (you see a ## Repo Config section earlier in the prompt), use that config to accelerate sync:

  • Base Branch specified? → Use it instead of auto-detecting in Step 1
  • Doc Inventory listed? → Skip doc discovery in Step 1, use the listed files (validate with ls first, skip missing). This also seeds Step 2.5's staleness/symbol-drift sweep — if the inventory names living-doc locations outside the default set (root *.md, docs/, _wiki/, CLAUDE.md/AGENTS.md), e.g. specs/, documentation/, guides/, adr/, you MUST add them to DOC_CANDIDATES there. A config-declared source-of-truth doc must never be silently skipped by the sweep.
  • Change-to-Doc Map specified? → Use it in Step 3 instead of the default mapping
  • Staleness Defaults specified (age / drift days)? → Use them as the repo-wide defaults in Step 2.5 (a per-doc ttl_days frontmatter contract still wins over the repo default). To make this actually take effect, substitute the configured values inline into the Step 2.5a bash: replace DEFAULT_AGE_DAYS=${DOCS_SYNC_AGE_DAYS:-90} with the configured age (e.g. DEFAULT_AGE_DAYS=60) and DEFAULT_DRIFT_DAYS=${DOCS_SYNC_DRIFT_DAYS:-30} with the configured drift, OR export DOCS_SYNC_AGE_DAYS / DOCS_SYNC_DRIFT_DAYS before running that block so the ${VAR:-default} expansion picks them up. The 90/30 literals are field fallbacks only — when the config supplies values, they must override.

If the config overlay date is more than 90 days old, mention: "Your /docs-sync config is over 90 days old — consider running /jacked-setup docs-sync to refresh it."

If no ## Repo Config section is present, run all discovery steps normally.

Step 1: Discover Repo Context

Skip this step entirely if ## Repo Config was found above.

Run these commands to gather context (all safe, read-only):

REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
echo "REPO_ROOT=$REPO_ROOT"
echo "REPO_NAME=$(basename "$REPO_ROOT")"

# Detect base branch
git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's|refs/remotes/origin/||' || echo "main"

# Doc files at repo root
ls README.md CONTRIBUTING.md CHANGELOG.md LICENSE.md AGENTS.md 2>/dev/null

# Wiki structure
ls -d _wiki 2>/dev/null
ls _wiki/*.md 2>/dev/null | head -30

# Agent-instruction files (CLAUDE.md / AGENTS.md) sections
grep -n "^#" CLAUDE.md 2>/dev/null | head -20
grep -n "^#" AGENTS.md 2>/dev/null | head -20

# docs/ and specs/ directories (this project defaults human docs to HTML; sweep both,
# and go deep — a real spec tree lives at docs/superpowers/specs/, depth 3)
find docs specs \( -name "*.md" -o -name "*.html" \) 2>/dev/null | head -30

# Other root-level markdown
ls *.md 2>/dev/null

# Symlink map — CLAUDE.md is frequently a symlink to AGENTS.md (cross-tool compat).
# Editing the alias leaves the canonical file stale, or silently forks the two.
ls -la *.md 2>/dev/null
for f in *.md; do
  [ -L "$f" ] && echo "SYMLINK: $f -> $(readlink -f "$f" 2>/dev/null || readlink "$f")"
done

Build a doc inventory from the results, checked for completeness against the three doc layers — a whole missing layer is itself a gap worth surfacing:

  • User-facing — README.md, docs/ guides, quick-starts, SDK/usage docs. What a consumer reads.
  • Contributor-facing — CONTRIBUTING.md, architecture docs, _wiki/. How to build, test, and extend.
  • Agent-facing — CLAUDE.md and AGENTS.md. Instructions coding agents (Claude, Cursor, Codex, Copilot) read. AGENTS.md is a first-class surface now — never skip it.

Symlink resolution (do this before any dispatch). From the ls -la / SYMLINK output, resolve every root-level markdown symlink to its canonical target:

  • Record each canonical (real) file and its aliases. Dispatch exactly ONE audit agent per canonical file — never against an alias. Editing the alias path leaves the canonical agent-instructions file stale, or silently forks the two.
  • If Repo Config or the user targets an alias, redirect the dispatch to the canonical target.
  • If CLAUDE.md and AGENTS.md exist as two separate, non-symlinked files, audit both independently AND diff their content — flag any divergence (a rule present in one but not the other) for human review in Step 6.
  • Carry the canonical↔alias map forward to Step 6 so the relationship is reported.

Step 2: Diff Analysis

Diff the current branch against the base branch:

# Get base branch (from Repo Config or detected above)
BASE_BRANCH=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's|refs/remotes/origin/||' || echo "main")

# Diff against the MERGE-BASE, not the moving base-branch tip. On long-lived
# branches the base tip advances independently, so diffing against it re-flags
# changes already merged to main and inflates false drift. merge-base is the
# stable fork point — use it as the reference for the diff AND for any downstream
# age-delta comparison.
MERGE_BASE=$(git merge-base origin/${BASE_BRANCH} HEAD 2>/dev/null || git merge-base ${BASE_BRANCH} HEAD 2>/dev/null)
echo "MERGE_BASE=${MERGE_BASE}"
DIFF_REF=${MERGE_BASE:-${BASE_BRANCH}}

# Summary of changes
git diff ${DIFF_REF}...HEAD --stat 2>/dev/null || git diff ${DIFF_REF}..HEAD --stat
git diff ${DIFF_REF}...HEAD --name-only 2>/dev/null || git diff ${DIFF_REF}..HEAD --name-only
git log ${DIFF_REF}..HEAD --oneline 2>/dev/null

If there are no changes (empty diff), do not stop — proceed to Step 2.5 in case stale docs need auditing. Otherwise, categorize changes below.

Categorize all changes into these buckets:

CategorySignals
Pipeline/ArchitectureNew entry points, changed main flows, new modules, renamed directories
ConfigurationNew env vars, changed settings, new config files
Commands/CLINew flags, changed usage, new subcommands, changed arguments
Models/SchemasNew models, changed fields, migration files
TestsNew test patterns, changed test commands, new test utilities
DependenciesNew requirements, changed versions, new package managers
UI/FrontendNew pages, changed components, route changes

Step 2.5: Stale Doc Detection (mandatory)

Documentation rots silently when PRs forget to run /docs-sync. Catch that drift here. A doc joins the audit queue if it is stale by age/TTL (2.5a) OR has symbol drift (2.5b) — regardless of whether the current branch touches it.

Use git's last-commit timestamp throughout (filesystem mtime is unreliable — it changes on checkout, with a stat fallback for untracked files).

Run the three bash blocks in this step in a single shell session (concatenate them into one Bash call). Blocks 2.5a and 2.5b reuse DOC_CANDIDATES and the is_excluded / doc_ttl_days helpers defined here — a fresh shell would lose them and silently stop excluding changelogs.

NOW=$(date +%s)

# Collect candidate doc files (root markdown, docs/, _wiki/, agent-instruction files).
# IMPORTANT: if the Repo Config ## Doc Inventory (or any doc's `sources:` frontmatter)
# names living-doc locations OUTSIDE this default set — e.g. specs/, documentation/,
# guides/, adr/ — add a matching `find` line below BEFORE running, so those docs are
# swept for staleness AND symbol drift too. Step 1 and Step 3 already honor the
# inventory; this sweep MUST match it, or config-declared source-of-truth docs are
# silently skipped (the #1 reported gap).
DOC_CANDIDATES=$(
  {
    # Sweep .html alongside .md: this project defaults human docs to HTML, and
    # several repos keep canonical docs in specs/ or an all-.html docs/ tree —
    # an .md-only sweep silently skips them.
    ls *.md *.html 2>/dev/null
    find docs \( -name "*.md" -o -name "*.html" \) 2>/dev/null
    find _wiki \( -name "*.md" -o -name "*.html" \) 2>/dev/null
    find specs \( -name "*.md" -o -name "*.html" \) 2>/dev/null
    find . -maxdepth 4 \( -name "CLAUDE.md" -o -name "AGENTS.md" \) 2>/dev/null
    # + config-declared doc dirs from ## Doc Inventory — add one per repo, e.g.:
    # find documentation \( -name "*.md" -o -name "*.html" \) 2>/dev/null
  } | sort -u
)

# Changelogs / historical / migration docs legitimately reference deleted code and
# old versions — exclude them from BOTH staleness and symbol-drift. A doc may also
# opt out with `freshness: { exclude: true }` (or `freshness_exclude: true`) in frontmatter.
is_excluded() {
  case "$(basename "$1" | tr 'A-Z' 'a-z')" in
    changelog.md|changelog*.md|history.md|changes.md|releases.md|migration*.md|upgrading.md) return 0;;
  esac
  case "$1" in
    */migrations/*|*/changelog/*|*/history/*) return 0;;
  esac
  head -20 "$1" 2>/dev/null | grep -qiE '(freshness_)?exclude:[[:space:]]*true' && return 0
  return 1
}

2.5a — Staleness (age + TTL)

One global 30-day cutoff over-flags slow-moving architecture docs and under-flags fast-moving quick-starts. Use tiered thresholds instead (docvet field defaults: age 90d, drift 30d):

  • Per-doc TTL — if the doc declares ttl_days: N (or freshness: { ttl_days: N }) in frontmatter, that wins.
  • Repo-wide default — else the Repo Config overlay's staleness default (DOCS_SYNC_AGE_DAYS), if set.
  • Field default — else 90 days.

A doc is also flagged if its declared sources: globs were edited >30d more recently than the doc itself (drift), even when the doc is younger than its TTL.

If the Repo Config overlay specified Staleness Defaults, substitute those values inline here before running: set DEFAULT_AGE_DAYS / DEFAULT_DRIFT_DAYS to the configured numbers (or export DOCS_SYNC_AGE_DAYS / DOCS_SYNC_DRIFT_DAYS ahead of this block). The :-90 / :-30 literals below are the field fallback used only when no config value is supplied.

DEFAULT_AGE_DAYS=${DOCS_SYNC_AGE_DAYS:-90}
DEFAULT_DRIFT_DAYS=${DOCS_SYNC_DRIFT_DAYS:-30}

doc_ttl_days() {  # per-doc ttl_days frontmatter, else repo default
  local t
  t=$(head -20 "$1" 2>/dev/null | grep -iE 'ttl_days:' | head -1 | grep -oE '[0-9]+' | head -1)
  [ -n "$t" ] && echo "$t" || echo "$DEFAULT_AGE_DAYS"
}

# Iterate with `while read` (not `for doc in $DOC_CANDIDATES`): unquoted `$var`
# word-splits in bash but NOT in zsh, so a `for` over a multiline var iterates
# once on the whole blob under zsh (the default macOS shell). A here-string is
# shell-agnostic.
STALE_DOCS=()
while IFS= read -r doc; do
  [ -z "$doc" ] && continue
  [ -f "$doc" ] || continue
  is_excluded "$doc" && { echo "SKIP (changelog/historical/excluded): $doc"; continue; }
  TS=$(git log -1 --format=%ct -- "$doc" 2>/dev/null)
  [ -z "$TS" ] && TS=$(stat -f %m "$doc" 2>/dev/null || stat -c %Y "$doc" 2>/dev/null)
  [ -z "$TS" ] && continue
  DAYS_OLD=$(( (NOW - TS) / 86400 ))
  TTL=$(doc_ttl_days "$doc")
  reason=""
  [ "$DAYS_OLD" -gt "$TTL" ] && reason="${DAYS_OLD}d old > ${TTL}d ttl"
  # drift: newest commit touching any declared `sources:` glob vs the doc's own
  # last edit. Hand the pathspec to git (quoted) so git does the glob match —
  # avoids shell glob-expansion, which differs across bash/zsh.
  newest=0
  globs=$(awk 'NR==1&&/^---/{fm=1;next} fm&&/^---/{exit} fm&&/^sources:/{s=1;next} fm&&/^[^[:space:]-]/{s=0} fm&&s&&/^[[:space:]]*-[[:space:]]/{sub(/^[[:space:]]*-[[:space:]]*/,"");sub(/[[:space:]]+$/,"");print}' "$doc" 2>/dev/null)
  while IFS= read -r glob; do
    [ -z "$glob" ] && continue
    sts=$(git log -1 --format=%ct -- "$glob" 2>/dev/null)
    [ -n "$sts" ] && [ "$sts" -gt "$newest" ] && newest=$sts
  done <<< "$globs"
  if [ "$newest" -gt 0 ]; then
    DELTA=$(( (newest - TS) / 86400 ))
    [ "$DELTA" -gt "$DEFAULT_DRIFT_DAYS" ] && reason="${reason:+$reason; }sources ${DELTA}d newer than doc (drift)"
  fi
  if [ -n "$reason" ]; then
    echo "STALE: $doc ($reason)"
    STALE_DOCS+=("$doc")
  fi
done <<< "$DOC_CANDIDATES"

2.5b — Symbol drift (deterministic pre-pass)

The field's #1 cheap drift signal: a 2024 study found 28.9% of repos document a symbol that no longer exists (wrong for 4.7 years on average). Before any LLM call, extract the identifiers each doc references and confirm they still exist in the codebase.

# Search only CODE for symbols. Exclude *.md so a symbol surviving only in prose
# (not code) still reads as missing — otherwise the doc's own backtick matches
# itself and hides the drift. Use a function (not a flags-in-a-variable) so it is
# shell-agnostic: unquoted `$VAR` word-splits in bash but NOT in zsh, which would
# pass the whole flag string as one bogus argument and silently skip the exclude.
code_grep() {
  grep -rqwI --exclude='*.md' \
    --exclude-dir=.git --exclude-dir=node_modules --exclude-dir=vendor \
    --exclude-dir=dist --exclude-dir=build --exclude-dir=.venv "$@"
}
SYMBOL_DRIFT_DOCS=()
while IFS= read -r doc; do                                                          # while-read, not `for in $var` (zsh-safe)
  [ -z "$doc" ] && continue
  [ -f "$doc" ] || continue
  is_excluded "$doc" && continue
  MISSING=()
  TOKENS=$(grep -oE '`[^`]+`' "$doc" 2>/dev/null | tr -d '`' | sort -u)
  while IFS= read -r tok; do
    [ -z "$tok" ] && continue
    word=$(echo "$tok" | awk '{print $1}' | tr -d '()[]{},;:')   # first word, strip call punctuation
    [ -z "$word" ] && continue
    case "$word" in
      */*|*.py|*.js|*.ts|*.tsx|*.go|*.rs|*.java|*.rb|*.json|*.yaml|*.yml|*.toml)   # file path
        [ -e "$word" ] || git ls-files --error-unmatch "$word" >/dev/null 2>&1 || MISSING+=("path:$word");;
      --*)                                                                          # CLI flag
        code_grep -- "$word" . 2>/dev/null || MISSING+=("flag:$word");;
      *.*)                                                                          # dotted Cls.method — present only if EVERY component resolves
        ok=1
        for part in $(echo "$word" | tr '.' ' '); do
          code_grep -- "$part" . 2>/dev/null || { ok=0; break; }
        done
        [ "$ok" = 0 ] && MISSING+=("symbol:$word");;
      *[a-zA-Z]*)                                                                   # bare identifier / env var
        code_grep -- "$word" . 2>/dev/null || MISSING+=("symbol:$word");;
    esac
  done <<< "$TOKENS"
  if [ "${#MISSING[@]}" -gt 0 ]; then
    echo "SYMBOL-DRIFT: $doc -> ${MISSING[*]}"
    SYMBOL_DRIFT_DOCS+=("$doc")
  fi
done <<< "$DOC_CANDIDATES"

The regex-on-backticks pass above is the starter. For polyglot repos where backtick-grep is too noisy, the upgrade path is tree-sitter / LSP (SCIP) symbol indexing for precise function/class/signature resolution — swap it in there.

Use the SYMBOL-DRIFT output two ways:

  1. Queue expansion — any doc with one or more missing symbols joins the audit queue (Step 4) even if it is fresh and untouched by the branch.
  2. Concrete Pass-2 worklist — pass that doc's missing-symbol list verbatim into its agent prompt as a ## Symbol worklist section. This turns Pass 2 from "notice every claim unprompted" into "clear this concrete list."

False-positive guards (mandatory):

  • A dotted Cls.method counts as PRESENT if every component resolves — only flag when a component is genuinely gone.
  • Backticked English words, shell builtins, and literals (true, null, cd, ls) are not symbols. The agent must not "fix" them.
  • The grep is a worklist, not a verdict — the agent confirms each before editing or removing.
  • Excluded docs (changelog/historical/migration, freshness: exclude) are skipped here too.

Assemble the audit queue

The audit queue = STALE_DOCS ∪ SYMBOL_DRIFT_DOCS. These docs go through the full audit pipeline in Step 4 even if the current branch doesn't touch their related code.

  • Queue empty AND no branch changes → say "No branch changes, no stale docs, no symbol drift. Nothing to sync." Stop.
  • Queue empty but branch changes exist → proceed to Step 3 with branch-driven mapping only.
  • Otherwise the queued docs merge into Step 4 dispatch: stale docs get the fresh-audit flag (no diff context — verify the whole doc against current code); symbol-drift docs carry their missing-symbol worklist into the prompt; a doc in both modes gets both.

Step 3: Map Changes to Docs

If ## Change-to-Doc Map exists in Repo Config: Use the table to determine which doc files are affected. Validate each target path still exists before including it — skip missing ones silently.

If no map exists (runtime discovery): Use this default mapping:

Change CategoryLikely Affected Docs
Pipeline/ArchitectureREADME.md (architecture section), CLAUDE.md / AGENTS.md
ConfigurationREADME.md (env vars / config section)
Commands/CLIREADME.md (usage section)
DependenciesREADME.md (install / requirements section)
UI/FrontendREADME.md (features section)
Models/Schemas_wiki/ pages if they exist, CLAUDE.md / AGENTS.md
TestsREADME.md (testing section)

Filter to only doc files that actually exist. Merge with the audit queue (STALE_DOCS ∪ SYMBOL_DRIFT_DOCS) from Step 2.5.

If the combined set (branch-affected ∪ audit queue) is empty, say: "Changes don't affect any docs, and no stale or symbol-drifted docs found." Stop.

Step 4: Spawn Audit Agents (Multi-Pass Verification)

Spawn one agent per doc file in the combined set. Agents run in parallel - send all Agent tool calls in a single message. Tiered dispatch: doc auditing/writing from vetted material is volume work - on a Fable-class session (any session model above Opus), spawn every doc agent with explicit model: "opus" and keep the one-agent-per-doc shape (the shape follows the model the agents run on; Opus agents doing one focused doc each beats fewer consolidated ones). The parent loop keeps the judgment: reading the agents' reports and deciding what ships. On Opus and below, spawn with the session's model (never below Opus).

Every agent runs a 3-pass verification protocol (described inside the agent prompt). This is non-negotiable. Single-pass updates produce confident-sounding-but-wrong docs that mislead future readers.

Dispatch modes:

  • branch-driven — doc is in the branch-change map. Agent gets diff context.
  • fresh-audit — doc is in STALE_DOCS. Agent gets NO diff (might be stale in ways nobody flagged). Must audit the entire file against current code from scratch.
  • both — doc is in both. Agent does fresh-audit AND incorporates branch changes.
  • symbol-drift — doc is in SYMBOL_DRIFT_DOCS (Step 2.5b found backticked references that no longer resolve). Combinable with any mode above. The agent gets the missing-symbol worklist and must verify/fix each.

Symbol worklist. When a doc carries symbol drift, add a ## Symbol worklist section to its prompt listing the exact path:/symbol:/flag: entries from Step 2.5b, with this instruction: "These backticked references could not be located in the codebase. Verify each — a backticked prose word or literal (true, null, cd) is NOT a symbol and must be left alone — then fix or remove the genuinely-dead ones. Pass 2 must clear this list."

Use the appropriate template below. Each template embeds the 3-pass protocol verbatim — do not summarize or shorten it when constructing the agent prompt.

The 3-Pass Verification Protocol (embedded in every agent prompt)

You will run THREE passes over this doc. Do not skip passes. Do not collapse them. Each pass has a different lens — running them separately catches different failure modes.

PASS 1 — UPDATE
1. Read the entire target doc file from line 1 to EOF. Do not skim. Do not jump to "the relevant section" — you do not yet know what is relevant.
2. Read every source file referenced in your Context section, fully. If the doc mentions a file, function, env var, CLI flag, or directory, locate it in the codebase and read it. Do not trust the doc's description over the code.
3. Make edits. Update factual claims that are wrong. Update examples that no longer run. Update file paths that have moved. Update version numbers, env var names, and command syntax that have drifted. Do not rewrite voice, structure, or tone.

PASS 2 — CROSS-CHECK (the "lying detector" pass)
After Pass 1, re-read the doc you just edited. For EVERY factual claim that remains in the doc — including ones you didn't touch — verify it against the current code:
- Every file path mentioned: does the file exist at that path right now? Run ls or Read to confirm.
- Every function/class/method named: does it exist with that signature? Grep to confirm.
- Every env var: is it actually read by the code? Grep for it.
- Every CLI flag, subcommand, or argument: does the current CLI accept it? Read the argparse/click definitions.
- Every code example or snippet: would it actually run today? Trace it mentally against the current code.
- Every cross-reference to another doc or section: does the target still exist?
For each claim that fails verification, fix it. If you cannot verify a claim with reasonable effort, mark it: insert an HTML comment <!-- docs-sync: unable to verify --> next to it and note it in your final report. Do not silently leave unverifiable claims.

PASS 3 — MISDIRECTION AUDIT (the "fresh reader" pass)
Re-read the doc one more time, this time as if you were a developer encountering this codebase for the first time. For each section ask:
- If I followed these instructions literally, would I succeed, or would I hit an error?
- Does this section accurately describe what the code does, or does it describe what it USED to do, or what someone WANTED it to do?
- Are there critical setup steps, dependencies, or gotchas not mentioned that the code clearly requires?
- Is anything dangerously misleading — wrong defaults, wrong order of operations, wrong assumptions about state?
Fix anything that would misdirect a fresh reader. Misdirection is worse than absence — silence is honest, wrong instructions are not.

OUTPUT
At the end, report:
- Sections updated and why
- Verification failures discovered in Pass 2 (and how you fixed them)
- Misdirection issues caught in Pass 3 (and how you fixed them)
- Any claims left marked as unverifiable and why

README Agent — branch-driven or fresh-audit:

You are a README auditor and updater. Your job is to make README.md perfectly accurate against the current codebase. Future readers will rely on this doc — every wrong claim is a trap.

## Mode
<branch-driven | fresh-audit | both>

## Context (branch-driven or both)
Base branch: <base>
Changes summary:
<paste git diff --stat output>

Changed files:
<paste git diff --name-only output>

Commit messages:
<paste git log --oneline output>

## Context (fresh-audit or both)
This doc is past its freshness window (its `ttl_days`, or the 90-day field default). The codebase has likely drifted. Audit the entire README against the current code. Do not assume any claim is correct just because it has been there for a while.

## Symbol worklist (symbol-drift mode only)
<paste this doc's missing-symbol list from Step 2.5b, with the "verify or remove" instruction>

## Style constraints
- Match existing voice, formatting, and structure
- Do not add emojis unless the existing README uses them
- Do not rewrite sections that are still accurate, even if you would phrase them differently
- If a new feature exists with no README coverage, add a minimal section in the appropriate location

## Verification protocol
<paste the entire 3-Pass Verification Protocol above, verbatim>

Wiki Agent — branch-driven or fresh-audit:

You are a wiki page auditor and updater for pages under _wiki/. Your job is to make these pages perfectly accurate against the current codebase.

## Mode
<branch-driven | fresh-audit | both>

## Target pages
<list specific pages — one agent per page if pages are large, otherwise group related pages>

## Context (branch-driven or both)
Base branch: <base>
Changes summary:
<paste git diff --stat output>

## Context (fresh-audit or both)
This page is past its freshness window (its `ttl_days`, or the 90-day field default). Audit the entire page against the current code from scratch.

## Symbol worklist (symbol-drift mode only)
<paste this page's missing-symbol list from Step 2.5b, with the "verify or remove" instruction>

## Style constraints
- Match existing wiki formatting and structure
- If _wiki/_Sidebar.md exists and you added/removed pages, update the sidebar
- Cross-link to related wiki pages when natural; check those links resolve

## Verification protocol
<paste the entire 3-Pass Verification Protocol above, verbatim>

Agent-instruction Agent (CLAUDE.md / AGENTS.md) — branch-driven or fresh-audit:

You are an agent-instruction auditor and updater for <CLAUDE.md | AGENTS.md>. Your job is to make these project instructions factually accurate against the current codebase. These files are read by coding agents (Claude, Cursor, Codex, Copilot) — a wrong instruction misdirects every future agent run.

## Target file
<the CANONICAL path resolved in Step 1 — never an alias/symlink>

## Mode
<branch-driven | fresh-audit | both>

## Context (branch-driven or both)
Base branch: <base>
Changes summary:
<paste git diff --stat output>

## Context (fresh-audit or both)
This file is past its freshness window (its `ttl_days`, or the 90-day field default). Audit the entire file against the current codebase.

## Symbol worklist (symbol-drift mode only)
<paste this file's missing-symbol list from Step 2.5b, with the "verify or remove" instruction>

## Divergence check (only if CLAUDE.md and AGENTS.md both exist as separate files)
The repo carries CLAUDE.md and AGENTS.md as two independent (non-symlinked) files. Compare them: any rule present in one but absent or contradictory in the other is divergence — flag it for human review with an HTML comment; do NOT unilaterally reconcile them.

## Scope constraints
- Update factual descriptions: architecture, testing commands, env vars, file paths, build steps
- Do NOT add new behavioral rules or instructions on your own
- Do NOT remove existing rules unless they unambiguously reference deleted code
- If a rule references a removed file/function, update it to reference the replacement (if there is one) or flag it for human review with an HTML comment

## Verification protocol
<paste the entire 3-Pass Verification Protocol above, verbatim>

Generic Doc Agent — for any other markdown file in the stale audit queue (e.g., docs/architecture.md, CONTRIBUTING.md):

You are a documentation auditor for <doc path>. Your job is to make this doc perfectly accurate against the current codebase.

## Mode
<fresh-audit (past its freshness window) | symbol-drift | both>

## Symbol worklist (symbol-drift mode only)
<paste this doc's missing-symbol list from Step 2.5b, with the "verify or remove" instruction>

## Style constraints
- Match existing voice, formatting, and structure
- Preserve intentional formatting choices (callouts, tables, code blocks)

## Verification protocol
<paste the entire 3-Pass Verification Protocol above, verbatim>

Step 5: Suggest New Docs

After agents complete, check if the changes introduced anything with no existing doc coverage:

  • New env vars with no env var table in README
  • New CLI flags with no usage section
  • New major features with no feature description
  • New dependencies with no install instructions

For each gap found, suggest (but do NOT auto-create):

Suggestion: <description> — consider adding a section to <doc file>

Step 6: Stage and Report

Aggregate every agent's verification report. Show a structured summary:

## docs-sync complete

**Freshness summary** (advisory — does not gate; a trackable signal for a human or future CI wrapper)
| Doc | Status | Missing symbols | Days since edit |
|-----|--------|-----------------|-----------------|
| README.md | drifted→fixed | 0 | 12 |
| docs/architecture.md | drifted→fixed | 0 | 95 |
| CONTRIBUTING.md | needs-human | 2 | 412 |

**Branch-driven updates:**
- README.md: updated <sections>
- _wiki/page.md: updated <sections>

**Stale-doc audits (past freshness window):**
- docs/architecture.md (95d > 90d ttl): <fixes applied>
- CONTRIBUTING.md (412d): <fixes applied>

**Symbol drift (Step 2.5b):**
- <doc>: <missing symbol> → <fixed | removed | confirmed-real false positive>

**Verification findings (Pass 2 cross-check):**
- <claim that was wrong> → <how it was fixed>
- <claim left unverifiable> → flagged with <!-- docs-sync: unable to verify -->

**Misdirection caught (Pass 3 fresh-reader audit):**
- <misleading instruction> → <correction>

**Agent-instruction divergence (CLAUDE.md vs AGENTS.md):**
- <rule present in one but not the other> → flagged for human review

**Symlinks resolved:**
- <alias> → <canonical> (audited the canonical file only)

**Suggestions:**
- <any new doc suggestions from Step 5>

The freshness summary stays advisory — never block on it. If any agent left <!-- docs-sync: unable to verify --> markers, or any CLAUDE.md↔AGENTS.md divergence was flagged, list them explicitly so the human can resolve them before merging.

Stage the changed doc files (use the CANONICAL paths from Step 1, never aliases):

git add README.md CLAUDE.md AGENTS.md CONTRIBUTING.md _wiki/ docs/ 2>/dev/null
git status --short

Say: "Doc changes staged. Review the diffs and commit when ready."

What NOT to Update

  • Internal-only refactors with no user-facing impact
  • Auto-generated docs (check for generation scripts first — e.g., docs/api/ with a Makefile)
  • Changelogs, release notes, historical/migration docs (CHANGELOG.md, HISTORY.md, migrations/, UPGRADING.md) — they legitimately reference removed code and old versions, so scoring them like living docs flags every "replaced X with Y" line as false drift. Step 2.5 already excludes them from the staleness and symbol-drift queues; honor a freshness: { exclude: true } opt-out for any other doc that should be frozen.
  • Scratch/debug scripts
  • Unfinished/WIP features unless explicitly asked
  • Test-only changes (unless they change how to run tests)

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.