Skip to main content
Compendium / Governance & Risk / Thresholds / Drift Detector
#039

Drift Detector

Gauge measuring how far implementation has drifted from approved intent.

STABLE GO
#svg #gauge #animated #reduced-motion #dark-mode-tested
Live

Intent ↔ implementation drift

DRIFT %
41% INTERVENE

Tolerance · 30%

Spec

Intent

Drift is the silent killer in autonomous systems — small unobserved divergences from spec that compound until an incident surfaces. This gauge gives it a face.

Acceptance Criteria

  • [ ] Needle position maps to drift % (0–100)
  • [ ] Threshold tick marks the tolerance line
  • [ ] Colour bands: green / pink / red
  • [ ] Numeric readout + status word

Constraints

  • Pure SVG, theme-aware fills
  • Below threshold = nominal; above = needs intervention
  • Respects prefers-reduced-motion
// drift-detector.astro
---
import type { CompendiumMeta } from '../../../data/types';

export const meta: CompendiumMeta = {
  id: 39,
  slug: 'drift-detector',
  name: 'Drift Detector',
  category: 'governance-risk',
  subcategory: 'thresholds',
  pillar: 'go',
  description: 'Gauge measuring how far implementation has drifted from approved intent.',
  spec: `## Intent
Drift is the silent killer in autonomous systems — small unobserved divergences from spec that compound until an incident surfaces. This gauge gives it a face.

## Acceptance Criteria
- [ ] Needle position maps to drift % (0–100)
- [ ] Threshold tick marks the tolerance line
- [ ] Colour bands: green / pink / red
- [ ] Numeric readout + status word

## Constraints
- Pure SVG, theme-aware fills
- Below threshold = nominal; above = needs intervention
- Respects prefers-reduced-motion`,
  tags: ['svg', 'gauge', 'animated', 'reduced-motion', 'dark-mode-tested'],
  status: 'stable',
  health: { ics: 86, a11y: 'aa', themed: true, animated: true },
};

const drift = 41;
const threshold = 30;
const status = drift > threshold ? 'INTERVENE' : 'NOMINAL';
const statusTone = drift > threshold ? 'bg-danger' : 'bg-success';

const angle = -90 + (drift / 100) * 180;
const tickAngle = -90 + (threshold / 100) * 180;
---

<div data-this-component class="w-full bg-paper text-black border-4 border-black p-4 flex flex-col items-center overflow-hidden">
  <p class="font-mono text-[10px] uppercase tracking-widest opacity-60 self-start">Intent ↔ implementation drift</p>

  <svg viewBox="0 0 200 120" class="w-full max-w-[260px] block mt-1" role="img" aria-label={`Drift ${drift} percent, threshold ${threshold} percent`}>
    <path d="M 10 100 A 90 90 0 0 1 64 31"  stroke="var(--color-success)"   stroke-width="14" fill="none" />
    <path d="M 64 31  A 90 90 0 0 1 136 31" stroke="var(--color-attention)" stroke-width="14" fill="none" />
    <path d="M 136 31 A 90 90 0 0 1 190 100" stroke="var(--color-danger)"   stroke-width="14" fill="none" />

    <g transform={`rotate(${tickAngle} 100 100)`}>
      <line x1="100" y1="14" x2="100" y2="32" stroke="var(--color-ink)" stroke-width="3" />
    </g>

    <g class="dd-needle" transform={`rotate(${angle} 100 100)`}>
      <polygon points="100,100 96,108 100,22 104,108" fill="var(--color-ink)" />
      <circle cx="100" cy="100" r="6" fill="var(--color-ink)" />
      <circle cx="100" cy="100" r="2" fill="var(--color-paper)" />
    </g>

    <text x="100" y="118" text-anchor="middle" font-family="JetBrains Mono, monospace" font-size="9" letter-spacing="2" fill="var(--color-ink)" opacity="0.6">DRIFT %</text>
  </svg>

  <div class="mt-1 flex items-baseline gap-3">
    <span class="font-black text-2xl leading-none tabular-nums">{drift}%</span>
    <span class:list={['px-2 py-0.5 border-2 border-black text-white font-black text-[10px] uppercase tracking-widest', statusTone]}>{status}</span>
  </div>
  <p class="mt-1 font-mono text-[10px] uppercase tracking-widest opacity-60">Tolerance · {threshold}%</p>
</div>

<style>
  [data-this-component] .dd-needle {
    transform-origin: 100px 100px;
    transition: transform 700ms cubic-bezier(0.2, 0, 0.13, 1.4);
  }
  @media (prefers-reduced-motion: reduce) {
    [data-this-component] .dd-needle {
      transition: none;
    }
  }
</style>