Board is now a clean master-task view:
- GetBoardAsync returns only top-level (master) tasks; child-tasks render
nested inside their parent card instead of as separate column cards, so a
big task split into many sub-tasks stays one card (orphans treated as master)
- New DoneChildTaskCount on the DTO for real progress bars
- Child/detail consumers (GetChildren endpoint, TaskBridgeService) query
children directly instead of scraping the flat board
Stall watchdog (replaces destructive auto-reset):
- StaleTaskRecoveryService.FlagStalledInProgressTasksAsync marks In-progress
tasks with no activity past the threshold as stalled (activity event +
Iris notification) WITHOUT resetting the column — no work is discarded.
Idempotent: a task is not re-flagged until real progress happens
- BackgroundService now runs this watchdog (TaskRecovery:StalledMinutes=40,
interval 10m); hard reset kept only on the explicit manual endpoint
Review flow (Bao/Iris only):
- POST tasks/{id}/approve (Review -> Done)
- POST tasks/{id}/request-changes (Review -> target, mandatory comment,
ExpectedFrom=iris, notifies Iris)
Frontend:
- BoardCard component: master card with ball chip (who has it), progress from
children, expand to show children grouped by agent with per-child state +
stalled marker, stalled chip on the master, review action buttons
- Request-changes modal; tasks store approveReview/requestChanges actions
Tests: watchdog flag/idempotency + review threshold; 135 backend tests green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Shell:
- One shared NexusLayout for ALL routes (dashboard + pages): compact 68px
icon rail with hover-expand overlay, replaces both old sidebars + topbars
- Single flat nav source (railNav) — same menu everywhere incl. Settings
- Removed dead shell components (AppSidebar, AppHeader, Topbar, NavGroup,
NavItem, ModuleView) and dead nav routes; App.vue is now just RouterView
Live updates:
- Fix SSE loop in DashboardController: PeriodicTimer.WaitForNextTickAsync and
ChannelReader.ReadAsync were re-invoked while pending — every published
update threw InvalidOperationException and killed ALL live streams
- liveSync store: treat graceful stream close as disconnect (was stuck
connected=true with polling stopped -> page frozen until manual reload),
fast first retry, heartbeat watchdog (65s), reconnect on online/visibility
- One app-wide SSE connection owned by the layout instead of per-view
connect/disconnect churn; removed duplicate live-sync.ts store
Performance:
- Board endpoint: drop nested childTasks duplication (counts stay) — payload
104KB -> 65KB; SSE snapshots shrink equally
- nginx: gzip for JSON/JS/CSS (board 18KB, bundle 95KB over the wire);
text/event-stream excluded to keep SSE unbuffered
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Map legacy v1 CSS variables (--nx-*, --panel, --text-*, --surface*) to V2 tokens in nexus-tokens.css
- Restyle global shell (main.css), AppSidebar, AppHeader to match V2 Sidebar/Topbar (glass, gradients, Space Grotesk)
- Add GalaxyBackground to the v1 shell in App.vue
- Replace hardcoded v1 hex colors with V2 tokens in all deviating views (Agents, Calendar, Docs, Incidents, Memory, Notifications, ProjectDetail, Security, Team, TaskDetail, Settings)
- Keep JS status colors as hex where alpha suffixes are concatenated (AgentsIndex, Team, Notifications)
- Add Settings nav item (System group) + gear icon to V2 sidebar so it shows on the dashboard
- No component or layout structure changes; LoginView and TaskBoardView untouched
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Root cause: /api/bridge/health and /api/dashboard/live returned 404 live because
the source files existed on disk but were never committed to git. The CD pipeline
deploys from committed main, so the deployed containers lacked these endpoints.
Changes:
- Add GatewayBridgeController with /api/bridge/health endpoint
- Add DashboardController Live() SSE endpoint for /api/dashboard/live
- Add LiveUpdateService (in-memory pub/sub for SSE updates)
- Add TaskBridgeService (structured agent-to-backend bridge)
- Add nginx routing blocks for /api/bridge/ and /api/dashboard/live
- Add host-level nginx-nexus.conf blocks for bridge + live pass-through
- Update ServiceCollectionExtensions with DI registrations
- Update Dashboard.cs model with SSE-related DTOs
Verification after deploy:
curl https://nexus.noveria.net/api/bridge/health → 200 JSON
curl https://nexus.noveria.net/api/dashboard/live → 200 SSE stream
- Postgres memory: 256M→384M limits, 64M→96M reservations
- Added pg_resetwal -f pre-deploy step to recover from corrupt WAL
('PANIC: could not locate a valid checkpoint record' caused by
force-killed postgres during --force-recreate)
- Added data-checksums initdb arg for future corruption detection
- api→postgres and web→api depends_on: service_healthy→service_started
- Deploy wait loop: fail fast on unhealthy, wait on starting (180s)
- Added researcher/executor to ValidAssignees and frontend dropdowns
- Bao darf jetzt Status ändern (neben Iris), Sub-Agents weiterhin nicht
- CanEditContent für Inhaltsbearbeitung durch alle bekannten Caller
- Bao-Content-Änderungen triggern task_content_changed-Notification an Iris
- Bao-Status-Änderungen triggern task_status_changed-Notification an Iris
- Iris-Status-Änderungen triggern task_status_changed-Notification an Bao
- Neue WorkTask-Felder: IsAgentTask (bool), ExpectedFrom (string)
- Agent-Workflow-API: CreateAgentTask, WaitingTasks, AgentOverview
- Frontend: Agent-Task-Badge, Iris-Overview-Panel, isBao-Getter
- Login-Rate-Limiter mit strukturiertem JSON-Fehlermeldungs-Body
- Volume-Name: nexus-postgres → postgres-data (Standardisierung)
- [Authorize(Roles = "owner,admin")] statt nur owner – admin darf jetzt
ebenfalls User verwalten
- CreateUser erlaubt nur Rollen admin|user|viewer; owner ist blockiert
- UpdateUserRole erlaubt nur admin|user|viewer; owner kann weder gesetzt
noch überschrieben werden; admin darf andere admins nicht ändern
und sich nicht selbst herabstufen
- SettingsView: canManageUsers = role owner || admin statt nur owner
- UI-Dropdown zeigt nur admin|user|viewer (owner als Kommentar notiert)
- Remove duplicate @keyframes pulse-* from 3 component files (already in nexus-tokens.css)
- Rename AgentDetail → AgentDetailData in dashboard types to avoid collision with types/agent.ts
- Extract shared formatNumber/initials/formatTime to utils/format.ts
- Simplify FlowBoard: use agentStore modal/selection getters instead of duplicating local state
- Add error banner + empty state to IrisChat; add loading skeleton + error/empty states to TaskStrip
- Remove 105-line unused mockAgents array from useFlowLayout
- Reduce operations store fallbacks from hardcoded preview data to minimal safe defaults
- Update operations store tests to match lean fallback structure
- Net: -73 lines, cleaner imports, fewer magic strings
- useDashboardData: Mock-Daten durch API-Calls ersetzt
- fetchStatus/Agents/Operations/ChatMessages/Queue via /api/dashboard/*
- sendChatMessage via POST /api/dashboard/chat/send
- Polling: Status 5s, Chat 3s, Agents/Queue 10s
- Agent Catalog mit static Fields + API-Daten
- ChatPanel direkt mit sendChatMessage verdrahtet
- Build: 0 errors, vue-tsc + vite ✅