# Grid Blueprint Visualization System

**Version:** 1.0
**Status:** Specification
**Last Updated:** 2026-01-24

---

## Overview

The Blueprint Visualization System provides GPU PCB schematic-style visualization of Grid mission topology. It renders before execution starts, showing the complete mission structure including phases, task groups, agents, and data flow.

---

## Design Principles

### GPU PCB Aesthetic

The visualization draws inspiration from graphics card PCB schematics:

1. **Technical Precision** - Engineering drawing quality, not artistic interpretation
2. **Orthogonal Routing** - All edges use right-angle paths (Manhattan routing)
3. **Port-Based Connections** - Edges connect to specific labeled ports on nodes
4. **Grid Background** - Subtle grid pattern for technical feel
5. **Monospace Typography** - Fixed-width fonts for all text
6. **Angular Geometry** - Rectangles and straight lines, no rounded corners

---

## Color Specification

### Base Palette

```css
:root {
  /* Background */
  --bp-background: #0a1628;           /* Deep navy-black */
  --bp-background-grid: #0f1f35;      /* Slightly lighter for grid lines */

  /* Primary Accent */
  --bp-accent-primary: #00ff88;       /* Bright cyan-green */
  --bp-accent-secondary: #00ccff;     /* Cyan blue */
  --bp-accent-tertiary: #ff6600;      /* Warning orange */

  /* Node Colors */
  --bp-node-border: #00ff88;          /* Cyan-green borders */
  --bp-node-fill: #0a1628;            /* Same as background (transparent feel) */
  --bp-node-header: #102030;          /* Slightly lighter header area */

  /* Status Colors */
  --bp-status-pending: #4a5568;       /* Gray - waiting */
  --bp-status-running: #00ff88;       /* Cyan-green - active (pulsing) */
  --bp-status-complete: #00ff88;      /* Cyan-green - done (solid) */
  --bp-status-failed: #ff3366;        /* Red-pink - error */

  /* Edge Colors */
  --bp-edge-inactive: #1a3a5c;        /* Dim blue - not yet active */
  --bp-edge-active: #00ff88;          /* Cyan-green - data flowing */
  --bp-edge-data: #00ccff;            /* Cyan - data transfer animation */

  /* Text */
  --bp-text-primary: #e0e0e0;         /* Light gray */
  --bp-text-secondary: #8899aa;       /* Medium gray */
  --bp-text-label: #00ff88;           /* Accent for labels */
}
```

### Status Icon Specification

| Status | Icon | Color | Animation |
|--------|------|-------|-----------|
| Pending | `\u25CC` (dotted circle) | `--bp-status-pending` | None |
| Running | `\u25C9` (fisheye) | `--bp-status-running` | Pulse 1.5s ease-in-out |
| Complete | `\u25CF` (filled circle) | `--bp-status-complete` | None |
| Failed | `\u2715` (X mark) | `--bp-status-failed` | None |

---

## Data Schema

### Blueprint Data Structure

```typescript
interface Blueprint {
  version: "1.0";
  generated_at: string;  // ISO timestamp
  mission: MissionMetadata;
  nodes: BlueprintNode[];
  edges: BlueprintEdge[];
  layout: LayoutHints;
}

interface MissionMetadata {
  id: string;
  name: string;
  mode: "AUTOPILOT" | "GUIDED" | "HANDS_ON";
  complexity: "TRIVIAL" | "SIMPLE" | "MEDIUM" | "COMPLEX" | "MASSIVE";
  total_phases: number;
  total_blocks: number;
  total_threads: number;
  estimated_duration_minutes: number;
}

interface BlueprintNode {
  id: string;
  type: NodeType;
  label: string;
  sublabel?: string;
  status: NodeStatus;
  parent_id?: string;  // For hierarchical grouping
  metadata: NodeMetadata;
  ports: Port[];
  position?: Position;  // Computed by layout engine
}

type NodeType =
  | "master_control"
  | "phase_coordinator"
  | "task_group"         // Block
  | "agent"              // Executor, Recognizer, etc.
  | "checkpoint"
  | "data_store";        // .grid files, databases

type NodeStatus = "pending" | "running" | "complete" | "failed";

interface NodeMetadata {
  wave?: number;
  depends_on?: string[];
  files_modified?: string[];
  autonomous?: boolean;
  agent_type?: string;   // "planner", "executor", "recognizer", etc.
  checkpoint_type?: string;  // "human-verify", "decision", "human-action"
  estimated_duration?: number;  // minutes
  [key: string]: any;
}

interface Port {
  id: string;
  type: "input" | "output" | "bidirectional";
  label: string;
  position: "top" | "right" | "bottom" | "left";
  index: number;  // Position along the side (0-indexed)
}

interface BlueprintEdge {
  id: string;
  source_node: string;
  source_port: string;
  target_node: string;
  target_port: string;
  type: EdgeType;
  label?: string;
  status: EdgeStatus;
  data_type?: string;  // "plan", "warmth", "summary", "verification"
}

type EdgeType =
  | "control_flow"      // Execution sequence
  | "data_flow"         // Data transfer
  | "dependency"        // Must complete before
  | "verification";     // Recognizer checks

type EdgeStatus = "inactive" | "active" | "complete";

interface LayoutHints {
  direction: "TB" | "LR";  // Top-to-bottom or left-to-right
  spacing: {
    horizontal: number;
    vertical: number;
  };
  group_padding: number;
  node_width: number;
  node_height: number;
}

interface Position {
  x: number;
  y: number;
  width: number;
  height: number;
}
```

### Example Blueprint Data

```json
{
  "version": "1.0",
  "generated_at": "2026-01-24T15:30:00Z",
  "mission": {
    "id": "mission-20260124-153000",
    "name": "REST API with Auth",
    "mode": "AUTOPILOT",
    "complexity": "MEDIUM",
    "total_phases": 2,
    "total_blocks": 4,
    "total_threads": 10,
    "estimated_duration_minutes": 25
  },
  "nodes": [
    {
      "id": "mc",
      "type": "master_control",
      "label": "MASTER CONTROL",
      "sublabel": "Orchestration Layer",
      "status": "running",
      "metadata": {},
      "ports": [
        {"id": "mc-out-plan", "type": "output", "label": "PLAN", "position": "bottom", "index": 0},
        {"id": "mc-in-status", "type": "input", "label": "STATUS", "position": "top", "index": 0}
      ]
    },
    {
      "id": "phase-01",
      "type": "phase_coordinator",
      "label": "PHASE 01",
      "sublabel": "Foundation",
      "status": "pending",
      "parent_id": "mc",
      "metadata": {"wave": 1},
      "ports": [
        {"id": "p01-in", "type": "input", "label": "IN", "position": "top", "index": 0},
        {"id": "p01-out", "type": "output", "label": "OUT", "position": "bottom", "index": 0}
      ]
    },
    {
      "id": "block-01",
      "type": "task_group",
      "label": "BLOCK 01",
      "sublabel": "Database Setup",
      "status": "pending",
      "parent_id": "phase-01",
      "metadata": {
        "wave": 1,
        "autonomous": true,
        "files_modified": ["prisma/schema.prisma", "src/lib/db.ts"]
      },
      "ports": [
        {"id": "b01-in", "type": "input", "label": "IN", "position": "top", "index": 0},
        {"id": "b01-out-exec", "type": "output", "label": "EXEC", "position": "right", "index": 0},
        {"id": "b01-out-verify", "type": "output", "label": "VERIFY", "position": "bottom", "index": 0}
      ]
    },
    {
      "id": "exec-01",
      "type": "agent",
      "label": "EXECUTOR",
      "sublabel": "Block 01",
      "status": "pending",
      "parent_id": "block-01",
      "metadata": {"agent_type": "executor"},
      "ports": [
        {"id": "e01-in", "type": "input", "label": "PLAN", "position": "left", "index": 0},
        {"id": "e01-out", "type": "output", "label": "SUMMARY", "position": "right", "index": 0}
      ]
    },
    {
      "id": "recog-01",
      "type": "agent",
      "label": "RECOGNIZER",
      "sublabel": "Verification",
      "status": "pending",
      "parent_id": "block-01",
      "metadata": {"agent_type": "recognizer"},
      "ports": [
        {"id": "r01-in", "type": "input", "label": "CHECK", "position": "top", "index": 0},
        {"id": "r01-out", "type": "output", "label": "RESULT", "position": "bottom", "index": 0}
      ]
    }
  ],
  "edges": [
    {
      "id": "e-mc-p01",
      "source_node": "mc",
      "source_port": "mc-out-plan",
      "target_node": "phase-01",
      "target_port": "p01-in",
      "type": "control_flow",
      "status": "inactive"
    },
    {
      "id": "e-p01-b01",
      "source_node": "phase-01",
      "source_port": "p01-out",
      "target_node": "block-01",
      "target_port": "b01-in",
      "type": "control_flow",
      "status": "inactive"
    },
    {
      "id": "e-b01-exec",
      "source_node": "block-01",
      "source_port": "b01-out-exec",
      "target_node": "exec-01",
      "target_port": "e01-in",
      "type": "data_flow",
      "label": "PLAN",
      "status": "inactive",
      "data_type": "plan"
    },
    {
      "id": "e-exec-recog",
      "source_node": "exec-01",
      "source_port": "e01-out",
      "target_node": "recog-01",
      "target_port": "r01-in",
      "type": "verification",
      "label": "SUMMARY",
      "status": "inactive",
      "data_type": "summary"
    }
  ],
  "layout": {
    "direction": "TB",
    "spacing": {"horizontal": 200, "vertical": 100},
    "group_padding": 40,
    "node_width": 180,
    "node_height": 80
  }
}
```

---

## CSS Specification

### Core Styles

```css
/* Blueprint Container */
.grid-blueprint {
  font-family: 'JetBrains Mono', 'Fira Code', 'SF Mono', monospace;
  background-color: var(--bp-background);
  background-image:
    linear-gradient(var(--bp-background-grid) 1px, transparent 1px),
    linear-gradient(90deg, var(--bp-background-grid) 1px, transparent 1px);
  background-size: 20px 20px;
  color: var(--bp-text-primary);
  min-height: 600px;
  position: relative;
  overflow: hidden;
}

/* Grid Header */
.grid-blueprint__header {
  position: absolute;
  top: 20px;
  left: 20px;
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 2px;
  color: var(--bp-text-secondary);
}

.grid-blueprint__title {
  font-size: 14px;
  color: var(--bp-accent-primary);
  margin-bottom: 4px;
}

/* Node Base Styles */
.bp-node {
  position: absolute;
  background: var(--bp-node-fill);
  border: 2px solid var(--bp-node-border);
  min-width: 180px;
  min-height: 80px;
}

.bp-node__header {
  background: var(--bp-node-header);
  padding: 8px 12px;
  border-bottom: 1px solid var(--bp-node-border);
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.bp-node__label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--bp-text-label);
}

.bp-node__sublabel {
  font-size: 9px;
  color: var(--bp-text-secondary);
  margin-top: 2px;
}

.bp-node__status {
  font-size: 12px;
}

.bp-node__body {
  padding: 10px 12px;
  font-size: 10px;
  color: var(--bp-text-secondary);
}

/* Node Type Variants */
.bp-node--master-control {
  border-width: 3px;
  border-color: var(--bp-accent-secondary);
}

.bp-node--master-control .bp-node__header {
  background: linear-gradient(135deg, #102030 0%, #1a3050 100%);
}

.bp-node--phase-coordinator {
  border-style: dashed;
}

.bp-node--task-group {
  border-color: var(--bp-accent-primary);
}

.bp-node--agent {
  min-width: 120px;
  min-height: 60px;
  border-width: 1px;
}

.bp-node--checkpoint {
  border-color: var(--bp-accent-tertiary);
  border-style: dotted;
}

/* Status Indicators */
.bp-status--pending {
  color: var(--bp-status-pending);
}

.bp-status--running {
  color: var(--bp-status-running);
  animation: bp-pulse 1.5s ease-in-out infinite;
}

.bp-status--complete {
  color: var(--bp-status-complete);
}

.bp-status--failed {
  color: var(--bp-status-failed);
}

@keyframes bp-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.4; }
}

/* Ports */
.bp-port {
  position: absolute;
  width: 10px;
  height: 10px;
  background: var(--bp-node-fill);
  border: 2px solid var(--bp-node-border);
}

.bp-port--top {
  top: -6px;
  left: 50%;
  transform: translateX(-50%);
}

.bp-port--right {
  right: -6px;
  top: 50%;
  transform: translateY(-50%);
}

.bp-port--bottom {
  bottom: -6px;
  left: 50%;
  transform: translateX(-50%);
}

.bp-port--left {
  left: -6px;
  top: 50%;
  transform: translateY(-50%);
}

.bp-port__label {
  position: absolute;
  font-size: 7px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--bp-text-secondary);
  white-space: nowrap;
}

/* Edges (SVG paths) */
.bp-edge {
  fill: none;
  stroke-width: 2;
}

.bp-edge--inactive {
  stroke: var(--bp-edge-inactive);
}

.bp-edge--active {
  stroke: var(--bp-edge-active);
  stroke-dasharray: 8 4;
  animation: bp-edge-flow 0.5s linear infinite;
}

.bp-edge--complete {
  stroke: var(--bp-edge-active);
}

@keyframes bp-edge-flow {
  from { stroke-dashoffset: 12; }
  to { stroke-dashoffset: 0; }
}

/* Edge Labels */
.bp-edge-label {
  font-size: 8px;
  fill: var(--bp-text-secondary);
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

/* Wave Indicator */
.bp-wave-indicator {
  position: absolute;
  left: 10px;
  font-size: 9px;
  color: var(--bp-accent-secondary);
  text-transform: uppercase;
  letter-spacing: 1px;
  writing-mode: vertical-rl;
  text-orientation: mixed;
  transform: rotate(180deg);
}

/* Progress Overlay */
.bp-progress {
  position: absolute;
  bottom: 20px;
  right: 20px;
  background: rgba(10, 22, 40, 0.9);
  border: 1px solid var(--bp-node-border);
  padding: 12px 16px;
  font-size: 10px;
}

.bp-progress__bar {
  height: 4px;
  background: var(--bp-edge-inactive);
  margin-top: 8px;
  width: 150px;
}

.bp-progress__fill {
  height: 100%;
  background: var(--bp-accent-primary);
  transition: width 0.3s ease;
}

/* Legend */
.bp-legend {
  position: absolute;
  bottom: 20px;
  left: 20px;
  font-size: 9px;
  color: var(--bp-text-secondary);
}

.bp-legend__item {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 4px;
}

.bp-legend__icon {
  width: 12px;
  height: 12px;
  border: 1px solid currentColor;
}
```

---

## React Flow Component Structure

### Main Blueprint Component

```tsx
// components/Blueprint/Blueprint.tsx
import React, { useMemo, useCallback } from 'react';
import ReactFlow, {
  Background,
  Controls,
  MiniMap,
  useNodesState,
  useEdgesState,
  Node,
  Edge,
  ConnectionMode,
} from 'reactflow';
import ELK from 'elkjs/lib/elk.bundled.js';
import 'reactflow/dist/style.css';

import { MasterControlNode } from './nodes/MasterControlNode';
import { PhaseNode } from './nodes/PhaseNode';
import { BlockNode } from './nodes/BlockNode';
import { AgentNode } from './nodes/AgentNode';
import { CheckpointNode } from './nodes/CheckpointNode';
import { OrthogonalEdge } from './edges/OrthogonalEdge';
import { BlueprintHeader } from './BlueprintHeader';
import { BlueprintProgress } from './BlueprintProgress';
import { BlueprintLegend } from './BlueprintLegend';

import type { Blueprint, BlueprintNode, BlueprintEdge } from './types';
import './Blueprint.css';

const nodeTypes = {
  master_control: MasterControlNode,
  phase_coordinator: PhaseNode,
  task_group: BlockNode,
  agent: AgentNode,
  checkpoint: CheckpointNode,
  data_store: BlockNode,
};

const edgeTypes = {
  orthogonal: OrthogonalEdge,
};

interface BlueprintProps {
  data: Blueprint;
  onNodeClick?: (nodeId: string) => void;
  onEdgeClick?: (edgeId: string) => void;
}

export function Blueprint({ data, onNodeClick, onEdgeClick }: BlueprintProps) {
  const elk = useMemo(() => new ELK(), []);

  // Convert blueprint data to React Flow format
  const initialNodes: Node[] = useMemo(() =>
    data.nodes.map(node => ({
      id: node.id,
      type: node.type,
      position: node.position || { x: 0, y: 0 },
      data: {
        label: node.label,
        sublabel: node.sublabel,
        status: node.status,
        metadata: node.metadata,
        ports: node.ports,
      },
      parentNode: node.parent_id,
      extent: node.parent_id ? 'parent' : undefined,
    })),
    [data.nodes]
  );

  const initialEdges: Edge[] = useMemo(() =>
    data.edges.map(edge => ({
      id: edge.id,
      source: edge.source_node,
      sourceHandle: edge.source_port,
      target: edge.target_node,
      targetHandle: edge.target_port,
      type: 'orthogonal',
      data: {
        edgeType: edge.type,
        label: edge.label,
        status: edge.status,
        dataType: edge.data_type,
      },
    })),
    [data.edges]
  );

  const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
  const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);

  // Auto-layout using ELK
  const runLayout = useCallback(async () => {
    const elkGraph = {
      id: 'root',
      layoutOptions: {
        'elk.algorithm': 'layered',
        'elk.direction': data.layout.direction === 'TB' ? 'DOWN' : 'RIGHT',
        'elk.spacing.nodeNode': String(data.layout.spacing.horizontal),
        'elk.layered.spacing.nodeNodeBetweenLayers': String(data.layout.spacing.vertical),
        'elk.edgeRouting': 'ORTHOGONAL',
      },
      children: data.nodes.map(node => ({
        id: node.id,
        width: data.layout.node_width,
        height: data.layout.node_height,
      })),
      edges: data.edges.map(edge => ({
        id: edge.id,
        sources: [edge.source_node],
        targets: [edge.target_node],
      })),
    };

    const layoutedGraph = await elk.layout(elkGraph);

    const layoutedNodes = nodes.map(node => {
      const elkNode = layoutedGraph.children?.find(n => n.id === node.id);
      return {
        ...node,
        position: elkNode ? { x: elkNode.x || 0, y: elkNode.y || 0 } : node.position,
      };
    });

    setNodes(layoutedNodes);
  }, [elk, data, nodes, setNodes]);

  // Run layout on mount
  React.useEffect(() => {
    runLayout();
  }, []);

  const handleNodeClick = useCallback((_, node: Node) => {
    onNodeClick?.(node.id);
  }, [onNodeClick]);

  const handleEdgeClick = useCallback((_, edge: Edge) => {
    onEdgeClick?.(edge.id);
  }, [onEdgeClick]);

  return (
    <div className="grid-blueprint">
      <BlueprintHeader mission={data.mission} />

      <ReactFlow
        nodes={nodes}
        edges={edges}
        onNodesChange={onNodesChange}
        onEdgesChange={onEdgesChange}
        onNodeClick={handleNodeClick}
        onEdgeClick={handleEdgeClick}
        nodeTypes={nodeTypes}
        edgeTypes={edgeTypes}
        connectionMode={ConnectionMode.Loose}
        fitView
        fitViewOptions={{ padding: 0.2 }}
        minZoom={0.1}
        maxZoom={2}
      >
        <Background
          color="var(--bp-background-grid)"
          gap={20}
          size={1}
        />
        <Controls />
        <MiniMap
          nodeColor={(node) => {
            switch (node.data?.status) {
              case 'complete': return 'var(--bp-status-complete)';
              case 'running': return 'var(--bp-status-running)';
              case 'failed': return 'var(--bp-status-failed)';
              default: return 'var(--bp-status-pending)';
            }
          }}
        />
      </ReactFlow>

      <BlueprintProgress mission={data.mission} nodes={data.nodes} />
      <BlueprintLegend />
    </div>
  );
}
```

### Node Components

```tsx
// components/Blueprint/nodes/MasterControlNode.tsx
import React, { memo } from 'react';
import { Handle, Position } from 'reactflow';
import { StatusIcon } from '../StatusIcon';
import type { NodeData } from '../types';

interface MasterControlNodeProps {
  data: NodeData;
  selected: boolean;
}

export const MasterControlNode = memo(({ data, selected }: MasterControlNodeProps) => {
  return (
    <div className={`bp-node bp-node--master-control ${selected ? 'bp-node--selected' : ''}`}>
      <div className="bp-node__header">
        <div>
          <div className="bp-node__label">{data.label}</div>
          {data.sublabel && <div className="bp-node__sublabel">{data.sublabel}</div>}
        </div>
        <StatusIcon status={data.status} />
      </div>
      <div className="bp-node__body">
        <div className="bp-node__metric">
          <span className="bp-node__metric-label">MODE</span>
          <span className="bp-node__metric-value">{data.metadata?.mode || 'AUTOPILOT'}</span>
        </div>
      </div>

      {/* Ports */}
      {data.ports?.map(port => (
        <Handle
          key={port.id}
          type={port.type === 'input' ? 'target' : 'source'}
          position={getPosition(port.position)}
          id={port.id}
          className="bp-port"
        />
      ))}
    </div>
  );
});

function getPosition(pos: string): Position {
  switch (pos) {
    case 'top': return Position.Top;
    case 'right': return Position.Right;
    case 'bottom': return Position.Bottom;
    case 'left': return Position.Left;
    default: return Position.Bottom;
  }
}

// components/Blueprint/nodes/AgentNode.tsx
export const AgentNode = memo(({ data, selected }: AgentNodeProps) => {
  const agentIcon = getAgentIcon(data.metadata?.agent_type);

  return (
    <div className={`bp-node bp-node--agent bp-node--agent-${data.metadata?.agent_type} ${selected ? 'bp-node--selected' : ''}`}>
      <div className="bp-node__header">
        <div className="bp-node__icon">{agentIcon}</div>
        <div>
          <div className="bp-node__label">{data.label}</div>
          {data.sublabel && <div className="bp-node__sublabel">{data.sublabel}</div>}
        </div>
        <StatusIcon status={data.status} />
      </div>

      {data.ports?.map(port => (
        <Handle
          key={port.id}
          type={port.type === 'input' ? 'target' : 'source'}
          position={getPosition(port.position)}
          id={port.id}
          className="bp-port"
        >
          <span className="bp-port__label">{port.label}</span>
        </Handle>
      ))}
    </div>
  );
});

function getAgentIcon(agentType?: string): string {
  switch (agentType) {
    case 'planner': return '\u2630';     // Trigram
    case 'executor': return '\u26A1';    // Lightning
    case 'recognizer': return '\u2713';  // Check
    case 'scout': return '\u2605';       // Star
    case 'upscaler': return '\u2191';    // Up arrow
    default: return '\u25C6';            // Diamond
  }
}
```

### Orthogonal Edge Component

```tsx
// components/Blueprint/edges/OrthogonalEdge.tsx
import React, { memo } from 'react';
import { EdgeProps, getBezierPath, EdgeLabelRenderer } from 'reactflow';

interface OrthogonalEdgeData {
  edgeType: string;
  label?: string;
  status: 'inactive' | 'active' | 'complete';
  dataType?: string;
}

export const OrthogonalEdge = memo(({
  id,
  sourceX,
  sourceY,
  targetX,
  targetY,
  sourcePosition,
  targetPosition,
  data,
  style,
}: EdgeProps<OrthogonalEdgeData>) => {
  // Generate orthogonal path (Manhattan routing)
  const path = getOrthogonalPath(
    sourceX, sourceY, targetX, targetY,
    sourcePosition, targetPosition
  );

  const edgeClassName = `bp-edge bp-edge--${data?.edgeType || 'control_flow'} bp-edge--${data?.status || 'inactive'}`;

  return (
    <>
      <path
        id={id}
        className={edgeClassName}
        d={path}
        markerEnd="url(#arrowhead)"
      />

      {data?.label && (
        <EdgeLabelRenderer>
          <div
            className="bp-edge-label"
            style={{
              position: 'absolute',
              transform: `translate(-50%, -50%) translate(${(sourceX + targetX) / 2}px, ${(sourceY + targetY) / 2}px)`,
              pointerEvents: 'all',
            }}
          >
            {data.label}
          </div>
        </EdgeLabelRenderer>
      )}
    </>
  );
});

function getOrthogonalPath(
  sx: number, sy: number,
  tx: number, ty: number,
  sourcePos: string, targetPos: string
): string {
  const midX = (sx + tx) / 2;
  const midY = (sy + ty) / 2;

  // Simple L-shaped routing
  if (sourcePos === 'bottom' || sourcePos === 'top') {
    return `M ${sx} ${sy} L ${sx} ${midY} L ${tx} ${midY} L ${tx} ${ty}`;
  } else {
    return `M ${sx} ${sy} L ${midX} ${sy} L ${midX} ${ty} L ${tx} ${ty}`;
  }
}
```

---

## Mermaid Fallback Template

For environments without React support, use Mermaid diagrams.

### Generation Template

```typescript
function generateMermaid(blueprint: Blueprint): string {
  const lines: string[] = [
    '```mermaid',
    'flowchart TB',
    '',
    '%% Styling',
    'classDef mc fill:#102030,stroke:#00ccff,stroke-width:3px,color:#e0e0e0',
    'classDef phase fill:#0a1628,stroke:#00ff88,stroke-width:2px,stroke-dasharray:5 5,color:#e0e0e0',
    'classDef block fill:#0a1628,stroke:#00ff88,stroke-width:2px,color:#e0e0e0',
    'classDef agent fill:#0a1628,stroke:#00ff88,stroke-width:1px,color:#e0e0e0',
    'classDef checkpoint fill:#0a1628,stroke:#ff6600,stroke-width:2px,stroke-dasharray:3 3,color:#e0e0e0',
    'classDef pending fill:#0a1628,stroke:#4a5568',
    'classDef running fill:#0a1628,stroke:#00ff88,stroke-width:3px',
    'classDef complete fill:#102030,stroke:#00ff88',
    'classDef failed fill:#0a1628,stroke:#ff3366',
    '',
  ];

  // Generate subgraphs for phases
  const phaseNodes = blueprint.nodes.filter(n => n.type === 'phase_coordinator');

  for (const phase of phaseNodes) {
    const phaseBlocks = blueprint.nodes.filter(n => n.parent_id === phase.id);

    lines.push(`subgraph ${phase.id}["${phase.label}: ${phase.sublabel || ''}"]`);
    lines.push(`direction TB`);

    for (const block of phaseBlocks) {
      const blockAgents = blueprint.nodes.filter(n => n.parent_id === block.id);

      lines.push(`  subgraph ${block.id}["${block.label}"]`);
      for (const agent of blockAgents) {
        const icon = getAgentMermaidIcon(agent.metadata?.agent_type);
        lines.push(`    ${agent.id}["${icon} ${agent.label}"]`);
      }
      lines.push(`  end`);
    }

    lines.push(`end`);
    lines.push('');
  }

  // Generate edges
  for (const edge of blueprint.edges) {
    const arrow = edge.type === 'dependency' ? '-.->': '-->';
    const label = edge.label ? `|${edge.label}|` : '';
    lines.push(`${edge.source_node} ${arrow}${label} ${edge.target_node}`);
  }

  // Apply status classes
  lines.push('');
  for (const node of blueprint.nodes) {
    const typeClass = node.type.replace('_', '-');
    lines.push(`class ${node.id} ${typeClass},${node.status}`);
  }

  lines.push('```');

  return lines.join('\n');
}

function getAgentMermaidIcon(agentType?: string): string {
  switch (agentType) {
    case 'planner': return 'fa:fa-sitemap';
    case 'executor': return 'fa:fa-bolt';
    case 'recognizer': return 'fa:fa-check-circle';
    case 'scout': return 'fa:fa-search';
    default: return 'fa:fa-cog';
  }
}
```

### Example Mermaid Output

```mermaid
flowchart TB

%% Styling
classDef mc fill:#102030,stroke:#00ccff,stroke-width:3px,color:#e0e0e0
classDef phase fill:#0a1628,stroke:#00ff88,stroke-width:2px,stroke-dasharray:5 5,color:#e0e0e0
classDef block fill:#0a1628,stroke:#00ff88,stroke-width:2px,color:#e0e0e0
classDef agent fill:#0a1628,stroke:#00ff88,stroke-width:1px,color:#e0e0e0

MC["MASTER CONTROL\nOrchestration Layer"]

subgraph phase-01["PHASE 01: Foundation"]
direction TB
  subgraph block-01["BLOCK 01: Database Setup"]
    exec-01["fa:fa-bolt EXECUTOR"]
    recog-01["fa:fa-check-circle RECOGNIZER"]
  end
  subgraph block-02["BLOCK 02: Auth Setup"]
    exec-02["fa:fa-bolt EXECUTOR"]
    recog-02["fa:fa-check-circle RECOGNIZER"]
  end
end

subgraph phase-02["PHASE 02: Features"]
direction TB
  subgraph block-03["BLOCK 03: API Routes"]
    exec-03["fa:fa-bolt EXECUTOR"]
    recog-03["fa:fa-check-circle RECOGNIZER"]
  end
end

MC --> phase-01
phase-01 --> block-01
phase-01 --> block-02
block-01 --> exec-01
exec-01 -->|SUMMARY| recog-01
block-02 --> exec-02
exec-02 -->|SUMMARY| recog-02
phase-01 -.->|dependency| phase-02
phase-02 --> block-03

class MC mc,running
class phase-01 phase,running
class block-01 block,complete
class block-02 block,running
class exec-01 agent,complete
class recog-01 agent,complete
class exec-02 agent,running
```

---

## ASCII Fallback Template

For pure terminal environments, use ASCII art.

### Generation Template

```typescript
function generateASCII(blueprint: Blueprint): string {
  const lines: string[] = [];
  const width = 72;

  // Header
  lines.push('\u250C' + '\u2500'.repeat(width - 2) + '\u2510');
  lines.push('\u2502' + centerText('GRID BLUEPRINT', width - 2) + '\u2502');
  lines.push('\u2502' + centerText(blueprint.mission.name, width - 2) + '\u2502');
  lines.push('\u2502' + centerText(`Mode: ${blueprint.mission.mode} | Complexity: ${blueprint.mission.complexity}`, width - 2) + '\u2502');
  lines.push('\u251C' + '\u2500'.repeat(width - 2) + '\u2524');

  // Master Control
  lines.push('\u2502' + centerText('', width - 2) + '\u2502');
  lines.push('\u2502' + centerText('\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510', width - 2) + '\u2502');
  lines.push('\u2502' + centerText('\u2502  MASTER CONTROL   \u2502', width - 2) + '\u2502');
  lines.push('\u2502' + centerText('\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518', width - 2) + '\u2502');
  lines.push('\u2502' + centerText('        \u2502', width - 2) + '\u2502');

  // Phases and Blocks
  const phases = blueprint.nodes.filter(n => n.type === 'phase_coordinator');

  for (let i = 0; i < phases.length; i++) {
    const phase = phases[i];
    const blocks = blueprint.nodes.filter(n => n.parent_id === phase.id);
    const statusIcon = getASCIIStatusIcon(phase.status);

    lines.push('\u2502' + centerText('\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510', width - 2) + '\u2502');
    lines.push('\u2502' + centerText(`\u2502 ${statusIcon} ${phase.label}: ${phase.sublabel || ''} \u2502`.padEnd(30), width - 2) + '\u2502');
    lines.push('\u2502' + centerText('\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524', width - 2) + '\u2502');

    for (const block of blocks) {
      const blockStatus = getASCIIStatusIcon(block.status);
      const agents = blueprint.nodes.filter(n => n.parent_id === block.id);

      lines.push('\u2502' + centerText(`\u2502  ${blockStatus} ${block.label}`.padEnd(28) + '\u2502', width - 2) + '\u2502');

      for (const agent of agents) {
        const agentStatus = getASCIIStatusIcon(agent.status);
        const agentIcon = getASCIIAgentIcon(agent.metadata?.agent_type);
        lines.push('\u2502' + centerText(`\u2502    ${agentStatus} ${agentIcon} ${agent.label}`.padEnd(28) + '\u2502', width - 2) + '\u2502');
      }
    }

    lines.push('\u2502' + centerText('\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518', width - 2) + '\u2502');

    if (i < phases.length - 1) {
      lines.push('\u2502' + centerText('        \u2502', width - 2) + '\u2502');
      lines.push('\u2502' + centerText('        \u25BC', width - 2) + '\u2502');
      lines.push('\u2502' + centerText('        \u2502', width - 2) + '\u2502');
    }
  }

  // Footer
  lines.push('\u2502' + centerText('', width - 2) + '\u2502');
  lines.push('\u251C' + '\u2500'.repeat(width - 2) + '\u2524');

  // Progress
  const complete = blueprint.nodes.filter(n => n.status === 'complete').length;
  const total = blueprint.nodes.length;
  const pct = Math.round((complete / total) * 100);
  const barWidth = 30;
  const filled = Math.round((pct / 100) * barWidth);
  const progressBar = '\u2588'.repeat(filled) + '\u2591'.repeat(barWidth - filled);

  lines.push('\u2502' + ` Progress: [${progressBar}] ${pct}%`.padEnd(width - 2) + '\u2502');
  lines.push('\u2502' + ` Blocks: ${complete}/${total} | Est: ${blueprint.mission.estimated_duration_minutes}min`.padEnd(width - 2) + '\u2502');

  // Legend
  lines.push('\u251C' + '\u2500'.repeat(width - 2) + '\u2524');
  lines.push('\u2502' + ' Legend: \u25CC Pending  \u25C9 Running  \u25CF Complete  \u2715 Failed'.padEnd(width - 2) + '\u2502');

  lines.push('\u2514' + '\u2500'.repeat(width - 2) + '\u2518');

  return lines.join('\n');
}

function centerText(text: string, width: number): string {
  const padding = Math.max(0, Math.floor((width - text.length) / 2));
  return ' '.repeat(padding) + text + ' '.repeat(width - text.length - padding);
}

function getASCIIStatusIcon(status: string): string {
  switch (status) {
    case 'pending': return '\u25CC';   // Dotted circle
    case 'running': return '\u25C9';   // Fisheye
    case 'complete': return '\u25CF';  // Filled circle
    case 'failed': return '\u2715';    // X
    default: return '\u25CC';
  }
}

function getASCIIAgentIcon(agentType?: string): string {
  switch (agentType) {
    case 'planner': return '\u2261';     // Identical
    case 'executor': return '\u26A1';    // Lightning (may not render)
    case 'recognizer': return '\u2713';  // Check
    case 'scout': return '*';
    default: return '\u25C6';            // Diamond
  }
}
```

### Example ASCII Output

```
+----------------------------------------------------------------------+
|                          GRID BLUEPRINT                               |
|                       REST API with Auth                              |
|                Mode: AUTOPILOT | Complexity: MEDIUM                   |
+----------------------------------------------------------------------+
|                                                                        |
|                    +--------------------+                              |
|                    |  MASTER CONTROL   |                              |
|                    +--------+----------+                              |
|                             |                                          |
|                    +----------------------------+                      |
|                    | * PHASE 01: Foundation     |                      |
|                    +----------------------------+                      |
|                    |  * BLOCK 01: Database      |                      |
|                    |    * # EXECUTOR            |                      |
|                    |    * v RECOGNIZER          |                      |
|                    |  o BLOCK 02: Auth          |                      |
|                    |    o # EXECUTOR            |                      |
|                    |    . v RECOGNIZER          |                      |
|                    +----------------------------+                      |
|                             |                                          |
|                             V                                          |
|                             |                                          |
|                    +----------------------------+                      |
|                    | . PHASE 02: Features       |                      |
|                    +----------------------------+                      |
|                    |  . BLOCK 03: API Routes    |                      |
|                    |    . # EXECUTOR            |                      |
|                    |    . v RECOGNIZER          |                      |
|                    +----------------------------+                      |
|                                                                        |
+----------------------------------------------------------------------+
| Progress: [########......................] 25%                        |
| Blocks: 2/8 | Est: 25min                                              |
+----------------------------------------------------------------------+
| Legend: . Pending  o Running  * Complete  x Failed                   |
+----------------------------------------------------------------------+
```

---

## Integration Points

### 1. Planner Output Extension

The Planner must output blueprint data alongside the execution plan.

```yaml
# In plan YAML frontmatter, add:
blueprint:
  nodes:
    - id: "block-01"
      type: "task_group"
      label: "BLOCK 01"
      sublabel: "Database Setup"
      wave: 1
      ports:
        - {id: "b01-in", type: "input", position: "top"}
        - {id: "b01-out", type: "output", position: "bottom"}
  edges:
    - source: "phase-01"
      target: "block-01"
      type: "control_flow"
```

### 2. MC Rendering Protocol

Master Control renders the blueprint before spawning.

```python
def render_blueprint_before_spawn(plan_data):
    """Render blueprint visualization before execution begins."""

    # 1. Extract blueprint data from plan
    blueprint = extract_blueprint_from_plan(plan_data)

    # 2. Determine rendering mode
    render_mode = detect_render_environment()

    # 3. Generate visualization
    if render_mode == "react":
        # Write blueprint data to .grid/blueprint.json
        # Open web viewer
        write(".grid/blueprint.json", json.dumps(blueprint))
        open_browser("http://localhost:3000/blueprint")

    elif render_mode == "mermaid":
        # Generate and display mermaid
        mermaid = generate_mermaid(blueprint)
        display(mermaid)

    else:  # ASCII
        # Generate and display ASCII
        ascii_bp = generate_ascii(blueprint)
        display(ascii_bp)

    # 4. Store for updates
    write(".grid/BLUEPRINT_STATE.json", json.dumps(blueprint))
```

### 3. Event Bus Updates

Status changes trigger blueprint re-render.

```python
def on_agent_status_change(agent_id: str, new_status: str):
    """Update blueprint when agent status changes."""

    # 1. Load current blueprint
    blueprint = read_json(".grid/BLUEPRINT_STATE.json")

    # 2. Update node status
    for node in blueprint["nodes"]:
        if node["id"] == agent_id:
            node["status"] = new_status
            break

    # 3. Update edge status
    for edge in blueprint["edges"]:
        if edge["source_node"] == agent_id and new_status == "complete":
            edge["status"] = "complete"
        elif edge["target_node"] == agent_id and new_status == "running":
            edge["status"] = "active"

    # 4. Persist and trigger re-render
    write(".grid/BLUEPRINT_STATE.json", json.dumps(blueprint))
    emit_event("blueprint_updated", blueprint)

# Event types
BLUEPRINT_EVENTS = [
    "mission_started",
    "phase_started",
    "phase_completed",
    "block_started",
    "block_completed",
    "agent_spawned",
    "agent_completed",
    "agent_failed",
    "checkpoint_reached",
    "data_transferred",
]
```

---

## File Locations

| File | Location | Purpose |
|------|----------|---------|
| Spec | `~/.claude/docs/GRID_BLUEPRINT_SPEC.md` | This document |
| State | `.grid/BLUEPRINT_STATE.json` | Current blueprint state |
| Plan Data | `.grid/blueprint.json` | Generated from plan |
| React App | `~/.claude/tools/blueprint-viewer/` | React Flow viewer |
| CSS | `~/.claude/tools/blueprint-viewer/styles/` | GPU PCB styles |

---

## Implementation Checklist

- [ ] Add blueprint data generation to Planner
- [ ] Implement blueprint renderer in MC
- [ ] Create React Flow components
- [ ] Create Mermaid template generator
- [ ] Create ASCII template generator
- [ ] Implement event bus for updates
- [ ] Add status animations
- [ ] Add progress tracking
- [ ] Test all three rendering modes

---

*End of Blueprint Visualization Specification*
