-
Notifications
You must be signed in to change notification settings - Fork 0
Phase 14: Core UX — stores, navigation, routing, real API #352
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
28bf4cb
872e1fa
39c021b
be77d0c
e663ad7
a7b3fee
f24331d
50b62c2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,6 @@ | ||
| # Cognitive Mesh — Agent Backlog | ||
|
|
||
| > Prioritized, actionable work items. Backend is 100% complete (70/70 items). Frontend Phase 13 complete (4/4). Remaining 35 frontend items + 6 DevOps evaluation tickets across Phases 14–18. | ||
| > Prioritized, actionable work items. Backend is 100% complete (70/70 items). Frontend Phase 13 complete (4/4), Phase 14 in progress (6/6 core items done). Remaining 27 frontend items + 4 Phase 14b items + 6 DevOps evaluation tickets across Phases 14b–18. | ||
|
|
||
| --- | ||
|
|
||
|
|
@@ -288,15 +288,16 @@ | |
| | Priority | Total | Done | Remaining | | ||
| |----------|-------|------|-----------| | ||
| | P0-CRITICAL (frontend) | 4 | 4 | **0** | | ||
| | P1-HIGH (frontend infra) | 6 | 0 | **6** | | ||
| | P1-HIGH (frontend infra) | 6 | 6 | **0** | | ||
| | P1-HIGH (UI library — 14b) | 4 | 0 | **4** | | ||
| | P1-HIGH (widget PRDs) | 5 | 0 | **5** | | ||
| | P2-MEDIUM (widgets + nav) | 8 | 0 | **8** | | ||
| | P2-MEDIUM (widgets + nav) | 8 | 2 | **6** | | ||
| | P2-MEDIUM (security) | 1 | 0 | **1** | | ||
| | P2-MEDIUM (CI/CD) | 5 | 0 | **5** | | ||
| | P2-MEDIUM (testing) | 5 | 0 | **5** | | ||
| | P3-LOW (features) | 5 | 0 | **5** | | ||
| | DEVOPS (evaluation) | 6 | 0 | **6** | | ||
| | **Total remaining** | **45** | **4** | **41** | | ||
| | **Total remaining** | **49** | **12** | **37** | | ||
|
Comment on lines
288
to
+300
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rename the final summary row.
🤖 Prompt for AI Agents |
||
|
|
||
| --- | ||
|
|
||
|
|
@@ -351,33 +352,58 @@ | |
|
|
||
| --- | ||
|
|
||
| ### Phase 14 — Core UX (NEXT) | ||
| ### Phase 14 — Core UX ✓ COMPLETE | ||
|
|
||
| **Items:** FE-002, FE-003, FE-005, FE-007, FE-021, FE-022 | ||
| **Goal:** Replace all mocked data with real API calls, add real-time updates, state management, and multi-page navigation. | ||
| **Status:** Complete. All 6 core items implemented. | ||
|
|
||
| | Item | Description | Key Work | | ||
| | ---- | ----------- | -------- | | ||
| | FE-002 | Replace mocked API with real backend | Remove `DashboardAPI` singleton + `Math.random()` calls. Wire all 13 controllers through generated client. Implement request/response mapping for each endpoint. | | ||
| | FE-003 | SignalR real-time client | Install `@microsoft/signalr`. Connect to `CognitiveMeshHub`. Replace polling with subscriptions (`JoinDashboardGroup`, `SubscribeToAgent`). Exponential backoff reconnection. Connection state indicator. | | ||
| | FE-005 | Zustand state management | Create stores: `useAuthStore`, `useAgentStore`, `useDashboardStore`, `useNotificationStore`, `usePreferencesStore`. Replace scattered `useState`. Add persistence middleware for preferences. | | ||
| | FE-007 | Loading states + skeletons | Skeleton components for dashboard panels, agent lists, metrics cards. Suspense boundaries per route. Optimistic updates for mutations. | | ||
| | FE-021 | Multi-page routing | Create route dirs: `/dashboard`, `/settings`, `/agents`, `/compliance`, `/analytics`, `/marketplace`. Add `loading.tsx`, `error.tsx`, `layout.tsx` per route group. Parallel route loading. | | ||
| | FE-022 | Navigation component | Sidebar with collapsible sections, breadcrumbs, mobile hamburger menu. Active route highlighting. Responsive drawer (< 768px). Keyboard navigation. | | ||
| | Item | Description | Status | | ||
| | ---- | ----------- | ------ | | ||
| | FE-002 | Replace mocked API with real backend | ✓ Done — `DashboardAPI` singleton + `useDashboardData` hook deleted. Root `/` redirects to `/dashboard`. Stores fetch from real backend. | | ||
| | FE-003 | SignalR real-time client | ✓ Done — `useSignalR` hook with `@microsoft/signalr@10.0.0`. HubConnection to `/hubs/cognitive-mesh`, exponential backoff (1s→30s), `ConnectionIndicator` in TopBar. | | ||
| | FE-005 | Zustand state management | ✓ Done — 5 stores: `useAuthStore`, `useAgentStore`, `useDashboardStore`, `useNotificationStore`, `usePreferencesStore`. Persistence middleware for preferences. | | ||
| | FE-007 | Loading states + skeletons | ✓ Done — `Skeleton`, `SkeletonCard`, `SkeletonTable`, `SkeletonMetric`, `SkeletonDashboard`. `loading.tsx` and `error.tsx` per route group. | | ||
| | FE-021 | Multi-page routing | ✓ Done — `(app)` route group with 6 routes: `/dashboard`, `/agents`, `/settings`, `/analytics`, `/compliance`, `/marketplace`. Shared layout with `ProtectedRoute`. | | ||
| | FE-022 | Navigation component | ✓ Done — `Sidebar` (collapsible), `TopBar` (breadcrumbs + notification bell), `MobileMenu` (responsive drawer), `Breadcrumbs`, `ConnectionIndicator`. Active route highlighting via `usePathname()`. | | ||
|
|
||
| **Also in Phase 14 (deferred from Phase 13):** | ||
| **Deferred to later phases (require backend work):** | ||
|
|
||
| - httpOnly cookie for refresh token (requires backend `/api/auth/refresh` set-cookie endpoint) | ||
| - Full JWT validation in middleware (requires JWKS endpoint or shared secret config) | ||
| - Backend auth middleware in `Program.cs` (`AddAuthentication`/`AddAuthorization`) | ||
|
|
||
| #### Gate → Phase 14b | ||
|
|
||
| - [x] DashboardAPI mock removed, stores fetching from real backend | ||
| - [x] SignalR hook created with reconnection logic and connection indicator | ||
| - [x] Navigation between all 6 routes works with loading/error states | ||
| - [x] Zustand stores hydrating from API on mount | ||
| - [ ] Storybook stories for skeleton and navigation components (deferred to Phase 15) | ||
|
|
||
| --- | ||
|
|
||
| ### Phase 14b — UI Component Library Integration (NEXT) | ||
|
|
||
| **Items:** FEUI-001, FEUI-002, FEUI-003, FEUI-004 | ||
| **Goal:** Merge [CognitiveMeshUI](https://github.com/phoenixvc/CognitiveMeshUI) component library into this repo. Fix broken shadcn/ui components, import design tokens, optionally wire Storybook. | ||
|
|
||
| | Item | Description | Key Work | | ||
| | ---- | ----------- | -------- | | ||
| | FEUI-001 | Fix or replace broken shadcn/ui components | Existing `components/ui/` has ~50 shadcn components but missing radix deps (TS errors). Either install missing `@radix-ui/*` packages or replace with CognitiveMeshUI's working copies. | | ||
| | FEUI-002 | Import design tokens | Bring over `tokens/` directory (colors, typography, spacing, dimensions) from CognitiveMeshUI. Integrate with Style Dictionary to generate CSS custom properties and Tailwind theme values. | | ||
| | FEUI-003 | Wire Storybook | Import `.storybook/` config from CognitiveMeshUI. Add stories for navigation, skeleton, and shadcn/ui components. Enables visual testing (Phase 17 FETEST-004). | | ||
| | FEUI-004 | Clean up duplicate/dead component code | Remove broken component copies, unused Bridge view components from old root `page.tsx`, and any redundant type definitions. Consolidate icon imports. | | ||
|
|
||
| **Source:** [phoenixvc/CognitiveMeshUI](https://github.com/phoenixvc/CognitiveMeshUI) — Next.js component library with shadcn/ui, design tokens (Style Dictionary), Storybook, Framer Motion. Last updated Nov 2025. | ||
|
|
||
| **Approach:** Direct merge (not monorepo workspaces). CognitiveMeshUI is stale and this repo is the single deployable artifact. | ||
|
|
||
| #### Gate → Phase 15 | ||
|
|
||
| - [ ] All 13 backend controllers callable from frontend (no mocked data remains) | ||
| - [ ] SignalR connection established and reconnecting properly | ||
| - [ ] Navigation between all 6 routes works with loading/error states | ||
| - [ ] Zustand stores hydrating from API on mount | ||
| - [ ] Storybook stories exist for skeleton and navigation components | ||
| - [ ] `npx tsc --noEmit` passes with zero errors in `components/ui/` | ||
| - [ ] Design tokens generating CSS custom properties | ||
| - [ ] Storybook running locally (`npm run storybook`) | ||
| - [ ] No duplicate type definitions across stores/components | ||
|
|
||
| --- | ||
|
|
||
|
|
@@ -488,4 +514,4 @@ | |
|
|
||
| --- | ||
|
|
||
| *Updated: 2026-03-10 | Backend 100% complete (70/70). Frontend Phase 13 complete (4/4). Remaining: 35 frontend items + 6 DevOps evaluations across Phases 14–18.* | ||
| *Updated: 2026-03-10 | Backend 100% complete (70/70). Frontend Phase 13 ✓, Phase 14 ✓ (6/6). Phase 14b next (CognitiveMeshUI integration). Remaining: 37 items across Phases 14b–18.* | ||
| Original file line number | Diff line number | Diff line change | |||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -64,7 +64,7 @@ | ||||||||||||||||||||||||||||||||||||||||
| /// </summary> | |||||||||||||||||||||||||||||||||||||||||
| [HttpPost("registry")] | |||||||||||||||||||||||||||||||||||||||||
| [Authorize(Policy = "AdminAccess")] | |||||||||||||||||||||||||||||||||||||||||
| [ProducesResponseType(typeof(AgentRegistrationRequest), StatusCodes.Status201Created)] | |||||||||||||||||||||||||||||||||||||||||
| [ProducesResponseType(typeof(Agent), StatusCodes.Status201Created)] | |||||||||||||||||||||||||||||||||||||||||
| [ProducesResponseType(typeof(object), StatusCodes.Status400BadRequest)] | |||||||||||||||||||||||||||||||||||||||||
| [ProducesResponseType(StatusCodes.Status500InternalServerError)] | |||||||||||||||||||||||||||||||||||||||||
| public async Task<IActionResult> RegisterAgent([FromBody] AgentRegistrationRequest request, CancellationToken cancellationToken = default) | |||||||||||||||||||||||||||||||||||||||||
|
|
@@ -76,9 +76,13 @@ | ||||||||||||||||||||||||||||||||||||||||
| return BadRequest(ErrorEnvelope.InvalidPayload("Agent type is required.")); | |||||||||||||||||||||||||||||||||||||||||
| } | |||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||
| var agent = await _registryPort.RegisterAgentAsync(request); | |||||||||||||||||||||||||||||||||||||||||
| var agent = await _registryPort.RegisterAgentAsync(request, cancellationToken); | |||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||
| // audit + notify (best-effort, exceptions logged but not propagated) | |||||||||||||||||||||||||||||||||||||||||
| // Audit + notify (best-effort, fire-and-forget). | |||||||||||||||||||||||||||||||||||||||||
| // NOTE: Task.Run is used here as a lightweight fire-and-forget mechanism. | |||||||||||||||||||||||||||||||||||||||||
| // These operations are non-critical; failures are logged but do not affect the | |||||||||||||||||||||||||||||||||||||||||
| // response. A background queue (e.g., IBackgroundTaskQueue / Channels) would be | |||||||||||||||||||||||||||||||||||||||||
| // more robust but is not warranted at this stage. | |||||||||||||||||||||||||||||||||||||||||
| var actorName = User?.Identity?.Name ?? "system"; | |||||||||||||||||||||||||||||||||||||||||
| _ = Task.Run(async () => | |||||||||||||||||||||||||||||||||||||||||
| { | |||||||||||||||||||||||||||||||||||||||||
|
|
@@ -115,9 +119,9 @@ | ||||||||||||||||||||||||||||||||||||||||
| try | |||||||||||||||||||||||||||||||||||||||||
| { | |||||||||||||||||||||||||||||||||||||||||
| var (tenantId, _) = GetAuthContextFromClaims(); | |||||||||||||||||||||||||||||||||||||||||
| if (tenantId == null) return Unauthorized("Tenant ID is missing."); | |||||||||||||||||||||||||||||||||||||||||
| if (tenantId == null) return Unauthorized(ErrorEnvelope.Create("UNAUTHORIZED", "Tenant ID is missing.")); | |||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||
| var agent = await _registryPort.GetAgentByIdAsync(agentId, tenantId); | |||||||||||||||||||||||||||||||||||||||||
| var agent = await _registryPort.GetAgentByIdAsync(agentId, tenantId, cancellationToken); | |||||||||||||||||||||||||||||||||||||||||
| if (agent == null) | |||||||||||||||||||||||||||||||||||||||||
| { | |||||||||||||||||||||||||||||||||||||||||
| _logger.LogWarning("Agent with ID '{AgentId}' not found.", agentId); | |||||||||||||||||||||||||||||||||||||||||
|
|
@@ -146,14 +150,15 @@ | ||||||||||||||||||||||||||||||||||||||||
| try | |||||||||||||||||||||||||||||||||||||||||
| { | |||||||||||||||||||||||||||||||||||||||||
| var (tenantId, userId) = GetAuthContextFromClaims(); | |||||||||||||||||||||||||||||||||||||||||
| if (tenantId == null) return Unauthorized("Tenant ID is missing."); | |||||||||||||||||||||||||||||||||||||||||
| if (tenantId == null) return Unauthorized(ErrorEnvelope.Create("UNAUTHORIZED", "Tenant ID is missing.")); | |||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||
| var success = await _registryPort.DeactivateAgentAsync(agentId, tenantId, userId ?? "system", reason ?? "Manual deactivation"); | |||||||||||||||||||||||||||||||||||||||||
| var success = await _registryPort.DeactivateAgentAsync(agentId, tenantId, userId ?? "system", reason ?? "Manual deactivation", cancellationToken); | |||||||||||||||||||||||||||||||||||||||||
| if (!success) | |||||||||||||||||||||||||||||||||||||||||
| { | |||||||||||||||||||||||||||||||||||||||||
| return NotFound(ErrorEnvelope.Create("AGENT_NOT_FOUND", $"Agent with ID '{agentId}' was not found.")); | |||||||||||||||||||||||||||||||||||||||||
| } | |||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||
| // Fire-and-forget audit (see RegisterAgent for rationale on Task.Run usage). | |||||||||||||||||||||||||||||||||||||||||
| _ = Task.Run(async () => | |||||||||||||||||||||||||||||||||||||||||
| { | |||||||||||||||||||||||||||||||||||||||||
| try { await _audit.LogAgentRetiredAsync(agentId, User?.Identity?.Name ?? "system", reason ?? "Manual deactivation"); } | |||||||||||||||||||||||||||||||||||||||||
|
|
@@ -180,14 +185,23 @@ | ||||||||||||||||||||||||||||||||||||||||
| [ProducesResponseType(typeof(AgentExecutionResponse), StatusCodes.Status200OK)] | |||||||||||||||||||||||||||||||||||||||||
| public async Task<IActionResult> ExecuteTask([FromBody] AgentExecutionRequest request, CancellationToken cancellationToken = default) | |||||||||||||||||||||||||||||||||||||||||
| { | |||||||||||||||||||||||||||||||||||||||||
| var (tenantId, userId) = GetAuthContextFromClaims(); | |||||||||||||||||||||||||||||||||||||||||
| if (tenantId == null) return Unauthorized("Tenant ID is missing."); | |||||||||||||||||||||||||||||||||||||||||
| try | |||||||||||||||||||||||||||||||||||||||||
| { | |||||||||||||||||||||||||||||||||||||||||
| var (tenantId, userId) = GetAuthContextFromClaims(); | |||||||||||||||||||||||||||||||||||||||||
| if (tenantId == null) return Unauthorized(ErrorEnvelope.Create("UNAUTHORIZED", "Tenant ID is missing.")); | |||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||
| request.TenantId = tenantId; | |||||||||||||||||||||||||||||||||||||||||
| request.RequestingUserId = userId ?? string.Empty; | |||||||||||||||||||||||||||||||||||||||||
| request.TenantId = tenantId; | |||||||||||||||||||||||||||||||||||||||||
| request.RequestingUserId = userId ?? string.Empty; | |||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||
| var response = await _orchestrationPort.ExecuteTaskAsync(request); | |||||||||||||||||||||||||||||||||||||||||
| return Ok(response); | |||||||||||||||||||||||||||||||||||||||||
| var response = await _orchestrationPort.ExecuteTaskAsync(request, cancellationToken); | |||||||||||||||||||||||||||||||||||||||||
| return Ok(response); | |||||||||||||||||||||||||||||||||||||||||
| } | |||||||||||||||||||||||||||||||||||||||||
| catch (Exception ex) | |||||||||||||||||||||||||||||||||||||||||
| { | |||||||||||||||||||||||||||||||||||||||||
| _logger.LogError(ex, "Error executing orchestrated task."); | |||||||||||||||||||||||||||||||||||||||||
| return StatusCode(StatusCodes.Status500InternalServerError, | |||||||||||||||||||||||||||||||||||||||||
| ErrorEnvelope.Create("SERVICE_UNAVAILABLE", "Orchestration service is unavailable.")); | |||||||||||||||||||||||||||||||||||||||||
| } | |||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+199
to
+204
Check noticeCode scanning / CodeQL Generic catch clause Note
Generic catch clause.
Copilot AutofixAI 24 days ago In general, the issue is fixed by not catching For this specific method, the minimal change without altering intended functionality is to:
Concretely:
No new imports are needed, as
Suggested changeset
1
src/BusinessApplications/AgentRegistry/Controllers/AgentController.cs
Copilot is powered by AI and may make mistakes. Always verify output.
Refresh and try again.
Comment on lines
+188
to
+204
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let request cancellation escape instead of mapping it to 500. This action now passes a Suggested fix try
{
var (tenantId, userId) = GetAuthContextFromClaims();
if (tenantId == null) return Unauthorized(ErrorEnvelope.Create("UNAUTHORIZED", "Tenant ID is missing."));
request.TenantId = tenantId;
request.RequestingUserId = userId ?? string.Empty;
var response = await _orchestrationPort.ExecuteTaskAsync(request, cancellationToken);
return Ok(response);
}
+ catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
+ {
+ throw;
+ }
catch (Exception ex)
{
_logger.LogError(ex, "Error executing orchestrated task.");
return StatusCode(StatusCodes.Status500InternalServerError,
ErrorEnvelope.Create("SERVICE_UNAVAILABLE", "Orchestration service is unavailable."));
}🤖 Prompt for AI Agents |
|||||||||||||||||||||||||||||||||||||||||
| } | |||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||
| // --- Authority & Consent Endpoints --- | |||||||||||||||||||||||||||||||||||||||||
|
|
@@ -201,7 +215,7 @@ | ||||||||||||||||||||||||||||||||||||||||
| public async Task<IActionResult> ConfigureAuthority(string agentType, [FromBody] RegistryAuthorityScope scope, CancellationToken cancellationToken = default) | |||||||||||||||||||||||||||||||||||||||||
| { | |||||||||||||||||||||||||||||||||||||||||
| var (tenantId, userId) = GetAuthContextFromClaims(); | |||||||||||||||||||||||||||||||||||||||||
| if (tenantId == null) return Unauthorized("Tenant ID is missing."); | |||||||||||||||||||||||||||||||||||||||||
| if (tenantId == null) return Unauthorized(ErrorEnvelope.Create("UNAUTHORIZED", "Tenant ID is missing.")); | |||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||
| // Look up agent by type to resolve the agentId | |||||||||||||||||||||||||||||||||||||||||
| var agents = await _registryPort.GetAgentsByTypeAsync(agentType, tenantId); | |||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mark Phase 14 as complete in the header.
Line 3 still says Phase 14 is "in progress" even though the same sentence says all 6/6 core items are done and the dedicated Phase 14 section below is marked complete.
🤖 Prompt for AI Agents