Architecture Decision Records
Every decision that shapes Rulebearing's design is recorded here as an Architecture Decision Record (ADR). The format and the process are set by ADR-0001. Each ADR links to the section of the design document it derives from, to the architecture section it constrains, and to the plan that implements it.
A rule in rulebearing.yaml cites a decision with the token adr:NNNN in its comment. The same convention applies to this repository's own rules once wave 1 lands.
| ADR | Title | Status |
|---|---|---|
| 0001 | Record architecture decisions, plans and artifacts in docs/ |
Accepted |
| 0002 | Rust as the implementation language, one static binary | Accepted |
| 0003 | The .NET extractor falls back to a C# dotnet tool on a measured trigger |
Superseded by 0022 |
| 0004 | The graph document is an additive superset of dependency-cruiser's cruise-result schema |
Accepted |
| 0005 | A native config format that is a strict superset, and dependency-cruiser's format accepted as is | Accepted |
| 0006 | JavaScript configs are evaluated in a sandboxed embedded QuickJS engine | Accepted |
| 0007 | A rule whose selection is empty fails by default | Accepted |
| 0008 | Exit codes: error count, 2 for an untrustworthy run, 3 for an invalid config | Accepted |
| 0009 | dependency-cruiser's and ArchUnitNET's test suites are the specification | Accepted |
| 0010 | Crate layout: extractors behind a feature-gated boundary, the engine language-agnostic | Accepted |
| 0011 | .NET edges come from built assemblies and portable PDBs, not C# source | Accepted |
| 0012 | oxc_parser and oxc_resolver for TypeScript and JavaScript |
Accepted |
| 0013 | ruff_python_parser and a versioned stdlib list for Python |
Accepted |
| 0014 | No cross-language edges are invented | Accepted |
| 0015 | Every violation carries a stable id and a line and column | Accepted |
| 0016 | Linear-time regex engine, with --strict-compat for portability |
Accepted |
| 0017 | CoffeeScript and LiveScript run through a Node sidecar | Accepted |
| 0018 | 70% line coverage is a required check on every crate and wrapper | Accepted |
| 0019 | MIT licence | Accepted |
| 0020 | One name, rulebearing, on every registry |
Accepted |
| 0021 | The command line with the agent reporter is the primary agent surface; MCP and LSP are additive |
Accepted |
| 0022 | The .NET extractor stays the Rust metadata reader; the C# fallback is not invoked (trigger figure 0.9929) | Accepted |
| 0023 | Documentation links are checked on every compile, and every language has a configured linter behind one entry point | Accepted |
| 0024 | Test quality is gated by mutation testing, property tests and snapshots, not by coverage alone | Accepted |
| 0025 | CI runs least-privileged, pinned and bounded, and the dependency supply chain is closed | Accepted |
| 0026 | One crate-scoped licence exception: BSL-1.0 for dragonbox_ecma, which oxc requires |
Accepted |
| 0027 | The configuration sandbox offers pure path and url modules; every other Node built-in stays behind --config-via-node |
Accepted |
| 0028 | Backreferences are matched by instantiation on the linear-time engine; lookaround stays refused | Accepted |
| 0029 | Ratchets are enforced by cruise and reported in summary.ratchets[]; a missing budget exits 2 |
Accepted |
| 0030 | The reporter decides whether the error count is the exit code, as in dependency-cruiser; 2 and 3 do not depend on it | Accepted |
| 0031 | A saved result carries everything the exit code counts: summary.expired[]; fmt --exit-code reads it and vacuousRules |
Accepted |
| 0032 | Liveness follows the configuration's format: warn for a dependency-cruiser file, strict for a native one; named exceptions in allowEmpty |
Accepted |
| 0033 | One crate-scoped licence exception: Apache-2.0 WITH LLVM-exception for ar_archive_writer, which the Python parser's build requires |
Accepted |
| 0034 | A slice groups .NET types or TypeScript and Python modules; (*) names a slice as ArchUnitNET does; segments keeps the first segments |
Accepted |
| 0035 | The code layer holds the types the analysed code references, as ArchUnitNET's ReferencedTypes |
Accepted |
| 0036 | Markdown fences are read for a native configuration; a dependency-cruiser one keeps upstream's extraExtensionsToScan |
Accepted |
| 0037 | baseline has three modes of Rulebearing's own (full, shrink-only, format); dependency-cruiser has none |
Accepted |
| 0038 | A rule narrows the graph it sees: graph.ignore, graph.dependencyTypesNot, graph.modulesNot, graph.chainsThrough; import-linter's filters are written with it |
Accepted |
| 0039 | The delivery plans and the exported design are kept local; the link check skips links into them on CI only | Accepted |
| 0040 | A TypeScript 7 tsconfig resolves as TypeScript resolves it where dependency-cruiser's tsconfig-paths departs; the tsconfig's module decides what is compiled for acorn |
Accepted |
| 0051 | A rule redirects the imports it sees: graph.redirect leads an import to another module; the import-linter importer writes it where grimp, with include_external_packages, attributes an import of an unwalked module to a module it walks |
Proposed |
| 0052 | --affected follows the configuration format: dependency-cruiser's reaches filter byte for byte for a dependency-cruiser configuration, the closure with every violation touching it for a native one; the .NET and Python modules a change affects, --affected-depth and the summary.affected receipt in both |
Accepted |
| 0053 | x-dot-webpage draws its graph with GraphViz' dot from PATH, as dependency-cruiser does, and only for that output type; rb-report decides, rb-cli spawns |
Accepted |
| 0058 | The edit-compile cycle rebuilds only what changed: the link check runs from xtask-gate, which nothing depends on; dev builds carry line tables; sccache is set per machine |
Accepted |
| 0059 | Gates run by tier (inner loop, step merge, sub-wave close); a pull request mutates the lines it changes, a push to main the whole scope |
Accepted |
| 0060 | The guard's 100 ms is set for a developer machine; the Linux runner gates the unchanged-graph save and holds the graph-changing one to a ceiling | Accepted |
| 0061 | A type is attributed to a file its developer wrote; a source generator's output under obj/ only when the type is in no other |
Accepted |
| 0062 | The Node binding answers extractTSConfig and extractBabelConfig with the caller's own TypeScript and Babel; every other export is the command line's code through rb-node |
Accepted |
| 0063 | The cache's run start is also read from the clock that stamps files, so an edit in the start's tick on Windows is read again | Accepted |
| 0064 | A .ts file is parsed as TypeScript when the tsconfig sets jsx; upstream's TSX misparse, which drops imports, is a documented divergence |
Accepted |
| 0065 | No unsolicited offers to other projects: no issue or pull request proposing Rulebearing is opened on a test bed or any other repository; the evidence is published here | Accepted |
| 0066 | Project scope and the project layer: projects[] mirrors folders[] over the project each module belongs to; scope: project compares projects, native configurations only |
Accepted |
| 0067 | The playground's wasm facade joins the crate table; QuickJS becomes rb-config's js-config feature, which the playground builds without |
Accepted |
| 0068 | NuGet publishes by trusted publishing: nuget-publish exchanges its OIDC token for a one-hour key through NuGet/login; the stored NUGET_API_KEY is deleted after the first such release |
Accepted |
| 0069 | npm publishes by trusted publishing: each package's trust setting names release.yml, npm-publish runs npm 11 and falls back to NPM_TOKEN only for a package the release creates; the token is deleted once all eight have a trust setting |
Accepted |
| 0070 | A new NuGet package ID is first pushed with the stored key: the trusted-publishing key cannot create an ID, so a 403 is retried with NUGET_API_KEY, which is kept; amends ADR-0068 |
Accepted |
| 0071 | The rule library ships from this repository: presets/frameworks/ is published as rulebearing-rules / Rulebearing.Rules by release.yml at the release version; the second repository is archived; amends FR-REACH-04 |
Accepted |
| 0072 | The docs site is generated from the tracked docs/ by a small TypeScript generator on markdown-it, with the link check's anchors and a no-inline-script policy; one Pages deployment from main serves it with the schemas of the latest release, and release.yml no longer deploys Pages |
Accepted |
| 0073 | The extractors read files through rb-model's Files (the disk by default, a tree in memory for the playground); explain, its sentences, wants_metrics and optionsUsed move into rb-rules and rb-config; rb-wasm may link both extractors |
Accepted |
| 0074 | The cookbook's question pages are fixtures under cookbook/questions/, beside the guard catalogue: a runner holds their converted form, violations and imported contracts to the binary in the guards job and the docs build, and the site renders each page from those files with a playground preloaded with its repository; amends plan 0004 Step 5's paths |
Proposed |
| 0075 | The coverage tabs' rows are kept in a tracked ledger (conformance/ledger/) with each row's evidence; the gates write per-item results under target/conformance/, a row is proven only when all its evidence holds, and the nightly writes site/data/coverage.json by pull request; amends ADR-0039 for the tabs' rows |
Proposed |
| 0076 | Usage counts are counted and printed on the machine, and not sent: telemetry.enabled counts commands and firing rules per month under .graph/usage/ with salted hashes, --usage-counts print shows the payload, the monthly report and the CI guard run without a send; no send path, HTTP client or send value exists until an ADR names the endpoint; amends plan 0004 Step 13 |
Proposed |
| 0077 | fix --plan prints one verified candidate per template (move, port, invert), the first legal in a fixed order, costed by the import statements the agent edits; legal means can-import allows every added edge and the full evaluation adds no error; cruise --with-plans puts the same plans in the agent report; the eval harness scores fix text on the mutation branch and on violations injected into two .NET oracles |
Proposed |
| 0078 | fleet cruises each repository with its own configuration and cache and evaluates dependency rules over the prefixed union, folding each repository's findings into one report; declared edges attach there only, marked declared with declaredKind and comment; an OpenAPI operation declares an edge only through the x-rulebearing-handler its server document records and a client module that names its operationId exactly |
Proposed |