Deadlock Graph Viz
The three production-grade failure modes that hang every distributed-training job, side by side. Pick a scenario (1 / 2 / 3), hit Play (or Space), and the storyboard auto-advances through running → waiting → blocked (or timeout) on a 2×2 grid of four ranks. Wait-for arrows trace the circular dependency once it forms; on the deadlock frame, the cycle edges pulse red and the grid glows. After the run settles, a fix card surfaces the one-line remediation.
Ready
0.0s
Rank 0
idle
Rank 1
idle
Rank 2
idle
Rank 3
idle
1-3 select scenario / Space to play
Pick a scenario and press Play to watch operation order unfold across four ranks.
Operation order scenario selected. Press Play or Space to start the animation.
Customize
Playback
order
2200ms
Installation
npx shadcn@latest add https://craftbits.dev/r/deadlock-graph-viz.jsonUsage
import { DeadlockGraphViz } from "@craft-bits/viz/deadlock-graph-viz";
<DeadlockGraphViz />Drive the active scenario from outside (controlled mode):
const [scenario, setScenario] = useState<DeadlockGraphVizScenario>("branch");
<DeadlockGraphViz scenario={scenario} onScenarioChange={setScenario} />;Subscribe to phase transitions for an external counter:
<DeadlockGraphViz
onPhaseChange={(phase) => {
if (phase === "done") trackDeadlockSeen();
}}
/>Understanding the component
- Three chips. Each chip corresponds to one scenario and is wired to a keyboard shortcut (
1,2,3). The active chip gets the accent palette; the rest stay muted. - 2×2 rank grid. Four rank boxes laid out at fixed positions. Each rank renders the current operation and a status pill;
waitingranks pulse,blockedanddeadranks shake with a smallxkeyframe and shrink toscale: 0.97. - Frame engine. Each scenario compiles to three frames:
active → waiting → blocked(ortimeout). The playback loop walks the array on a2.2scadence and lands onphase: "done"once the last frame settles. - Wait-for arrows. Curved quadratic paths trace the dependency between ranks. Non-cycle arrows render warning-coloured and dashed; cycle arrows render error-coloured, solid, and breathe in opacity / stroke width to spotlight the loop.
- Status bar. Headline status (
Running/Waiting.../DEADLOCK DETECTED/TIMEOUT) with a colour-coded dot that pulses on the final frame, plus an elapsed-time chip that shifts colour as it grows. - Fix card. Surfaces the scenario's one-line remediation once
phase === "done". Wrapped in arole="note"for screen readers. - Reduced motion. Under
prefers-reduced-motion: reduce, every entrance collapses toduration: 0, the cycle arrows stop pulsing, the rank shake disappears, and the auto-advance falls to a90mscadence so the final state still resolves.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
scenario | DeadlockGraphVizScenario | — | Controlled active scenario. Pair with onScenarioChange. |
defaultScenario | DeadlockGraphVizScenario | "order" | Uncontrolled initial scenario. |
onScenarioChange | (scenario) => void | — | Fires after the active scenario changes. |
onPhaseChange | (phase) => void | — | Fires on every idle → playing → done transition. |
scenarios | readonly DeadlockGraphVizScenarioDef[] | three canonical NCCL scenarios | Override the built-in scenario list — supply your own chips, frames, and fix copy. |
frameDurationMs | number | 2200 | Milliseconds between auto-advanced frames while phase === "playing". |
transition | Transition | SPRINGS.snap | Override the rank / arrow entrance spring. |
className | string | — | Merged onto the root via cn(). |
Accessibility
- The root is
role="figure"with anaria-labeldescribing the viz; the live status region announces the active scenario, the current frame's prose label while playing, and the final "complete" line. - Chips are real
<button type="button">elements witharia-pressedreflecting the active scenario andaria-labelnaming the keyboard shortcut. - The fix card is
role="note"witharia-label="Remediation"so screen-reader users can navigate to it directly. - Keyboard:
1–3selects a scenario,Spacetogglesplay/reset. Focus is visible viafocus-visible:ring. - Colour is never the only signal — every rank carries its
statuspill, the headline status carries its label, the narration paragraph encodes state in prose, and the SR live region repeats it all. - Motion respects
prefers-reduced-motion: reduce— every entrance, the cycle-arrow pulse, the box-shadow glow on the deadlock frame, and the rank-distress shake collapse to instant, and the auto-advance shortens to90msso the final state still resolves quickly.
Credits
- Extracted from:
craftingattention(app/src/lessons/primitives/systems/DeadlockGraphViz.tsx). The source was a tri-mode lesson primitive (Explore / Predict / Challenge) wrapped in the project'sWidgetchrome with hard dependencies onModeStrip,ChallengeBtn,FeedbackBadge, andScoreDots, plus per-track palette tokens (--color-accent-400,--color-warn-400,--color-fail-400,--color-ink-*) and lesson-specificSPRINGS.snappy/SPRINGS.gentle/STAGGER.tightaliases. The craft-bits extract drops the predict and challenge modes (and their scoring chrome) to ship a focused, single-purpose scenario stepper; rebuilds the chip strip, status bar, narration panel, and fix card as inline token-styled elements; routes every colour through the semantic--cb-accent/--cb-warning/--cb-error/--cb-fg-*palette so consumer themes repaint freely; re-keys every transition to the canonicalSPRINGS.snap/SPRINGS.smoothfrom@craft-bits/core/motion; replacesSTAGGER.tightwith the library-wideSTAGGERconstant; exposes a Radix-style controlled / uncontrolledscenarioAPI withonScenarioChange+onPhaseChange; lifts the hardcoded scenario list into ascenariosprop with the canonical NCCL triad as the default; and wraps the component inforwardRefacceptingclassNameviacn()and spreading...propson the root.