blue/.blue/docs/rfcs/0056-alignment-visualization-dashboard.draft.md
Eric Garcia 6e8f0db6c0 chore: add dialogues, RFCs, docs and minor improvements
- Add dialogue prompt file writing for audit/debugging
- Update README install instructions
- Add new RFCs (0053, 0055-0059, 0062)
- Add recorded dialogues and expert pools
- Add ADR 0018 dynamodb-portable-schema
- Update TODO with hook configuration notes

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-26 08:51:56 -05:00

26 KiB

RFC 0056: Alignment Visualization Dashboard

Status Draft
Date 2026-02-02
ADRs 0014 (Alignment Dialogue Agents), 0018 (DynamoDB-Portable Schema)
Depends On RFC 0051 (Global Perspective & Tension Tracking), RFC 0053 (Storage Abstraction Layer)

Summary

Build a Next.js web application to visualize alignment dialogues with three core capabilities:

  1. Real-time Monitoring — Watch dialogues in progress via WebSocket
  2. Post-Dialogue Analysis — Deep-dive into completed dialogues
  3. Cross-Dialogue Analytics — Discover patterns across many dialogues

Key design principle: Storage-agnostic architecture that works identically with SQLite (local) or DynamoDB (AWS production).

Problem

The alignment dialogue system generates rich structured data:

  • Expert contributions and scores
  • Perspectives, tensions, recommendations, evidence, claims
  • Cross-references between entities
  • Velocity and convergence metrics
  • Verdicts and dissents

Currently this data is only accessible via:

  • Raw SQLite queries
  • MCP tool calls
  • JSON exports

There's no visual way to:

  • Monitor a dialogue in real-time during execution
  • Explore the relationship graph between entities
  • Compare dialogues or track patterns over time
  • Share dialogue results with stakeholders

Design

Architecture

Local Development:

┌─────────────────────────────────────────────────────────────┐
│                    Next.js Dashboard                         │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────┐  │
│  │   Live      │  │   Analysis  │  │   Analytics         │  │
│  │   Monitor   │  │   Explorer  │  │   Dashboard         │  │
│  └─────────────┘  └─────────────┘  └─────────────────────┘  │
└─────────────────────────────────────────────────────────────┘
            │                               │
     WebSocket (ws://)              REST API (http://)
            │                               │
            ▼                               ▼
┌─────────────────────────────────────────────────────────────┐
│                 Next.js API Routes                           │
│  WS  /api/ws/dialogues/:id        GET /api/dialogues/:id    │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│              DialogueStore (RFC 0053)                        │
│              ┌─────────────────────┐                         │
│              │  SqliteDialogueStore │                        │
│              │   (better-sqlite3)   │                        │
│              └─────────────────────┘                         │
└─────────────────────────────────────────────────────────────┘

AWS Production:

┌─────────────────────────────────────────────────────────────┐
│                    Next.js Dashboard                         │
│                   (Vercel / Amplify)                         │
└─────────────────────────────────────────────────────────────┘
            │                               │
     WebSocket (wss://)             REST API (https://)
            │                               │
            ▼                               ▼
┌─────────────────┐             ┌─────────────────────────────┐
│  API Gateway    │             │      Lambda Functions       │
│  WebSocket API  │             │   GET /dialogues/:id        │
│  $connect       │             │   GET /stats                │
│  $disconnect    │             └─────────────────────────────┘
│  subscribe      │                         │
└─────────────────┘                         │
            │                               │
            └───────────────┬───────────────┘
                            ▼
┌─────────────────────────────────────────────────────────────┐
│              DialogueStore (RFC 0053)                        │
│              ┌─────────────────────┐                         │
│              │ DynamoDialogueStore │                         │
│              │  (encrypted, KMS)   │                         │
│              └─────────────────────┘                         │
└─────────────────────────────────────────────────────────────┘

Key Principle: The DialogueStore interface (RFC 0053) is identical in both environments. The dashboard code doesn't know or care which backend is active.

Tech Stack

Layer Technology Rationale
Framework Next.js 14+ (App Router) SSR, API routes, React Server Components
Styling Tailwind CSS Rapid iteration, consistent design
Charts Recharts or Victory React-native charting
Graph React Flow or D3 Interactive node-edge visualization
State Zustand or React Query Lightweight, SSR-friendly
Real-time Server-Sent Events or WebSocket Live updates

Core Views

1. Live Monitor (/live/:dialogueId)

Real-time view during active dialogue execution.

Components:

  • Velocity Chart — Line chart showing ALIGNMENT score per round
  • Convergence Indicator — Visual signal when velocity approaches zero
  • Expert Leaderboard — Live-updating score table
  • Tension Tracker — Open/addressed/resolved counts with progress bar
  • Activity Feed — Stream of new perspectives, tensions, verdicts

Data Source: get_dialogue_progress() polled or streamed

Wireframe:

┌────────────────────────────────────────────────────────────┐
│  📊 Live: Investment Portfolio Analysis          [Round 3] │
├────────────────────────────────────────────────────────────┤
│  ┌──────────────────────┐  ┌────────────────────────────┐  │
│  │ ALIGNMENT Velocity   │  │ Expert Leaderboard         │  │
│  │   ▲                  │  │ 1. 🧁 Donut      33 pts    │  │
│  │   │    ●             │  │ 2. 🧁 Muffin    22 pts    │  │
│  │   │  ●   ●           │  │ 3. 🧁 Palmier   12 pts    │  │
│  │   │●                 │  │ 4. 🧁 Cupcake   13 pts    │  │
│  │   └──────────▶       │  └────────────────────────────┘  │
│  │   R0  R1  R2  R3     │                                  │
│  └──────────────────────┘  ┌────────────────────────────┐  │
│                            │ Tensions                    │  │
│  ┌──────────────────────┐  │ ████████░░ 4/5 resolved    │  │
│  │ 🟢 Converging        │  └────────────────────────────┘  │
│  │ Velocity: +2         │                                  │
│  │ Est. 1 round left    │                                  │
│  └──────────────────────┘                                  │
├────────────────────────────────────────────────────────────┤
│  Activity Feed                                             │
│  • [R3] Muffin: CONVERGE on R0001 options overlay         │
│  • [R3] Donut: RESOLVE T0001 via C0101                    │
│  • [R2] Palmier: NEW P0101 concentration risk             │
└────────────────────────────────────────────────────────────┘

2. Analysis Explorer (/dialogue/:dialogueId)

Post-hoc exploration of a completed dialogue.

Components:

  • Summary Card — Title, question, final verdict, total ALIGNMENT
  • Entity Graph — Interactive visualization of P/R/T/E/C relationships
  • Round Timeline — Expandable accordion with round details
  • Expert Profiles — Per-expert contribution breakdown
  • Verdict Panel — Final, minority, dissent verdicts

Entity Graph:

        ┌─────────┐
        │  P0001  │◄──── support ────┐
        │ Income  │                  │
        └────┬────┘                  │
             │                   ┌───┴────┐
         support                 │ E0101  │
             │                   │Premium │
             ▼                   └────────┘
        ┌─────────┐                  │
        │  T0001  │                depend
        │ Conflict│                  │
        └────┬────┘                  ▼
             │                   ┌────────┐
         address                 │ C0101  │
             │                   │Resolved│
             ▼                   └───┬────┘
        ┌─────────┐                  │
        │  R0001  │◄──── resolve ────┘
        │ Options │
        └─────────┘

Data Source:

  • get_dialogue(), get_perspectives(), get_tensions(), etc.
  • expand_citation() for hover tooltips

3. Analytics Dashboard (/analytics)

Cross-dialogue patterns and trends.

Components:

  • Stats Overview — Total dialogues, perspectives, tensions, avg ALIGNMENT
  • Top Experts — Leaderboard across all dialogues
  • Tension Patterns — Common tension labels/themes
  • Dialogue Comparison — Side-by-side metrics
  • Search — Find dialogues by topic

Data Source: get_cross_dialogue_stats(), find_similar_dialogues()

Wireframe:

┌────────────────────────────────────────────────────────────┐
│  📈 Analytics Dashboard                                    │
├────────────────────────────────────────────────────────────┤
│  ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐              │
│  │   12   │ │  156   │ │   34   │ │   78   │              │
│  │Dialogues│ │Perspect│ │Tensions│ │Avg ALIGN│             │
│  └────────┘ └────────┘ └────────┘ └────────┘              │
├────────────────────────────────────────────────────────────┤
│  Top Experts (All Time)        │  Recent Dialogues         │
│  1. muffin    456 pts (12 dlg) │  • Investment Analysis ✓  │
│  2. donut     389 pts (11 dlg) │  • API Design ✓           │
│  3. cupcake   312 pts (10 dlg) │  • Risk Assessment 🔄     │
│  4. brioche   287 pts (9 dlg)  │  • Product Strategy ✓     │
├────────────────────────────────────────────────────────────┤
│  [Search dialogues...]                                     │
│  Related: "investment" → 3 matches                        │
└────────────────────────────────────────────────────────────┘

API Routes

Route Method Description Data Source
/api/dialogues GET List all dialogues get_dialogues()
/api/dialogues/:id GET Full dialogue with entities get_dialogue() + entities
/api/dialogues/:id/progress GET Real-time progress get_dialogue_progress()
/api/dialogues/:id/graph GET Entity relationship graph entities + refs
/api/dialogues/:id/stream SSE Live updates poll get_dialogue_progress()
/api/stats GET Cross-dialogue stats get_cross_dialogue_stats()
/api/search GET Find similar dialogues find_similar_dialogues()
/api/citation/:id GET Expand citation expand_citation()

Database Access

Dual-mode storage matching RFC 0053 (Storage Abstraction Layer):

Local Development: SQLite

  • Next.js API routes use better-sqlite3
  • Read-only access to Blue's SQLite database
  • Simple, fast, zero infrastructure

Production: DynamoDB

  • Uses RFC 0053 storage abstraction
  • Encrypted at rest (AWS KMS)
  • Same query interface, different backend
  • Authentication required
// lib/db.ts
import { createStore } from '@blue/storage';

const store = createStore({
  backend: process.env.NODE_ENV === 'production' ? 'dynamodb' : 'sqlite',
  sqlite: { path: process.env.BLUE_DB_PATH },
  dynamodb: { table: process.env.DYNAMODB_TABLE, region: 'us-east-1' }
});

export const getDialogue = (id: string) => store.dialogue.get(id);
export const getProgress = (id: string) => store.dialogue.progress(id);
// ... etc

Real-Time Updates (WebSocket)

WebSocket for live monitoring, with environment-aware implementation:

Local: Next.js WebSocket (via socket.io or ws)

// pages/api/ws/dialogues/[id].ts (using next-ws or similar)
import { WebSocketServer } from 'ws';
import { store } from '@/lib/db';

export default function handler(ws: WebSocket, req: Request) {
  const dialogueId = req.params.id;

  // Subscribe to dialogue updates
  const interval = setInterval(async () => {
    const progress = await store.dialogue.progress(dialogueId);
    ws.send(JSON.stringify({ type: 'progress', data: progress }));
  }, 1000);

  ws.on('close', () => clearInterval(interval));
}

Production: API Gateway WebSocket

// Lambda handler for API Gateway WebSocket
export const handler = async (event: APIGatewayWebSocketEvent) => {
  const { routeKey, connectionId, body } = event;

  switch (routeKey) {
    case '$connect':
      // Store connectionId in DynamoDB connections table
      await store.connections.add(connectionId);
      break;

    case 'subscribe':
      const { dialogueId } = JSON.parse(body);
      await store.subscriptions.add(connectionId, dialogueId);
      break;

    case '$disconnect':
      await store.connections.remove(connectionId);
      break;
  }

  return { statusCode: 200 };
};

// Separate Lambda triggered by DynamoDB Streams or EventBridge
export const broadcastProgress = async (dialogueId: string) => {
  const subscribers = await store.subscriptions.forDialogue(dialogueId);
  const progress = await store.dialogue.progress(dialogueId);

  for (const connectionId of subscribers) {
    await apiGateway.postToConnection({
      ConnectionId: connectionId,
      Data: JSON.stringify({ type: 'progress', data: progress })
    });
  }
};

Client Hook:

// hooks/useDialogueProgress.ts
export function useDialogueProgress(dialogueId: string) {
  const [progress, setProgress] = useState<DialogueProgress | null>(null);

  useEffect(() => {
    const wsUrl = process.env.NEXT_PUBLIC_WS_URL || 'ws://localhost:3000/api/ws';
    const ws = new WebSocket(`${wsUrl}/dialogues/${dialogueId}`);

    ws.onmessage = (event) => {
      const { type, data } = JSON.parse(event.data);
      if (type === 'progress') setProgress(data);
    };

    return () => ws.close();
  }, [dialogueId]);

  return progress;
}

Entity Graph Visualization

Using React Flow for interactive graphs:

const entityToNode = (entity: Entity): Node => ({
  id: entity.display_id,
  type: entity.type, // perspective | tension | recommendation | evidence | claim
  data: {
    label: entity.label,
    contributors: entity.contributors,
    status: entity.status
  },
  position: calculatePosition(entity), // Force-directed or hierarchical
});

const refToEdge = (ref: Ref): Edge => ({
  id: `${ref.source_id}-${ref.target_id}`,
  source: ref.source_id,
  target: ref.target_id,
  label: ref.ref_type, // support, oppose, resolve, etc.
  animated: ref.ref_type === 'resolve',
  style: getEdgeStyle(ref.ref_type),
});

Implementation Plan

Phase 1: Foundation

  • Initialize Next.js project with Tailwind
  • Set up SQLite connection (read-only)
  • Implement core API routes (/dialogues, /stats)
  • Create basic layout and navigation

Phase 2: Analytics Dashboard

  • Stats overview cards
  • Top experts leaderboard
  • Dialogue list with search
  • Basic filtering

Phase 3: Dialogue Explorer

  • Dialogue detail page
  • Round timeline accordion
  • Expert profiles
  • Verdict display

Phase 4: Entity Graph

  • React Flow integration
  • Entity nodes by type
  • Reference edges with labels
  • Hover tooltips via expand_citation()
  • Click to expand/focus

Phase 5: Live Monitor

  • WebSocket endpoint for progress
  • Velocity chart (Recharts)
  • Live leaderboard
  • Convergence indicator
  • Activity feed

Phase 6: Polish

  • Responsive design
  • Dark mode
  • Export to PNG/PDF
  • Shareable links

Phase 7: AWS Deployment

  • Implement DynamoDialogueStore (RFC 0053)
  • API Gateway WebSocket API for real-time
  • Lambda functions for REST endpoints
  • CloudFront distribution
  • Cognito authentication
  • KMS encryption for DynamoDB
  • CDK/Terraform infrastructure as code

File Structure

blue-viz/
├── app/
│   ├── layout.tsx
│   ├── page.tsx                    # Home → redirect to /analytics
│   ├── analytics/
│   │   └── page.tsx                # Cross-dialogue dashboard
│   ├── dialogue/
│   │   └── [id]/
│   │       └── page.tsx            # Post-hoc explorer
│   ├── live/
│   │   └── [id]/
│   │       └── page.tsx            # Real-time monitor
│   └── api/
│       ├── dialogues/
│       │   ├── route.ts            # GET list
│       │   └── [id]/
│       │       ├── route.ts        # GET detail
│       │       ├── progress/route.ts
│       │       └── graph/route.ts
│       ├── ws/
│       │   └── dialogues/[id].ts   # WebSocket endpoint
│       ├── stats/route.ts
│       ├── search/route.ts
│       └── citation/[id]/route.ts
├── components/
│   ├── VelocityChart.tsx
│   ├── Leaderboard.tsx
│   ├── TensionTracker.tsx
│   ├── EntityGraph.tsx
│   ├── RoundTimeline.tsx
│   ├── VerdictPanel.tsx
│   └── CitationTooltip.tsx
├── hooks/
│   ├── useDialogueProgress.ts      # WebSocket hook
│   └── useDialogue.ts              # Data fetching
├── lib/
│   ├── store.ts                    # Storage abstraction (RFC 0053)
│   └── types.ts                    # Shared types
└── package.json

AWS Infrastructure (Production)

┌─────────────────────────────────────────────────────────────────────┐
│                         CloudFront                                   │
│                     (CDN + HTTPS termination)                        │
└─────────────────────────────────────────────────────────────────────┘
                    │                           │
            Static Assets                 API Requests
                    │                           │
                    ▼                           ▼
┌─────────────────────────┐     ┌─────────────────────────────────────┐
│      S3 Bucket          │     │         API Gateway                 │
│   (Next.js static)      │     │  ┌─────────────┬─────────────────┐  │
└─────────────────────────┘     │  │ REST API    │ WebSocket API   │  │
                                │  │ /dialogues  │ $connect        │  │
                                │  │ /stats      │ subscribe       │  │
                                │  │ /search     │ $disconnect     │  │
                                │  └─────────────┴─────────────────┘  │
                                └─────────────────────────────────────┘
                                                │
                                                ▼
                                ┌─────────────────────────────────────┐
                                │         Lambda Functions            │
                                │  - dialogue-get                     │
                                │  - dialogue-list                    │
                                │  - progress-get                     │
                                │  - ws-connect                       │
                                │  - ws-subscribe                     │
                                │  - ws-broadcast                     │
                                └─────────────────────────────────────┘
                                                │
                                                ▼
┌─────────────────────────┐     ┌─────────────────────────────────────┐
│       Cognito           │     │          DynamoDB                   │
│   (Authentication)      │     │  - blue_dialogues (main table)      │
│   - User Pool           │     │  - blue_connections (WebSocket)     │
│   - Identity Pool       │     │  - Encrypted with KMS               │
└─────────────────────────┘     └─────────────────────────────────────┘

DynamoDB Table Design (Single-Table):

PK SK Attributes
DLG#investment-analysis META title, status, total_alignment, ...
DLG#investment-analysis EXPERT#muffin role, tier, scores, ...
DLG#investment-analysis ROUND#00 score, summary, ...
DLG#investment-analysis P#0001 label, content, contributors, ...
DLG#investment-analysis T#0001 label, status, ...
DLG#investment-analysis REF#P0001#T0001 ref_type, ...
WS#abc123 SUB#investment-analysis connectionId, subscribedAt

GSI for queries:

  • GSI1: status → list dialogues by status
  • GSI2: expert_slug → find all dialogues for an expert

Decisions

  1. Deployment: Local for development/testing, hosted for production
    • Local: SQLite directly
    • Hosted: DynamoDB with encryption (see RFC 0053 Storage Abstraction)
  2. Authentication: None locally; required for hosted (TBD)
  3. Write Operations: Read-only for v1
  4. Embedding: Deferred — consider later for sharing widgets in Notion, Slack, etc.

Success Criteria

  • Can monitor an active dialogue in real-time
  • Can explore entity relationships visually
  • Can compare multiple dialogues side-by-side
  • Loads in under 2 seconds
  • Works on mobile (responsive)

"The elephant becomes visible — now let's draw it."