Rulebearing Playground Coverage Schemas GitHub

ADR-0023: Documentation links are checked on every compile, and every language has a configured linter behind one entry point

Context

Two rules were written down but only weakly enforced.

Linked references. ADR-0001 requires every source file, plan and ADR to link the architecture section, the ADRs, the plan and the requirement it serves. A shell script checked that the file at the end of a link existed. It did not check anchors, did not run on Windows, and ran only in its own CI job, so a rename could sit broken in a working tree through any number of builds. This repository has more than 1,800 relative links across five plans, 23 decision records, a PRD and the crate doc comments; at that size a link is a dependency, and an unchecked dependency rots.

Linters. The working agreement names conventions for Rust, TypeScript, C# and Python, but only Rust had configuration on disk. The other three would have been enforced by memory at review time, which is the failure mode the whole project exists to remove for architecture rules.

Decision

1. The documentation link check is a compile-time gate. It lives in xtask/src/doclinks.rs and runs from three places: crates/rb-model/build.rs (every crate depends on rb-model, so every cargo build, cargo test and cargo clippy runs it once), cargo xtask lint, and the docs-links CI job. It checks that a relative target exists and, when the link carries an anchor, that the target holds a heading with that slug, using GitHub's slug rules. It skips absolute URLs, fenced code blocks (examples, not references), the file/<tab-id> tab links inside the verbatim export in docs/artifacts/, and, in Rust sources, everything that is not a //! or /// line. RB_SKIP_DOC_LINK_CHECK=1 bypasses it locally; nothing in CI sets it.

2. One lint entry point covers all four languages. cargo xtask lint runs, in order: the link check, cargo fmt --check, cargo clippy -D warnings, eslint and prettier, ruff (lint and format) and mypy, and dotnet format --verify-no-changes. --fix applies what each linter can fix. A language whose tree does not exist yet reports not applicable rather than passing, so the first file of that language brings its linter with it. A linter that is not installed reports skipped locally and fails under --strict, which is what CI passes.

3. Each language's configuration is on disk, at the repository root.

Language Tools Configuration
Rust rustfmt, clippy (pedantic, unwrap/expect/panic denied), cargo deny rustfmt.toml, [workspace.lints] in Cargo.toml, deny.toml
TypeScript, JavaScript eslint (flat config, typescript-eslint strict and stylistic type-checked), prettier eslint.config.mjs, .prettierrc.json, .prettierignore, tsconfig.base.json, package.json
Python ruff (select = ["ALL"] with four documented ignores), ruff format, mypy strict pyproject.toml
C# .NET analyzers at latest-recommended, warnings as errors, dotnet format, style severities Directory.Build.props, .editorconfig
Documentation the link check above xtask/

4. xtask is a workspace member outside crates/. It carries no dependencies beyond the standard library, so compiling it as a build dependency of rb-model costs almost nothing and adds no licence surface. It is not an rb-* crate and is not part of the product; ADR-0010's crate list is unchanged, in the same way wrappers/, adapters/ and frontends/ sit outside it. It carries the same 70% coverage floor as every other crate (ADR-0018).

Consequences

Alternatives considered