A decision can stay written long after it stops being true.
The link breaks. The status changes. Governed code moves without the record. A premise disappears.
duckadrift reads the decision, then checks the repository it describes.
Tier 0 · deterministic
Facts can fail the build.
Seven checks catch drift that can be proven from the repository itself.
Broken references. Conflicting states. Stale records. Index drift. Governed code changed without an ADR-ACK.
No model. No API key. No network calls.
Tier 1 · semantic
Judgment shows its work.
Some drift isn't a missing file or a broken link.
Contradictions. Code that outgrew the decision. Decisions nobody recorded. The same argument returning under new names. Premises that no longer exist.
Every semantic finding must quote the documents it relies on. A finding that can't cite its source is discarded, and the discard is counted.
Public calibration corpus · human label: true
Four decisions. Four different subjects. One problem nobody solved.
S4 · Recurring revision
Four Accepted ADRs, each about something different: connector kerning, placement, height normalization, eye-body placement.
All four keep circling the same unresolved primitive: no build-time measure separates a connector bridge from a weld. Every one parks it. None resolves it.
Public calibration corpus · human label: true
One appliance. Two incompatible decisions.
S1 · Inter-record contradiction
ADR-0001 makes the product self-contained: all persistence in an embedded SQLite file, nothing listening on any port, no outbound connections.
ADR-0003, accepted three months later, makes an external PostgreSQL cluster the system of record.
Both are Accepted. Neither supersedes the other. A box that opens no connections cannot depend on a database cluster it connects to.
Calibration
Authority is measured, not assumed.
Every semantic finding routes through a published calibration built from human-labeled results. Each severity has its own precision floor.
calibrationloading…
The gate is already live. Channels open through data. No release, no hand-typed threshold.
Details
Tier 0 · deterministic
Checks that can be proven
Tier 0 runs without a model, an API key, or a network call. Every finding is mechanical: duckadrift can point at the file, the record, and the rule. If it cannot prove a finding from the repository itself, Tier 0 does not fail CI.
Broken references. An ADR points at a file, path, or record that no longer exists.
Conflicting states. Records that cannot both be true stand as Accepted at the same time.
Stale records. A decision's status no longer matches the repository it governs.
Index drift. The ADR index and the records on disk have fallen out of sync.
Governed-path changes without ADR-ACK. Code under a governed path moved without an ADR update or an acknowledgment marker.
The full enumeration of the seven checks, with their exact rules, lives with the source.
Two real findings from the public review corpus. Every semantic finding quotes the documents it relies on. The lines below are quoted verbatim, not paraphrased.
S4 · Recurring revision
Four Accepted ADRs, each about something different: connector kerning, connector placement, height normalization, eye-body placement. All circle one unresolved primitive: no build-time measure separates a connector bridge from a weld.
connect-kern parked: no build-time measure separates the connector bridge from a weldADR 0040
parked on bridge-vs-weld protectionADR 0043
Named by the check: one primitive, four revisions, unresolved
S1 · Inter-record contradiction
ADR-0001 makes the product self-contained: all persistence in an embedded SQLite file, nothing listening on any port, no outbound connections. ADR-0003, accepted three months later, makes an external PostgreSQL cluster the system of record. Both are Accepted; neither supersedes the other.
The deploy target runs no network services: nothing listens on any port, and the application opens no connections to other processes.ADR-0001 · accepted 2026-03-03
Adopt PostgreSQL as the system of record for all persistence. The database runs as a managed cluster; the application authenticates with rotating credentials and connects over TLS.ADR-0003 · accepted 2026-06-15
The two decisions are three months apart, and neither marks the other superseded. That quiet reversal is exactly the drift the check exists to catch.
Routed by the published calibration
Calibration
The published calibration
duckadrift does not trust a confidence score just because a model produced it. Every semantic finding routes through a published calibration built from human-labeled results, and each severity has its own precision floor. A finding interrupts only when the measured evidence for that channel clears the floor.
Calibration is matched to the exact backend, model, and effort used for the run. At runtime, duckadrift recomputes each opening condition from the artifact's underlying measurements. Unsupported, altered, or decreed thresholds are refused.
The system isn't waiting to be built. It's waiting for the data to make the ruling. As the corpus grows and the measurements change, channels open through data. No release, no judgment call, no hand-typed threshold.
Tier 0 can fail CI on deterministic findings. Tier 1 findings route according to the published calibration for the exact backend, model, and effort the run used.
Questions
FAQ
What does duckadrift check?
Two enforcement tiers. Tier 0 runs seven deterministic checks for drift that can be mechanically proven from the repository: broken references, status conflicts, stale records, index drift, and governed paths changed without acknowledgment. Tier 1 runs five model-assisted checks: contradictions, code-to-decision drift, unrecorded decisions, recurring revision, and dead premises.
Does duckadrift write or generate ADRs?
No. duckadrift is an enforcement tool, not an authoring tool. It checks whether the decisions you already recorded still agree with the repository. It never writes, edits, or auto-fixes an ADR.
Do I need an API key?
Not for Tier 0. Deterministic checks run without a model or API key. Tier 1 currently uses the Anthropic API backend: set tier1.enabled: true in .duckadrift.yml and provide ANTHROPIC_API_KEY through your GitHub Actions secrets.
How does semantic interruption work?
Every accepted semantic finding gets a severity and routes through the calibration entry matching the run's backend, model, and effort. Each severity has a published precision floor. A channel opens only when the measured lower confidence bound clears that floor. Within an open channel, a finding interrupts only when its own confidence clears the measured threshold. Either way the finding stays in the report annex. Interruption is an extra push, not a replacement for the record.
Can someone open a channel by editing the threshold?
Not by editing the threshold alone. At runtime duckadrift recomputes the opening condition from the calibration curve and checks it against the severity floor. A threshold with no supporting measurements is refused; a stored bound that disagrees with the underlying curve is refused.
What happens when a channel opens?
No product release is required. The runtime already consumes the calibration artifact and routes through it. When a new human-labeled corpus produces measurements that clear a severity floor, the resulting calibration entry opens that channel as a data change.
Does my code leave the runner?
Tier 0 sends nothing to a model. When Tier 1 is enabled, duckadrift sends the ADR and code documents selected for a check to Anthropic using the API key you provide. Inputs are bounded by a 600,000-byte cap per Tier 1 call; whole-log checks include only the accepted ADR records selected for that run.
Do my ADRs need a specific format?
duckadrift reads Markdown ADRs and recognizes common status forms: frontmatter, a ## Status heading, and common bold status lines. Conventional ADR directories are detected automatically; unconventional locations can be configured.
Why not let users choose a confidence threshold?
A model's confidence number doesn't establish how often the model is right. duckadrift measures precision on human-labeled findings, computes a lower confidence bound, and publishes the curve. Authority comes from measured performance, not a number typed into config.
Is duckadrift open source?
Yes, MIT licensed. The source, checks, calibration method, public review corpus, and published calibration artifact are all on GitHub.