Overview

Decern is a CI gate that verifies every pull request — human or AI-generated — respects your team's architecture decisions. Decisions live as markdown files in your repo (docs/adr/*.md). An LLM compares each PR against the relevant decisions and blocks (or flags) violations. Everything stays yours: if you uninstall Decern, your ADRs remain in the repo as plain files.

Decern exists because the tech lead can't review every PR anymore — especially when half of them are written by AI. Copilot, Cursor, and Claude Code write code fast, but they don't know what your team decided last quarter about database access patterns, or why you deprecated that caching layer, or which dependencies are approved.

Decern turns those decisions into rules the CI enforces automatically, so you can stop being the architecture police.

Three phases

1. Bootstrap

Run `decern init` to analyze your codebase and generate ADR drafts. Review, approve, and commit to /docs/adr/.

2. CI Gate

Add `decern gate` to your CI. Every PR is evaluated against ADRs. Violations on blocking ADRs fail the build.

3. Evolution

Signals surface new patterns. The tech lead generates draft ADRs from the dashboard. The repo stays the source of truth.

Quick Start

Get from zero to enforced ADRs in 5 minutes.

1. Bootstrap ADRs

Terminal
export DECERN_LLM_BASE_URL=https://api.anthropic.com
export DECERN_LLM_API_KEY=sk-ant-...
export DECERN_LLM_MODEL=claude-sonnet-4-6

npx decern init

Decern scans your codebase (directory tree, package.json, git history, README) and proposes 15-25 ADR drafts. You review each one interactively: [A]pprove / [S]kip / [Q]uit. Approved ADRs are written to docs/adr/.

2. Commit and push

Terminal
git add docs/adr/
git commit -m "docs: bootstrap architecture decisions"
git push

3. Add the gate to CI

.github/workflows/decern.yml
name: Decern Gate
on: [pull_request]
jobs:
  gate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Decern gate
        run: npx decern gate
        env:
          DECERN_LLM_BASE_URL: ${{ secrets.DECERN_LLM_BASE_URL }}
          DECERN_LLM_API_KEY: ${{ secrets.DECERN_LLM_API_KEY }}
          DECERN_LLM_MODEL: ${{ secrets.DECERN_LLM_MODEL }}
          DECERN_BASE_URL: ${{ secrets.DECERN_BASE_URL }}
          DECERN_CI_TOKEN: ${{ secrets.DECERN_CI_TOKEN }}
          CI_PR_URL: ${{ github.event.pull_request.html_url }}
fetch-depth: 0 is required so git can compute the diff between base and head.

4. Done

Every PR is now evaluated against your ADRs. Violations on blocking ADRs fail the build. Warnings are logged. Signals are reported to the dashboard.

ADR Format

ADRs are markdown files in docs/adr/ with YAML frontmatter and three sections.

docs/adr/adr-007-use-postgres.md
---
id: ADR-007
title: Use PostgreSQL for persistence
status: approved
enforcement: blocking
scope:
  - src/db/**
  - migrations/**
supersedes: null
date: 2026-04-10
---

## Context
The team evaluated PostgreSQL, MySQL, and MongoDB.
PostgreSQL was chosen for its JSONB support and transactional integrity.

## Decision
All persistent data storage uses PostgreSQL.
No other database engines are allowed in production.

## Consequences
- All team members must know SQL
- MongoDB skills are not leveraged
- JSONB provides flexibility without a separate document store

Frontmatter fields

FieldValuesDescription
idADR-NNNUnique identifier
titleFree textConcise, descriptive title
statusproposed | approved | superseded | rejectedOnly approved ADRs are enforced by the gate
enforcementblocking | warningblocking fails CI on violation. warning logs but passes.
scopeGlob patternsFiles this ADR applies to. Empty = all files. Supports *, **, ?
supersedesADR-NNN | nullWhich ADR this one replaces
dateYYYY-MM-DDWhen the decision was made

Writing ADRs That Work With the Gate

The quality of the gate depends directly on the quality of your ADRs. A vague ADR produces vague verdicts. A precise ADR produces precise verdicts. This section is the difference between a gate that catches real violations and one that produces false positives until your team disables it.

1. Decision must be specific and verifiable

The LLM needs to answer a binary question: does this diff respect this decision, yes or no? If the decision is fuzzy, the LLM guesses.

Bad

"Code should be readable and well-structured."

Good

"All database queries go through the service layer in src/services/. No direct DB access from route handlers."

2. Scope must be specific

Narrow scope = fewer files sent to the LLM = faster evaluation, lower cost, fewer false positives. An ADR with empty scope evaluates against every file in every PR.

Bad

scope: (empty)

Good

scope: [src/api/**, src/routes/**]

3. Context must explain the why

Context is not just for humans — the LLM reads it too. A good Context section helps the LLM understand intent, which reduces false positives on edge cases. "We chose X because of Y" is more useful than "We use X."

4. Consequences must be honest

Include trade-offs. This helps the LLM distinguish between "intentional deviation" (a known trade-off) and "violation" (something the team didn't consider). It also helps future team members understand what they're signing up for.

5. Blocking vs Warning — the most important decision

This is the choice that determines whether Decern helps or annoys your team.

Use blocking when...Use warning when...
The violation is clear-cut and binary (yes/no, not maybe)The violation is a judgment call or has legitimate exceptions
A violation in production would be costly or hard to reverseA violation is a style preference or soft convention
The team has strong consensus (everyone agrees this is non-negotiable)The ADR is new and you're still calibrating whether the gate catches it correctly
The scope is narrow (less risk of false positives on unrelated code)The scope is broad or the decision is philosophical
Start every ADR as warning. Observe the gate output for 1-2 weeks. When you're confident it catches real violations without false positives, promote to blocking. This is the safe path.

6. Anti-patterns to avoid

  • ADRs about process, not code: "Every PR must be reviewed by 2 people" — the gate can't see your GitHub review settings, only the diff.
  • ADRs that are always true: "We use TypeScript" for a TypeScript repo — every diff will pass, the ADR adds no value.
  • ADRs with overlapping scope: Two ADRs both covering src/** with conflicting decisions confuse the LLM.
  • Too many blocking ADRs too early: Start with 2-3 blocking ADRs. Add more as confidence grows.

Bootstrap (decern init)

The bootstrap command analyzes your codebase and proposes ADR drafts for decisions the team has implicitly made but never written down. It looks at: directory structure, package manifests, git history, README, and key entry point files.

All proposed ADRs have status: approved and enforcement: warning. The tech lead promotes specific ADRs to blocking after review.

VariableRequiredDescription
DECERN_LLM_BASE_URLYesLLM API base URL
DECERN_LLM_API_KEYYesLLM API key
DECERN_LLM_MODELYesModel ID (e.g. claude-sonnet-4-6)
DECERN_ADR_DIRNoADR directory (default: docs/adr)
After bootstrap, run decern adr sync to push the ADR index to the dashboard (if cloud is configured). Or just add the gate to CI — it syncs automatically on the next PR.

CI Gate Setup

The gate runs on every pull request and evaluates the diff against your approved ADRs. It requires a BYO LLM (you provide the API key) and optionally reports results to the Decern cloud dashboard.

Environment variables

VariableRequiredDescription
DECERN_LLM_BASE_URLYesLLM API base URL (e.g. https://api.anthropic.com or https://api.openai.com/v1)
DECERN_LLM_API_KEYYesLLM API key
DECERN_LLM_MODELYesModel ID. See Recommended Models section.
DECERN_ADR_DIRNoADR directory (default: docs/adr)
DECERN_BASE_URLNoDecern cloud URL. Enables evidence reporting and PR comments.
DECERN_CI_TOKENNoWorkspace CI token. Required if DECERN_BASE_URL is set.
DECERN_CONFIDENCE_THRESHOLDNoMinimum confidence to block (0-1, default: 0.75). Violations below this are degraded to warnings.
DECERN_EVAL_CONCURRENCYNoMax parallel LLM calls (default: 3)
CI_BASE_SHANoBase commit SHA. Auto-detected from origin/main if not set.
CI_HEAD_SHANoHead commit SHA. Auto-detected as HEAD if not set.
CI_PR_URLNoPR URL. Enables PR violation comments and nudges.
CI_PR_TITLENoPR title. Included in evidence record.

GitHub Actions

.github/workflows/decern.yml
name: Decern Gate
on: [pull_request]
jobs:
  gate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Decern gate
        run: npx decern gate
        env:
          DECERN_LLM_BASE_URL: ${{ secrets.DECERN_LLM_BASE_URL }}
          DECERN_LLM_API_KEY: ${{ secrets.DECERN_LLM_API_KEY }}
          DECERN_LLM_MODEL: ${{ secrets.DECERN_LLM_MODEL }}
          DECERN_BASE_URL: ${{ secrets.DECERN_BASE_URL }}
          DECERN_CI_TOKEN: ${{ secrets.DECERN_CI_TOKEN }}
          CI_PR_URL: ${{ github.event.pull_request.html_url }}

GitLab CI

.gitlab-ci.yml
decern-gate:
  image: node:20
  stage: test
  script:
    - npx decern gate
  variables:
    DECERN_LLM_BASE_URL: $DECERN_LLM_BASE_URL
    DECERN_LLM_API_KEY: $DECERN_LLM_API_KEY
    DECERN_LLM_MODEL: $DECERN_LLM_MODEL
    DECERN_BASE_URL: $DECERN_BASE_URL
    DECERN_CI_TOKEN: $DECERN_CI_TOKEN
  only:
    - merge_requests

Bitbucket Pipelines

bitbucket-pipelines.yml
pipelines:
  pull-requests:
    '**':
      - step:
          name: Decern Gate
          image: node:20
          script:
            - npx decern gate

Jenkins

Jenkinsfile
pipeline {
  agent { docker { image 'node:20' } }
  stages {
    stage('Decern Gate') {
      steps {
        sh 'npx decern gate'
      }
    }
  }
}

Azure DevOps

azure-pipelines.yml
trigger: none
pr:
  branches:
    include: ['*']
pool:
  vmImage: 'ubuntu-latest'
steps:
  - task: NodeTool@0
    inputs:
      versionSpec: '20.x'
  - script: npx decern gate
    displayName: Decern Gate
    env:
      DECERN_LLM_BASE_URL: $(DECERN_LLM_BASE_URL)
      DECERN_LLM_API_KEY: $(DECERN_LLM_API_KEY)
      DECERN_LLM_MODEL: $(DECERN_LLM_MODEL)

How Evaluation Works

Step by step

For each PR, the gate:

  1. Reads approved ADRs from docs/adr/*.md
  2. Computes the diff (git diff base...head)
  3. Scope pre-filter: skips ADRs whose glob patterns don't match any changed file (zero LLM cost)
  4. Scope-filtered diff: for each relevant ADR, sends only the diff hunks for matching files (not the full diff)
  5. LLM evaluation: concurrent calls (configurable, default 3) asking the LLM: does this diff respect, violate, or is unrelated to this ADR? Plus a confidence score (0-1).
  6. Confidence threshold: violations with confidence below threshold (default 0.75) are degraded from blocking to warning
  7. Signal detection: in parallel, a separate LLM call scans for new patterns not covered by any ADR (1-3 signals max)

Verdict types

ResultBlocks CI?Description
passNoDiff respects the ADR
violation + blocking + high confidenceYesClear violation, CI fails
violation + blocking + low confidenceNoAmbiguous, degraded to warning
violation + warningNoAdvisory only, logged
unrelatedNoADR not relevant to this diff
skippedNoScope pre-filter, no LLM call
errorNoLLM failure, fail-open with warning

LLM cost

N = ADRs that pass scope filter. Total LLM calls = N + 1 (N evaluations + 1 signal detection). Skipped ADRs cost nothing. With 15 ADRs and 4 matching scope, you make 5 LLM calls per PR.

Diff truncation

Diffs are capped at 2MB total (100K chars per ADR evaluation, 80K for signal detection). When truncated, the gate logs: "Note: the diff was truncated for analysis. Some changes may not have been evaluated."

Signal Detection

Every PR is scanned for new architectural patterns not covered by any existing ADR. This runs in parallel with ADR evaluation — a PR can pass ADR-007, violate ADR-012, AND generate a signal for a new caching pattern, all at the same time.

The LLM receives the diff, the list of existing ADR titles + decision summaries, and is asked: "does this diff introduce something architecturally significant not covered by the existing ADRs?" It returns 0-3 signals.

What counts as a signal

  • New external dependency (library, framework, service)
  • New structural pattern (layer, abstraction boundary, module system)
  • New technology (first gRPC, first message queue, first IaC)
  • New convention not covered by existing ADRs

What does NOT count

  • Bug fixes, refactors, renames, style changes
  • Patch version updates
  • Tests, docs, comments
  • Routine feature work following existing patterns

Dashboard flow

Signals appear in the Signals page grouped by repo. The tech lead can:

  • Generate draft ADR (Enterprise) — cloud LLM creates a full ADR markdown from the signal(s)
  • Create PR (GitHub) — commits the ADR file to the repo via a PR
  • Copy markdown — for non-GitHub repos, copy and commit manually
  • Dismiss — mark as not a real architectural decision

ADR Lifecycle

Principle: nothing enters the repo or changes state without an explicit merge by a human. Decern proposes, you decide. Every lifecycle transition — approve, promote to blocking, supersede — generates a pull request on your repo. No silent changes, no magic commits, no overwritten files.

ADRs follow a status flow. Only approved ADRs are enforced by the gate.

proposed → approved → superseded
                ↘ rejected

Lifecycle from the dashboard

The ADR detail drawer shows contextual action buttons. Each action generates a markdown preview that you can edit, then either copy or create a PR (GitHub).

Current stateActions available
proposed + warningApprove, Reject
approved + warningPromote to blocking, Supersede
approved + blockingDemote to warning, Supersede
superseded / rejectedNo actions (terminal states)
Every lifecycle action creates a PR on the repo. Nothing changes without a merge. The suggested branch name is shown in the preview.

Override & Escape Hatches

Sometimes a developer needs to bypass a blocking ADR intentionally — a production hotfix on Friday evening, a one-time migration that temporarily violates a pattern, or an edge case the ADR didn't anticipate. Decern supports this with a documented override workflow.

How override works

  1. The gate blocks the PR with a violation (exit code 1, CI fails)
  2. The developer or tech lead calls POST /api/override with the evidence ID and a mandatory reason (minimum 20 characters)
  3. Decern creates a new evidence record of type "override" linked to the original blocked record, containing who overrode, when, and why
  4. The original evidence record is updated with the override data
  5. The developer can re-run the gate or merge with a manual approval

Override via API

curl
curl -X POST https://your-decern.app/api/override \
  -H "Authorization: Bearer $DECERN_CI_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "evidence_id": "01912345-...",
    "override_reason": "Production hotfix for payment processing bug. ADR-012 violation is intentional and will be reverted in follow-up PR #234."
  }'

What gets recorded

The override is part of the evidence chain. An auditor sees:

  • The original gate run (blocked, with ADR evaluation and confidence)
  • The override record (who, when, reason)
  • Both are hash-chained and signed — the override can't be silently inserted after the fact
The override reason is stored permanently in the evidence chain. Write it as if an auditor will read it in 6 months — because they might. "needed to deploy" is not enough. "Production hotfix for #123, ADR-012 violation intentional, revert in PR #234" is.

Reducing the need for overrides

If your team overrides the same ADR frequently, it's a signal that the ADR needs updating — the decision may have changed, the scope may be too broad, or the enforcement should be warning instead of blocking. Check the drift report for patterns.

Multi-Repo

A workspace can host multiple repos. Each repo has its own docs/adr/ tree. ADRs are scoped per-repo — two repos can both have ADR-001 without collision.

Repos connect implicitly: any repo that presents the workspace's CI token is associated with that workspace. No explicit "add repo" UI — the first gate run or ADR sync from a repo makes it appear.

Repository identifier format: github.com/owner/repo, gitlab.com/group/project. Detected automatically from GITHUB_REPOSITORY (in CI) or git remote.origin.url (local).

Cross-repo ADRs

Decern does not support cross-repo ADRs today. Each ADR lives in one repo and is enforced only in that repo's PRs. For organization-wide policies (e.g. a security standard that applies to all repos), the recommended pattern is to replicate the ADR file in each repo's docs/adr/. This is intentionally explicit: each team owns their copy and can adapt it to their context. Cross-repo governance is on the roadmap.

Dashboard

The dashboard is the control plane for your architecture governance. It reads from the same data the gate writes — ADR cache, evidence records, signals — and lets the tech lead manage everything without touching the terminal.

ADRs

View all ADRs across repos. Collapsible accordion per repo with search bar. Each repo header shows counts (total, blocking, proposed) and a Sync button (GitHub repos). Click an ADR to open the detail drawer with full body, lifecycle actions, and raw markdown.

Signals

New architectural decisions detected in PRs. Grouped by repo. Generate draft ADR (Enterprise), create PR, or dismiss. Resolved signals (formalized or dismissed) shown at the bottom.

Gate Runs

Evidence records from CI. Stats (total, passed, warned, blocked) for the current month. Table with verdict badges, PR link, repo, and a detail modal showing commit SHA, CI provider, author, ADRs evaluated, and files changed.

Workspace

CI token management (generate, revoke). Evidence retention policy (days). Member management (invite, roles). Signing key warning if not configured.

Evidence Chain

Every gate run produces an immutable evidence record with:

  • All ADRs evaluated, each verdict with confidence score, enforcement level, and reason
  • Diff hash (SHA-256) and files touched
  • Author identity from CI metadata
  • Timestamp with microsecond precision
  • Hash chain: each record's previous_evidence_hash points to the preceding record
  • Ed25519 signature (algorithm, key_id, value)

Export

GET /api/evidence/export?from=...&to=... returns a JSON bundle containing records, access logs, chain tip, manifest, and public keys. The manifest includes signing_key_type: "persistent" | "ephemeral" so auditors can verify integrity.

Verification

Terminal
npx decern verify-evidence evidence-export.json

Verifies hash chain continuity and Ed25519 signatures offline.

Signing key

Generate a persistent Ed25519 signing key:

Terminal
node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"

Add to your environment:

DECERN_EVIDENCE_SIGNING_KEY=K7x2mFp3Q1nR8vYb...==
Without this key, evidence is signed with an ephemeral key that changes on server restart. Signatures will not be verifiable across restarts. The dashboard shows a warning banner when the key is not configured.

Recommended Models

The gate quality depends directly on the LLM. Tested and recommended for production:

  • claude-sonnet-4-6, claude-opus-4-6 (Anthropic)
  • gpt-4o, gpt-4.1, gpt-5 (OpenAI)
  • gemini-2.5-pro (Google)

Smaller models (gpt-4o-mini, claude-haiku) work but produce significantly more false negatives — violations that the model misses. A runtime warning is logged in CI when using a non-recommended model.

The LLM is BYO (Bring Your Own). You provide the API key, Decern calls the API per-request. Keys stay in your CI environment, never stored by Decern.

Troubleshooting & FAQ

The gate is slow (30+ seconds)

Each ADR that passes scope filter = one LLM call. With 10 matching ADRs at 3 concurrent (default), that's 4 rounds of calls. Solutions: (1) Tighten scope patterns so fewer ADRs match per PR. (2) Increase DECERN_EVAL_CONCURRENCY to 5 (watch for LLM rate limits). (3) Use a faster model.

I see "error" verdicts in gate runs

An error verdict means the LLM call failed (timeout, rate limit, malformed response). The gate treats errors as fail-open: the PR passes, but the error is logged. If you see many errors: check your LLM API key, rate limits, and model availability. The CI log shows the exact error message for each failed evaluation.

Too many false positives (blocking on things that aren't violations)

Ranked by impact:

  1. Check the ADR: is the Decision section specific and verifiable? Vague ADRs produce vague verdicts.
  2. Tighten the scope: a broad scope means the ADR evaluates diffs it shouldn't care about.
  3. Lower the confidence threshold: set DECERN_CONFIDENCE_THRESHOLD=0.85 to only block high-confidence violations.
  4. Upgrade the model: smaller models produce more false positives. Check the Recommended Models section.
  5. Demote to warning: if the ADR is consistently problematic, switch from blocking to warning while you calibrate.

The LLM provider is down — does the gate block all PRs?

No. LLM failures are fail-open: the verdict is error (not violation), the PR passes, and the gate logs a warning. Your CI is never blocked by an LLM outage.

How do I handle very large diffs (1000+ lines)?

The gate sends a scope-filtered diff per ADR (only hunks matching the ADR's scope, not the full diff). For most PRs this keeps the payload small. If the total diff exceeds 2MB, it's truncated with a logged warning. For very large PRs, consider splitting into smaller PRs — this is good practice regardless of Decern.

Can I run the gate locally before pushing?

Yes. From your repo root:

Terminal
export DECERN_LLM_BASE_URL=https://api.anthropic.com
export DECERN_LLM_API_KEY=sk-ant-...
export DECERN_LLM_MODEL=claude-sonnet-4-6
npx decern gate

It uses origin/main...HEAD as the diff range. Useful for testing before opening a PR.

What happens if I have no ADRs?

The gate passes immediately: "No approved ADRs found. Gate passes (nothing to enforce)." Signal detection also doesn't run. Run decern init to bootstrap.

Can two repos in the same workspace have the same ADR ID?

Yes. ADRs are scoped per-repo. ADR-001 in github.com/acme/api and ADR-001 in github.com/acme/web are independent records in the dashboard.

How do I remove an ADR?

Delete the file from docs/adr/ and commit. On the next sync (or gate run with cloud reporting), the ADR disappears from the dashboard. Alternatively, set status: rejected to keep the record but stop enforcement.

Does Decern store my code?

No. The gate runs in your CI and sends the LLM call directly to your BYO provider. The cloud receives only: verdict, confidence, ADR IDs evaluated, diff hash (not the diff itself), file paths, and author metadata. Your code never passes through Decern servers.

Self-Hosted

Self-hosted is for teams in regulated industries (finance, healthcare, defense, public sector) or with strict data residency requirements. If you can use the cloud, use the cloud — it's simpler and gets updates automatically. Choose self-hosted when your security policy requires that no data leaves your network, or when you need air-gapped deployment.

The gate CLI runs in your CI as always. The dashboard and cloud API run on your servers.

Requirements

  • Node.js 20+
  • PostgreSQL 15+ (Supabase or standalone)
  • BYO LLM endpoint accessible from CI

Key environment variables

VariableRequiredDescription
NEXT_PUBLIC_SUPABASE_URLYesSupabase project URL
NEXT_PUBLIC_SUPABASE_ANON_KEYYesSupabase anon key
SUPABASE_SERVICE_ROLE_KEYYesSupabase service role key
DECERN_EVIDENCE_SIGNING_KEYYesEd25519 seed (32 bytes, base64) for persistent evidence signatures
CLOUD_LLM_API_KEYNoAnthropic key for draft ADR generation (Enterprise feature)
CLOUD_LLM_MODELNoModel for draft generation (default: claude-sonnet-4-6)
CRON_SECRETNoBearer token for cron endpoints (evidence archival)
No data leaves your infrastructure unless you configure an external LLM endpoint. The gate CLI calls the LLM directly from CI; the cloud dashboard calls the LLM only for Enterprise draft generation.

Plans

The Free plan is free forever — no trial, no credit card, no expiration. It includes the full CI gate with blocking, signal detection, evidence chain, and export. The only limits are 1 workspace and 3 developers. Enterprise adds unlimited scale, draft ADR generation, PR creation from dashboard, self-hosted deployment, SSO, and dedicated support.

FeatureFreeEnterprise
Workspaces1Unlimited
Developers3Unlimited
ADRs + gate runsUnlimitedUnlimited
CI blockingYesYes
Signal detectionYesYes
Evidence chain + exportYesYes
Draft ADR from signalsNoCloud LLM
Create PR from dashboardNoGitHub
Self-hostedNoVPC / air-gapped
SSONoSAML, OIDC
SupportCommunityDedicated with SLA