Intent → Reasoning → Action → Feedback — the four-step civic procedure cycle that governs any AI-assisted task.
STABLE
#svg
#real-content
#dark-mode-tested
Live
Procedure № IRAF
Cyclic
Spec
Intent
Visualise the IRAF loop (Intent · Reasoning · Action · Feedback) as a four-node cycle. Frame it like an institutional procedure diagram — clean, symmetric, every label fits its frame.
Acceptance Criteria
[ ] Renders all four IRAF stages in a 2×2 corner layout
[ ] Connecting arrows show clockwise progression
[ ] Each node has its civic ordinal (I-IV) and a verb
[ ] Every label fits inside its frame at the rendered size
[ ] Does not require JS — pure SVG render
Constraints
Pure SVG
Theme-aware via tokens
No hex literals in brand-colour slots
≤ 150 lines body
// iraf-loop.astro
---
import type { CompendiumMeta } from '../../../data/types';
export const meta: CompendiumMeta = {
id: 20,
slug: 'iraf-loop',
name: 'IRAF Loop',
category: 'pillars-roles',
subcategory: 'loops-cycles',
description: 'Intent → Reasoning → Action → Feedback — the four-step civic procedure cycle that governs any AI-assisted task.',
spec: `## Intent
Visualise the IRAF loop (Intent · Reasoning · Action · Feedback) as a four-node cycle. Frame it like an institutional procedure diagram — clean, symmetric, every label fits its frame.
## Acceptance Criteria
- [ ] Renders all four IRAF stages in a 2×2 corner layout
- [ ] Connecting arrows show clockwise progression
- [ ] Each node has its civic ordinal (I-IV) and a verb
- [ ] Every label fits inside its frame at the rendered size
- [ ] Does not require JS — pure SVG render
## Constraints
- Pure SVG
- Theme-aware via tokens
- No hex literals in brand-colour slots
- ≤ 150 lines body`,
tags: ['svg', 'real-content', 'dark-mode-tested'],
status: 'stable',
health: { ics: 100, a11y: 'aa', themed: true, animated: false },
};
// 2×2 corner layout. Each box is generous enough for the longest
// label ("REASONING", 9 chars at 14px Inter-Black ≈ 95px). Arrows
// run along the perimeter, never through the boxes.
const nodes = [
{ id: 'TL', x: 20, y: 20, c: 'var(--color-do)', k: 'I', t: 'INTENT', sub: 'DECLARE' },
{ id: 'TR', x: 180, y: 20, c: 'var(--color-co)', k: 'II', t: 'REASONING', sub: 'DELIBERATE' },
{ id: 'BR', x: 180, y: 140, c: 'var(--color-de)', k: 'III', t: 'ACTION', sub: 'EXECUTE' },
{ id: 'BL', x: 20, y: 140, c: 'var(--color-go)', k: 'IV', t: 'FEEDBACK', sub: 'REVIEW' },
];
const boxW = 120;
const boxH = 50;
---
<div data-this-component class="w-full bg-paper text-black border-4 border-black shadow-nb-sm p-3 overflow-hidden">
<div class="flex items-center justify-between mb-2 gap-2">
<div class="font-mono text-[9px] uppercase tracking-[0.25em] opacity-60 truncate">Procedure № IRAF</div>
<div class="font-mono text-[9px] uppercase tracking-[0.25em] opacity-60 shrink-0">Cyclic</div>
</div>
<svg viewBox="0 0 320 210" class="w-full block" aria-label="IRAF cycle: Intent, Reasoning, Action, Feedback" role="img">
<defs>
<marker id="iraf-arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" fill="var(--color-ink)" />
</marker>
</defs>
<!-- Perimeter arrows (clockwise: TL → TR → BR → BL → TL).
Each arrow runs along the gutter between boxes; never crosses
a box. End-points stop 8px shy of the next box for a clean
arrowhead landing. -->
<path d="M 144 45 L 176 45" stroke="var(--color-ink)" stroke-width="3" fill="none" marker-end="url(#iraf-arrow)" />
<path d="M 240 74 L 240 136" stroke="var(--color-ink)" stroke-width="3" fill="none" marker-end="url(#iraf-arrow)" />
<path d="M 176 165 L 144 165" stroke="var(--color-ink)" stroke-width="3" fill="none" marker-end="url(#iraf-arrow)" />
<path d="M 80 136 L 80 74" stroke="var(--color-ink)" stroke-width="3" fill="none" marker-end="url(#iraf-arrow)" />
<!-- Centre stamp -->
<text x="160" y="100" text-anchor="middle" font-family="JetBrains Mono, monospace" font-size="9" letter-spacing="3" fill="var(--color-ink)" opacity="0.45">CYCLE</text>
<text x="160" y="118" text-anchor="middle" font-family="Inter, sans-serif" font-weight="900" font-size="20" letter-spacing="2" fill="var(--color-ink)">IRAF</text>
<!-- Four corner nodes -->
{nodes.map((n) => (
<g>
<rect
x={n.x} y={n.y} width={boxW} height={boxH}
fill={n.c} stroke="var(--color-ink)" stroke-width="3"
/>
<!-- Civic ordinal in the top-left of the tile -->
<text x={n.x + 8} y={n.y + 14} font-family="JetBrains Mono, monospace" font-size="9" font-weight="700" fill="var(--color-ink)" opacity="0.7">{n.k}</text>
<!-- Main label, centred -->
<text x={n.x + boxW / 2} y={n.y + 30} text-anchor="middle" font-family="Inter, sans-serif" font-weight="900" font-size="16" fill="var(--color-ink)">{n.t}</text>
<!-- Verb sub-label -->
<text x={n.x + boxW / 2} y={n.y + 43} text-anchor="middle" font-family="JetBrains Mono, monospace" font-size="8" letter-spacing="1.5" fill="var(--color-ink)" opacity="0.7">{n.sub}</text>
</g>
))}
</svg>
</div>