Court-admissible, not just enforced

The decision authority that survives a regulator.

Cedar substrate. ADL surface. Per-evaluation transcript chained to a qualified electronic timestamp (D-Trust QTSP + Sigstore Cosign). Customer-portable signed bundle the regulator can replay offline. block wins over everything; flag_for_review paired with auto_approve resolves to auto-approval (Auto-Approval Engine merger) — fixed semantics, every match, every time.

Live today: Cedar substrate evaluation, ADL compile, per-evaluation transcripts (with cedarRequestHash verifier digest + policyVersionHash immutability), SHA-256 chain, Auto-Approval merger. Gated on founder action: D-Trust QTSP qualified-timestamp signing (commercial contract in procurement), Sigstore Cosign keyless OIDC (GitHub Actions setup), pre-built signed standalone replay binary (Phase 3.1). Until each gate fires the corresponding layer runs as a deterministic mock preserving chain integrity. See court-admissibility for the per-gate detail.

Write a policy Read the matcher
JSON or ADL · compiled to Cedar·5 action types · 7 operators·§371a Abs. 3 ZPO + eIDAS Art. 41(2)
Engine resolution · compute-time
{
  "trace":         "trc_847b3f...",
  "action":        "flag_for_review",
  "matchedPolicy": {
    "name":     "Hold low-confidence loan denials",
    "priority": 1
  },
  "reason":        "Policy \"Hold low-confidence loan denials\" required human review."
}
This resolution returns 202 with action: hold_for_review and a reviewId. A block action would return 403 with code: ADJ_BLOCKED_BY_POLICY.

Author in ADL. Compile to Cedar. Chain every match.

ADL v0.1 is the typed authoring surface — predicate calls, not a JSON object tree. The compiler lowers ADL to AWS Cedar (CNCF Sandbox, formal semantics), pins it to an immutable PolicyVersion via SHA-256 chain, and writes one PolicyTranscript per match. Resolution order: block always wins; flag_for_review paired with auto_approve resolves to auto-approval; otherwise flag_for_review > notify > standalone auto_approve > approve.

01 Author — ADL v0.1 (predicate calls, typed)

ADL is the customer-authoring surface. Predicates come from a curated registry — each one carries a domain (decimal / string / bool) so the compiler catches type errors before the policy ever runs. The example holds every loan-underwriter trace under 0.7 confidence for human review, scoped to one organizationId and one workspaceId.

name "Hold low-confidence loan denials"
priority 1
enabled true

when agent_equals("loan_underwriter")
  and confidence_below(0.7)

then flag_for_review
02 Compile — ADL → Cedar → immutable PolicyVersion

The compiler lowers ADL through an intermediate representation to AWS Cedar. The Cedar text + cedarSchema + canonical hash get written to a new PolicyVersion record, hash-linked to the previous version. Append-only: every version exists forever. A regulator reading a 2030 transcript fetches the 2026 policy text in force at that moment.

// backend/services/policyVersionService.js
const { cedarPolicyText, cedarSchema } =
  policyCompiler.compileV1ToCedar(policy);

const canonicalContentHash = sha256(
  canonicalJson({ policy, cedarPolicyText, cedarSchema })
);

await PolicyVersion.create({
  organizationId,                       // Cardinal Rule #1 — org-first index
  policyId,
  canonicalContentHash,                 // SHA-256 hex, 64 chars
  priorVersionHash,                     // null for genesis, else last version's hash
  cedarPolicyText,
  cedarSchema,
  lifecycleState: 'draft',              // 'draft' | 'shadow' | 'canary' | 'live'
  algoSuite: 'sha256-jcs-rfc8785',
  schemaVersion: 'adjudon-policy-version-v1',
});
03 Resolve — block wins; flag + auto_approve → auto_approve; else flag > notify > auto_approve standalone

The engine walks active policies in priority order, keeps the first match per action type, and writes one immutable PolicyTranscript per fired policy — chained via SHA-256, signed by D-Trust QTSP at the daily Merkle anchor. auto_approve (Auto-Approval Engine merger) never overrides a block from a different policy on the same trace. The hard-rule contract holds.

// backend/services/policyEngine.js + policyTranscriptService.js
const fired = [];
for (const p of policies) {
  if (!policyMatches(p, trace)) continue;
  fired.push(p);
  for (const a of p.actions || []) {
    if (a.type === 'block')           blockPolicy        ||= p;
    if (a.type === 'flag_for_review') flagPolicy         ||= p;
    if (a.type === 'auto_approve')    autoApprovePolicy  ||= p;
    if (a.type === 'notify')          notifyPolicy       ||= p;
  }
}

// One transcript per fired policy — append-only, PII-scrubbed at write
for (const policy of fired) {
  await policyTranscriptService.emit({
    organizationId, policyId: policy._id,
    policyVersionHash: policy.versionHash,        // pins to immutable PolicyVersion
    traceId, decision, firedConditions,
    cedarRequestHash: sha256(canonicalJson(cedarRequest)),
    algoSuite: 'sha256-jcs-rfc8785',
  });
}

if (blockPolicy)        return { action: 'block',           matchedPolicy: blockPolicy };
if (flagPolicy)         return { action: 'flag_for_review', matchedPolicy: flagPolicy };
if (autoApprovePolicy)  return { action: 'allow', autoApproved: true, matchedPolicy: autoApprovePolicy };
if (notifyPolicy)       return { action: 'allow', matchedPolicy: notifyPolicy, notify: true };
return { action: 'allow' };
04 Respond — action becomes HTTP code

The trace controller reads the engine's action and returns the matching HTTP code. block returns 403 with code ADJ_BLOCKED_BY_POLICY and writes a blocked trace. flag_for_review returns 202 with action: hold_for_review and creates a Review Queue item. auto_approve bypasses the queue and writes an AutoApprovalDecision. Everything else lands as 201.

// trace ingestion — backend/controllers/traceController.js
const policyResult = await evaluatePolicies(trace, organizationId, workspaceId);

if (policyResult.action === 'block') {
  finalTraceData.status = 'blocked';
  // hash-chain append, alerts, webhook 'trace.blocked' — all fired before response
  return fail(res, 403, policyResult.reason, 'ADJ_BLOCKED_BY_POLICY',
              { allowed: false, action: 'block', traceId, id });
}

if (policyResult.action === 'flag_for_review') {
  finalTraceData.status = 'flagged';
  // ReviewItem created, webhook 'trace.hold_for_review' fired
  return ok(res, { traceId, id, reviewId },
            { allowed: false, action: 'hold_for_review', message }, 202);
}

if (policyResult.autoApproved) {
  finalTraceData.status = 'success';
  // AutoApprovalDecision written, ReviewItem skipped (hard-rule preserved upstream)
  return ok(res, { traceId, id, autoApproved: true }, {}, 201);
}

// approve / notify-only / no-match
return ok(res, { message: 'Decision Trace successfully recorded', traceId, id }, {}, 201);

Three things the engine refuses.

Three engineering choices distinguish the Policy Engine from a probabilistic AI-firewall: ADL predicates are typed and compile to AWS Cedar's formal semantics, conflicts resolve by published action ladder, and user-supplied regex patterns compile through Google's RE2 engine — full regex syntax, linear-time matching, no catastrophic backtracking. None of the three is configurable; all three are mechanical.

01 Engine refuses

Ambiguous semantics

ADL predicates are typed at the registry level — confidence_below(decimal), status_equals(string), human_override_enabled(bool). The compiler rejects arity + type mismatches before a policy reaches the substrate. AWS Cedar (CNCF Sandbox, peer-reviewed formal semantics) evaluates the compiled form. No Rego inference, no yaml-with-jinja, no customer-defined eval order.

02 Engine refuses

Surprise outcomes

Two policies match the same trace? block always wins. If flag_for_review and auto_approve both fire, the trace resolves as auto-approved (Auto-Approval Engine merger — LD-9). Otherwise flag_for_review > notify > standalone auto_approve > approve. Order is burnt into the evaluator. No customer-tunable priority math. No probability mixing.

03 Engine refuses

ReDoS payloads

User-supplied regex patterns compile through Google's RE2 engine. A pattern like (a+)+b evaluates in linear time on any input — RE2 has no backtracking-based execution path, so adversarial patterns cannot trigger exponential blow-up. Operators get full regex syntax (\d, {n,m}, character classes, anchors); the engine just refuses the algorithmic shape that makes ReDoS possible.

Four articles, one artefact each.

Each regulator-article maps to one concrete Policy Engine artefact — not a marketing claim, a record class. The auditor reads them directly. PolicyTranscript for Art. 14 oversight; the 3-role approval queue for BaFin MaRisk functional separation; the portable bundle for DORA Art. 30 exit; the insurance feed for AI underwriting.

ArticleObligationPolicy Engine artefact
Art. 14Human oversight on high-risk AI — natural persons must be able to monitor, intervene, and override.One immutable PolicyTranscript per fired policy — pinned to the policyVersionHash, fields scrubbed at write per Cardinal Rule #4, chained via SHA-256 (RFC 8785 JCS), surfaced in the Review Queue with reviewer + enforcer identities.
BaFin MaRisk AT 4.3.1Functional separation in IT-supported processes — author, reviewer, and approver must be distinct natural persons.The approval workflow rejects with THREE_ROLE_VIOLATION when submittedBy, reviewedBy, or enforcedBy overlap. Break-glass requires a typed-confirm phrase + 50-char justification, all recorded on the audit chain.
DORA Art. 30ICT third-party exit plan — financial entities must be able to leave a critical provider without losing audit evidence.The portable bundle export wraps every PolicyVersion, PolicyTranscript, review outcome, and effectiveness snapshot — dual-signed (D-Trust QTSP + Sigstore Cosign) and verifiable offline via the bundled adjudon-replay binary. See portable-bundle.
§371a Abs. 3 ZPOElectronic records with a qualified electronic timestamp carry statutory evidentiary presumption in German civil court.Daily Merkle anchor over every PolicyTranscript chained to a D-Trust qSeal (eIDAS Art. 35 qualified electronic seal). See court-admissibility.

Two policies match. Block beats flag.

You've seen four code blocks, three refusals, four article-anchors. Next: write a policy in ADL, watch the immutable PolicyVersion chain grow, and read which trace got which transcript. The first call lands at the engineer who wrote the compiler — not at an SDR.