Skip to content

feat(docs): aether docs, a Documentation critic, and the sync-docs skill - #10

Merged
ValentinFigue merged 1 commit into
mainfrom
feat/aether-docs
Jul 31, 2026
Merged

feat(docs): aether docs, a Documentation critic, and the sync-docs skill#10
ValentinFigue merged 1 commit into
mainfrom
feat/aether-docs

Conversation

@ValentinFigue

Copy link
Copy Markdown
Owner

Every defect in the 1.5.0 documentation audit (#8) was found by hand. Three layers so that does not have to happen again — each catching a class the others cannot, at a different moment, and all three installed globally so they work in any repository rather than only this one.

Layer Moment Catches
sync-docs skill while editing the change that should have touched a doc
Documentation critic, on by default at review sentences this diff makes false
aether docs any time, and inside aether check claims checkable against the schema, the config and the filesystem

aether docs

A new engine verb beside doctor and check, so the schema and the subcommand list travel with the install rather than coming from the working directory.

$ aether docs
README.md
  ✗ README.md:230  [temper] critical_paths is pipe-separated; this example is
                   space-separated and matches nothing
      fix: join the patterns with |
  ! README.md:88   [project] test runs this as `uv run --frozen pytest`
      the doc omits the wrapper, so following it skips what the wrapper does

1 problem(s), 1 warning(s) across 7 file(s).

Four families, in order of what they are worth:

  • Prose contradicting [project]. Your config already declares how the repo really runs. A README saying pytest where the config says uv run --frozen pytest is drift, and it is what a new joiner follows on day one. Resolved per area, so a monorepo command right for web/ and wrong for backend/ is caught. This is the family only aether can check — no generic markdown linter knows your real commands.
  • Commands that do not existnpm run X with no such script, make Y with no such target, bash scripts/z.sh that moved.
  • Dead references — relative links, and ](#anchor) against the headings actually in the file.
  • aether's own claims — config keys and values against the manifest schema, subcommands and flags against the engine, retired paths outside a migration note.

Wiring it into every review is one line, in any repo:

[project]
check.docs: aether docs

aether check runs any check.<name> key and both critique commands call aether check. This is also the first time this repo has used the escape hatch its own README advertises.

[project] commands are ignored until aether trust, so without it the first family cannot run — and aether docs says so rather than quietly checking less than the config implies.

Verified against the audit itself

That was the plan's stated test, and the commits are right there:

pre-audit tree (93d1ef4^)   12 problem(s), 8 warning(s)   ← incl. both broken critical_paths
current main                nothing to report

And against a real monorepo, ~/Code/pimento: it passes. I confirmed that is a real pass rather than silence by injecting a wrong command and watching [project:backend] test runs this as uv run --frozen pytest fire, then restoring the file.

Not crying wolf was the hard part

The plan named this as the risk: a checker that reports plausible-but-wrong findings gets deleted from the config within a week. Building it surfaced five of its own false positives before it shipped, each now a negative assertion:

Reported Actually
every #monorepos--projectpath anchor my slug collapsed whitespace runs; GitHub does not
aether version is not a subcommand my regex was [a-z|-]+, so version|--version|-V) never matched
aether v1.5.0 is not a subcommand it is output under $ aether status, not a command
bash scripts/setup.sh does not exist the block does cd aether/plugins/bonsai first
whetstone.config.md is a retired path it was inside the "Upgrading from 1.0.0" section

Nothing is inferred: every check compares a documented string against a declared or on-disk fact. Judgement belongs to the critic.

Documentation critic, on by default

[temper] critics becomes correctness, design, risk, coverage, docs. It asks what no script can: this diff changed a behaviour — is every sentence describing that behaviour still true? Finding 18 of the audit was exactly that shape (the docs said temper blocks a push; nothing exits 2), and no schema check would have found it.

Two deliberate choices: it is scoped to behaviours this diff changes, not the documentation as a whole, and it is told to say nothing when there is nothing — a critic that manufactures a finding every review is one people learn to skip. --skip=docs opts out; --only=docs runs it alone. Anyone who has set their own critics: keeps exactly what they set, since resolution is per key.

sync-docs skill

Installed by temper to ~/.claude/skills/, so it fires in every repo. It covers the cheapest moment — while you are editing — and says to grep for the string you are changing rather than the file you expect, then fix the prose in the same commit. The description does the work of saying it is a rule to follow, not a generator: it never writes documentation.

Also fixed

  • bonsai enable-hook and disable-hook are documented but are not subcommands. The new check found what the manual audit in docs: correct six READMEs against the code they describe #8 missed.
  • acceptance.sh's spaced-path test was latently broken by v1.5.0. It asserts cairn's nudge, but the budget prints only the earliest lifecycle stage, so it failed whenever the developer's checkout had an uncritiqued plan lying about. Not introduced here, but exposed here; it now runs from a scratch directory.

One design constraint worth knowing

The planned [docs] enabled key is gone. The manifest format identifies a key by name alone, so a second config.enabled.* collides with temper's own and wins at random — visible immediately as a duplicate row in aether config show docs. An empty paths is how you turn the check off: one concept rather than two that can disagree.

Verification

  • 32 new assertions in tests/test_docs.sh; 758 across 11 files
  • acceptance.sh --full: 46/46, hook cost unchanged at 57ms
  • Every family has a negative case — a correct document must be silent — plus the two properties that make "any repo" mean something: it runs in a fixture that is not this repo, and it catches a two-area monorepo command that is right for one area and wrong for the other

Every defect in the 1.5.0 documentation audit was found by hand. Three layers
so that does not have to happen again, each catching a class the others cannot,
and all three installed globally so they work in any repo rather than this one.

**aether docs** — the mechanical half. Four families, in order of what they are
worth:

  - Prose contradicting [project]. The config already declares how the repo
    really runs, so a README saying `pytest` where the config says `uv run
    --frozen pytest` is drift, and it is what a new joiner follows on day one.
    Resolved per area, so a monorepo command right for web/ and wrong for
    backend/ is caught. No generic linter knows your real commands; this is the
    family only aether can check.
  - Commands that do not exist: npm run X with no such script, make Y with no
    such target, bash scripts/z.sh that moved.
  - Dead references: relative links, and anchors against real headings.
  - aether's own claims: config keys and values against the manifest schema,
    subcommands and flags against the engine, retired paths outside a migration
    note.

Deliberately narrow — nothing is inferred. A checker that reports
plausible-but-wrong findings gets switched off within a week, so every check
compares a documented string against a declared or on-disk fact. Building it
surfaced five of its own false positives before it shipped: a slug function
that collapsed whitespace runs where GitHub does not, a subcommand regex that
dropped `version|--version|-V`, transcripts read as commands, blocks that `cd`
first, and migration notes read as drift. Each is now a negative assertion.

**A Documentation critic**, on by default, for the half a script cannot reach:
this diff changed a behaviour, so is every sentence describing it still true?
Finding 18 of the audit was exactly that shape — the docs said temper blocks a
push while nothing exits 2 — and no schema check would have found it.

**A sync-docs skill**, so the cheapest moment is covered too: grep for the
string you are changing and fix the prose in the same commit.

Verified against the audit itself. On the pre-audit tree it reports 12 problems
and 8 warnings including both broken critical_paths examples; on current main
it is clean. Run against ~/Code/pimento it passes, and injecting a wrong
command there makes the [project] family fire, so the pass is real rather than
silent.

Also fixed, found by the new check: bonsai's README documented `bonsai
enable-hook` and `disable-hook`, which are not subcommands.

Also fixed, not mine but exposed here: acceptance's spaced-path test asserted
cairn's nudge while the 1.5.0 budget prints only the earliest stage, so it
failed whenever the developer's checkout had an uncritiqued plan. It now runs
from a scratch directory.

32 new assertions in tests/test_docs.sh; 758 across the suite; acceptance 46/46.
@ValentinFigue
ValentinFigue merged commit f9edfae into main Jul 31, 2026
36 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant