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.4.tar.gz (32.4 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.4-py3-none-any.whl (28.5 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: staledocs-0.1.4.tar.gz
  • Upload date:
  • Size: 32.4 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.4.tar.gz
Algorithm Hash digest
SHA256 a788b65597fa40cb2492e87cd98eb070f9b7310295fbb3d98e580316af0fed0c
MD5 d837ad46740ed99ca9d704c7a37b0bd1
BLAKE2b-256 9ac551ed477cb2781fb6bd3aaa88023fe02be4881d9559d51b4bff06518dea32

See more details on using hashes here.

Provenance

The following attestation bundles were made for staledocs-0.1.4.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.4-py3-none-any.whl.

File metadata

  • Download URL: staledocs-0.1.4-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.4-py3-none-any.whl
Algorithm Hash digest
SHA256 9b980f75aad2e41889a73772da2ed95d95951eddd6afd8a12b7b13e60aa462e9
MD5 909d3ce90cc0f243dea99e3bae96339f
BLAKE2b-256 45303c22e9e98e0b6a5c2c40b7e8a6f98de1436ba60ee1f67c38cb3aa1e9b20c

See more details on using hashes here.

Provenance

The following attestation bundles were made for staledocs-0.1.4-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