ADR-0031: A saved result carries everything the exit code counts
- Status: Accepted
- Date: 2026-09-23
- Derives from: ADR-0030 (
fmt --exit-codeapplies the same rule ascruise), design ยง Three pipelines, the wave 1 review - Constrains:
crates/rb-model(summary.expired[]),crates/rb-rules(fills it),crates/rb-report(--strict-schemastrips it),crates/rb-cli(fmt --exit-code) - Implemented by: Wave 1 plan, Step 13
- Requirements: FR-CORE-06
Context
ADR-0030 made cruise --output-type json exit 0 on a trustworthy run and moved the gate to fmt --exit-code --output-type err over the saved file. That is sound only if the saved file holds everything cruise counts. Two things were missing:
- Expired entries. A rule or known-violation entry past its
expiresdate is one error (docs/rules.md). The engine kept the list in memory andcruiseprinted it to stderr, but it never reached the document.cruise -T jsonthenfmt --exit-code -T errpassed a run thatcruise -T errfailed. - Vacuous rules. They were in
summary.vacuousRules, butfmtdid not read them, so a saved run thatcruiseexited 2 on was re-reported with the error count.
Decision
summary.expired[]is an additive field: one entry per expired rule or known violation, withname,expires(YYYY-MM-DD) andkind(ruleorknownViolation). It is absent when nothing has expired, and--strict-schemaremoves it with the other additions (ADR-0004).fmt --exit-codecomputes the code from the saved result alone. A non-emptysummary.vacuousRulesor a ratchet withno-budgetgives 2. Otherwise a gating reporter's code issummary.errorplus the expired entries plus the exceeded ratchets, the same sumcruiseuses.
Consequences
- The design's two-step pipeline fails exactly when a one-step
cruise -T errwould. - A result written by dependency-cruiser has no
expiredorvacuousRules, sofmttreats it as before.
Alternatives considered
- Count expired entries into
summary.error. Rejected:summary.erroris dependency-cruiser's count of error-severity violations, and changing what it means breaks the "nothing is dropped" promise for anyone who reads the field. - Leave the two-step pipeline blind to expiry. Rejected: an entry whose date passes would stop failing CI as soon as a repository adopts the documented pipeline.