Skip to main content
Compendium / Maturity & Process / Reviews & Logs / GTR Tracker
#029

GTR Tracker

Green-to-Red tracker — a horizontal strip of the last N cycles coloured by validation status.

STABLE DE
#chart #real-content #dark-mode-tested #responsive
Live
// GTR · last 20 cycles now →
met 15
deg 3
miss 2
Spec

Intent

The Green-To-Red tracker is DoCoDeGo's "shipped vs slipped" memory. Each segment is one cycle. Green = met intent, amber = degraded, red = missed. The strip exposes trend without averaging it away.

Acceptance Criteria

  • [ ] N segments left-to-right, oldest to newest
  • [ ] Three states colour-coded: green / amber / red
  • [ ] Numeric tally of each state in the footer

Constraints

  • No JS
  • Segments equal width via CSS grid
// gtr-tracker.astro
---
import type { CompendiumMeta } from '../../../data/types';

export const meta: CompendiumMeta = {
  id: 29,
  slug: 'gtr-tracker',
  name: 'GTR Tracker',
  category: 'maturity-process',
  subcategory: 'reviews-and-logs',
  pillar: 'de',
  description: 'Green-to-Red tracker — a horizontal strip of the last N cycles coloured by validation status.',
  spec: `## Intent
The Green-To-Red tracker is DoCoDeGo's "shipped vs slipped" memory. Each segment is one cycle. Green = met intent, amber = degraded, red = missed. The strip exposes trend without averaging it away.

## Acceptance Criteria
- [ ] N segments left-to-right, oldest to newest
- [ ] Three states colour-coded: green / amber / red
- [ ] Numeric tally of each state in the footer

## Constraints
- No JS
- Segments equal width via CSS grid`,
  tags: ['chart', 'real-content', 'dark-mode-tested', 'responsive'],
  status: 'stable',
  health: { ics: 87, a11y: 'aa', themed: true, animated: false },
};

type S = 'g' | 'a' | 'r';
const cycles: S[] = ['g','g','a','g','r','g','g','a','g','g','g','a','g','g','g','g','r','g','g','g'];
const tally = {
  g: cycles.filter(c=>c==='g').length,
  a: cycles.filter(c=>c==='a').length,
  r: cycles.filter(c=>c==='r').length,
};
const colorOf = (s: S) =>
  s === 'g' ? 'var(--color-success)' : s === 'a' ? 'var(--color-do)' : 'var(--color-danger)';
const labelOf = (s: S) => s === 'g' ? 'met' : s === 'a' ? 'degraded' : 'missed';
---

<div class="w-full bg-paper text-ink border-4 border-ink">
  <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 truncate">// GTR · last {cycles.length} cycles</span>
    <span class="font-mono text-[10px] uppercase tracking-widest shrink-0">now &rarr;</span>
  </header>
  <div class="p-3">
    <div
      class="grid border-2 border-ink overflow-hidden"
      style={`grid-template-columns: repeat(${cycles.length}, minmax(0, 1fr));`}
      role="img"
      aria-label={`Last ${cycles.length} cycles: ${tally.g} met, ${tally.a} degraded, ${tally.r} missed`}
    >
      {cycles.map((c, i) => (
        <div
          class="h-8 border-e border-ink/40 last:border-e-0"
          style={`background:${colorOf(c)}`}
          title={`Cycle ${i+1}: ${labelOf(c)}`}
        ></div>
      ))}
    </div>
    <div class="mt-2 grid grid-cols-3 gap-1 font-mono text-[10px] uppercase tracking-widest">
      <div class="flex items-center gap-1.5 min-w-0">
        <span class="inline-block w-2.5 h-2.5 border-2 border-ink bg-success shrink-0"></span>
        <span class="truncate">met</span>
        <span class="ms-auto font-black tabular-nums">{tally.g}</span>
      </div>
      <div class="flex items-center gap-1.5 min-w-0">
        <span class="inline-block w-2.5 h-2.5 border-2 border-ink bg-do shrink-0"></span>
        <span class="truncate">deg</span>
        <span class="ms-auto font-black tabular-nums">{tally.a}</span>
      </div>
      <div class="flex items-center gap-1.5 min-w-0">
        <span class="inline-block w-2.5 h-2.5 border-2 border-ink bg-danger shrink-0"></span>
        <span class="truncate">miss</span>
        <span class="ms-auto font-black tabular-nums">{tally.r}</span>
      </div>
    </div>
  </div>
</div>