Orchestrata Ultimate / Specs / 051-vizint-ui

051-vizint-ui: VizInt UI - Visual Interface Dashboard

VizInt (Visual Interface) is a modern web-based dashboard for the Ultimate Hybrid system, providing project management, task orchestration, agent configuration, and governance rule editing through a visual interface.

At a glance

Item Value
Spec directory specs/051-vizint-ui/
Status line in the spec Deprecated — superseded by SPEC-016
Requirement IDs defined 93
With a @traces citation in src/ 0 (0.0%)
With a @traces citation in tests/ 0 (0.0%)
Tracked files 2
RTM page 051-vizint-ui

Reading this spec

This spec describes VizInt, a web dashboard for the Ultimate Hybrid system that gives people who do not work in a terminal a visual way to manage projects and tasks, configure agents and edit governance rules. The spec is deprecated: its status line reads "Deprecated — superseded by SPEC-016", and its first line points readers to SPEC-016, the VizInt Command Center, where all VizInt requirements, architecture and guidance now live. This document is kept for historical context.

The design it records is a FastAPI backend serving an HTMX and TailwindCSS front end, a WebSocket channel for real-time updates, Neo4j for persistence, and integration with the Orchestrata kernel over CXP. The four user stories are a project manager viewing the dashboard, a developer configuring agents, visual task management, and offline support. The functional requirements run from FR-001 to FR-020: project dashboard display, real-time updates, a task Kanban board with drag-and-drop, agent configuration and live model switching, a governance rule editor, WebSocket connection management with reconnection, offline support with a change queue, authentication, role-based access control, CSRF protection, project and task CRUD, filtering and search, audit logging, rate limiting, multi-project support, task dependencies and agent status monitoring.

The 93 requirement IDs, all numbered REQ-051-NNN, also cover the API contracts (a shared ErrorResponse schema with error, message and timestamp, resource schemas for projects, tasks and agent configurations, and versioning), the error conditions (for example automatic WebSocket reconnect with exponential backoff, and an offline indicator with a change queue that syncs when the connection returns), and authorization that combines PAM++ policy with Admin, User and Viewer roles.

No code module cites this spec, which fits its deprecated status. The requirements traceability matrix lists every ID. The spec tracks 2 files. No src/ symbol cites any of the 93 requirements, so measured trace coverage is 0.0%, and in tests/, 0 requirements are cited by a test-side @traces. All 93 requirements have no citing symbol; untraced means no @traces names the ID, and the behaviour may still exist in code that was never annotated.

Requirements

Checkbox is the state recorded in the requirements checklist; Traced by is the count of src/ symbols whose @traces names the ID.

ID Done Requirement Traced by
REQ-051-001 - Every error response SHALL conform to the ErrorResponse schema: it SHALL carry the required fields error, message and timestamp (date-time), and it admits the optional fields request_id, trace_id and details. none
REQ-051-002 - All APIs SHALL use path-based versioning (/api/v1/). A breaking change SHALL require an increment to v2. none
REQ-051-003 - The dashboard SHALL show project cards with status, SHALL update in real time when tasks change, SHALL allow projects to be reordered by drag-and-drop, and SHALL support filtering by status, agent and priority. none
REQ-051-004 - The agent configuration interface SHALL present a form covering all agent settings, SHALL support real-time model switching, SHALL provide tool enable/disable toggles, and SHALL validate the configuration before save. none
REQ-051-005 - The task view SHALL provide a Kanban board with backlog, in-progress, review and done columns, SHALL support drag-and-drop task movement, SHALL open a task detail modal on click, and SHALL support bulk operations on selected tasks. none
REQ-051-006 - A service worker SHALL cache the UI assets, the UI SHALL display an offline indicator, changes made offline SHALL be queued for sync when the connection returns, and the pending changes SHALL be shown to the user. none
REQ-051-007 - A Project resource SHALL conform to the Project schema: id, name and status are required; id SHALL match ^proj_[a-zA-Z0-9]+$; name SHALL be 1 to 100 characters; status SHALL be one of active, paused, completed, archived; and description, agents, created_at and updated_at (date-time) are optional. none
REQ-051-008 - A Task resource SHALL conform to the Task schema: id, title, status and priority are required; id SHALL match ^T:[0-9]+$; title SHALL be 1 to 200 characters; status SHALL be one of pending, in_progress, in_review, completed, blocked; priority SHALL be an integer from 1 to 5; and description, agent_id, project_id (matching ^proj_[a-zA-Z0-9]+$), dependencies, created_at and updated_at are optional. none
REQ-051-009 - An AgentConfig resource SHALL conform to the AgentConfig schema: id, type and model are required; id SHALL match ^U:[A-Za-z]+$; type SHALL be one of coding, review, research, orchestration; max_concurrent_tasks SHALL be an integer of at least 1; and timeout_seconds SHALL be an integer of at least 60. none
REQ-051-010 - The system MUST display a dashboard showing all projects with their current status, task counts, and agent assignments. none
REQ-051-011 - The system MUST provide real-time updates to the dashboard when project status changes, using WebSocket connections. none
REQ-051-012 - The system MUST display tasks in a Kanban board format with columns for backlog, in-progress, review, and completed states. none
REQ-051-013 - The system MUST allow users to move tasks between columns using drag-and-drop, updating task status in real-time. none
REQ-051-014 - The system MUST provide a visual interface for configuring agent settings including model selection, tool enablement, and concurrency limits. none
REQ-051-015 - The system MUST allow instant model switching for agents without requiring page reload, with changes persisted immediately. none
REQ-051-016 - The system MUST provide a visual interface for creating, editing, and deleting governance rules with condition builders. none
REQ-051-017 - The system MUST maintain persistent WebSocket connections with automatic reconnection using exponential backoff. none
REQ-051-018 - The system MUST cache UI assets via service worker and queue changes for sync when connection is restored. none
REQ-051-019 - The system MUST support OAuth2 authentication with session management and automatic session expiration. none
REQ-051-020 - The system MUST enforce role-based permissions with Admin, User, and Viewer roles. none
REQ-051-021 - The system MUST validate CSRF tokens on all state-changing operations. none
REQ-051-022 - The system MUST support creating, reading, updating, and deleting projects via REST API. none
REQ-051-023 - The system MUST support creating, reading, updating, and deleting tasks via REST API. none
REQ-051-024 - The system MUST support filtering projects and tasks by status, agent, priority, and custom criteria. none
REQ-051-025 - The system MUST log all user actions for audit purposes with timestamps and user identification. none
REQ-051-026 - The system MUST enforce rate limiting on authentication attempts, API calls, and WebSocket messages. none
REQ-051-027 - The system MUST allow users to manage multiple projects simultaneously with project switching. none
REQ-051-028 - The system MUST support defining and visualizing task dependencies with blocking indicators. none
REQ-051-029 - The system MUST display real-time agent status including active task, idle state, and error conditions. none
REQ-051-030 - On WebSocket disconnection (ERR-001) the system SHALL auto-reconnect with exponential backoff, to a maximum of 5 attempts. none
REQ-051-031 - On authentication failure (ERR-002) the system SHALL redirect to the login page with a return URL. none
REQ-051-032 - On API timeout (ERR-003) the system SHALL show a timeout message and SHALL auto-retry up to 3 times. none
REQ-051-033 - When the network is offline (ERR-004) the system SHALL show an offline indicator and SHALL enable the change queue. none
REQ-051-034 - On permission denied (ERR-005) the system SHALL show an access-denied message and SHALL log the attempt. none
REQ-051-035 - On session expiry (ERR-006) the system SHALL clear the session and redirect to login. none
REQ-051-036 - On an invalid CSRF token (ERR-007) the system SHALL reject the request and return 403. none
REQ-051-037 - When a rate limit is exceeded (ERR-008) the system SHALL return 429 and show the retry-after header. none
REQ-051-038 - When a project is not found (ERR-009) the system SHALL return 404 and show the project list. none
REQ-051-039 - When a task is not found (ERR-010) the system SHALL return 404 and show the task list. none
REQ-051-040 - When an agent is not found (ERR-011) the system SHALL return 404 and show the agent configuration. none
REQ-051-041 - On invalid project data (ERR-012) the system SHALL return 400 with the validation errors. none
REQ-051-042 - On invalid task data (ERR-013) the system SHALL return 400 with the validation errors. none
REQ-051-043 - On a circular task dependency (ERR-014) the system SHALL return 400 and highlight the conflicting tasks. none
REQ-051-044 - On a governance rule violation (ERR-015) the system SHALL return 451 and show the rule details. none
REQ-051-045 - On database connection failure (ERR-016) the system SHALL return 503 and enable the retry queue. none
REQ-051-046 - On a WebSocket protocol error (ERR-017) the system SHALL close the connection, log the error, and attempt reconnection. none
REQ-051-047 - On an invalid OAuth callback (ERR-018) the system SHALL redirect to login with an error message. none
REQ-051-048 - On a concurrent modification conflict (ERR-019) the system SHALL return 409 and show the conflict-resolution UI. none
REQ-051-049 - On an unsupported media type (ERR-020) the system SHALL return 415 and show the supported types. none
REQ-051-101 - The UI SHALL meet the performance budgets: page load (first contentful paint) < 500ms, API response time (p95) < 200ms, WebSocket event latency < 100ms, HTMX partial render < 50ms, and drag-and-drop response < 16ms (60fps). none
REQ-051-102 - The system SHALL sustain the scalability capacities: 100 concurrent users, 50 projects per user, 1,000 tasks per project, 500 WebSocket connections, and 6,000 API requests per minute. none
REQ-051-103 - The system SHALL meet a 99.9% uptime target, SHALL confine planned maintenance to the Sunday 02:00-04:00 UTC window, and SHALL degrade gracefully to read-only mode during maintenance. none
REQ-051-104 - The UI SHALL comply with WCAG 2.1 Level AA, SHALL support keyboard navigation, SHALL be screen-reader compatible, SHALL provide a high-contrast mode, and SHALL show focus indicators on all interactive elements. none
REQ-051-105 - The UI SHALL support Chrome 90+, Firefox 88+, Safari 14+, Edge 90+, Mobile Safari iOS 14+, and Chrome for Android 90+. none
REQ-051-106 - The system SHALL depend on fastapi >= 0.100 (web framework), websockets >= 11.0 (real-time updates), redis >= 5.0 (session store), neo4j >= 5.0 (data persistence), htmx >= 1.9 (reactive HTML), and tailwindcss >= 3.0 (styling). none
REQ-051-050 - Authentication SHALL use OAuth 2.0, SHALL manage sessions with secure cookies, SHALL expire sessions automatically, and SHALL support MFA via TOTP. none
REQ-051-051 - Authorization SHALL enforce PAM++ policy, role-based access control, project-scoped permissions, and the Admin/User/Viewer role set. none
REQ-051-052 - The system SHALL sanitize all user input, SHALL send Content Security Policy headers, SHALL use HTTP-only cookies, and SHALL send the secure headers X-Content-Type-Options and X-Frame-Options. none
REQ-051-053 - The system SHALL rate-limit login attempts to 5 per minute, API calls to 100 per minute, and WebSocket messages to 60 per minute. none
REQ-051-054 - All UI actions SHALL be subject to PAM++ checks, governance errors SHALL return status 451, admin-only routes SHALL be protected, and user actions SHALL be logged for audit. none
REQ-051-055 - Data retention SHALL be 30 days for user sessions, 90 days for audit logs, 30 days for activity logs, and until deletion for project data. none
REQ-051-056 - VizInt SHALL follow a layered architecture with clear separation of concerns across four layers: a Presentation layer of HTMX templates with TailwindCSS styling, an API layer of FastAPI REST endpoints with request/response models, a Service layer holding the business logic for project, task and agent management, and a Data layer of a Neo4j graph database for persistence with Redis for sessions. none
REQ-051-057 - Server state SHALL be held in Neo4j for persistent data and in Redis for sessions and cache; client state SHALL be held in HTMX DOM state and in localStorage for the offline queue; shared state SHALL be synchronized across clients over WebSocket. none
REQ-051-058 - The system SHALL integrate with the Orchestrata kernel over /api/orchestrata/* (HTTP/REST) for agent command execution, /ws/orchestrata (WebSocket) for real-time agent events, and /api/cxp/* (CXP) for cross-process communication. none
REQ-051-059 - The system SHALL access Neo4j with Cypher: project CRUD and task CRUD on user action, relationship queries on page load, and full-text search on search. none
REQ-051-060 - The system SHALL hold in Redis: user sessions with a 30-day TTL for authentication state, CSRF tokens with a 1-hour TTL, WebSocket heartbeats with a 1-minute TTL for connection health, rate-limit counters with a 1-minute TTL for API throttling, and the offline change queue retained until sync. none
REQ-051-061 - The system SHALL support user authentication through the GitHub and Google OAuth providers, each configured with a client ID and secret. none
REQ-051-062 - The dashboard SHALL display all projects with status (SC-001), validated by visual inspection. none
REQ-051-063 - Tasks SHALL be draggable between Kanban columns (SC-002), validated by manual testing. none
REQ-051-064 - Agent configuration changes SHALL persist immediately (SC-003), validated by API verification. none
REQ-051-065 - WebSocket SHALL deliver updates within 100ms (SC-004), validated by performance test. none
REQ-051-066 - Governance rules SHALL block prohibited actions (SC-005), validated by rule enforcement test. none
REQ-051-067 - Offline changes SHALL sync when the connection is restored (SC-006), validated by offline testing. none
REQ-051-107 - Test coverage SHALL exceed 80% (QC-001). none
REQ-051-108 - API availability SHALL be 99.9% (QC-002). none
REQ-051-109 - Page load time SHALL be under 500ms (QC-003). none
REQ-051-110 - The number of critical vulnerabilities SHALL be 0 (QC-004). none
REQ-051-111 - Accessibility SHALL comply with WCAG 2.1 AA (QC-005). none
REQ-051-112 - Production deployment SHALL succeed (DC-001), validated by the deployment pipeline. none
REQ-051-113 - The health check endpoint SHALL return 200 (DC-002), validated by monitoring. none
REQ-051-114 - The release SHALL introduce no breaking changes to existing APIs (DC-003), validated by contract tests. none
REQ-051-115 - The rollback procedure SHALL be documented (DC-004), validated by runbook review. none
REQ-051-116 - Integration tests SHALL cover database integration, WebSocket connections, the authentication flow, project CRUD operations, and task management. none
REQ-051-117 - End-to-end tests SHALL cover complete user workflows, cross-browser compatibility, mobile responsiveness, and offline mode functionality. none
REQ-051-118 - Test coverage SHALL exceed 80% for unit tests, SHALL exceed 90% for integration tests, and SHALL be 100% for API coverage. none
REQ-051-119 - The system SHALL report metrics for user sessions, API response times, error rates, WebSocket connections, and database queries. none
REQ-051-120 - The system SHALL alert on a high error rate (>5%), slow responses (>1s), authentication failures, and database connection issues. none
REQ-051-068 - The system SHALL expose the project endpoints GET /api/projects (list), POST /api/projects (create, accepting name, template and volk_configs), GET /api/projects/{project_id} (detail), PUT /api/projects/{project_id} (update) and DELETE /api/projects/{project_id} (delete). none
REQ-051-069 - The system SHALL expose the task endpoints GET /api/projects/{project_id}/tasks (list, filterable by the status and assigned_volk query parameters), POST /api/projects/{project_id}/tasks (create), PUT /api/projects/{project_id}/tasks/{task_id} (update) and PATCH /api/projects/{project_id}/tasks/{task_id}/status (drag-and-drop status update). none
REQ-051-070 - The system SHALL expose the agent-configuration endpoints GET /api/projects/{project_id}/config/agents and PUT /api/projects/{project_id}/config/agents/{agent_alias}, the latter accepting model, enabled, max_concurrent, timeout and tools. none
REQ-051-071 - The system SHALL expose the governance-rule endpoints GET /api/projects/{project_id}/rules (list), POST /api/projects/{project_id}/rules (create, accepting name, scope, condition, action and priority), PUT /api/projects/{project_id}/rules/{rule_id} (update) and DELETE /api/projects/{project_id}/rules/{rule_id} (delete). none
REQ-051-072 - The system SHALL accept WebSocket connections at WS /ws/{project_id} and SHALL send server-to-client messages carrying a type and a data payload, including task_updated, agent_status and checkpoint_created. none
REQ-051-073 - The WebSocket event vocabulary SHALL be task_updated, task_created, task_deleted, agent_status, agent_progress, checkpoint_created, checkpoint_restored, rule_updated, rule_violation and project_updated. none

Code that traces this spec

No src/ symbol cites a requirement of this spec.

Other tracked files

Full spec text

Verbatim from specs/051-vizint-ui/spec.md; headings are pushed down two levels.

⚠️ DEPRECATED: This specification has been superseded by SPEC-016: VizInt Command Center. All VizInt requirements, architecture, and implementation guidance now live in SPEC-016. This document is retained for historical context only.

SPEC-051: VizInt UI - Visual Interface Dashboard

Metadata


API Contracts

REST API Endpoints
# Example endpoint definition
GET /api/v1/resource/{id}
Response (200):
  type: object
  required:
    - id
    - name
  properties:
    id: { type: string }
    name: { type: string }
Response (404):
  $ref: '#/components/schemas/ErrorResponse'
Error Response Schema
components:
  schemas:
    ErrorResponse:
      type: object
      required:
        - error
        - message
        - timestamp
      properties:
        error: { type: string }
        message: { type: string }
        timestamp: { type: string, format: date-time }
        request_id: { type: string }
        trace_id: { type: string }
        details: { type: array, items: { type: object } }

REQ-051-001: Every error response SHALL conform to the ErrorResponse schema: it SHALL carry the required fields error, message and timestamp (date-time), and it admits the optional fields request_id, trace_id and details.

API Versioning

REQ-051-002: All APIs SHALL use path-based versioning (/api/v1/). A breaking change SHALL require an increment to v2.


Visual Architecture

Class Diagram

Diagram source shown as text: Mermaid could not parse it (Error: Parse error on line 22:).

classDiagram-direction LR
    class CheetahClawsTUI {
        +__init__()
        #_load_commands(): dict[str, Any]
        +async create_checkpoint(name: str): dict[str, Any]
        +async rewind(steps: int = 1): dict[str, Any]
        +async query_memory(query: str, depth: int = 1): dict[str, Any]
        +async edit_file(file_path: str, old_string: str, new_string: str): dict[str, Any]
        +async search_files(pattern: str, path: str = '.'): list[str]
        +async execute_command(command: str, args: list[str] | None = None): dict[str, Any]
    }
    class VizIntWebUI {
        +__init__(host: str = 'localhost', port: int = 8765)
        +async start()
        +async shutdown()
        +async add_project(project_data: dict[str, Any])
        +async broadcast_edit_complete(file_path: str, result: dict[str, Any])
        +async update_task_status(task_id: str, status: str)
        +async get_projects(): list[dict[str, Any]]
    }

Revision History

Version Date Author Changes
1.0.0 2026-04-08 Mohamed Initial specification

User Scenarios

US-001: Project Manager Viewing Dashboard

As a project manager, I want to see all projects and their status so I can track progress across multiple projects.

Acceptance Criteria:

REQ-051-003: The dashboard SHALL show project cards with status, SHALL update in real time when tasks change, SHALL allow projects to be reordered by drag-and-drop, and SHALL support filtering by status, agent and priority.

US-002: Developer Configuring Agents

As a developer, I want to configure agent settings so I can optimize agent behavior for my project.

Acceptance Criteria:

REQ-051-004: The agent configuration interface SHALL present a form covering all agent settings, SHALL support real-time model switching, SHALL provide tool enable/disable toggles, and SHALL validate the configuration before save.

US-003: Visual Task Management

As a team lead, I want to manage tasks visually so I can organize work efficiently.

Acceptance Criteria:

REQ-051-005: The task view SHALL provide a Kanban board with backlog, in-progress, review and done columns, SHALL support drag-and-drop task movement, SHALL open a task detail modal on click, and SHALL support bulk operations on selected tasks.

US-004: Offline Support

As a user, I want to access the UI offline so I can continue working without internet.

Acceptance Criteria:

REQ-051-006: A service worker SHALL cache the UI assets, the UI SHALL display an offline indicator, changes made offline SHALL be queued for sync when the connection returns, and the pending changes SHALL be shown to the user.


Data Models

Project
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "required": ["id", "name", "status"],
  "properties": {
    "id": {
      "type": "string",
      "pattern": "^proj_[a-zA-Z0-9]+$"
    },
    "name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 100
    },
    "status": {
      "type": "string",
      "enum": ["active", "paused", "completed", "archived"]
    },
    "description": {
      "type": "string"
    },
    "agents": {
      "type": "array",
      "items": {"type": "string"}
    },
    "created_at": {
      "type": "string",
      "format": "date-time"
    },
    "updated_at": {
      "type": "string",
      "format": "date-time"
    }
  }
}

REQ-051-007: A Project resource SHALL conform to the Project schema: id, name and status are required; id SHALL match ^proj_[a-zA-Z0-9]+$; name SHALL be 1 to 100 characters; status SHALL be one of active, paused, completed, archived; and description, agents, created_at and updated_at (date-time) are optional.

Task
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "required": ["id", "title", "status", "priority"],
  "properties": {
    "id": {
      "type": "string",
      "pattern": "^T:[0-9]+$"
    },
    "title": {
      "type": "string",
      "minLength": 1,
      "maxLength": 200
    },
    "description": {
      "type": "string"
    },
    "status": {
      "type": "string",
      "enum": ["pending", "in_progress", "in_review", "completed", "blocked"]
    },
    "priority": {
      "type": "integer",
      "minimum": 1,
      "maximum": 5
    },
    "agent_id": {
      "type": "string"
    },
    "project_id": {
      "type": "string",
      "pattern": "^proj_[a-zA-Z0-9]+$"
    },
    "dependencies": {
      "type": "array",
      "items": {"type": "string"}
    },
    "created_at": {
      "type": "string",
      "format": "date-time"
    },
    "updated_at": {
      "type": "string",
      "format": "date-time"
    }
  }
}

REQ-051-008: A Task resource SHALL conform to the Task schema: id, title, status and priority are required; id SHALL match ^T:[0-9]+$; title SHALL be 1 to 200 characters; status SHALL be one of pending, in_progress, in_review, completed, blocked; priority SHALL be an integer from 1 to 5; and description, agent_id, project_id (matching ^proj_[a-zA-Z0-9]+$), dependencies, created_at and updated_at are optional.

AgentConfig
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "required": ["id", "type", "model"],
  "properties": {
    "id": {
      "type": "string",
      "pattern": "^U:[A-Za-z]+$"
    },
    "type": {
      "type": "string",
      "enum": ["coding", "review", "research", "orchestration"]
    },
    "model": {
      "type": "string"
    },
    "tools_enabled": {
      "type": "array",
      "items": {"type": "string"}
    },
    "max_concurrent_tasks": {
      "type": "integer",
      "minimum": 1
    },
    "timeout_seconds": {
      "type": "integer",
      "minimum": 60
    }
  }
 }

REQ-051-009: An AgentConfig resource SHALL conform to the AgentConfig schema: id, type and model are required; id SHALL match ^U:[A-Za-z]+$; type SHALL be one of coding, review, research, orchestration; max_concurrent_tasks SHALL be an integer of at least 1; and timeout_seconds SHALL be an integer of at least 60.


Functional Requirements

FR-001: Project Dashboard Display

REQ-051-010: The system MUST display a dashboard showing all projects with their current status, task counts, and agent assignments.

FR-002: Real-time Project Updates

REQ-051-011: The system MUST provide real-time updates to the dashboard when project status changes, using WebSocket connections.

FR-003: Task Kanban Board

REQ-051-012: The system MUST display tasks in a Kanban board format with columns for backlog, in-progress, review, and completed states.

FR-004: Drag-and-Drop Task Management

REQ-051-013: The system MUST allow users to move tasks between columns using drag-and-drop, updating task status in real-time.

FR-005: Agent Configuration Interface

REQ-051-014: The system MUST provide a visual interface for configuring agent settings including model selection, tool enablement, and concurrency limits.

FR-006: Real-time Model Switching

REQ-051-015: The system MUST allow instant model switching for agents without requiring page reload, with changes persisted immediately.

FR-007: Governance Rule Editor

REQ-051-016: The system MUST provide a visual interface for creating, editing, and deleting governance rules with condition builders.

FR-008: WebSocket Connection Management

REQ-051-017: The system MUST maintain persistent WebSocket connections with automatic reconnection using exponential backoff.

FR-009: Offline Support

REQ-051-018: The system MUST cache UI assets via service worker and queue changes for sync when connection is restored.

FR-010: User Authentication

REQ-051-019: The system MUST support OAuth2 authentication with session management and automatic session expiration.

FR-011: Role-Based Access Control

REQ-051-020: The system MUST enforce role-based permissions with Admin, User, and Viewer roles.

FR-012: CSRF Protection

REQ-051-021: The system MUST validate CSRF tokens on all state-changing operations.

FR-013: Project CRUD Operations

REQ-051-022: The system MUST support creating, reading, updating, and deleting projects via REST API.

FR-014: Task CRUD Operations

REQ-051-023: The system MUST support creating, reading, updating, and deleting tasks via REST API.

REQ-051-024: The system MUST support filtering projects and tasks by status, agent, priority, and custom criteria.

FR-016: Audit Logging

REQ-051-025: The system MUST log all user actions for audit purposes with timestamps and user identification.

FR-017: Rate Limiting

REQ-051-026: The system MUST enforce rate limiting on authentication attempts, API calls, and WebSocket messages.

FR-018: Multi-Project Support

REQ-051-027: The system MUST allow users to manage multiple projects simultaneously with project switching.

FR-019: Task Dependencies

REQ-051-028: The system MUST support defining and visualizing task dependencies with blocking indicators.

FR-020: Agent Status Monitoring

REQ-051-029: The system MUST display real-time agent status including active task, idle state, and error conditions.


Authentication

OAuth2 Flow
@app.get("/auth/login")
async def login():
    """Redirect to OAuth provider."""
    return RedirectResponse(
        url=f"{OAUTH_URL}?client_id={CLIENT_ID}&response_type=code"
    )

@app.get("/auth/callback")
async def callback(code: str):
    """Handle OAuth callback."""
    token = await exchange_code(code)
    # Create session
    return RedirectResponse(url="/dashboard")
Session Management
async def get_current_user(request: Request):
    """Get current user from session."""
    session_id = request.cookies.get("session_id")
    if not session_id:
        raise HTTPException(401, "Not authenticated")
    
    user = await redis.get(f"session:{session_id}")
    if not user:
        raise HTTPException(401, "Session expired")
    
    return json.loads(user)

WebSocket Reconnection

Connection Manager
class ConnectionManager:
    def __init__(self):
        self.active_connections: Dict[str, WebSocket] = {}
    
    async def connect(self, websocket: WebSocket, client_id: str):
        await websocket.accept()
        self.active_connections[client_id] = websocket
    
    def disconnect(self, client_id: str):
        self.active_connections.pop(client_id, None)
    
    async def broadcast(self, message: dict):
        for connection in self.active_connections.values():
            await connection.send_json(message)
Client Reconnection Logic
const ws = new WebSocket(WS_URL);

ws.onopen = () => {
  console.log('Connected to WebSocket');
};

ws.onclose = () => {
  // Attempt reconnection with exponential backoff
  setTimeout(() => {
    connectWebSocket();
  }, 1000 * Math.pow(2, reconnectAttempts));
};

ws.onerror = (error) => {
  console.error('WebSocket error:', error);
};

Error Conditions

ID Condition Handling
ERR-001 WebSocket disconnected Auto-reconnect with exponential backoff (max 5 attempts)
ERR-002 Authentication failed Redirect to login page with return URL
ERR-003 API timeout Show timeout message, auto-retry up to 3 times
ERR-004 Network offline Show offline indicator, enable change queue
ERR-005 Permission denied Show access denied message, log attempt
ERR-006 Session expired Clear session, redirect to login
ERR-007 Invalid CSRF token Reject request, return 403
ERR-008 Rate limit exceeded Return 429, show retry-after header
ERR-009 Project not found Return 404, show project list
ERR-010 Task not found Return 404, show task list
ERR-011 Agent not found Return 404, show agent configuration
ERR-012 Invalid project data Return 400 with validation errors
ERR-013 Invalid task data Return 400 with validation errors
ERR-014 Circular task dependency Return 400, highlight conflicting tasks
ERR-015 Governance rule violation Return 451, show rule details
ERR-016 Database connection failed Return 503, enable retry queue
ERR-017 WebSocket protocol error Close connection, log error, attempt reconnect
ERR-018 Invalid OAuth callback Redirect to login with error message
ERR-019 Concurrent modification conflict Return 409, show conflict resolution UI
ERR-020 Unsupported media type Return 415, show supported types

REQ-051-030: On WebSocket disconnection (ERR-001) the system SHALL auto-reconnect with exponential backoff, to a maximum of 5 attempts.

REQ-051-031: On authentication failure (ERR-002) the system SHALL redirect to the login page with a return URL.

REQ-051-032: On API timeout (ERR-003) the system SHALL show a timeout message and SHALL auto-retry up to 3 times.

REQ-051-033: When the network is offline (ERR-004) the system SHALL show an offline indicator and SHALL enable the change queue.

REQ-051-034: On permission denied (ERR-005) the system SHALL show an access-denied message and SHALL log the attempt.

REQ-051-035: On session expiry (ERR-006) the system SHALL clear the session and redirect to login.

REQ-051-036: On an invalid CSRF token (ERR-007) the system SHALL reject the request and return 403.

REQ-051-037: When a rate limit is exceeded (ERR-008) the system SHALL return 429 and show the retry-after header.

REQ-051-038: When a project is not found (ERR-009) the system SHALL return 404 and show the project list.

REQ-051-039: When a task is not found (ERR-010) the system SHALL return 404 and show the task list.

REQ-051-040: When an agent is not found (ERR-011) the system SHALL return 404 and show the agent configuration.

REQ-051-041: On invalid project data (ERR-012) the system SHALL return 400 with the validation errors.

REQ-051-042: On invalid task data (ERR-013) the system SHALL return 400 with the validation errors.

REQ-051-043: On a circular task dependency (ERR-014) the system SHALL return 400 and highlight the conflicting tasks.

REQ-051-044: On a governance rule violation (ERR-015) the system SHALL return 451 and show the rule details.

REQ-051-045: On database connection failure (ERR-016) the system SHALL return 503 and enable the retry queue.

REQ-051-046: On a WebSocket protocol error (ERR-017) the system SHALL close the connection, log the error, and attempt reconnection.

REQ-051-047: On an invalid OAuth callback (ERR-018) the system SHALL redirect to login with an error message.

REQ-051-048: On a concurrent modification conflict (ERR-019) the system SHALL return 409 and show the conflict-resolution UI.

REQ-051-049: On an unsupported media type (ERR-020) the system SHALL return 415 and show the supported types.


Non-Functional Requirements

Performance
Metric Target
Page load (first contentful paint) < 500ms
API response time (p95) < 200ms
WebSocket event latency < 100ms
HTMX partial render < 50ms
Drag-and-drop response < 16ms (60fps)

REQ-051-101: The UI SHALL meet the performance budgets: page load (first contentful paint) < 500ms, API response time (p95) < 200ms, WebSocket event latency < 100ms, HTMX partial render < 50ms, and drag-and-drop response < 16ms (60fps).

Scalability
Resource Capacity
Concurrent users 100
Projects per user 50
Tasks per project 1,000
WebSocket connections 500
API requests per minute 6,000

REQ-051-102: The system SHALL sustain the scalability capacities: 100 concurrent users, 50 projects per user, 1,000 tasks per project, 500 WebSocket connections, and 6,000 API requests per minute.

Availability

REQ-051-103: The system SHALL meet a 99.9% uptime target, SHALL confine planned maintenance to the Sunday 02:00-04:00 UTC window, and SHALL degrade gracefully to read-only mode during maintenance.

Accessibility

REQ-051-104: The UI SHALL comply with WCAG 2.1 Level AA, SHALL support keyboard navigation, SHALL be screen-reader compatible, SHALL provide a high-contrast mode, and SHALL show focus indicators on all interactive elements.

Browser Support

REQ-051-105: The UI SHALL support Chrome 90+, Firefox 88+, Safari 14+, Edge 90+, Mobile Safari iOS 14+, and Chrome for Android 90+.


Dependencies

Dependency Version Purpose
fastapi >=0.100 Web framework
websockets >=11.0 Real-time updates
redis >=5.0 Session store
neo4j >=5.0 Data persistence
htmx >=1.9 Reactive HTML
tailwindcss >=3.0 Styling

REQ-051-106: The system SHALL depend on fastapi >= 0.100 (web framework), websockets >= 11.0 (real-time updates), redis >= 5.0 (session store), neo4j >= 5.0 (data persistence), htmx >= 1.9 (reactive HTML), and tailwindcss >= 3.0 (styling).


Security

Authentication

REQ-051-050: Authentication SHALL use OAuth 2.0, SHALL manage sessions with secure cookies, SHALL expire sessions automatically, and SHALL support MFA via TOTP.

Authorization

REQ-051-051: Authorization SHALL enforce PAM++ policy, role-based access control, project-scoped permissions, and the Admin/User/Viewer role set.

CSRF Protection
@app.middleware("http")
async def csrf_protection(request: Request, call_next):
    if request.method in ["POST", "PUT", "DELETE", "PATCH"]:
        csrf_token = «redacted»
        session_csrf = request.cookies.get("csrf_token")
        
        if not csrf_token or csrf_token != session_csrf:
            raise HTTPException(403, "CSRF token invalid")
    
    response = await call_next(request)
    return response
XSS Prevention

REQ-051-052: The system SHALL sanitize all user input, SHALL send Content Security Policy headers, SHALL use HTTP-only cookies, and SHALL send the secure headers X-Content-Type-Options and X-Frame-Options.

Rate Limiting

REQ-051-053: The system SHALL rate-limit login attempts to 5 per minute, API calls to 100 per minute, and WebSocket messages to 60 per minute.


Governance (PAM++)

Policy Enforcement

REQ-051-054: All UI actions SHALL be subject to PAM++ checks, governance errors SHALL return status 451, admin-only routes SHALL be protected, and user actions SHALL be logged for audit.

Audit Logging
@app.middleware("http")
async def audit_log(request: Request, call_next):
    start_time = time.time()
    response = await call_next(request)
    
    await log_audit({
        "user": request.user.id if request.user else "anonymous",
        "action": f"{request.method} {request.url.path}",
        "status": response.status_code,
        "duration_ms": (time.time() - start_time) * 1000
    })
    
    return response
Data Retention

REQ-051-055: Data retention SHALL be 30 days for user sessions, 90 days for audit logs, 30 days for activity logs, and until deletion for project data.


Technical Design

Architecture Pattern

VizInt follows a layered architecture pattern with clear separation of concerns:

  1. Presentation Layer - HTMX templates with TailwindCSS styling
  2. API Layer - FastAPI REST endpoints with request/response models
  3. Service Layer - Business logic for project, task, and agent management
  4. Data Layer - Neo4j graph database for persistence and Redis for sessions

REQ-051-056: VizInt SHALL follow a layered architecture with clear separation of concerns across four layers: a Presentation layer of HTMX templates with TailwindCSS styling, an API layer of FastAPI REST endpoints with request/response models, a Service layer holding the business logic for project, task and agent management, and a Data layer of a Neo4j graph database for persistence with Redis for sessions.

Component Interactions
┌─────────────────────────────────────────────────────────────────┐
│                      VizInt Web Server                          │
│                   (FastAPI + HTMX + TailwindCSS)               │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐              │
│  │   Projects  │  │    Tasks    │  │   Config    │              │
│  │    View     │  │    View     │  │    View    │              │
│  └──────┬──────┘  └──────┬──────┘  └──────┬──────┘              │
│         │                 │                 │                     │
│         └─────────────────┼─────────────────┘                     │
│                           │                                     │
│                    ┌──────▼──────┐                              │
│                    │     API      │                              │
│                    │   Service    │                              │
│                    └──────┬──────┘                              │
│                           │                                      │
│         ┌─────────────────┼─────────────────┐                    │
│         │                 │                 │                    │
│         ▼                 ▼                 ▼                    │
│  ┌────────────┐   ┌────────────┐   ┌────────────┐              │
│  │Orchestrata │   │   Neo4j    │   │  WebSocket │              │
│  │   Kernel   │   │     KG     │   │   Updates  │              │
│  └────────────┘   └────────────┘   └────────────┘              │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘
Data Flow
  1. User Request Flow: User browser → HTMX request → FastAPI endpoint → Service layer → Neo4j → Response → HTMX swap
  2. Real-time Updates: Orchestrata kernel → WebSocket broadcast → Client JavaScript → DOM update
  3. Authentication Flow: OAuth2 provider → Callback → Session creation → Redis storage → Cookie set
State Management

REQ-051-057: Server state SHALL be held in Neo4j for persistent data and in Redis for sessions and cache; client state SHALL be held in HTMX DOM state and in localStorage for the offline queue; shared state SHALL be synchronized across clients over WebSocket.


Examples

Example 1: Creating a New Project
POST /api/projects HTTP/1.1
Host: vizint.orchestrata.local
Content-Type: application/json
Authorization: Bearer <token>

{
  "name": "ecommerce-platform",
  "template": "fullstack",
  "volk_configs": {
    "U:Cd": {
      "enabled": true,
      "model": "claude-3-5-sonnet",
      "max_concurrent": 3
    },
    "U:Qa": {
      "enabled": true,
      "model": "gpt-4o",
      "max_concurrent": 2
    }
  }
}

Response (201 Created):

{
  "project_id": "proj_abc123",
  "name": "ecommerce-platform",
  "status": "active",
  "path": "/home/user/projects/ecommerce-platform",
  "task_count": 0,
  "agent_count": 2,
  "created_at": "2026-04-09T10:00:00Z",
  "updated_at": "2026-04-09T10:00:00Z"
}
Example 2: Drag-and-Drop Task Update
PATCH /api/projects/proj_abc123/tasks/T:001/status HTTP/1.1
Host: vizint.orchestrata.local
Content-Type: application/json
Authorization: Bearer <token>

{
  "status": "in_progress"
}

Response (200 OK):

{
  "task_id": "T:001",
  "status": "in_progress",
  "updated_at": "2026-04-09T10:05:00Z"
}

WebSocket Broadcast:

{
  "type": "task_updated",
  "data": {
    "task_id": "T:001",
    "status": "in_progress",
    "assigned_volk": "U:Cd",
    "timestamp": "2026-04-09T10:05:00Z"
  }
}
Example 3: Governance Rule Creation
POST /api/projects/proj_abc123/rules HTTP/1.1
Host: vizint.orchestrata.local
Content-Type: application/json
Authorization: Bearer <token>

{
  "name": "require_code_review",
  "scope": "project",
  "condition": "action == 'merge' AND has_changes('.py')",
  "action": "require_approval",
  "priority": 50,
  "description": "All Python changes require code review before merge"
}

Response (201 Created):

{
  "rule_id": "rule_xyz789",
  "name": "require_code_review",
  "scope": "project",
  "condition": "action == 'merge' AND has_changes('.py')",
  "action": "require_approval",
  "priority": 50,
  "description": "All Python changes require code review before merge",
  "created_at": "2026-04-09T10:10:00Z"
}

Integration Points

Orchestrata Kernel Integration
Endpoint Purpose Protocol
/api/orchestrata/* Agent command execution HTTP/REST
/ws/orchestrata Real-time agent events WebSocket
/api/cxp/* Cross-process communication CXP

REQ-051-058: The system SHALL integrate with the Orchestrata kernel over /api/orchestrata/* (HTTP/REST) for agent command execution, /ws/orchestrata (WebSocket) for real-time agent events, and /api/cxp/* (CXP) for cross-process communication.

Neo4j Database Integration
Operation Query Type Frequency
Project CRUD Cypher On user action
Task CRUD Cypher On user action
Relationship queries Cypher On page load
Full-text search Cypher On search

REQ-051-059: The system SHALL access Neo4j with Cypher: project CRUD and task CRUD on user action, relationship queries on page load, and full-text search on search.

Redis Integration
Data Type TTL Purpose
User sessions 30 days Authentication state
CSRF tokens 1 hour CSRF protection
WebSocket heartbeats 1 minute Connection health
Rate limit counters 1 minute API throttling
Offline change queue Until sync Offline support

REQ-051-060: The system SHALL hold in Redis: user sessions with a 30-day TTL for authentication state, CSRF tokens with a 1-hour TTL, WebSocket heartbeats with a 1-minute TTL for connection health, rate-limit counters with a 1-minute TTL for API throttling, and the offline change queue retained until sync.

External OAuth Providers
Provider Capability Config
GitHub User authentication Client ID/Secret
Google User authentication Client ID/Secret

REQ-051-061: The system SHALL support user authentication through the GitHub and Google OAuth providers, each configured with a client ID and secret.


Success Criteria

Functional Criteria
ID Criterion Validation Method
SC-001 Dashboard displays all projects with status Visual inspection
SC-002 Tasks can be dragged between Kanban columns Manual testing
SC-003 Agent configuration changes persist immediately API verification
SC-004 WebSocket delivers updates within 100ms Performance test
SC-005 Governance rules block prohibited actions Rule enforcement test
SC-006 Offline changes sync when connection restored Offline testing

REQ-051-062: The dashboard SHALL display all projects with status (SC-001), validated by visual inspection.

REQ-051-063: Tasks SHALL be draggable between Kanban columns (SC-002), validated by manual testing.

REQ-051-064: Agent configuration changes SHALL persist immediately (SC-003), validated by API verification.

REQ-051-065: WebSocket SHALL deliver updates within 100ms (SC-004), validated by performance test.

REQ-051-066: Governance rules SHALL block prohibited actions (SC-005), validated by rule enforcement test.

REQ-051-067: Offline changes SHALL sync when the connection is restored (SC-006), validated by offline testing.

Quality Criteria
ID Criterion Target
QC-001 Test coverage > 80%
QC-002 API availability 99.9%
QC-003 Page load time < 500ms
QC-004 Critical vulnerabilities 0
QC-005 Accessibility compliance WCAG 2.1 AA

REQ-051-107: Test coverage SHALL exceed 80% (QC-001).

REQ-051-108: API availability SHALL be 99.9% (QC-002).

REQ-051-109: Page load time SHALL be under 500ms (QC-003).

REQ-051-110: The number of critical vulnerabilities SHALL be 0 (QC-004).

REQ-051-111: Accessibility SHALL comply with WCAG 2.1 AA (QC-005).

Deployment Criteria
ID Criterion Validation
DC-001 Production deployment succeeds Deployment pipeline
DC-002 Health check endpoint returns 200 Monitoring
DC-003 No breaking changes to existing APIs Contract tests
DC-004 Rollback procedure documented Runbook review

REQ-051-112: Production deployment SHALL succeed (DC-001), validated by the deployment pipeline.

REQ-051-113: The health check endpoint SHALL return 200 (DC-002), validated by monitoring.

REQ-051-114: The release SHALL introduce no breaking changes to existing APIs (DC-003), validated by contract tests.

REQ-051-115: The rollback procedure SHALL be documented (DC-004), validated by runbook review.


Testing Requirements

Unit Tests
class TestProjectAPI:
    async def test_create_project_success(self):
        response = await client.post("/api/v1/projects", json={
            "name": "Test Project",
            "description": "Test"
        })
        assert response.status_code == 201
    
    async def test_create_project_unauthorized(self):
        response = await client.post("/api/v1/projects", json={
            "name": "Test"
        })
        assert response.status_code == 401
Integration Tests

REQ-051-116: Integration tests SHALL cover database integration, WebSocket connections, the authentication flow, project CRUD operations, and task management.

E2E Tests

REQ-051-117: End-to-end tests SHALL cover complete user workflows, cross-browser compatibility, mobile responsiveness, and offline mode functionality.

Test Coverage

REQ-051-118: Test coverage SHALL exceed 80% for unit tests, SHALL exceed 90% for integration tests, and SHALL be 100% for API coverage.


Performance Targets

Response Times
Scalability

Monitoring

Metrics

REQ-051-119: The system SHALL report metrics for user sessions, API response times, error rates, WebSocket connections, and database queries.

Alerts

REQ-051-120: The system SHALL alert on a high error rate (>5%), slow responses (>1s), authentication failures, and database connection issues.


Overview

What

VizInt (Visual Interface) is a modern web-based dashboard for the Ultimate Hybrid system, providing project management, task orchestration, agent configuration, and governance rule editing through a visual interface.

Why
How

Terminology

VizInt

Visual Interface - the web-based dashboard system described in this specification.

Ultimate Hybrid

The orchestrata system combining CheetahClaws TUI and VizInt UI for comprehensive agent management.

CheetahClaws

The TUI (Terminal User Interface) companion to VizInt for command-line agent interactions.

CXP

Communication Protocol - the inter-process communication mechanism between VizInt and the Orchestrata kernel.

HTMX

HTML Markup eXtensions - library for creating dynamic web interfaces using HTML attributes.

WebSocket

Bi-directional communication protocol for real-time updates between server and client.

PAM++

Policy and Authorization Management - the governance framework for enforcing rules and permissions.

Neo4j

Graph database used for storing projects, tasks, and agent relationships.

Agent

An autonomous software entity that performs tasks within the Ultimate Hybrid system.

Project

A container for tasks, agents, and configuration within the Ultimate Hybrid system.

Task

A unit of work assigned to an agent with defined parameters and dependencies.


Architecture

Tech Stack
┌─────────────────────────────────────────────────────────────────┐
│                      VizInt Web Server                            │
│                   (FastAPI + HTMX + TailwindCSS)                 │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐          │
│  │   Projects  │  │    Tasks    │  │   Config    │          │
│  │    View     │  │    View     │  │    View     │          │
│  └──────┬──────┘  └──────┬──────┘  └──────┬──────┘          │
│         │                 │                 │                   │
│         └─────────────────┼─────────────────┘                   │
│                           │                                     │
│                    ┌──────▼──────┐                             │
│                    │     API     │                             │
│                    │   Service   │                             │
│                    └──────┬──────┘                             │
│                           │                                     │
│         ┌─────────────────┼─────────────────┐                    │
│         │                 │                 │                    │
│         ▼                 ▼                 ▼                    │
│  ┌────────────┐   ┌────────────┐   ┌────────────┐           │
│  │Orchestrata │   │   Neo4j    │   │  WebSocket │           │
│  │   Kernel   │   │     KG     │   │   Updates  │           │
│  └────────────┘   └────────────┘   └────────────┘           │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘
Project Structure
vizint/
├── app/
│   ├── main.py              # FastAPI application
│   ├── routes/
│   │   ├── projects.py     # Project endpoints
│   │   ├── tasks.py        # Task endpoints
│   │   ├── config.py       # Configuration endpoints
│   │   ├── rules.py        # Governance rules endpoints
│   │   └── ws.py           # WebSocket handler
│   ├── services/
│   │   ├── orchestrator.py # Orchestrata integration
│   │   ├── neo4j.py        # Neo4j client
│   │   └── todoist.py      # Todoist integration
│   ├── models/
│   │   ├── project.py      # Project models
│   │   ├── task.py         # Task models
│   │   └── config.py       # Config models
│   └── templates/           # HTMX templates
│       ├── base.html
│       ├── projects.html
│       ├── tasks.html
│       ├── config.html
│       └── rules.html
├── static/
│   ├── css/
│   │   └── app.css        # TailwindCSS
│   └── js/
│       └── app.js         # HTMX + interactivity
└── pyproject.toml

API Endpoints

Projects
List Projects
GET /api/projects

Response:

{
  "projects": [
    {
      "project_id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "webapp-ecommerce",
      "path": "/home/user/projects/ecommerce",
      "status": "active",
      "session_id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
      "task_count": 12,
      "agent_count": 4,
      "created_at": "2026-04-01T10:00:00Z",
      "updated_at": "2026-04-08T14:30:00Z"
    }
  ]
}
Create Project
POST /api/projects
Content-Type: application/json

{
  "name": "webapp-ecommerce",
  "template": "fullstack",
  "volk_configs": {
    "U:Cd": {"enabled": true, "model": "claude-3-5-sonnet"},
    "U:Qa": {"enabled": true, "model": "gpt-4o"}
  }
}
Get Project Details
GET /api/projects/{project_id}
Update Project
PUT /api/projects/{project_id}
Content-Type: application/json

{
  "name": "webapp-ecommerce-v2",
  "volk_configs": {...}
}
Delete Project
DELETE /api/projects/{project_id}

REQ-051-068: The system SHALL expose the project endpoints GET /api/projects (list), POST /api/projects (create, accepting name, template and volk_configs), GET /api/projects/{project_id} (detail), PUT /api/projects/{project_id} (update) and DELETE /api/projects/{project_id} (delete).

Tasks
List Tasks
GET /api/projects/{project_id}/tasks

Query Parameters:

Create Task
POST /api/projects/{project_id}/tasks
Content-Type: application/json

{
  "title": "Implement user authentication",
  "description": "Add OAuth2 and JWT support",
  "assigned_volk": "U:Cd",
  "priority": 1,
  "due_date": "2026-04-15T00:00:00Z",
  "depends_on": []
}
Update Task
PUT /api/projects/{project_id}/tasks/{task_id}
Content-Type: application/json

{
  "status": "completed",
  "assigned_volk": "U:Cd"
}
Update Task Status (Drag & Drop)
PATCH /api/projects/{project_id}/tasks/{task_id}/status
Content-Type: application/json

{
  "status": "in_progress"
}

REQ-051-069: The system SHALL expose the task endpoints GET /api/projects/{project_id}/tasks (list, filterable by the status and assigned_volk query parameters), POST /api/projects/{project_id}/tasks (create), PUT /api/projects/{project_id}/tasks/{task_id} (update) and PATCH /api/projects/{project_id}/tasks/{task_id}/status (drag-and-drop status update).

Configuration
Get Agent Configuration
GET /api/projects/{project_id}/config/agents
Update Agent Configuration
PUT /api/projects/{project_id}/config/agents/{agent_alias}
Content-Type: application/json

{
  "model": "gpt-4o",
  "enabled": true,
  "max_concurrent": 3,
  "timeout": 300,
  "tools": ["Read", "Write", "Edit", "Bash"]
}

REQ-051-070: The system SHALL expose the agent-configuration endpoints GET /api/projects/{project_id}/config/agents and PUT /api/projects/{project_id}/config/agents/{agent_alias}, the latter accepting model, enabled, max_concurrent, timeout and tools.

Governance Rules
List Rules
GET /api/projects/{project_id}/rules
Create Rule
POST /api/projects/{project_id}/rules
Content-Type: application/json

{
  "name": "no_delete_prod",
  "scope": "project",
  "condition": "action == 'delete' AND path contains 'prod'",
  "action": "deny",
  "priority": 100
}
Update Rule
PUT /api/projects/{project_id}/rules/{rule_id}
Delete Rule
DELETE /api/projects/{project_id}/rules/{rule_id}

REQ-051-071: The system SHALL expose the governance-rule endpoints GET /api/projects/{project_id}/rules (list), POST /api/projects/{project_id}/rules (create, accepting name, scope, condition, action and priority), PUT /api/projects/{project_id}/rules/{rule_id} (update) and DELETE /api/projects/{project_id}/rules/{rule_id} (delete).

WebSocket
Connect
WS /ws/{project_id}

Messages (Server → Client):

{
  "type": "task_updated",
  "data": {
    "task_id": "task-123",
    "status": "in_progress",
    "assigned_volk": "U:Cd",
    "timestamp": "2026-04-08T14:30:00Z"
  }
}
{
  "type": "agent_status",
  "data": {
    "U:Cd": {"status": "active", "current_task": "task-123"},
    "U:Qa": {"status": "idle", "current_task": null}
  }
}
{
  "type": "checkpoint_created",
  "data": {
    "checkpoint_id": "c5",
    "description": "OAuth2 implementation",
    "timestamp": "2026-04-08T14:30:00Z"
  }
}

REQ-051-072: The system SHALL accept WebSocket connections at WS /ws/{project_id} and SHALL send server-to-client messages carrying a type and a data payload, including task_updated, agent_status and checkpoint_created.


Frontend Components

Base Layout
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>VizInt - Ultimate Hybrid Dashboard</title>
    <script src="https://unpkg.com/htmx.org@1.9.10"></script>
    <script src="https://cdn.tailwindcss.com"></script>
    <link rel="stylesheet" href="/static/css/app.css">
</head>
<body class="bg-gray-900 text-gray-100">
    <div class="flex h-screen">
        <!-- Sidebar -->
        <aside class="w-64 bg-gray-800 border-r border-gray-700 flex flex-col">
            <div class="p-4 border-b border-gray-700">
                <h1 class="text-xl font-bold text-cyan-400">VizInt</h1>
                <p class="text-xs text-gray-400">Ultimate Hybrid Dashboard</p>
            </div>
            
            <nav class="flex-1 p-4 space-y-2">
                <a href="/projects" 
                   class="flex items-center gap-3 px-4 py-2 rounded-lg hover:bg-gray-700"
                   hx-get="/partials/nav/projects" hx-target="#main-content">
                    <svg class="w-5 h-5">...</svg>
                    Projects
                </a>
                <a href="/tasks" 
                   class="flex items-center gap-3 px-4 py-2 rounded-lg hover:bg-gray-700"
                   hx-get="/partials/nav/tasks" hx-target="#main-content">
                    <svg class="w-5 h-5">...</svg>
                    Tasks
                </a>
                <a href="/config" 
                   class="flex items-center gap-3 px-4 py-2 rounded-lg hover:bg-gray-700"
                   hx-get="/partials/nav/config" hx-target="#main-content">
                    <svg class="w-5 h-5">...</svg>
                    Configuration
                </a>
                <a href="/rules" 
                   class="flex items-center gap-3 px-4 py-2 rounded-lg hover:bg-gray-700"
                   hx-get="/partials/nav/rules" hx-target="#main-content">
                    <svg class="w-5 h-5">...</svg>
                    Rules
                </a>
            </nav>
            
            <!-- Status Bar -->
            <div class="p-4 border-t border-gray-700">
                <div class="flex items-center gap-2 text-sm">
                    <span class="w-2 h-2 rounded-full bg-green-500"></span>
                    <span>System Online</span>
                </div>
            </div>
        </aside>
        
        <!-- Main Content -->
        <main class="flex-1 flex flex-col overflow-hidden">
            <!-- Header -->
            <header class="h-16 bg-gray-800 border-b border-gray-700 flex items-center justify-between px-6">
                <div class="flex items-center gap-4">
                    <h2 class="text-lg font-semibold" id="page-title">Projects</h2>
                </div>
                <div class="flex items-center gap-4">
                    <button class="px-4 py-2 bg-cyan-600 rounded-lg hover:bg-cyan-700">
                        + New Project
                    </button>
                </div>
            </header>
            
            <!-- Content Area -->
            <div id="main-content" class="flex-1 overflow-auto p-6">
                <!-- Dynamic Content Loaded Here -->
            </div>
        </main>
    </div>
    
    <!-- WebSocket Connection -->
    <script src="/static/js/websocket.js"></script>
</body>
</html>
Projects View
<!-- projects.html -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
    <!-- Project Card -->
    <div class="bg-gray-800 rounded-xl border border-gray-700 overflow-hidden hover:border-cyan-500 transition-colors cursor-pointer"
         hx-get="/api/projects/{{ project.id }}"
         hx-target="#project-detail">
        
        <div class="p-4">
            <div class="flex items-start justify-between">
                <div>
                    <h3 class="text-lg font-semibold text-white">{{ project.name }}</h3>
                    <p class="text-sm text-gray-400">{{ project.path }}</p>
                </div>
                <span class="px-2 py-1 text-xs rounded-full 
                    {% if project.status == 'active' %}bg-green-900 text-green-300
                    {% else %}bg-gray-700 text-gray-400{% endif %}">
                    {{ project.status }}
                </span>
            </div>
            
            <div class="mt-4 flex items-center gap-4 text-sm text-gray-400">
                <div class="flex items-center gap-1">
                    <svg class="w-4 h-4">...</svg>
                    {{ project.task_count }} tasks
                </div>
                <div class="flex items-center gap-1">
                    <svg class="w-4 h-4">...</svg>
                    {{ project.agent_count }} agents
                </div>
            </div>
            
            <!-- Agent Status -->
            <div class="mt-4 flex items-center gap-2">
                {% for agent in project.agents %}
                <div class="flex items-center gap-1 text-xs">
                    <span class="w-2 h-2 rounded-full 
                        {% if agent.status == 'active' %}bg-green-500
                        {% elif agent.status == 'idle' %}bg-yellow-500
                        {% else %}bg-gray-500{% endif %}">
                    </span>
                    {{ agent.alias }}
                </div>
                {% endfor %}
            </div>
        </div>
    </div>
</div>
Tasks View (Kanban)
<!-- tasks.html -->
<div class="flex gap-6 h-full">
    <!-- Pending Column -->
    <div class="flex-1 bg-gray-800 rounded-xl p-4" 
         hx-controller="tasks"
         hx-get="/api/projects/{{ project_id }}/tasks?status=pending"
         hx-trigger="task_updated from:body">
        
        <div class="flex items-center justify-between mb-4">
            <h3 class="font-semibold text-gray-300">Pending</h3>
            <span class="px-2 py-1 text-xs bg-gray-700 rounded-full">{{ pending_count }}</span>
        </div>
        
        <div class="space-y-3 min-h-[200px]" 
             hx-drop-zone="true"
             hx-put="/api/projects/{{ project_id }}/tasks/drop?status=pending">
            
            {% for task in pending_tasks %}
            <div class="bg-gray-700 rounded-lg p-3 cursor-move" 
                 draggable="true"
                 hx-put="/api/projects/{{ project_id }}/tasks/{{ task.id }}/status"
                 hx-vals='{"status": "pending"}'>
                
                <div class="flex items-start justify-between">
                    <h4 class="font-medium text-white">{{ task.title }}</h4>
                    <span class="text-xs text-gray-400">P{{ task.priority }}</span>
                </div>
                
                <p class="mt-2 text-sm text-gray-400 line-clamp-2">
                    {{ task.description }}
                </p>
                
                <div class="mt-3 flex items-center justify-between">
                    <span class="text-xs text-gray-500">
                        {{ task.assigned_volk }}
                    </span>
                    {% if task.due_date %}
                    <span class="text-xs text-gray-500">
                        Due: {{ task.due_date }}
                    </span>
                    {% endif %}
                </div>
            </div>
            {% endfor %}
        </div>
    </div>
    
    <!-- In Progress Column -->
    <div class="flex-1 bg-gray-800 rounded-xl p-4">
        <div class="flex items-center justify-between mb-4">
            <h3 class="font-semibold text-yellow-300">In Progress</h3>
            <span class="px-2 py-1 text-xs bg-yellow-900 text-yellow-300 rounded-full">
                {{ in_progress_count }}
            </span>
        </div>
        
        <!-- Tasks with progress bars -->
    </div>
    
    <!-- Completed Column -->
    <div class="flex-1 bg-gray-800 rounded-xl p-4">
        <div class="flex items-center justify-between mb-4">
            <h3 class="font-semibold text-green-300">Completed</h3>
            <span class="px-2 py-1 text-xs bg-green-900 text-green-300 rounded-full">
                {{ completed_count }}
            </span>
        </div>
    </div>
</div>
Agent Configuration View
<!-- config.html -->
<div class="space-y-6">
    {% for agent in agents %}
    <div class="bg-gray-800 rounded-xl p-6 border border-gray-700">
        <div class="flex items-center justify-between mb-4">
            <div class="flex items-center gap-3">
                <span class="w-3 h-3 rounded-full 
                    {% if agent.enabled %}bg-green-500{% else %}bg-gray-500{% endif %}">
                </span>
                <h3 class="text-lg font-semibold">{{ agent.alias }}</h3>
                <span class="text-gray-400">{{ agent.role }}</span>
            </div>
            
            <label class="flex items-center gap-2">
                <input type="checkbox" 
                       {% if agent.enabled %}checked{% endif %}
                       class="form-checkbox h-5 w-5 text-cyan-600"
                       hx-put="/api/projects/{{ project_id }}/config/agents/{{ agent.alias }}/toggle">
                <span class="text-sm text-gray-300">Enabled</span>
            </label>
        </div>
        
        <div class="grid grid-cols-2 gap-4">
            <div>
                <label class="block text-sm text-gray-400 mb-2">Model</label>
                <select class="w-full bg-gray-700 border border-gray-600 rounded-lg px-3 py-2"
                        hx-put="/api/projects/{{ project_id }}/config/agents/{{ agent.alias }}/model">
                    {% for model in available_models %}
                    <option value="{{ model.id }}" 
                            {% if model.id == agent.model %}selected{% endif %}>
                        {{ model.name }} ({{ model.provider }})
                    </option>
                    {% endfor %}
                </select>
            </div>
            
            <div>
                <label class="block text-sm text-gray-400 mb-2">Max Concurrent</label>
                <input type="number" value="{{ agent.max_concurrent }}"
                       class="w-full bg-gray-700 border border-gray-600 rounded-lg px-3 py-2"
                       hx-put="/api/projects/{{ project_id }}/config/agents/{{ agent.alias }}/max_concurrent">
            </div>
            
            <div>
                <label class="block text-sm text-gray-400 mb-2">Timeout (seconds)</label>
                <input type="number" value="{{ agent.timeout }}"
                       class="w-full bg-gray-700 border border-gray-600 rounded-lg px-3 py-2"
                       hx-put="/api/projects/{{ project_id }}/config/agents/{{ agent.alias }}/timeout">
            </div>
            
            <div>
                <label class="block text-sm text-gray-400 mb-2">Cost Limit ($)</label>
                <input type="number" step="0.01" value="{{ agent.cost_limit }}"
                       class="w-full bg-gray-700 border border-gray-600 rounded-lg px-3 py-2"
                       hx-put="/api/projects/{{ project_id }}/config/agents/{{ agent.alias }}/cost_limit">
            </div>
        </div>
        
        <div class="mt-4">
            <label class="block text-sm text-gray-400 mb-2">Tools</label>
            <div class="flex flex-wrap gap-2">
                {% for tool in available_tools %}
                <label class="flex items-center gap-2 px-3 py-1 bg-gray-700 rounded-lg cursor-pointer hover:bg-gray-600">
                    <input type="checkbox" 
                           {% if tool in agent.tools %}checked{% endif %}
                           class="form-checkbox h-4 w-4 text-cyan-600"
                           hx-put="/api/projects/{{ project_id }}/config/agents/{{ agent.alias }}/tools/{{ tool }}">
                    <span class="text-sm">{{ tool }}</span>
                </label>
                {% endfor %}
            </div>
        </div>
    </div>
    {% endfor %}
</div>
Governance Rules View
<!-- rules.html -->
<div class="space-y-6">
    <!-- Global Rules Section -->
    <div class="bg-gray-800 rounded-xl p-6 border border-gray-700">
        <div class="flex items-center justify-between mb-4">
            <h3 class="text-lg font-semibold">Global Rules</h3>
            <button class="px-3 py-1 bg-cyan-600 rounded-lg hover:bg-cyan-700 text-sm"
                    hx-get="/partials/rules/create?scope=global"
                    hx-target="#rule-form">
                + Add Rule
            </button>
        </div>
        
        <div class="space-y-3">
            {% for rule in global_rules %}
            <div class="flex items-center justify-between p-3 bg-gray-700 rounded-lg">
                <div class="flex items-center gap-3">
                    <span class="px-2 py-1 text-xs font-medium rounded
                        {% if rule.action == 'deny' %}bg-red-900 text-red-300
                        {% elif rule.action == 'allow' %}bg-green-900 text-green-300
                        {% else %}bg-yellow-900 text-yellow-300{% endif %}">
                        {{ rule.action|upper }}
                    </span>
                    <span class="font-medium">{{ rule.name }}</span>
                </div>
                <div class="flex items-center gap-2 text-sm text-gray-400">
                    <span>Priority: {{ rule.priority }}</span>
                    <button class="p-1 hover:text-white"
                            hx-delete="/api/projects/{{ project_id }}/rules/{{ rule.id }}">
                        <svg class="w-4 h-4">...</svg>
                    </button>
                </div>
            </div>
            {% endfor %}
        </div>
    </div>
    
    <!-- Project Rules Section -->
    <div class="bg-gray-800 rounded-xl p-6 border border-gray-700">
        <div class="flex items-center justify-between mb-4">
            <h3 class="text-lg font-semibold">Project Rules</h3>
            <button class="px-3 py-1 bg-cyan-600 rounded-lg hover:bg-cyan-700 text-sm"
                    hx-get="/partials/rules/create?scope=project"
                    hx-target="#rule-form">
                + Add Rule
            </button>
        </div>
        
        <!-- Rule form container -->
        <div id="rule-form" class="mb-4"></div>
        
        <div class="space-y-3">
            {% for rule in project_rules %}
            <div class="p-3 bg-gray-700 rounded-lg">
                <div class="flex items-center justify-between">
                    <div class="flex items-center gap-3">
                        <span class="px-2 py-1 text-xs font-medium rounded
                            {% if rule.action == 'deny' %}bg-red-900 text-red-300
                            {% elif rule.action == 'allow' %}bg-green-900 text-green-300
                            {% else %}bg-yellow-900 text-yellow-300{% endif %}">
                            {{ rule.action|upper }}
                        </span>
                        <span class="font-medium">{{ rule.name }}</span>
                    </div>
                    <div class="flex items-center gap-2">
                        <button class="p-1 hover:text-white"
                                hx-get="/api/projects/{{ project_id }}/rules/{{ rule.id }}/edit"
                                hx-target="#rule-form">
                            <svg class="w-4 h-4">...</svg>
                        </button>
                        <button class="p-1 hover:text-white"
                                hx-delete="/api/projects/{{ project_id }}/rules/{{ rule.id }}">
                            <svg class="w-4 h-4">...</svg>
                        </button>
                    </div>
                </div>
                <p class="mt-2 text-sm text-gray-400">{{ rule.description }}</p>
                <code class="mt-2 block text-xs bg-gray-800 p-2 rounded">
                    {{ rule.condition }}
                </code>
            </div>
            {% endfor %}
        </div>
    </div>
</div>

WebSocket Events

Server-Side Implementation
from fastapi import WebSocket
from typing import Dict, Set

class WebSocketManager:
    """Manages WebSocket connections for real-time updates."""
    
    def __init__(self):
        self.active_connections: Dict[str, Set[WebSocket]] = {}
    
    async def connect(self, project_id: str, websocket: WebSocket):
        """Connect a new WebSocket client."""
        await websocket.accept()
        if project_id not in self.active_connections:
            self.active_connections[project_id] = set()
        self.active_connections[project_id].add(websocket)
    
    def disconnect(self, project_id: str, websocket: WebSocket):
        """Disconnect a WebSocket client."""
        if project_id in self.active_connections:
            self.active_connections[project_id].discard(websocket)
    
    async def broadcast(self, project_id: str, message: dict):
        """Broadcast message to all clients for a project."""
        if project_id in self.active_connections:
            for connection in self.active_connections[project_id]:
                try:
                    await connection.send_json(message)
                except Exception:
                    # Remove dead connections
                    self.active_connections[project_id].discard(connection)
Event Types
class WebSocketEvents:
    """WebSocket event types and payloads."""
    
    TASK_UPDATED = "task_updated"
    TASK_CREATED = "task_created"
    TASK_DELETED = "task_deleted"
    
    AGENT_STATUS = "agent_status"
    AGENT_PROGRESS = "agent_progress"
    
    CHECKPOINT_CREATED = "checkpoint_created"
    CHECKPOINT_RESTORED = "checkpoint_restored"
    
    RULE_UPDATED = "rule_updated"
    RULE_VIOLATION = "rule_violation"
    
    PROJECT_UPDATED = "project_updated"

REQ-051-073: The WebSocket event vocabulary SHALL be task_updated, task_created, task_deleted, agent_status, agent_progress, checkpoint_created, checkpoint_restored, rule_updated, rule_violation and project_updated.


Styling

Custom CSS
/* app.css */
@tailwind base;
@tailwind components;
@tailwind utilities;

@layer components {
    .btn-primary {
        @apply px-4 py-2 bg-cyan-600 text-white rounded-lg hover:bg-cyan-700 transition-colors;
    }
    
    .btn-secondary {
        @apply px-4 py-2 bg-gray-600 text-white rounded-lg hover:bg-gray-700 transition-colors;
    }
    
    .card {
        @apply bg-gray-800 rounded-xl border border-gray-700 overflow-hidden;
    }
    
    .input-field {
        @apply w-full bg-gray-700 border border-gray-600 rounded-lg px-3 py-2 text-white focus:outline-none focus:border-cyan-500;
    }
    
    .status-active {
        @apply w-2 h-2 rounded-full bg-green-500;
    }
    
    .status-idle {
        @apply w-2 h-2 rounded-full bg-yellow-500;
    }
    
    .status-offline {
        @apply w-2 h-2 rounded-full bg-gray-500;
    }
}

/* Smooth transitions */
* {
    transition-property: background-color, border-color, color, fill, stroke;
    transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
    transition-duration: 150ms;
}

/* Custom scrollbar */
::-webkit-scrollbar {
    width: 8px;
    height: 8px;
}

::-webkit-scrollbar-track {
    background: #1f2937;
}

::-webkit-scrollbar-thumb {
    background: #4b5563;
    border-radius: 4px;
}

::-webkit-scrollbar-thumb:hover {
    background: #6b7280;
}

JavaScript

WebSocket Client
// websocket.js
class VizIntWebSocket {
    constructor(projectId) {
        this.projectId = projectId;
        this.ws = null;
        this.reconnectAttempts = 0;
        this.maxReconnectAttempts = 5;
    }
    
    connect() {
        const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
        this.ws = new WebSocket(`${protocol}//${window.location.host}/ws/${this.projectId}`);
        
        this.ws.onopen = () => {
            console.log('WebSocket connected');
            this.reconnectAttempts = 0;
            this.setupHeartbeat();
        };
        
        this.ws.onmessage = (event) => {
            const message = JSON.parse(event.data);
            this.handleMessage(message);
        };
        
        this.ws.onclose = () => {
            console.log('WebSocket disconnected');
            this.attemptReconnect();
        };
        
        this.ws.onerror = (error) => {
            console.error('WebSocket error:', error);
        };
    }
    
    handleMessage(message) {
        switch (message.type) {
            case 'task_updated':
                this.updateTaskCard(message.data);
                break;
            case 'agent_status':
                this.updateAgentStatus(message.data);
                break;
            case 'checkpoint_created':
                this.showNotification('Checkpoint created', 'success');
                break;
            case 'rule_violation':
                this.showNotification(`Rule violation: ${message.data.rule}`, 'error');
                break;
        }
    }
    
    updateTaskCard(data) {
        const card = document.querySelector(`[data-task-id="${data.task_id}"]`);
        if (card) {
            card.setAttribute('hx-vals', JSON.stringify({ status: data.status }));
            htmx.process(card);
        }
    }
    
    updateAgentStatus(agents) {
        for (const [alias, status] of Object.entries(agents)) {
            const indicator = document.querySelector(`[data-agent="${alias}"]`);
            if (indicator) {
                indicator.className = `w-3 h-3 rounded-full ${
                    status.status === 'active' ? 'bg-green-500' :
                    status.status === 'idle' ? 'bg-yellow-500' : 'bg-gray-500'
                }`;
            }
        }
    }
    
    showNotification(message, type) {
        // Show toast notification
        const toast = document.createElement('div');
        toast.className = `fixed bottom-4 right-4 px-4 py-2 rounded-lg ${
            type === 'success' ? 'bg-green-600' : 'bg-red-600'
        } text-white`;
        toast.textContent = message;
        document.body.appendChild(toast);
        setTimeout(() => toast.remove(), 3000);
    }
    
    attemptReconnect() {
        if (this.reconnectAttempts < this.maxReconnectAttempts) {
            this.reconnectAttempts++;
            setTimeout(() => this.connect(), 1000 * this.reconnectAttempts);
        }
    }
    
    setupHeartbeat() {
        setInterval(() => {
            if (this.ws && this.ws.readyState === WebSocket.OPEN) {
                this.ws.send(JSON.stringify({ type: 'ping' }));
            }
        }, 30000);
    }
}

// Initialize WebSocket when page loads
document.addEventListener('DOMContentLoaded', () => {
    const projectId = new URLSearchParams(window.location.search).get('project');
    if (projectId) {
        const ws = new VizIntWebSocket(projectId);
        ws.connect();
    }
});