Skip to main content
Compendium / Maturity & Process / Instruments / Validation Meter
#033

Validation Meter

Mirror dial showing human validation throughput — the constraint that gates the velocity meter.

STABLE DE
#svg #gauge #animated #reduced-motion #real-content #dark-mode-tested
Live
// validation lag −8
0 5 10 15 20 6 validated / day
Spec

Intent

The validation-side twin of the velocity meter. Validation is the bottleneck DoCoDeGo's DE pillar exists to widen. Render as a mirror-image gauge: when this needle lags the velocity needle, drift accumulates.

Acceptance Criteria

  • [ ] Same scale as velocity meter for comparability
  • [ ] Indicates "lag vs velocity" if validation < generation
  • [ ] Coloured bands match velocity gauge convention

Constraints

  • Pure SVG, no runtime
  • Theme-aware via tokens
// validation-meter.astro
---
import type { CompendiumMeta } from '../../../data/types';

export const meta: CompendiumMeta = {
  id: 33,
  slug: 'validation-meter',
  name: 'Validation Meter',
  category: 'maturity-process',
  subcategory: 'instruments',
  pillar: 'de',
  description: 'Mirror dial showing human validation throughput — the constraint that gates the velocity meter.',
  spec: `## Intent
The validation-side twin of the velocity meter. Validation is the bottleneck DoCoDeGo's DE pillar exists to widen. Render as a mirror-image gauge: when this needle lags the velocity needle, drift accumulates.

## Acceptance Criteria
- [ ] Same scale as velocity meter for comparability
- [ ] Indicates "lag vs velocity" if validation < generation
- [ ] Coloured bands match velocity gauge convention

## Constraints
- Pure SVG, no runtime
- Theme-aware via tokens`,
  tags: ['svg', 'gauge', 'animated', 'reduced-motion', 'real-content', 'dark-mode-tested'],
  status: 'stable',
  health: { ics: 90, a11y: 'aa', themed: true, animated: true },
};

const value = 6;
const max = 20;
const velocity = 14;
const lag = velocity - value;
const pct = Math.min(value / max, 1);
const angle = 120 - pct * 240;
const ticks = [0, 25, 50, 75, 100].map((p) => ({ a: 120 - (p / 100) * 240, n: Math.round((p / 100) * max) }));
---

<div data-this-component class="w-full bg-paper text-ink border-4 border-ink overflow-hidden">
  <header class="flex items-baseline justify-between px-3 py-2 border-b-2 border-ink">
    <span class="font-mono text-[10px] uppercase tracking-widest opacity-60">// validation</span>
    <span class="font-mono text-[10px] uppercase tracking-widest text-attention">lag &minus;{lag}</span>
  </header>
  <div class="p-3 flex flex-col items-center">
    <svg viewBox="0 0 200 140" class="w-full max-w-[240px] block" role="img" aria-label={`Validation ${value} per day, lag ${lag} behind velocity`}>
      <path d="M 170 110 A 70 70 0 1 0 30 110" fill="none" stroke="var(--color-ink)" stroke-width="3" />
      <path d="M 170 110 A 70 70 0 0 0 100 40" fill="none" stroke="var(--color-success)" stroke-width="6" opacity="0.85" />
      <path d="M 100 40 A 70 70 0 0 0 60 52" fill="none" stroke="var(--color-do)" stroke-width="6" opacity="0.85" />
      <path d="M 60 52 A 70 70 0 0 0 30 110" fill="none" stroke="var(--color-danger)" stroke-width="6" opacity="0.85" />
      {ticks.map((t) => {
        const rad = ((t.a - 90) * Math.PI) / 180;
        const x1 = 100 + Math.cos(rad) * 60, y1 = 100 + Math.sin(rad) * 60;
        const x2 = 100 + Math.cos(rad) * 72, y2 = 100 + Math.sin(rad) * 72;
        const lx = 100 + Math.cos(rad) * 50, ly = 100 + Math.sin(rad) * 50;
        return (
          <>
            <line x1={x1} y1={y1} x2={x2} y2={y2} stroke="var(--color-ink)" stroke-width="2" />
            <text x={lx} y={ly + 3} text-anchor="middle" font-family="JetBrains Mono, monospace" font-size="7" fill="var(--color-ink)" opacity="0.7">{t.n}</text>
          </>
        );
      })}
      <g transform={`rotate(${angle} 100 100)`} class="needle">
        <polygon points="100,38 96,104 104,104" fill="var(--color-ink)" />
        <circle cx="100" cy="100" r="6" fill="var(--color-co)" stroke="var(--color-ink)" stroke-width="2" />
      </g>
      <text x="100" y="130" text-anchor="middle" font-family="Inter, sans-serif" font-weight="900" font-size="20" fill="var(--color-ink)">{value}</text>
    </svg>
    <span class="font-mono text-[10px] uppercase tracking-widest opacity-60 -mt-1">validated / day</span>
  </div>
</div>

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