Skip to content
seyonvPublic

About

Linters that keep a knowledge base true, and an agent that weeds it. Ten deterministic rules that read your documentation against your repository — plus a scheduled agent that fixes what they find and opens one pull request.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

docgarden

Your documentation is lying to you, and nothing in your build is checking.

Tests prove the code does what the tests say. Nothing proves the README does. So the paths drift, the flags get renamed, the spec describes a design that shipped differently, and the first thing a new person learns about the repo is that its documentation cannot be trusted.

docgarden is ten linters that read your documentation against your repository — and a scheduled agent that fixes what they find and opens one pull request a week.

The docgarden report: eleven documents read, nineteen findings, a coloured bar splitting three errors from sixteen warnings, and cards naming each rule with real examples — unreachable documents, references to code that has moved, commands that no longer work

It runs entirely on your machine. The linters make no network call and use no model, which is what lets them block a build.


Install

One Python file tree with no dependencies — Python 3.8 or newer and nothing else.

As a Claude Code plugin (gives Claude the skill, so you can just ask):

/plugin marketplace add seyonv/docgarden
/plugin install docgarden

Then type /docgarden in any repository. It reads the repo, proposes a config, runs the first pass, hands you the report, and installs the jobs you pick.

As a skill only:

git clone https://github.com/seyonv/docgarden
cp -r docgarden/plugins/docgarden/skills/docgarden ~/.claude/skills/

As a plain command line tool, no Claude Code required:

git clone https://github.com/seyonv/docgarden
docgarden/bin/docgarden demo

bin/docgarden works from wherever the clone lives. To type docgarden from anywhere, symlink it onto your PATH:

ln -s "$PWD/docgarden/bin/docgarden" /usr/local/bin/docgarden

Try it on a repository that isn't yours

docgarden demo

A small billing service with defects planted on purpose — a link to a spec that was deleted, a README table pointing at two files that were renamed, a runbook running an npm script nobody defined, a status page claiming a different test count from the README. Every picture on this page is made from it, so nothing of anyone's real work is here.

docgarden check in a terminal: a summary line reading eleven documents scanned, nineteen findings, then each rule as a section with its findings and hints


What it actually checks

Two families. Hygiene asks whether the knowledge base is well-formed. Reality asks whether it is still true.

docgarden explain listing ten rules in two families: hygiene covering links, structure, frontmatter, index, crosslinks and orphans; reality covering coderefs, commands, agreement and staleness

Hygiene

Rule What it catches
links Relative links to files that are gone — and #anchors naming a heading that no longer exists, which is the half nothing else checks. A dead link fails loudly; a dead anchor silently lands the reader at the top of the page and lets them conclude the document does not say what it promised.
orphans Documents no path leads to from a declared root. Not wrong on its own — but the strongest predictor of staleness there is, because nobody re-reads what they cannot reach.
index Both directions: a doc missing from its collection's index, and an index row pointing at a doc that no longer exists.
crosslinks Declared edges. "Every plan links the spec it implements." These are the joints of a knowledge base and the first thing to decay.
structure Filename conventions and required sections, per collection.
frontmatter Required keys, where a collection asks for them. Off by default.

Reality

Rule What it catches
coderefs Every path written in backticks resolves to a file that is really there — and, when it can, tells you where it went. "There is a session.py at src/auth/session.py — moved?", or "Nothing named invoices.py is anywhere in the repo — deleted?"
commands The copy-pasteable blocks are still commands: the script exists, the --flags appear in that script's source, the npm run target and make target are really defined. The most damaging kind of stale doc, because a fence looks executable so people execute it.
agreement A fact written in two places, written differently. Test counts, versions, ports. Finds them across hard-wrapped prose, where 1271 ends one line and tests starts the next.
staleness Ranked by how far the watched code has moved since the doc was last touched. Never a verdict — every finding is a suspect, suspects never fail a build, and they exist to tell the gardener where to read.

docgarden explain commands, showing the rule title, what it checks, and the reasoning behind it


The first run will not turn your build red

This is the part most doc linters get wrong. A repository with a hundred documents has a hundred findings on day one, CI goes red, and the tool is uninstalled by the afternoon.

docgarden check --update-baseline

.docgarden/baseline.json records today's backlog by fingerprint — not by line number, so an unrelated edit above a finding does not resurrect it. CI then fails only on findings that are not in it. Your build is green today, new breakage is caught tomorrow, and the backlog is what the gardener works through.

It is a debt ledger, not a suppression list. It should get smaller.


A rule that matches everything tells you nothing

Point docgarden at a repo where the docs were never cross-linked and orphans will fire on 128 of 130 files. That is not 128 problems. It is one: the convention is not one this repo follows, or the config is pointing at the wrong thing.

So above a threshold, a rule collapses into a single finding that says so:

muted: orphans matched 128 of the 130 documents it looks at (98%)

A rule that matches almost everything is describing your config, not your docs. Either fix the setting it depends on, turn it off with [rules.orphans] enabled = false, or raise its tolerance with [rules.orphans] saturation = 0.9 to see all 128 findings.

Measured against the documents each rule actually looks at, not against the whole repo — 27 findings across all 27 plans is saturated even though it is 20% of the tree.


Configure it once

docgarden init reads the repo and writes a starting .docgarden.toml. The interesting part is collections — a family of docs that share rules, which is where docgarden stops being generic:

[docgarden]
roots  = ["README.md", "docs/STATUS.md"]     # a doc reachable from none of these is an orphan
docs   = ["*.md", "docs/**/*.md"]
source = ["src/**/*.ts", "scripts/**/*.py"]

[[collections]]
name     = "spec"
match    = "docs/specs/*.md"
filename = "^\\d{4}-\\d{2}-\\d{2}-.+\\.md$"
require_sections = ["Decision"]
index    = "docs/specs/index.md"    # every spec must be listed there
watches  = ["src/**/*.ts"]          # this spec goes suspect when that code moves

[[crosslinks]]
name = "plan cites its spec"
from = "docs/plans/*.md"
to   = "docs/specs/*.md"

[[agreements]]
name       = "the test count"
pattern    = '(\d{3,5}) tests'
files      = ["README.md", "docs/STATUS.md"]
first_only = true                   # read each file's headline, not its changelog

Severities are per-rule: error fails the build, warn reports, suspect is gardener-only fuel.


The gardener

docgarden contains no agent. It contains the two things an agent is bad at supplying itself — a precise, bounded worklist and a safe way to land the result — and points whatever Claude you have at the gap.

docgarden garden --max 8

prints a brief: the defects with their evidence and leads, the documents to re-read, and the rules of engagement. Fix the documentation, never the code — the repo is the authority. Open both files before editing; a finding is a hypothesis. Say what you left alone, and why.

Then:

docgarden garden --open-pr

Three guardrails, in order of how much damage they prevent:

  1. Only files matching the doc globs are ever staged. A pass that wandered into source gets its source edits left in the working tree and the run reported as refused.
  2. One branch, reused. A gardener that opens a new pull request every week is a gardener somebody mutes in three weeks. It updates its open PR instead.
  3. It never edits its own config or baseline. Landing refuses those outright.

Where it runs

Two schedules ship alongside it, and they are genuinely different trades:

docgarden-gardener.yml gardener.cron
Runs on GitHub Actions, weekly your machine, weekly
Needs an ANTHROPIC_API_KEY secret the Claude Code you are already logged into
Costs per run nothing
Works when your laptop is shut yes no

Or run neither, and use /docgarden in a conversation when you feel like it — which is free, and the only version where you watch it work.


In CI

plugins/docgarden/skills/docgarden/templates/docgarden.yml is one scan with three outputs: annotations inline on the diff, a summary on the run page, and the full report as a downloadable artifact.

- name: Check the knowledge base
  run: |
    docgarden check \
      --format github \
      --summary "$GITHUB_STEP_SUMMARY" \
      --html docgarden-report.html

There is a pre-commit hook in the same directory too, running only the three rules that need nothing but the file itself, so it costs a second rather than a scan.


Every table sorts

The full docgarden report in dark mode: stat cards, the severity split, rule cards, and the sortable findings table with filter chips for each rule

The report is one self-contained HTML file. No fonts, no scripts, no images fetched from anywhere — the findings are embedded as JSON and the page renders itself from them, which is what lets the table sort on any column and filter by rule without a round trip. It opens from a file:// URL, survives being emailed, and looks the same in a CI artifact as on your laptop.

It follows your system theme.


Commands

docgarden init                              # read the repo, write a config
docgarden init --print                      # just show me what you inferred
docgarden check                             # run the linters
docgarden check --html report.html --open   # …and open the report
docgarden check --rule links --rule commands
docgarden check --strict                    # warnings fail too
docgarden check --update-baseline           # record today's backlog
docgarden garden --max 8                    # a worklist for an agent
docgarden garden --open-pr                  # commit docs, open or update the PR
docgarden explain                           # the ten rules
docgarden explain coderefs                  # one rule, and why it exists
docgarden demo                              # the bundled example repo

Exit codes: 0 clean, 1 new findings at error, 2 the tool itself failed. A crashed linter must never look like a clean repo.


What it cannot do

  • It finds shapes, not meaning. No deterministic rule can tell that a paragraph describes behaviour the code no longer has. staleness is the closest thing and it only ranks where to look. Reading the prose is the gardener's job, and the gardener is a language model, with everything that implies.
  • coderefs and commands trade recall for precision on purpose. Bare filenames and extensionless paths are skipped by default. Turning them on produced several thousand false positives on the first real repository this ran against — a git branch called overnight/today looks exactly like a directory. [rules.coderefs] bare_names = true if you want them.
  • staleness needs real git history. A shallow clone has none, and the rule finds nothing rather than pretending. Use fetch-depth: 0.

Tests

python3 -m pytest tests/ -q

92 of them. Each rule test builds a throwaway repository with exactly one planted defect and asserts the rule finds that one and nothing else — on disk, with a real walk, because three of the first four bugs in this tool were in the walk and the glob and none would have been visible to a test that handed the rules a pre-made file list.

docgarden also runs against its own documentation, in its own CI.


Licence

MIT.

About

Linters that keep a knowledge base true, and an agent that weeds it. Ten deterministic rules that read your documentation against your repository — plus a scheduled agent that fixes what they find and opens one pull request.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages