Skip to content

chore(docs): add lint:docs and fix the violations it found - #79

Merged
myselfsiddharth merged 2 commits into
mainfrom
wave0/b0-docs-lint
Jul 28, 2026
Merged

chore(docs): add lint:docs and fix the violations it found#79
myselfsiddharth merged 2 commits into
mainfrom
wave0/b0-docs-lint

Conversation

@myselfsiddharth

Copy link
Copy Markdown
Contributor

Closes #53.

What changed

scripts/lint-docs.mjs, wired as npm run lint:docs — inside npm run ci (after lint) and
as a Lint docs step in the build job. No new dependencies: frontmatter here is a flat
key: value list, so it is hand-parsed.

Rule id Fails when
frontmatter-missing no --- block at the top of the file
frontmatter-missing-key any of title, doc_type, status, owner, created, updated, confidence, supersedes, sources_verified is absent
frontmatter-bad-value value outside its allowed set, or created/updated not YYYY-MM-DD
not-in-index the doc is not linked from docs/README.md
missing-open-questions no ## Open questions / what I could not verify, or another ## follows it
broken-link a relative markdown link inside docs/ resolves to nothing

Errors are path:line rule message — a future agent reads those, not the source. Link
checking skips fenced code blocks so example markdown cannot trip it, and tolerates #anchor
and ?query suffixes.

doc_type: gatespec

docs/gate/testbed.md was the outlier. The allowed set follows CONTRIBUTING
(gate-result), since docs/README.md calls CONTRIBUTING the "doc-shape source of truth".

I did not relabel testbed.md gate-result. It is a spec for the test-bed — version matrix,
what the harness seeds, CLI, Docker limitation — not the report of a measured gate. Calling it
gate-result in a repo whose third hard rule is "never invent a metric", and which states
plainly that no gate number exists, would be a false claim in frontmatter. spec matches its
siblings compiler.md and runner.md.

What the linter found, all fixed here

Shape only — no research finding, number, or verdict changed. The diff is verifiable on that
point.

  1. docs/gate/testbed.mddoc_type: gate, above.
  2. Six docs missing from the index. The five-doc pitch pack was covered only by a pitch/
    directory link, and A2-grafana.md was hidden behind an [A1](…)–[A3](…) range link that
    reads as a range but only links two files. Rows added for all six.
  3. Two research docs had an appendix after Open questions
    vertical-search/adversary-report.md ("Searches run") and vertical-search/DECISION.md
    ("Method notes (integrity)" plus the adjudicator line). Both sections moved above Open
    questions; not a word altered. CONTRIBUTING says every doc ends with Open questions, so
    the rule checks position, not just presence.

Each rule demonstrated to fail

One breakage at a time, every one reverted before committing (git status clean afterwards):

R1  delete `owner:` from docs/ROADMAP.md
    docs/ROADMAP.md:10  frontmatter-missing-key  missing required key `owner`

R2  status: finalized  +  updated: 25-07-2026
    docs/ROADMAP.md:4  frontmatter-bad-value  `status: finalized` is not one of draft | review | accepted | superseded | killed
    docs/ROADMAP.md:7  frontmatter-bad-value  `updated: 25-07-2026` is not a YYYY-MM-DD date

R3  add docs/scratch.md, valid frontmatter, no index row
    docs/scratch.md  not-in-index  not linked from docs/README.md — add a row (link target `scratch.md`)

R4  rename the Open-questions heading in privacy/boundary-spec.md
    docs/privacy/boundary-spec.md:111  missing-open-questions  no `## Open questions / what I could not verify` heading …

R5  point a link at a missing file
    docs/gate/runner.md:28  broken-link  `../decisions/does-not-exist.md` does not exist

The "must be last" half of R4 is demonstrated by the linter's own first run, which is what
caught the two research docs above.

R5 also confirms the rules compose: breaking ./ROADMAP.md in the index produced three
broken-link errors and a not-in-index for docs/ROADMAP.md, which the broken link no
longer covered. Links carrying an #anchor stay clean.

INTEGRITY-AUDIT: what is now enforced, and what is not

E-09/E-10/E-11 rows gained a "Machine-checked?" column rather than being marked done, because
most of them are not:

  • E-09 (evidence-table access_date columns) — no. The linter never inspects table
    columns. Still review-enforced.
  • E-10 (A5–A8 pre-standard shape) — partly. Frontmatter and the trailing Open-questions
    section are enforced and green; body shape beyond that is not.
  • E-11 (root README / CONTRIBUTING / archive have no frontmatter) — no, out of scope by
    design
    . The linter walks docs/ only, per the issue. Note docs/README.md indexes those
    two as status: living, which is not a valid frontmatter status — so widening the glob needs
    a CONTRIBUTING decision first, not just a bigger glob. Recorded as an open question.

docs/DEVELOPMENT.md's open question "whether npm run lint:docs exists yet — issue #53" is
now answered in place, with the same caveats. CONTRIBUTING.md and the DEVELOPMENT command
table list the new command.

Interaction with #78

#78 (still open) also edits five docs under docs/. Test-merged the two branches locally:
they merge clean and lint:docs stays green on the combined tree, so landing them in either
order does not break main.

Tests

npm run lint:docs     # lint-docs: clean (43 docs)
npm run ci            # green, with lint:docs in the chain
npm run test:canary   # 6 pass

🤖 Generated with Claude Code

CONTRIBUTING mandates YAML frontmatter on every doc and an Open-questions
section at the end; docs/README.md says "update this file when you add or
supersede a document". Both were enforced by hope, and INTEGRITY-AUDIT
E-09/E-10/E-11 already logged the drift. For an agent picking the repo up cold,
docs/README.md IS the map, and a silently stale map is worse than no map.

scripts/lint-docs.mjs, wired as `npm run lint:docs` inside `npm run ci` and as a
CI step, exits 1 on:

  frontmatter-missing      no `---` block at the top
  frontmatter-missing-key  a required key is absent
  frontmatter-bad-value    value outside its allowed set, or a non-YYYY-MM-DD date
  not-in-index             the doc is not linked from docs/README.md
  missing-open-questions   no trailing Open-questions section
  broken-link              a relative link that does not resolve

No new dependencies — frontmatter is a flat key: value list and is hand-parsed.
Errors are `path:line  rule  message`, since a future agent reads those and not
this source.

What it found, all fixed here, shape only — no finding, number, or verdict
touched:

- docs/gate/testbed.md carried `doc_type: gate`, which is not in CONTRIBUTING's
  list. Allowed set follows CONTRIBUTING (`gate-result`); the outlier is
  relabelled `spec`, matching its siblings compiler.md and runner.md.
  `gate-result` would have been a false claim — no gate result exists.
- Six docs were absent from the index: the five-doc pitch pack (covered only by
  a `pitch/` directory link) and A2-grafana.md, hidden behind an `A1`-`A3` range
  link. Rows added.
- Two research docs put an appendix after Open questions
  (vertical-search/adversary-report.md "Searches run",
  vertical-search/DECISION.md "Method notes"). Sections reordered above it; not
  a word changed.

Each rule was demonstrated to fail on a broken input and every deliberate
breakage reverted. INTEGRITY-AUDIT E-09/E-10/E-11 now state what the linter does
and does not cover, so nothing there reads as closed when it is not: evidence
table columns are unchecked, and `.md` outside docs/ is out of scope until
CONTRIBUTING decides whether "living" is a status.

Closes #53

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@OM152002 OM152002 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approve.

Confirmed — I broke each rule deliberately and checked it fires: frontmatter-missing, -missing-key, -bad-value (bad enum and bad date), missing-open-questions (absent heading and a section after it), broken-link, not-in-index — all caught, and the tree returns clean (43 docs) afterwards. Wired into both npm run ci and a separate CI step.

The real risk in a "fix the violations" PR is a fix quietly editing a finding. It doesn't: comparing sorted line multisets before/after, adversary-report.md and DECISION.md are pure moves — identical content, only relocated above Open questions. No number or verdict touched.

The doc_type: gate → spec call is right, and refusing to label testbed.md gate-result when no gate number exists is the correct reading of rule 3. Scope is stated honestly too — E-09 "no", E-10 "partly", E-11 "out of scope" rather than marked done.

Two gaps worth a follow-up issue, not blockers

  1. Anchors are not validated. I appended [bad anchor](./ROADMAP.md#this-anchor-does-not-exist) and it still reported clean. By #54's own standard — "a dead link here is worse than a stale sentence" — and with main's README now depending on README-narrative.md#6-where-evidence-stands-now, an anchor typo is a realistic break. Cheap fix: slug the target file's ^#{1,6} headings and compare.
  2. Six markdown files outside docs/ are unlinted, including README.md and CONTRIBUTING.md — the first files a cold reader opens. Acknowledged as deliberate; a link-only pass over them would still be cheap.

@myselfsiddharth
myselfsiddharth enabled auto-merge (squash) July 28, 2026 15:26
@myselfsiddharth
myselfsiddharth disabled auto-merge July 28, 2026 15:27
@myselfsiddharth
myselfsiddharth merged commit 3db285c into main Jul 28, 2026
12 checks passed
@myselfsiddharth
myselfsiddharth deleted the wave0/b0-docs-lint branch July 28, 2026 15:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: ci Touches ci area: tooling Touches tooling documentation Improvements or additions to documentation gate PRD section 9 gate measurement size/L <= 600 changed lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add docs frontmatter + index linter (npm run lint:docs) and fix existing violations

2 participants