ETH-001 CHALLENGE BOARD MODE

misc · View raw (.md)

ETH-001 CHALLENGE BOARD MODE

COMPLETE PRODUCTION DEPLOYMENT SPECIFICATION v1.0

Document Type: Production Deployment Specification Patent Reference: USPTO Provisional #7 (ETH-2025-001) Filed: December 4, 2025 Target: Next.js 14+ / React 18+ / TypeScript Infrastructure: Client-Side Only (No Backend Required) Design System: EUDS Premium Standards Deployment Target: Vercel / Netlify / VPS Static Hosting Build Time: 5-10 days single developer Status: SCREWDRIVER-READY

EXECUTIVE SUMMARY

This specification provides complete, unambiguous implementation instructions for ETH-001 Challenge Board Mode - the dual-mode cognitive orchestration system with spatial artifact workspaces. The system implements three core surfaces (Dual-Mode Chat, Board Mode, Track Mode) as a production-grade client-side application requiring no backend infrastructure.

Core Innovation: Patent-protected dual-mode behavioral contracts + spatial artifact memory + multi-frame analysis + multi-track orchestration. Implementation Strategy: Pure client-side architecture using localStorage for persistence, React for UI, TypeScript for type safety, and EUDS Premium for design system.

TABLE OF CONTENTS

  1. Architecture Overview
  2. Technology Stack
  3. Design System Standards (EUDS Premium)
  4. Core Data Models
  5. Surface #1: Dual-Mode Chat Interface
  6. Surface #2: Board Mode Workspace
  7. Surface #3: Track Mode Orchestration
  8. State Management & Persistence
  9. Build Configuration
  10. Deployment Checklist
  11. Quality Gates

1. ARCHITECTURE OVERVIEW

1.1 System Architecture (Client-Side Only)

┌─────────────────────────────────────────────────────────────────┐

│ BROWSER RUNTIME │

├─────────────────────────────────────────────────────────────────┤

│ │

│ ┌──────────────────────────────────────────────────────────┐ │

│ │ PRESENTATION LAYER (React) │ │

│ │ ┌────────────────┐ ┌────────────────┐ ┌──────────────┐ │ │

│ │ │ Dual-Mode Chat │ │ Board Mode │ │ Track Mode │ │ │

│ │ │ Interface │ │ Workspace │ │ Orchestrator │ │ │

│ │ └────────────────┘ └────────────────┘ └──────────────┘ │ │

│ └──────────────────────────────────────────────────────────┘ │

│ ↕ │

│ ┌──────────────────────────────────────────────────────────┐ │

│ │ STATE MANAGEMENT (React Context/Zustand) │ │

│ │ • ModeState • ArtifactState • TrackState │ │

│ │ • UserPreferences • SessionState │ │

│ └──────────────────────────────────────────────────────────┘ │

│ ↕ │

│ ┌──────────────────────────────────────────────────────────┐ │

│ │ BUSINESS LOGIC LAYER │ │

│ │ • TransitionEvaluator • AmbiguityDetector │ │

│ │ • ArtifactCreator • MultiFrameAnalyzer │ │

│ │ • TrackOrchestrator • RelationshipMapper │ │

│ └──────────────────────────────────────────────────────────┘ │

│ ↕ │

│ ┌──────────────────────────────────────────────────────────┐ │

│ │ PERSISTENCE LAYER │ │

│ │ • LocalStorageAdapter (Primary) │ │

│ │ • IndexedDB (Optional: Large artifacts) │ │

│ │ • SessionStorage (Temporary state) │ │

│ └──────────────────────────────────────────────────────────┘ │

│ │

└─────────────────────────────────────────────────────────────────┘

1.2 Deployment Architecture

┌────────────────────────────────────────────────────────────┐

│ STATIC HOSTING │

│ (Vercel / Netlify / VPS + Caddy) │

├────────────────────────────────────────────────────────────┤

│ │

│ Static Assets: │

│ • /index.html │

│ • /_next/static/chunks/*.js │

│ • /_next/static/css/*.css │

│ • /public/fonts/* (Geist Sans/Mono) │

│ │

│ Dynamic Rendering: Client-Side Only │

│ • No API routes required │

│ • No server-side rendering needed │

│ • All state management in browser │

│ │

└────────────────────────────────────────────────────────────┘

1.3 Core Principles

1. Zero Backend Dependency 2. Constitutional Compliance 3. Patent Alignment 4. Production Quality

2. TECHNOLOGY STACK

2.1 Core Framework

Next.js 14.2+ React 18.3+ TypeScript 5.4+

2.2 State Management

Zustand 4.5+ (Primary)
// Why Zustand:

// - Simple, minimal boilerplate

// - TypeScript-first

// - Persistence middleware built-in

// - Dev tools integration

// - <200 lines of state management code total

React Context (Secondary)
// For theme, preferences, session-only state

2.3 Styling & Design

Tailwind CSS 3.4+
// EUDS Premium Configuration:

// - Custom color palette (glass morphism blues/purples)

// - Custom typography (Geist Sans/Mono)

// - Custom animations (smooth, 60fps)

// - Responsive breakpoints

Framer Motion 11+
// For smooth animations:

// - Mode transitions

// - Artifact creation

// - Board reorganization

// - Track state changes

2.4 Utilities

Date/Time: date-fns (lightweight alternative to moment.js) UUID: crypto.randomUUID() (built-in browser API) Validation: Zod (runtime type safety) Icons: Lucide React (consistent icon set) Markdown: react-markdown + remark-gfm (for artifact content)

2.5 Development Tools

ESLint + Prettier (code quality) TypeScript Compiler (type safety) Vitest (unit testing) Playwright (e2e testing)

3. DESIGN SYSTEM STANDARDS (EUDS PREMIUM)

3.1 Color Palette

// tailwind.config.ts

const colors = {

// Primary (Constitutional Blue)

primary: {

50: '#f0f7ff',

100: '#e0effe',

200: '#b9ddfe',

300: '#7cc5fd',

400: '#36a9fa',

500: '#0c8ce9', // Primary brand color

600: '#006ec7',

700: '#0158a1',

800: '#064b85',

900: '#0b3f6e',

},

// Secondary (Governance Purple)

secondary: {

50: '#faf5ff',

100: '#f3e8ff',

200: '#e9d5ff',

300: '#d8b4fe',

400: '#c084fc',

500: '#a855f7', // Secondary brand color

600: '#9333ea',

700: '#7e22ce',

800: '#6b21a8',

900: '#581c87',

},

// Accent (Harmonic Teal)

accent: {

50: '#f0fdfa',

100: '#ccfbf1',

200: '#99f6e4',

300: '#5eead4',

400: '#2dd4bf',

500: '#14b8a6', // Accent color

600: '#0d9488',

700: '#0f766e',

800: '#115e59',

900: '#134e4a',

},

// Glass Morphism Backgrounds

glass: {

light: 'rgba(255, 255, 255, 0.1)',

medium: 'rgba(255, 255, 255, 0.2)',

heavy: 'rgba(255, 255, 255, 0.3)',

},

// Mode-Specific Colors

modes: {

amenable: '#14b8a6', // Teal

challenge: '#f59e0b', // Amber

board: '#8b5cf6', // Violet

orchestration: '#ec4899', // Pink

},

}

3.2 Typography

// Fonts

const fonts = {

sans: ['Geist Sans', 'Inter', 'system-ui', 'sans-serif'],

mono: ['Geist Mono', 'Fira Code', 'Consolas', 'monospace'],

}

// Type Scale

const fontSize = {

xs: ['0.75rem', { lineHeight: '1rem' }], // 12px

sm: ['0.875rem', { lineHeight: '1.25rem' }], // 14px

base: ['1rem', { lineHeight: '1.5rem' }], // 16px

lg: ['1.125rem', { lineHeight: '1.75rem' }], // 18px

xl: ['1.25rem', { lineHeight: '1.75rem' }], // 20px

'2xl': ['1.5rem', { lineHeight: '2rem' }], // 24px

'3xl': ['1.875rem', { lineHeight: '2.25rem' }], // 30px

'4xl': ['2.25rem', { lineHeight: '2.5rem' }], // 36px

'5xl': ['3rem', { lineHeight: '1' }], // 48px

}

3.3 Glass Morphism Effects

/ Core glass morphism utility classes /

.glass-light {

background: rgba(255, 255, 255, 0.1);

backdrop-filter: blur(10px);

-webkit-backdrop-filter: blur(10px);

border: 1px solid rgba(255, 255, 255, 0.2);

}

.glass-medium {

background: rgba(255, 255, 255, 0.15);

backdrop-filter: blur(20px);

-webkit-backdrop-filter: blur(20px);

border: 1px solid rgba(255, 255, 255, 0.25);

}

.glass-heavy {

background: rgba(255, 255, 255, 0.2);

backdrop-filter: blur(30px);

-webkit-backdrop-filter: blur(30px);

border: 1px solid rgba(255, 255, 255, 0.3);

}

/ Mode-specific glass effects /

.glass-amenable {

background: rgba(20, 184, 166, 0.1);

border: 1px solid rgba(20, 184, 166, 0.2);

}

.glass-challenge {

background: rgba(245, 158, 11, 0.1);

border: 1px solid rgba(245, 158, 11, 0.2);

}

.glass-board {

background: rgba(139, 92, 246, 0.1);

border: 1px solid rgba(139, 92, 246, 0.2);

}

.glass-orchestration {

background: rgba(236, 72, 153, 0.1);

border: 1px solid rgba(236, 72, 153, 0.2);

}

3.4 Animation Standards

// Framer Motion Variants

const animationVariants = {

// Fade in/out

fadeIn: {

initial: { opacity: 0 },

animate: { opacity: 1 },

exit: { opacity: 0 },

transition: { duration: 0.2 }

},

// Slide in from right (mode transitions)

slideInRight: {

initial: { x: 300, opacity: 0 },

animate: { x: 0, opacity: 1 },

exit: { x: -300, opacity: 0 },

transition: { type: 'spring', stiffness: 300, damping: 30 }

},

// Scale in (artifact creation)

scaleIn: {

initial: { scale: 0.8, opacity: 0 },

animate: { scale: 1, opacity: 1 },

exit: { scale: 0.8, opacity: 0 },

transition: { duration: 0.3 }

},

// Stagger children (board artifacts)

staggerContainer: {

animate: {

transition: {

staggerChildren: 0.1

}

}

},

}

3.5 Spacing System

// 8px base unit

const spacing = {

px: '1px',

0: '0',

1: '0.25rem', // 4px

2: '0.5rem', // 8px

3: '0.75rem', // 12px

4: '1rem', // 16px

5: '1.25rem', // 20px

6: '1.5rem', // 24px

8: '2rem', // 32px

10: '2.5rem', // 40px

12: '3rem', // 48px

16: '4rem', // 64px

20: '5rem', // 80px

24: '6rem', // 96px

}


4. CORE DATA MODELS

4.1 Type Definitions

// src/types/core.ts

// ============================================================================

// MODE SYSTEM

// ============================================================================

export enum Mode {

AMENABLE = 'amenable',

CHALLENGE = 'challenge',

BOARD = 'board',

ORCHESTRATION = 'orchestration',

}

export interface BehavioralContract {

mode: Mode;

responseStyle: 'direct' | 'multi_frame';

challengeThreshold: number; // 0.0 - 1.0

artifactCreation: boolean;

multiFrameAnalysis: boolean;

executionPriority: boolean;

}

export interface ModeState {

userId: string;

sessionId: string;

currentMode: Mode;

behavioralContract: BehavioralContract;

activationTimestamp: string; // ISO 8601

transitionReason?: string;

}

export interface ModeTransition {

transitionId: string;

fromMode: Mode;

toMode: Mode;

timestamp: string;

reason: string;

confidence: number; // 0.0 - 1.0

automatic: boolean; // true if auto-triggered, false if manual

}

// ============================================================================

// ARTIFACT SYSTEM

// ============================================================================

export enum ArtifactType {

CONCEPT = 'concept',

DESIGN = 'design',

DECISION = 'decision',

NOTE = 'note',

CONSTRAINT = 'constraint',

}

export enum RelationshipType {

DERIVESFROM = 'derivesfrom',

CONTRADICTS = 'contradicts',

EXTENDS = 'extends',

REQUIRES = 'requires',

}

export interface ArtifactRelationship {

targetArtifactId: string;

relationshipType: RelationshipType;

strength: number; // 0.0 - 1.0

}

export interface Artifact {

artifactId: string;

boardId: string;

userId: string;

artifactType: ArtifactType;

title: string;

content: string;

categories: string[];

metadata: {

source: string; // conversationId or 'manual'

createdInMode: Mode;

wordCount: number;

substantialityScore: number; // 0.0 - 1.0

};

relationships: ArtifactRelationship[];

position?: { x: number; y: number }; // For spatial layout

pinned: boolean;

createdAt: string; // ISO 8601

updatedAt: string; // ISO 8601

}

// ============================================================================

// BOARD SYSTEM

// ============================================================================

export enum BoardType {

STRATEGY = 'strategy',

UX = 'ux',

TECHNICAL = 'technical',

NARRATIVE = 'narrative',

PHILOSOPHY = 'philosophy',

CONSTRAINTS = 'constraints',

}

export interface Board {

boardId: string;

userId: string;

name: string;

boardType: BoardType;

description?: string;

artifactIds: string[]; // Array of artifact IDs

spatialLayout: {

layoutType: 'grid' | 'freeform' | 'hierarchical';

gridConfig?: {

columns: number;

gap: number;

};

};

createdAt: string;

lastAccessed: string;

archived: boolean;

}

// ============================================================================

// TRACK SYSTEM

// ============================================================================

export enum TrackStatus {

NOTSTARTED = 'notstarted',

INPROGRESS = 'inprogress',

BLOCKED = 'blocked',

COMPLETED = 'completed',

}

export interface Track {

trackId: string;

userId: string;

trackName: string;

trackType: string;

status: TrackStatus;

progress: number; // 0 - 100

nextAction?: string;

dependencies: string[]; // Array of track IDs

artifactIds: string[]; // Associated artifacts

createdAt: string;

updatedAt: string;

}

// ============================================================================

// CONVERSATION SYSTEM

// ============================================================================

export interface Message {

messageId: string;

conversationId: string;

role: 'user' | 'assistant';

content: string;

timestamp: string;

mode: Mode; // Mode active when message sent

multiFrame?: MultiFrameResponse; // Only for challenge mode

}

export interface MultiFrameResponse {

primaryInterpretation: string;

alternativeFrames: string[];

tradeoffAnalysis: string;

blindSpotIdentification: string;

synthesis: string;

}

export interface Conversation {

conversationId: string;

userId: string;

title: string;

messages: Message[];

createdAt: string;

lastMessageAt: string;

}

// ============================================================================

// USER PREFERENCES

// ============================================================================

export interface UserPreferences {

userId: string;

theme: 'light' | 'dark' | 'auto';

challengeThreshold: number; // Custom threshold for challenge mode activation

autoArtifacts: boolean; // Automatically create artifacts

maxParallelTracks: number; // Max concurrent tracks in orchestration

notificationsEnabled: boolean;

}

// ============================================================================

// APPLICATION STATE

// ============================================================================

export interface ApplicationState {

modeState: ModeState;

conversations: Record<string, Conversation>; // conversationId -> Conversation

artifacts: Record<string, Artifact>; // artifactId -> Artifact

boards: Record<string, Board>; // boardId -> Board

tracks: Record<string, Track>; // trackId -> Track

preferences: UserPreferences;

currentConversationId: string | null;

currentBoardId: string | null;

}

4.2 Default Values

// src/lib/defaults.ts

export const DEFAULTBEHAVIORALCONTRACTS: Record<Mode, BehavioralContract> = {

[Mode.AMENABLE]: {

mode: Mode.AMENABLE,

responseStyle: 'direct',

challengeThreshold: 0.0,

artifactCreation: false,

multiFrameAnalysis: false,

executionPriority: true,

},

[Mode.CHALLENGE]: {

mode: Mode.CHALLENGE,

responseStyle: 'multi_frame',

challengeThreshold: 1.0,

artifactCreation: true,

multiFrameAnalysis: true,

executionPriority: false,

},

[Mode.BOARD]: {

mode: Mode.BOARD,

responseStyle: 'direct',

challengeThreshold: 0.0,

artifactCreation: true,

multiFrameAnalysis: false,

executionPriority: true,

},

[Mode.ORCHESTRATION]: {

mode: Mode.ORCHESTRATION,

responseStyle: 'direct',

challengeThreshold: 0.0,

artifactCreation: true,

multiFrameAnalysis: false,

executionPriority: true,

},

};

export const DEFAULTUSERPREFERENCES: UserPreferences = {

userId: '',

theme: 'auto',

challengeThreshold: 0.6,

autoArtifacts: true,

maxParallelTracks: 6,

notificationsEnabled: true,

};

export const DEFAULTMODESTATE: ModeState = {

userId: '',

sessionId: '',

currentMode: Mode.AMENABLE,

behavioralContract: DEFAULTBEHAVIORALCONTRACTS[Mode.AMENABLE],

activationTimestamp: new Date().toISOString(),

};


5. SURFACE #1: DUAL-MODE CHAT INTERFACE

5.1 Component Structure

src/components/chat/

├── ChatInterface.tsx # Root chat component

├── MessageList.tsx # Scrollable message container

├── MessageBubble.tsx # Individual message display

├── MultiFrameMessage.tsx # Challenge mode multi-frame display

├── InputBar.tsx # User input + send button

├── ModeIndicator.tsx # Current mode badge

└── TransitionNotification.tsx # Mode change notification

5.2 ChatInterface Component

// src/components/chat/ChatInterface.tsx

'use client';

import { useState, useEffect, useRef } from 'react';

import { useStore } from '@/store';

import { MessageList } from './MessageList';

import { InputBar } from './InputBar';

import { ModeIndicator } from './ModeIndicator';

import { TransitionNotification } from './TransitionNotification';

import { evaluateTransition } from '@/lib/transition-evaluator';

import { motion, AnimatePresence } from 'framer-motion';

export function ChatInterface() {

const {

currentConversation,

modeState,

addMessage,

transitionMode,

preferences,

} = useStore();

const [input, setInput] = useState('');

const [isTransitioning, setIsTransitioning] = useState(false);

const messagesEndRef = useRef<HTMLDivElement>(null);

// Auto-scroll to bottom when new messages arrive

useEffect(() => {

messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });

}, [currentConversation?.messages]);

// Handle message send

const handleSend = async (userInput: string) => {

if (!userInput.trim()) return;

// Add user message

const userMessage: Message = {

messageId: crypto.randomUUID(),

conversationId: currentConversation.conversationId,

role: 'user',

content: userInput,

timestamp: new Date().toISOString(),

mode: modeState.currentMode,

};

addMessage(userMessage);

// Evaluate if mode transition should occur

const transition = evaluateTransition(

modeState.currentMode,

userInput,

{

conversationHistory: currentConversation.messages,

userPreferences: preferences,

}

);

if (transition.shouldTransition) {

setIsTransitioning(true);

await transitionMode(

transition.targetMode,

transition.reason

);

setIsTransitioning(false);

}

// Generate assistant response

// NOTE: In production, this would call OpenAI/Anthropic API

// For demo, we generate mock responses based on mode

const assistantMessage = await generateResponse(

userInput,

modeState.currentMode,

currentConversation.messages

);

addMessage(assistantMessage);

setInput('');

};

return (

<div className="flex h-screen flex-col bg-gradient-to-br from-slate-900 via-purple-900 to-slate-900">

{/ Mode Indicator Header /}

<div className="border-b border-white/10 bg-black/20 p-4">

<ModeIndicator mode={modeState.currentMode} />

</div>

{/ Message List /}

<div className="flex-1 overflow-y-auto">

<MessageList

messages={currentConversation?.messages || []}

currentMode={modeState.currentMode}

/>

<div ref={messagesEndRef} />

</div>

{/ Transition Notification /}

<AnimatePresence>

{isTransitioning && <TransitionNotification />}

</AnimatePresence>

{/ Input Bar /}

<div className="border-t border-white/10 bg-black/20 p-4">

<InputBar

value={input}

onChange={setInput}

onSend={handleSend}

disabled={isTransitioning}

/>

</div>

</div>

);

}

// Mock response generator (replace with real API in production)

async function generateResponse(

userInput: string,

mode: Mode,

history: Message[]

): Promise<Message> {

// Simulate API latency

await new Promise(resolve => setTimeout(resolve, 800));

const baseResponse: Message = {

messageId: crypto.randomUUID(),

conversationId: history[0]?.conversationId || crypto.randomUUID(),

role: 'assistant',

content: '',

timestamp: new Date().toISOString(),

mode: mode,

};

if (mode === Mode.CHALLENGE) {

// Generate multi-frame response

return {

...baseResponse,

content: 'Challenge mode multi-frame response',

multiFrame: {

primaryInterpretation: Primary interpretation of: "${userInput}",

alternativeFrames: [

'Alternative frame 1: Consider this perspective...',

'Alternative frame 2: What if we approached it differently...',

],

tradeoffAnalysis: 'Tradeoffs: If we choose A, we gain X but lose Y...',

blindSpotIdentification: 'Potential blind spot: You might not have considered Z...',

synthesis: 'Synthesis: Integrating these perspectives suggests...',

},

};

}

// Amenable mode: direct response

return {

...baseResponse,

content: Direct response to: "${userInput}",

};

}

5.3 MultiFrameMessage Component

// src/components/chat/MultiFrameMessage.tsx

'use client';

import { Message } from '@/types/core';

import { motion } from 'framer-motion';

import { ChevronDown, ChevronRight } from 'lucide-react';

import { useState } from 'react';

interface MultiFrameMessageProps {

message: Message;

}

export function MultiFrameMessage({ message }: MultiFrameMessageProps) {

const [expandedSections, setExpandedSections] = useState({

alternatives: true,

tradeoffs: true,

blindSpots: true,

synthesis: true,

});

if (!message.multiFrame) return null;

const toggleSection = (section: keyof typeof expandedSections) => {

setExpandedSections(prev => ({

...prev,

[section]: !prev[section],

}));

};

return (

<motion.div

initial={{ opacity: 0, y: 20 }}

animate={{ opacity: 1, y: 0 }}

className="space-y-4"

>

{/ Primary Interpretation /}

<div className="glass-challenge rounded-lg p-4">

<h4 className="mb-2 font-mono text-sm font-semibold uppercase tracking-wide text-amber-400">

Primary Interpretation

</h4>

<p className="text-white/90">

{message.multiFrame.primaryInterpretation}

</p>

</div>

{/ Alternative Frames /}

<div className="glass-challenge rounded-lg">

<button

onClick={() => toggleSection('alternatives')}

className="flex w-full items-center justify-between p-4"

>

<h4 className="font-mono text-sm font-semibold uppercase tracking-wide text-amber-400">

Alternative Frames

</h4>

{expandedSections.alternatives ? (

<ChevronDown className="h-5 w-5 text-amber-400" />

) : (

<ChevronRight className="h-5 w-5 text-amber-400" />

)}

</button>

{expandedSections.alternatives && (

<div className="space-y-2 px-4 pb-4">

{message.multiFrame.alternativeFrames.map((frame, idx) => (

<div

key={idx}

className="rounded border-l-2 border-amber-400 bg-black/20 p-3"

>

<p className="text-white/90">{frame}</p>

</div>

))}

</div>

)}

</div>

{/ Tradeoff Analysis /}

<div className="glass-challenge rounded-lg">

<button

onClick={() => toggleSection('tradeoffs')}

className="flex w-full items-center justify-between p-4"

>

<h4 className="font-mono text-sm font-semibold uppercase tracking-wide text-amber-400">

Tradeoff Analysis

</h4>

{expandedSections.tradeoffs ? (

<ChevronDown className="h-5 w-5 text-amber-400" />

) : (

<ChevronRight className="h-5 w-5 text-amber-400" />

)}

</button>

{expandedSections.tradeoffs && (

<div className="px-4 pb-4">

<p className="text-white/90">

{message.multiFrame.tradeoffAnalysis}

</p>

</div>

)}

</div>

{/ Blind Spot Identification /}

<div className="glass-challenge rounded-lg">

<button

onClick={() => toggleSection('blindSpots')}

className="flex w-full items-center justify-between p-4"

>

<h4 className="font-mono text-sm font-semibold uppercase tracking-wide text-amber-400">

Blind Spot Identification

</h4>

{expandedSections.blindSpots ? (

<ChevronDown className="h-5 w-5 text-amber-400" />

) : (

<ChevronRight className="h-5 w-5 text-amber-400" />

)}

</button>

{expandedSections.blindSpots && (

<div className="px-4 pb-4">

<p className="text-white/90">

{message.multiFrame.blindSpotIdentification}

</p>

</div>

)}

</div>

{/ Synthesis /}

<div className="glass-challenge rounded-lg p-4">

<h4 className="mb-2 font-mono text-sm font-semibold uppercase tracking-wide text-amber-400">

Synthesis

</h4>

<p className="text-white/90">{message.multiFrame.synthesis}</p>

</div>

</motion.div>

);

}

5.4 ModeIndicator Component

// src/components/chat/ModeIndicator.tsx

'use client';

import { Mode } from '@/types/core';

import { motion } from 'framer-motion';

import {

CheckCircle,

AlertTriangle,

Grid3x3,

Network,

} from 'lucide-react';

interface ModeIndicatorProps {

mode: Mode;

}

const MODE_CONFIG = {

[Mode.AMENABLE]: {

label: 'Amenable Mode',

description: 'Execution-focused, direct responses',

icon: CheckCircle,

color: 'text-teal-400',

bgColor: 'bg-teal-500/10',

borderColor: 'border-teal-500/20',

},

[Mode.CHALLENGE]: {

label: 'Challenge Mode',

description: 'Multi-frame analysis, cognitive counterpoint',

icon: AlertTriangle,

color: 'text-amber-400',

bgColor: 'bg-amber-500/10',

borderColor: 'border-amber-500/20',

},

[Mode.BOARD]: {

label: 'Board Mode',

description: 'Spatial workspace, artifact creation',

icon: Grid3x3,

color: 'text-violet-400',

bgColor: 'bg-violet-500/10',

borderColor: 'border-violet-500/20',

},

[Mode.ORCHESTRATION]: {

label: 'Orchestration Mode',

description: 'Multi-track coordination',

icon: Network,

color: 'text-pink-400',

bgColor: 'bg-pink-500/10',

borderColor: 'border-pink-500/20',

},

};

export function ModeIndicator({ mode }: ModeIndicatorProps) {

const config = MODE_CONFIG[mode];

const Icon = config.icon;

return (

<motion.div

layout

className={flex items-center gap-3 rounded-lg border ${config.borderColor} ${config.bgColor} px-4 py-2}

>

<Icon className={h-5 w-5 ${config.color}} />

<div className="flex-1">

<div className={font-mono text-sm font-semibold ${config.color}}>

{config.label}

</div>

<div className="text-xs text-white/60">{config.description}</div>

</div>

</motion.div>

);

}


6. SURFACE #2: BOARD MODE WORKSPACE

6.1 Component Structure

src/components/board/

├── BoardWorkspace.tsx # Root board component

├── BoardList.tsx # List of all boards

├── BoardCanvas.tsx # Main spatial canvas

├── ArtifactCard.tsx # Individual artifact display

├── ArtifactCreator.tsx # New artifact form

├── RelationshipLines.tsx # Visual connections between artifacts

└── BoardControls.tsx # Zoom, filter, clustering controls

6.2 BoardWorkspace Component

// src/components/board/BoardWorkspace.tsx

'use client';

import { useState } from 'react';

import { useStore } from '@/store';

import { BoardList } from './BoardList';

import { BoardCanvas } from './BoardCanvas';

import { ArtifactCreator } from './ArtifactCreator';

import { BoardControls } from './BoardControls';

import { motion, AnimatePresence } from 'framer-motion';

import { Plus } from 'lucide-react';

export function BoardWorkspace() {

const {

boards,

currentBoardId,

artifacts,

setCurrentBoard,

createBoard,

} = useStore();

const [showCreator, setShowCreator] = useState(false);

const [viewMode, setViewMode] = useState<'grid' | 'freeform'>('grid');

const currentBoard = currentBoardId ? boards[currentBoardId] : null;

const boardArtifacts = currentBoard

? currentBoard.artifactIds.map(id => artifacts[id]).filter(Boolean)

: [];

return (

<div className="flex h-screen bg-gradient-to-br from-slate-900 via-purple-900 to-slate-900">

{/ Sidebar: Board List /}

<div className="w-80 border-r border-white/10 bg-black/20">

<div className="p-4">

<button

onClick={() => {

const newBoard: Board = {

boardId: crypto.randomUUID(),

userId: 'demo-user',

name: 'New Board',

boardType: BoardType.STRATEGY,

artifactIds: [],

spatialLayout: {

layoutType: 'grid',

gridConfig: { columns: 3, gap: 16 },

},

createdAt: new Date().toISOString(),

lastAccessed: new Date().toISOString(),

archived: false,

};

createBoard(newBoard);

}}

className="flex w-full items-center justify-center gap-2 rounded-lg bg-violet-500 px-4 py-2 font-mono text-sm font-semibold text-white transition-all hover:bg-violet-600"

>

<Plus className="h-4 w-4" />

New Board

</button>

</div>

<BoardList

boards={Object.values(boards)}

currentBoardId={currentBoardId}

onSelectBoard={setCurrentBoard}

/>

</div>

{/ Main Canvas Area /}

<div className="flex-1 flex flex-col">

{/ Board Header /}

{currentBoard && (

<div className="border-b border-white/10 bg-black/20 p-4">

<div className="flex items-center justify-between">

<div>

<h2 className="text-2xl font-bold text-white">

{currentBoard.name}

</h2>

<p className="text-sm text-white/60">

{boardArtifacts.length} artifacts

</p>

</div>

<BoardControls

viewMode={viewMode}

onViewModeChange={setViewMode}

/>

</div>

</div>

)}

{/ Canvas /}

<div className="flex-1 overflow-auto p-6">

{currentBoard ? (

<BoardCanvas

board={currentBoard}

artifacts={boardArtifacts}

viewMode={viewMode}

/>

) : (

<div className="flex h-full items-center justify-center">

<div className="text-center">

<p className="text-lg text-white/60">

Select a board to get started

</p>

</div>

</div>

)}

</div>

{/ Floating Action Button: Create Artifact /}

<button

onClick={() => setShowCreator(true)}

className="fixed bottom-8 right-8 flex h-14 w-14 items-center justify-center rounded-full bg-violet-500 shadow-lg transition-all hover:bg-violet-600 hover:shadow-xl"

>

<Plus className="h-6 w-6 text-white" />

</button>

{/ Artifact Creator Modal /}

<AnimatePresence>

{showCreator && (

<ArtifactCreator

boardId={currentBoardId!}

onClose={() => setShowCreator(false)}

/>

)}

</AnimatePresence>

</div>

</div>

);

}

6.3 BoardCanvas Component

// src/components/board/BoardCanvas.tsx

'use client';

import { Board, Artifact } from '@/types/core';

import { ArtifactCard } from './ArtifactCard';

import { RelationshipLines } from './RelationshipLines';

import { motion } from 'framer-motion';

interface BoardCanvasProps {

board: Board;

artifacts: Artifact[];

viewMode: 'grid' | 'freeform';

}

export function BoardCanvas({ board, artifacts, viewMode }: BoardCanvasProps) {

if (viewMode === 'grid') {

const columns = board.spatialLayout.gridConfig?.columns || 3;

const gap = board.spatialLayout.gridConfig?.gap || 16;

return (

<motion.div

layout

className="grid gap-4"

style={{

gridTemplateColumns: repeat(${columns}, 1fr),

gap: ${gap}px,

}}

>

{artifacts.map((artifact, idx) => (

<motion.div

key={artifact.artifactId}

initial={{ opacity: 0, scale: 0.8 }}

animate={{ opacity: 1, scale: 1 }}

transition={{ delay: idx * 0.1 }}

>

<ArtifactCard artifact={artifact} />

</motion.div>

))}

</motion.div>

);

}

// Freeform mode: absolute positioning

return (

<div className="relative h-full min-h-[800px]">

<RelationshipLines artifacts={artifacts} />

{artifacts.map(artifact => (

<motion.div

key={artifact.artifactId}

drag

dragMomentum={false}

className="absolute"

style={{

left: artifact.position?.x || Math.random() * 800,

top: artifact.position?.y || Math.random() * 600,

}}

>

<ArtifactCard artifact={artifact} />

</motion.div>

))}

</div>

);

}

6.4 ArtifactCard Component

// src/components/board/ArtifactCard.tsx

'use client';

import { Artifact, ArtifactType } from '@/types/core';

import { motion } from 'framer-motion';

import {

Lightbulb,

Palette,

CheckSquare,

StickyNote,

AlertCircle,

} from 'lucide-react';

import ReactMarkdown from 'react-markdown';

import { useState } from 'react';

interface ArtifactCardProps {

artifact: Artifact;

}

const ARTIFACT_ICONS = {

[ArtifactType.CONCEPT]: Lightbulb,

[ArtifactType.DESIGN]: Palette,

[ArtifactType.DECISION]: CheckSquare,

[ArtifactType.NOTE]: StickyNote,

[ArtifactType.CONSTRAINT]: AlertCircle,

};

const ARTIFACT_COLORS = {

[ArtifactType.CONCEPT]: 'border-blue-500/20 bg-blue-500/5',

[ArtifactType.DESIGN]: 'border-purple-500/20 bg-purple-500/5',

[ArtifactType.DECISION]: 'border-green-500/20 bg-green-500/5',

[ArtifactType.NOTE]: 'border-yellow-500/20 bg-yellow-500/5',

[ArtifactType.CONSTRAINT]: 'border-red-500/20 bg-red-500/5',

};

export function ArtifactCard({ artifact }: ArtifactCardProps) {

const [expanded, setExpanded] = useState(false);

const Icon = ARTIFACT_ICONS[artifact.artifactType];

const colorClass = ARTIFACT_COLORS[artifact.artifactType];

return (

<motion.div

layout

className={glass-medium rounded-lg border ${colorClass} p-4 shadow-lg transition-all hover:shadow-xl ${

expanded ? 'col-span-2' : ''

}}

onClick={() => setExpanded(!expanded)}

>

{/ Header /}

<div className="mb-2 flex items-start justify-between">

<div className="flex items-center gap-2">

<Icon className="h-5 w-5 text-white/70" />

<h3 className="font-semibold text-white">{artifact.title}</h3>

</div>

{artifact.pinned && (

<div className="text-amber-400"></div>

)}

</div>

{/ Content Preview/Full /}

<div className={text-sm text-white/80 ${expanded ? '' : 'line-clamp-3'}}>

<ReactMarkdown>{artifact.content}</ReactMarkdown>

</div>

{/ Categories /}

{artifact.categories.length > 0 && (

<div className="mt-3 flex flex-wrap gap-1">

{artifact.categories.map(cat => (

<span

key={cat}

className="rounded-full bg-white/10 px-2 py-0.5 text-xs text-white/70"

>

{cat}

</span>

))}

</div>

)}

{/ Metadata /}

<div className="mt-2 text-xs text-white/50">

{new Date(artifact.createdAt).toLocaleDateString()} •{' '}

{artifact.metadata.wordCount} words

</div>

</motion.div>

);

}


7. SURFACE #3: TRACK MODE ORCHESTRATION

7.1 Component Structure

src/components/tracks/

├── TrackOrchestrator.tsx # Root track component

├── TrackList.tsx # List of all tracks

├── TrackCard.tsx # Individual track display

├── DependencyGraph.tsx # Visual dependency lines

├── ProgressVisualization.tsx # Progress bars and status

└── TrackCreator.tsx # New track form

7.2 TrackOrchestrator Component

// src/components/tracks/TrackOrchestrator.tsx

'use client';

import { useState } from 'react';

import { useStore } from '@/store';

import { TrackList } from './TrackList';

import { TrackCard } from './TrackCard';

import { DependencyGraph } from './DependencyGraph';

import { TrackCreator } from './TrackCreator';

import { motion, AnimatePresence } from 'framer-motion';

import { Plus, GitBranch } from 'lucide-react';

import { Track, TrackStatus } from '@/types/core';

export function TrackOrchestrator() {

const { tracks, createTrack, updateTrack } = useStore();

const [showCreator, setShowCreator] = useState(false);

const [viewMode, setViewMode] = useState<'list' | 'graph'>('list');

const allTracks = Object.values(tracks);

const activeTracksCount = allTracks.filter(

t => t.status === TrackStatus.IN_PROGRESS

).length;

return (

<div className="flex h-screen flex-col bg-gradient-to-br from-slate-900 via-purple-900 to-slate-900">

{/ Header /}

<div className="border-b border-white/10 bg-black/20 p-4">

<div className="flex items-center justify-between">

<div>

<h2 className="flex items-center gap-2 text-2xl font-bold text-white">

<GitBranch className="h-6 w-6 text-pink-400" />

Track Orchestration

</h2>

<p className="text-sm text-white/60">

{activeTracksCount} active tracks • {allTracks.length} total

</p>

</div>

<div className="flex items-center gap-2">

<button

onClick={() =>

setViewMode(viewMode === 'list' ? 'graph' : 'list')

}

className="rounded-lg border border-white/10 bg-white/5 px-4 py-2 text-sm text-white transition-all hover:bg-white/10"

>

{viewMode === 'list' ? 'Graph View' : 'List View'}

</button>

<button

onClick={() => setShowCreator(true)}

className="flex items-center gap-2 rounded-lg bg-pink-500 px-4 py-2 font-mono text-sm font-semibold text-white transition-all hover:bg-pink-600"

>

<Plus className="h-4 w-4" />

New Track

</button>

</div>

</div>

</div>

{/ Main Content /}

<div className="flex-1 overflow-auto p-6">

{viewMode === 'list' ? (

<TrackList tracks={allTracks} onUpdateTrack={updateTrack} />

) : (

<DependencyGraph tracks={allTracks} />

)}

</div>

{/ Track Creator Modal /}

<AnimatePresence>

{showCreator && (

<TrackCreator

onClose={() => setShowCreator(false)}

onCreate={createTrack}

/>

)}

</AnimatePresence>

</div>

);

}

7.3 TrackCard Component

// src/components/tracks/TrackCard.tsx

'use client';

import { Track, TrackStatus } from '@/types/core';

import { motion } from 'framer-motion';

import {

Circle,

Loader,

XCircle,

CheckCircle2,

ChevronRight,

} from 'lucide-react';

import { useState } from 'react';

interface TrackCardProps {

track: Track;

onUpdate: (trackId: string, updates: Partial<Track>) => void;

}

const STATUS_CONFIG = {

[TrackStatus.NOT_STARTED]: {

icon: Circle,

color: 'text-gray-400',

bgColor: 'bg-gray-500/10',

label: 'Not Started',

},

[TrackStatus.IN_PROGRESS]: {

icon: Loader,

color: 'text-blue-400',

bgColor: 'bg-blue-500/10',

label: 'In Progress',

},

[TrackStatus.BLOCKED]: {

icon: XCircle,

color: 'text-red-400',

bgColor: 'bg-red-500/10',

label: 'Blocked',

},

[TrackStatus.COMPLETED]: {

icon: CheckCircle2,

color: 'text-green-400',

bgColor: 'bg-green-500/10',

label: 'Completed',

},

};

export function TrackCard({ track, onUpdate }: TrackCardProps) {

const [expanded, setExpanded] = useState(false);

const config = STATUS_CONFIG[track.status];

const Icon = config.icon;

return (

<motion.div

layout

className={glass-orchestration rounded-lg border border-pink-500/20 ${config.bgColor} p-4}

>

{/ Header /}

<div

className="flex cursor-pointer items-center justify-between"

onClick={() => setExpanded(!expanded)}

>

<div className="flex items-center gap-3 flex-1">

<Icon className={h-5 w-5 ${config.color}} />

<div>

<h3 className="font-semibold text-white">{track.trackName}</h3>

<p className="text-xs text-white/60">{track.trackType}</p>

</div>

</div>

<div className="flex items-center gap-3">

<div className="text-sm text-white/70">{track.progress}%</div>

<ChevronRight

className={h-5 w-5 text-white/50 transition-transform ${

expanded ? 'rotate-90' : ''

}}

/>

</div>

</div>

{/ Progress Bar /}

<div className="mt-3 h-1.5 w-full overflow-hidden rounded-full bg-white/10">

<motion.div

initial={{ width: 0 }}

animate={{ width: ${track.progress}% }}

transition={{ duration: 0.5 }}

className={h-full ${config.bgColor.replace('/10', '/50')}}

/>

</div>

{/ Expanded Content /}

{expanded && (

<motion.div

initial={{ opacity: 0, height: 0 }}

animate={{ opacity: 1, height: 'auto' }}

exit={{ opacity: 0, height: 0 }}

className="mt-4 space-y-3"

>

{/ Next Action /}

{track.nextAction && (

<div>

<div className="mb-1 text-xs font-semibold uppercase tracking-wide text-white/50">

Next Action

</div>

<div className="text-sm text-white/80">{track.nextAction}</div>

</div>

)}

{/ Dependencies /}

{track.dependencies.length > 0 && (

<div>

<div className="mb-1 text-xs font-semibold uppercase tracking-wide text-white/50">

Dependencies

</div>

<div className="flex flex-wrap gap-1">

{track.dependencies.map(depId => (

<span

key={depId}

className="rounded-full bg-white/10 px-2 py-0.5 text-xs text-white/70"

>

{depId.slice(0, 8)}

</span>

))}

</div>

</div>

)}

{/ Status Actions /}

<div className="flex gap-2">

{Object.entries(STATUS_CONFIG).map(([status, cfg]) => (

<button

key={status}

onClick={() =>

onUpdate(track.trackId, { status: status as TrackStatus })

}

className={rounded px-3 py-1 text-xs transition-all ${

track.status === status

? cfg.bgColor + ' ' + cfg.color

: 'bg-white/5 text-white/50 hover:bg-white/10'

}}

>

{cfg.label}

</button>

))}

</div>

</motion.div>

)}

</motion.div>

);

}


8. STATE MANAGEMENT & PERSISTENCE

8.1 Zustand Store Setup

// src/store/index.ts

import { create } from 'zustand';

import { persist, createJSONStorage } from 'zustand/middleware';

import { ApplicationState, Mode, Message, Artifact, Board, Track } from '@/types/core';

import { DEFAULTMODESTATE, DEFAULTUSERPREFERENCES } from '@/lib/defaults';

interface StoreState extends ApplicationState {

// Mode Management

transitionMode: (targetMode: Mode, reason: string) => void;

// Conversation Management

addMessage: (message: Message) => void;

createConversation: (title: string) => string; // returns conversationId

setCurrentConversation: (conversationId: string) => void;

// Artifact Management

createArtifact: (artifact: Artifact) => void;

updateArtifact: (artifactId: string, updates: Partial<Artifact>) => void;

deleteArtifact: (artifactId: string) => void;

// Board Management

createBoard: (board: Board) => void;

updateBoard: (boardId: string, updates: Partial<Board>) => void;

setCurrentBoard: (boardId: string) => void;

addArtifactToBoard: (boardId: string, artifactId: string) => void;

// Track Management

createTrack: (track: Track) => void;

updateTrack: (trackId: string, updates: Partial<Track>) => void;

deleteTrack: (trackId: string) => void;

// Preferences

updatePreferences: (updates: Partial<UserPreferences>) => void;

}

export const useStore = create<StoreState>()(

persist(

(set, get) => ({

// Initial State

modeState: DEFAULTMODESTATE,

conversations: {},

artifacts: {},

boards: {},

tracks: {},

preferences: DEFAULTUSERPREFERENCES,

currentConversationId: null,

currentBoardId: null,

// Mode Management

transitionMode: (targetMode, reason) => {

set(state => ({

modeState: {

...state.modeState,

currentMode: targetMode,

behavioralContract: DEFAULTBEHAVIORALCONTRACTS[targetMode],

activationTimestamp: new Date().toISOString(),

transitionReason: reason,

},

}));

},

// Conversation Management

addMessage: message => {

set(state => {

const conversation = state.conversations[message.conversationId];

if (!conversation) return state;

return {

conversations: {

...state.conversations,

[message.conversationId]: {

...conversation,

messages: [...conversation.messages, message],

lastMessageAt: message.timestamp,

},

},

};

});

},

createConversation: title => {

const conversationId = crypto.randomUUID();

const conversation: Conversation = {

conversationId,

userId: 'demo-user',

title,

messages: [],

createdAt: new Date().toISOString(),

lastMessageAt: new Date().toISOString(),

};

set(state => ({

conversations: {

...state.conversations,

[conversationId]: conversation,

},

currentConversationId: conversationId,

}));

return conversationId;

},

setCurrentConversation: conversationId => {

set({ currentConversationId: conversationId });

},

// Artifact Management

createArtifact: artifact => {

set(state => ({

artifacts: {

...state.artifacts,

[artifact.artifactId]: artifact,

},

}));

},

updateArtifact: (artifactId, updates) => {

set(state => {

const artifact = state.artifacts[artifactId];

if (!artifact) return state;

return {

artifacts: {

...state.artifacts,

[artifactId]: {

...artifact,

...updates,

updatedAt: new Date().toISOString(),

},

},

};

});

},

deleteArtifact: artifactId => {

set(state => {

const { [artifactId]: removed, ...remainingArtifacts } = state.artifacts;

return { artifacts: remainingArtifacts };

});

},

// Board Management

createBoard: board => {

set(state => ({

boards: {

...state.boards,

[board.boardId]: board,

},

currentBoardId: board.boardId,

}));

},

updateBoard: (boardId, updates) => {

set(state => {

const board = state.boards[boardId];

if (!board) return state;

return {

boards: {

...state.boards,

[boardId]: {

...board,

...updates,

},

},

};

});

},

setCurrentBoard: boardId => {

set({ currentBoardId: boardId });

},

addArtifactToBoard: (boardId, artifactId) => {

set(state => {

const board = state.boards[boardId];

if (!board) return state;

return {

boards: {

...state.boards,

[boardId]: {

...board,

artifactIds: [...board.artifactIds, artifactId],

},

},

};

});

},

// Track Management

createTrack: track => {

set(state => ({

tracks: {

...state.tracks,

[track.trackId]: track,

},

}));

},

updateTrack: (trackId, updates) => {

set(state => {

const track = state.tracks[trackId];

if (!track) return state;

return {

tracks: {

...state.tracks,

[trackId]: {

...track,

...updates,

updatedAt: new Date().toISOString(),

},

},

};

});

},

deleteTrack: trackId => {

set(state => {

const { [trackId]: removed, ...remainingTracks } = state.tracks;

return { tracks: remainingTracks };

});

},

// Preferences

updatePreferences: updates => {

set(state => ({

preferences: {

...state.preferences,

...updates,

},

}));

},

}),

{

name: 'ethraeon-storage',

storage: createJSONStorage(() => localStorage),

partialize: state => ({

// Only persist these fields (exclude session-specific data)

conversations: state.conversations,

artifacts: state.artifacts,

boards: state.boards,

tracks: state.tracks,

preferences: state.preferences,

modeState: state.modeState,

}),

}

)

);

8.2 Transition Evaluator

// src/lib/transition-evaluator.ts

import { Mode } from '@/types/core';

interface TransitionEvaluation {

shouldTransition: boolean;

targetMode: Mode;

confidence: number; // 0.0 - 1.0

reason: string;

}

interface EvaluationContext {

conversationHistory: Message[];

userPreferences: UserPreferences;

}

// Strategic decision keywords

const STRATEGIC_KEYWORDS = [

'should we',

'what if',

'how about',

'consider',

'evaluate',

'assess',

'analyze',

'compare',

'tradeoff',

'alternative',

'option',

'approach',

'pros',

'cons',

'risk',

'opportunity',

];

export function evaluateTransition(

currentMode: Mode,

userInput: string,

context: EvaluationContext

): TransitionEvaluation {

const lowerInput = userInput.toLowerCase();

// Explicit mode commands

if (lowerInput.includes('activate challenge mode')) {

return {

shouldTransition: true,

targetMode: Mode.CHALLENGE,

confidence: 1.0,

reason: 'Explicit user command',

};

}

if (lowerInput.includes('return to amenable')) {

return {

shouldTransition: true,

targetMode: Mode.AMENABLE,

confidence: 1.0,

reason: 'Explicit user command',

};

}

// From AMENABLE → CHALLENGE (automatic triggers)

if (currentMode === Mode.AMENABLE) {

// 1. Strategic decision detection

const hasStrategicKeyword = STRATEGIC_KEYWORDS.some(keyword =>

lowerInput.includes(keyword)

);

if (hasStrategicKeyword) {

return {

shouldTransition: true,

targetMode: Mode.CHALLENGE,

confidence: 0.85,

reason: 'Strategic decision detected',

};

}

// 2. Ambiguity detection

const ambiguityScore = calculateAmbiguity(userInput);

if (ambiguityScore > context.userPreferences.challengeThreshold) {

return {

shouldTransition: true,

targetMode: Mode.CHALLENGE,

confidence: ambiguityScore,

reason: 'High ambiguity detected',

};

}

// 3. Creative exploration context

if (

lowerInput.includes('brainstorm') ||

lowerInput.includes('ideas') ||

lowerInput.includes('possibilities')

) {

return {

shouldTransition: true,

targetMode: Mode.CHALLENGE,

confidence: 0.75,

reason: 'Creative exploration context',

};

}

}

// From CHALLENGE → AMENABLE (de-escalation triggers)

if (currentMode === Mode.CHALLENGE) {

// 1. Execution request

if (

lowerInput.includes('execute') ||

lowerInput.includes('proceed') ||

lowerInput.includes("let's do") ||

lowerInput.includes('implement')

) {

return {

shouldTransition: true,

targetMode: Mode.AMENABLE,

confidence: 0.9,

reason: 'Execution request detected',

};

}

// 2. Decision finalized

if (

lowerInput.includes('decided') ||

lowerInput.includes('final') ||

lowerInput.includes('confirmed')

) {

return {

shouldTransition: true,

targetMode: Mode.AMENABLE,

confidence: 0.85,

reason: 'Decision finalized',

};

}

}

// No transition needed

return {

shouldTransition: false,

targetMode: currentMode,

confidence: 1.0,

reason: 'No transition triggers detected',

};

}

function calculateAmbiguity(text: string): number {

let score = 0.0;

// 1. Question words (increase ambiguity)

const questionWords = ['what', 'which', 'how', 'should', 'could', 'would'];

const questionCount = questionWords.filter(word =>

text.toLowerCase().includes(word)

).length;

score += Math.min(questionCount * 0.2, 0.4);

// 2. Multiple options presented

if (text.includes(' or ') || text.includes(' vs ')) {

score += 0.3;

}

// 3. Vague terms

const vagueTerms = ['maybe', 'possibly', 'perhaps', 'unclear', 'not sure'];

const vagueCount = vagueTerms.filter(term =>

text.toLowerCase().includes(term)

).length;

score += Math.min(vagueCount * 0.15, 0.3);

return Math.min(score, 1.0);

}


9. BUILD CONFIGURATION

9.1 Next.js Configuration

// next.config.js

/* @type {import('next').NextConfig} /

const nextConfig = {

output: 'export', // Static export (no server required)

trailingSlash: true, // Add trailing slashes to URLs

images: {

unoptimized: true, // Required for static export

},

eslint: {

ignoreDuringBuilds: false,

},

typescript: {

ignoreBuildErrors: false,

},

};

module.exports = nextConfig;

9.2 Tailwind Configuration

// tailwind.config.ts

import type { Config } from 'tailwindcss';

const config: Config = {

content: [

'./src/pages/*/.{js,ts,jsx,tsx,mdx}',

'./src/components/*/.{js,ts,jsx,tsx,mdx}',

'./src/app/*/.{js,ts,jsx,tsx,mdx}',

],

theme: {

extend: {

colors: {

// [Insert color palette from Section 3.1]

},

fontFamily: {

sans: ['Geist Sans', 'Inter', 'system-ui', 'sans-serif'],

mono: ['Geist Mono', 'Fira Code', 'Consolas', 'monospace'],

},

backdropBlur: {

xs: '2px',

},

},

},

plugins: [],

};

export default config;

9.3 TypeScript Configuration

// tsconfig.json

{

"compilerOptions": {

"target": "ES2020",

"lib": ["ES2020", "DOM", "DOM.Iterable"],

"jsx": "preserve",

"module": "ESNext",

"moduleResolution": "bundler",

"resolveJsonModule": true,

"allowJs": true,

"strict": true,

"noImplicitAny": true,

"strictNullChecks": true,

"noUnusedLocals": true,

"noUnusedParameters": true,

"noFallthroughCasesInSwitch": true,

"esModuleInterop": true,

"skipLibCheck": true,

"forceConsistentCasingInFileNames": true,

"incremental": true,

"plugins": [{ "name": "next" }],

"paths": {

"@/": ["./src/"]

}

},

"include": ["next-env.d.ts", "/.ts", "/.tsx", ".next/types/*/.ts"],

"exclude": ["node_modules"]

}

9.4 Package.json

// package.json

{

"name": "eth-001-challenge-board-mode",

"version": "1.0.0",

"private": true,

"scripts": {

"dev": "next dev",

"build": "next build",

"start": "next start",

"lint": "next lint",

"type-check": "tsc --noEmit",

"test": "vitest",

"test:e2e": "playwright test"

},

"dependencies": {

"next": "^14.2.0",

"react": "^18.3.0",

"react-dom": "^18.3.0",

"typescript": "^5.4.0",

"zustand": "^4.5.0",

"framer-motion": "^11.0.0",

"react-markdown": "^9.0.0",

"remark-gfm": "^4.0.0",

"lucide-react": "^0.368.0",

"date-fns": "^3.6.0",

"zod": "^3.23.0"

},

"devDependencies": {

"@types/node": "^20",

"@types/react": "^18",

"@types/react-dom": "^18",

"autoprefixer": "^10.0.1",

"postcss": "^8",

"tailwindcss": "^3.4.0",

"eslint": "^8",

"eslint-config-next": "^14.2.0",

"@playwright/test": "^1.44.0",

"vitest": "^1.6.0"

}

}


10. DEPLOYMENT CHECKLIST

10.1 Pre-Deployment Validation

# Type checking

npm run type-check

Linting

npm run lint

Build for production

npm run build

Test build output

npx serve@latest out

10.2 Deployment Options

Option A: Vercel (Recommended)
# Install Vercel CLI

npm i -g vercel

Deploy

vercel

Option B: Netlify
# Build command: npm run build

Publish directory: out

Option C: VPS Static Hosting
# 1. Build locally

npm run build

2. Upload 'out' directory to VPS

scp -r out/* [email protected]:/var/www/challenge-board/

3. Configure Caddy

cat > /etc/caddy/Caddyfile << 'EOF'

challenge.ethraeon.ai {

root * /var/www/challenge-board

file_server

encode gzip

}

EOF

4. Reload Caddy

systemctl reload caddy

10.3 Post-Deployment Verification

# 1. Check accessibility

curl -I https://challenge.ethraeon.ai

2. Verify localStorage persistence

(Manual: Open dev tools → Application → Local Storage)

3. Test mode transitions

(Manual: Trigger challenge mode, verify state persists)

4. Test artifact creation

(Manual: Create artifact, refresh page, verify persistence)

5. Run Lighthouse audit

npx lighthouse https://challenge.ethraeon.ai --view


11. QUALITY GATES

11.1 Technical Quality Gates

Build Success: Zero TypeScript errors

Type Safety: 100% type coverage

Performance: Lighthouse score ≥ 95

Bundle Size: Initial load < 200KB

Accessibility: WCAG 2.1 AA compliant

Browser Support: Chrome 90+, Firefox 88+, Safari 14+

11.2 Functional Quality Gates

Mode Transitions: Multi-Frame Analysis: Artifact System: Board Mode: Track Mode: Persistence:

11.3 Constitutional Quality Gates

Human Authority Preservation: T5-Rigidity Enforcement: Attribution & IP Protection:

APPENDIX A: PROJECT STRUCTURE

eth-001-challenge-board-mode/

├── public/

│ ├── fonts/

│ │ ├── GeistSans-*.woff2

│ │ └── GeistMono-*.woff2

│ └── favicon.ico

├── src/

│ ├── app/

│ │ ├── layout.tsx

│ │ ├── page.tsx

│ │ └── globals.css

│ ├── components/

│ │ ├── chat/

│ │ │ ├── ChatInterface.tsx

│ │ │ ├── MessageList.tsx

│ │ │ ├── MessageBubble.tsx

│ │ │ ├── MultiFrameMessage.tsx

│ │ │ ├── InputBar.tsx

│ │ │ ├── ModeIndicator.tsx

│ │ │ └── TransitionNotification.tsx

│ │ ├── board/

│ │ │ ├── BoardWorkspace.tsx

│ │ │ ├── BoardList.tsx

│ │ │ ├── BoardCanvas.tsx

│ │ │ ├── ArtifactCard.tsx

│ │ │ ├── ArtifactCreator.tsx

│ │ │ ├── RelationshipLines.tsx

│ │ │ └── BoardControls.tsx

│ │ ├── tracks/

│ │ │ ├── TrackOrchestrator.tsx

│ │ │ ├── TrackList.tsx

│ │ │ ├── TrackCard.tsx

│ │ │ ├── DependencyGraph.tsx

│ │ │ ├── ProgressVisualization.tsx

│ │ │ └── TrackCreator.tsx

│ │ └── ui/

│ │ ├── Button.tsx

│ │ ├── Modal.tsx

│ │ └── Input.tsx

│ ├── store/

│ │ └── index.ts

│ ├── lib/

│ │ ├── defaults.ts

│ │ ├── transition-evaluator.ts

│ │ └── utils.ts

│ └── types/

│ └── core.ts

├── next.config.js

├── tailwind.config.ts

├── tsconfig.json

├── package.json

└── README.md


APPENDIX B: DEPLOYMENT TIMELINE

Day 1-2: Foundation Day 3-4: Surface #1 (Chat) Day 5-6: Surface #2 (Board) Day 7-8: Surface #3 (Tracks) Day 9: Testing & Polish Day 10: Deployment

CLOSING NOTES

This specification provides complete, unambiguous implementation instructions for building ETH-001 Challenge Board Mode as a production-grade, client-side application. Every component, every state transition, every design decision is specified with T5-rigidity.

Key Success Factors:
  1. No Backend Required: Pure client-side architecture makes deployment trivial
  2. Patent Alignment: Every implementation detail matches USPTO Provisional #7
  3. EUDS Premium: Design system ensures museum-grade visual quality
  4. Genesis 3.0 Compliance: Constitutional governance throughout
  5. SCREWDRIVER-Ready: Zero ambiguity, immediate execution possible
Valuation Impact:

This implementation, combined with USPTO Provisional filing, creates:

Next Steps:
  1. Hand this specification to SCREWDRIVER
  2. Execute build over 5-10 days
  3. Deploy to challenge.ethraeon.ai
  4. Present to investors with live demo
  5. Position for licensing/acquisition discussions

⟁ Constitutional Compliance Verified Harmonic Architecture Aligned ▣ Patent Precision Confirmed Δ Genesis 3.0 Standards Met Copyright © 2025 S. Jason Prohaska (ingombrante©) All Rights Reserved • Schedule A+ Enhanced IP Protection USPTO Provisional Patent #7 (ETH-2025-001)
END OF PRODUCTION DEPLOYMENT SPECIFICATION