Skip to main content

Deterministic drift detection between code and docs — language-agnostic, no LLM in the detection path.

Project description

staledocs

Deterministic drift detection between code and docs. Language-agnostic, no LLM in the detection path.

Documentation language: English (README.ja.md carries the Japanese version).

The problem

Docs rot silently. You change the code, the doc keeps describing the old behaviour, and nobody notices until the doc misleads a teammate — or an AI agent, which then confidently implements against a stale spec. The fear of that rot is also why people stop writing docs at all: every page is a maintenance debt you have to remember.

staledocs removes the remembering. It pairs every doc with the code it describes, records a fingerprint when you confirm they match, and from then on any one-sided change is flagged mechanically — in either direction.

How it works

Four detection layers; the first three are deterministic, no AI anywhere:

  1. Pair ledger (L1) — each doc/code pair stores the git blob hashes from the last time a human (or agent) confirmed coherence (an ack). Code moved but the doc did not → DOC_STALE. Doc moved but the code did not → CODE_LAG (unimplemented spec). Both moved in commits that travelled together → AMBER (provisionally coherent). Both moved separately → BROKEN.
  2. Anchor liveness (L2) — docs naturally quote identifiers, CLI flags, and paths in backticks. staledocs extracts those anchors and verifies each one still exists on the paired code side, reporting the exact doc line that rotted. The doc is parsed; the code is only grepped — that is what keeps the tool language-agnostic.
  3. Coverage gates — every source file must belong to at least one doc, and every doc must be classified (paired, standalone, or global). New files with no owner show up red immediately. Silence is never coverage.
  4. Semantic reconciliation (L3, external by design) — judging whether prose still matches behaviour is not a deterministic problem. staledocs emits a machine-readable report (check --json) and leaves the fixing to you or your AI agent, which then closes the loop with an ack.

Quick start

pip install staledocs

cd your-repo
staledocs init            # scaffold .staledocs.yaml + ledger dir
$EDITOR .staledocs.yaml   # declare the pairing (see below)
staledocs check           # see what's unowned / unacked
staledocs ack --all       # baseline: "everything is coherent as of now"

From then on:

staledocs check             # after any change: what broke?
staledocs ack docs/auth.md  # confirmed coherent again

Pairing model

CODEOWNERS-style globs, one YAML file:

version: 1
gate: warn                 # warn (report) | strict (non-zero exit on red)

source:
  include: ["src/**"]
docs:
  include: ["docs/**/*.md", "README.md"]

pairs:
  - doc: docs/auth.md
    code: ["src/auth/**"]           # a folder
  - doc: docs/token.md
    code: ["src/auth/token_*.py"]   # or a slice of one

mirror:                    # optional convention: docs/<x>.md <-> src/<x>/**
  enabled: true
  docs_root: docs
  code_roots: [src]

standalone:                # docs that intentionally have no code side
  - "docs/ops/**"
global:                    # whole-repo docs: anchors only, no pair ledger
  - README.md

Explicit pairs win over the mirror convention. N:M is natural — one file may be owned by several docs, one doc may own several globs.

Acks

An ack is the recorded statement "this pair is coherent right now". Three ways to give one:

  • staledocs ack docs/auth.md — explicit, after you reconciled
  • staledocs ack --broken / --all — bulk (refactor days, onboarding)
  • a Staledocs-Ack: docs/auth.md (or Staledocs-Ack: all) commit-message trailer — ack in the same breath as the change

Editing code and doc in the same commit is treated as AMBER automatically: honest "probably coherent, unconfirmed", never a silent green.

The ledger lives in .staledocs/pairs/ as one JSON file per pair and is meant to be committed. A merge conflict in a ledger entry fails safe: the entry stops parsing, the pair reverts to unacked, and gets re-checked.

CI and hooks

# GitHub Actions
- run: pip install staledocs
- run: staledocs check --gate strict
# pre-commit hook
staledocs check --gate strict || exit 1

Start with gate: warn while onboarding a brownfield repo, flip to strict once check is quiet.

AI-agent integration

staledocs check --json is the agent API: every broken pair with its moved files, every dead anchor with its doc line, every coverage hole. A coding agent can consume it, decide per finding whether the doc or the code is wrong, fix that side, and staledocs ack the pair. staledocs supplies the trustworthy signal; the agent supplies the judgement. See docs/agent-integration.md.

Design principles (also the non-goals)

  • Detection is deterministic. Git blob hashes, commit topology, and anchor grepping. If staledocs says a pair broke, it broke.
  • No doc generation. The tool never adds to your documentation burden; it guards what you chose to write.
  • No code parsing. No per-language AST, no parser tiers, no silent degradation on language N+1. Works the same for Python, TypeScript, Rust, shell, or anything else.
  • No LLM in the detection path. Semantic judgement is the ack's job — yours, or your agent's.

Prior art: the coherence-driven idea owes a nod to CoDD, which attacks the same problem from the generative side. staledocs deliberately takes the opposite bet: detect deterministically, generate nothing.

License

Apache-2.0 (LICENSE). Dependency notices in THIRD_PARTY_NOTICES.md; vulnerability reporting in SECURITY.md.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

staledocs-0.1.3.tar.gz (32.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

staledocs-0.1.3-py3-none-any.whl (28.5 kB view details)

Uploaded Python 3

File details

Details for the file staledocs-0.1.3.tar.gz.

File metadata

  • Download URL: staledocs-0.1.3.tar.gz
  • Upload date:
  • Size: 32.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for staledocs-0.1.3.tar.gz
Algorithm Hash digest
SHA256 78179d2f1c9a64293dcbca8cf0dfe14bde9ec6cb48fd9e7841b8207084d668b9
MD5 34af736322e388f831d2ac3b7c3a6e79
BLAKE2b-256 90a2ffc643432caeefcf62fc42e99ebab30374f00ad39b2c90aeaa6ce2e4d848

See more details on using hashes here.

Provenance

The following attestation bundles were made for staledocs-0.1.3.tar.gz:

Publisher: publish.yml on Synforger/staledocs

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file staledocs-0.1.3-py3-none-any.whl.

File metadata

  • Download URL: staledocs-0.1.3-py3-none-any.whl
  • Upload date:
  • Size: 28.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for staledocs-0.1.3-py3-none-any.whl
Algorithm Hash digest
SHA256 a65453207ff3435f0d2fab9c352f00df3a082b49a7999a156028e9665563c2af
MD5 952a8e53c699e1541752acf1fce24469
BLAKE2b-256 39a5467cc8dd1253ff6777d1d9ba8aff2ef9aaad9cc741e67eb2cc089e7e2455

See more details on using hashes here.

Provenance

The following attestation bundles were made for staledocs-0.1.3-py3-none-any.whl:

Publisher: publish.yml on Synforger/staledocs

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page