Skip to content

Add cargo xtask docs-pdf and cargo xtask docs-drift - #306

Merged
stephane-segning merged 3 commits into
mainfrom
docs/pdf-and-drift
Aug 15, 2026
Merged

Add cargo xtask docs-pdf and cargo xtask docs-drift#306
stephane-segning merged 3 commits into
mainfrom
docs/pdf-and-drift

Conversation

@stephane-segning

Copy link
Copy Markdown
Contributor

Replaces #303 and #304. Both were casualties of a stacked-PR accident, explained below.

What happened to #303/#304

The original series was stacked: #303 based on docs/1-include-str, #304 on docs/2-pdf-pipeline. When #302 merged, its branch was deleted and GitHub auto-closed #303. #304 then merged into docs/2-pdf-pipeline, not main — so despite showing as merged, the drift guard never reached main.

Neither could be reopened: GitHub refuses to reopen a PR whose base branch is gone, and refuses to change the base of a closed PR. Hence this replacement, cherry-picked onto current main and verified byte-identical to the content that was reviewed.

Both commits target main directly. No stacking this time.

Summary

Two additions, kept as separate commits:

cargo xtask docs-pdf — merges 22 documents into one 196-page A4 PDF with title page and TOC. pandoc + Typst run inside a single digest-pinned container, so it needs docker and nothing else. Zero new Rust dependencies.

cargo xtask docs-drift — every documentation path this repo names must resolve. Wired into just all-checks.

Intent

AGENTS.md records "documentation asserts something the code does not do" as this repository's most-repeated defect — at least eight times, including msisdnHash documented as HMAC while shipping bare SHA-256, and runbooks pointing operators at a seed-provider command that never seeded a Route. workflow_paths closed the build-file half; this closes the docs half.

Rule 2 deliberately reaches beyond docs: deploy/prometheus/alerts.yml names runbook sections an on-call engineer clicks mid-incident, and backup-tool/src/restore.rs embeds one in a warning printed during recovery.

Verification

  • PDF built independently: 196 pages, A4, 4.27 MB, reproducible across runs, content grep-verified against docs/architecture.md and CONTRIBUTING.md
  • just lint, cargo test -p xtask (38 passed), all five xtask guards — pass
  • Guard-failure proof, three ways, each broken then restored:
README.md:85: docs path -> docs/runbooks/deployment.md (no such file)
deploy/prometheus/alerts.yml:168: docs path -> docs/runbooks/alerting.md (no such file)
docs/runbooks/local-development.adoc:94: link -> ../integrating.md (no such file)

Deliberately not checked (to avoid the false positives that get a guard deleted): bare non-docs/ path mentions, since AGENTS.md is a historical record discussing renamed paths in the past tense; and links inside the include_str! sidecars, which are rustdoc intra-doc references — the first version reported all seven as broken.

Risk Assessment

Additive. A new gate can block unrelated PRs on pre-existing debt; it does not here, the tree is clean. The opposite risk — a guard tuned so loosely it never fires — is what the three failure proofs address.

AI Usage Declaration

  • A human directed this change and is accountable for it.
  • Verified by building the artifact and breaking the guard, not assumed.

Reviewer Focus

The exclusions in docs_drift.rs's module doc. Skipping non-docs/ mentions is the judgement call — it keeps the guard quiet enough to survive, at the cost of missing a stale backends/… path in prose.


Checklist

  • docs/roadmap.md checked — no edit needed; tooling, not a milestone.
  • No R1 exceptions touched.

🤖 Generated with Claude Code

There was no way to read this project's documentation as a single
document. `docs/architecture.md` alone is 2,884 lines, and the runbooks,
CONTRIBUTING.md and OPEN_QUESTIONS.md all cross-reference it.

`cargo xtask docs-pdf` (or `just docs-pdf`) merges 22 documents into one
196-page A4 PDF with a title page and table of contents. pandoc converts
each source to a Typst fragment; Typst compiles the assembled book.

Both tools run inside a single pinned container (pandoc/typst, pinned by
digest), so this needs `docker` and nothing else -- no host installs,
matching how the rest of this repo's tooling works. Zero new Rust
dependencies.

Handles `.md` and `.adoc` sources uniformly, since the runbooks are
mid-migration to asciidoc.

Two problems only a full merge run surfaces, both fixed:

  - AsciiDoc's leading `= Title` is metadata pandoc strips from the body
    (Markdown's `# Title` is not), so those chapters lost their headings.
    Each doc's title is now extracted and re-injected.

  - Cross-document links compile to same-document Typst label references
    that do not exist once merged, which Typst treats as a hard error. A
    resolver rewrites them into real intra-book jumps and degrades the two
    genuinely external targets to plain text.

AGENTS.md/CLAUDE.md are excluded by construction: institutional memory,
not part of the book.

Rust API docs via `cargo doc --output-format json` are deliberately not
wired in -- that flag is nightly-only and emits rustdoc's internal IR
rather than prose. The module doc names where it would plug in.
AGENTS.md records "documentation asserts something the code does not do"
as this repository's most-repeated defect -- at least eight times,
including msisdnHash documented as HMAC while shipping bare SHA-256, and
runbooks sending an operator to a `seed-provider` command that never
seeded a Route. Every one was found by a human noticing, or by production.

`workflow_paths` closed the build-file half of this after release.yml
shipped five broken merges. This closes the docs half: the part of a
documentation claim a machine can check is whether what it points at is
still there.

Two rules, both chosen because failure is unambiguous:

  1. Hyperlinks in .md/.adoc resolve (markdown, plus asciidoc xref:/link:).
     Someone wrote a link meaning it to be followed.

  2. Bare `docs/...` mentions resolve anywhere in the repo -- not only in
     docs. deploy/prometheus/alerts.yml points at runbook sections an
     on-call engineer clicks mid-incident, and backup-tool/src/restore.rs
     embeds one in a warning printed to an operator during recovery. A
     stale path there misdirects a human at the worst possible moment.

Deliberately not checked, to avoid the false positives that get a guard
deleted: bare mentions other than `docs/...` (AGENTS.md is a historical
record and discusses renamed paths in the past tense), and links inside
the include_str! sidecars (rustdoc intra-doc references like
`crate::FakeOrange`, not filesystem paths -- the first version reported
all seven as broken, and rustdoc already warns on genuinely broken ones).

Proven to fail before being trusted, three ways: a broken markdown link,
a stale docs/ path in a Prometheus alert annotation, and renaming a doc
out from under its referrers. Each was caught, then restored.

Wired into `just all-checks` alongside the other four guards.
@changeset-bot

changeset-bot Bot commented Aug 15, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 1855c18

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@lightbridge-assistant lightbridge-assistant Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🅵 Fast automated pass — SAST + a quick, diff-scoped look (no repo-wide retrieval). For a deeper, repo-aware review, mention @lightbridge-assistant on this PR.

Reviewed all five changed files. docs_drift.rs, docs_pdf.rs, helpers.typ, and the justfile entries are correct and safe: the PDF pipeline's docker args are all repo-derived paths (no injection surface), the image is pinned by digest with /repo mounted read-only, the cross-document-link scanner/title-extraction/escaping logic is sound and unit-tested, and the drift checker's relative-vs-root resolution, sidecar/AGENTS exclusions, and conservative skip-instead-of-false-positive rejection all hold. SAST returned no findings. The only thing worth raising is a P2 quality gap: the two new cargo xtask subcommands are dispatched but missing from help_text()'s command list, so cargo xtask help under-documents them.


🤖 AI-generated review — treat it as untrusted, verify before acting; a human owns the final decision (AI governance).

Comment thread .xtask/src/main.rs
Both were wired into the dispatch match (and `just all-checks` runs
docs-drift) but neither appeared in `cargo xtask help`, so a developer
could not discover them from the CLI's own help surface.

Caught by review on #306. The original edit that should have added them
was a string replacement whose anchor didn't match -- it silently made no
change rather than failing, the same shape of mistake this repo keeps
finding. This one asserts the anchor exists before writing.

Verified by running `cargo xtask help` and checking every dispatch arm
appears in the output, not just the two added here.
stephane-segning added a commit that referenced this pull request Aug 15, 2026
Both were wired into the dispatch match (and `just all-checks` runs
docs-drift) but neither appeared in `cargo xtask help`, so a developer
could not discover them from the CLI's own help surface.

Caught by review on #306. The original edit that should have added them
was a string replacement whose anchor didn't match -- it silently made no
change rather than failing, the same shape of mistake this repo keeps
finding. This one asserts the anchor exists before writing.

Verified by running `cargo xtask help` and checking every dispatch arm
appears in the output, not just the two added here.
@stephane-segning
stephane-segning merged commit c068ce3 into main Aug 15, 2026
8 checks passed
@stephane-segning
stephane-segning deleted the docs/pdf-and-drift branch August 15, 2026 14:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant