Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,14 @@
"Bash(git push --no-verify*)",
"Bash(gh repo delete*)",
"Bash(gh repo transfer*)",
"Bash(gh secret list*)",
"Bash(gh secret delete*)",
"Bash(gh secret set*)",
"Bash(gh gpg-key delete*)",
"Bash(az group delete*)",
"Bash(az vm delete*)",
"Bash(az keyvault delete*)",
"Bash(az keyvault secret list*)",
"Bash(az keyvault secret delete*)",
"Bash(az keyvault secret set*)",
"Bash(az storage account delete*)",
Expand Down
68 changes: 47 additions & 21 deletions AGENT_BACKLOG.md
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.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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
Verify each finding against the current code and only fix it if needed.

In `@AGENT_BACKLOG.md` at line 3, Update the top header sentence that currently
says "Phase 14 in progress" to mark Phase 14 as complete so it matches the
detailed section; specifically edit the header text that mentions "Phase 14 in
progress (6/6 core items done)" and change it to indicate Phase 14 is complete
(e.g., "Phase 14 complete (6/6 core items done)"), ensuring consistency with the
dedicated Phase 14 section below.


---

Expand Down Expand Up @@ -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
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Rename the final summary row.

49 / 12 / 37 is the overall rollup, not the "remaining" rollup. A label like Total or Overall would match the numbers shown.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@AGENT_BACKLOG.md` around lines 288 - 300, The final summary row label
"**Total remaining**" is misleading; update that table row in AGENT_BACKLOG.md
(the row with "**Total remaining | **49** | **12** | **37**") to a correct label
such as "**Total**" or "**Overall**" so it reflects the overall rollup values
(49 / 12 / 37); ensure the bold formatting and column values remain unchanged.


---

Expand Down Expand Up @@ -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

---

Expand Down Expand Up @@ -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.*
8 changes: 8 additions & 0 deletions src/ApiHost/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@
?? (builder.Environment.IsDevelopment()
? ["http://localhost:3000"]
: throw new InvalidOperationException("Cors:AllowedOrigins must be configured in non-development environments"));

if (origins.Length == 0)
{
origins = builder.Environment.IsDevelopment()
? ["http://localhost:3000"]
: throw new InvalidOperationException("Cors:AllowedOrigins must be configured and non-empty");
}

policy.WithOrigins(origins)
.AllowAnyHeader()
.AllowAnyMethod()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,10 @@ public async Task<ActionResult<OverrideResponse>> ApplyOverrideAsync([FromBody]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<ActionResult<SpectrumHistoryResponse>> GetSpectrumHistoryAsync(string dimension, CancellationToken cancellationToken = default)
{
ArgumentException.ThrowIfNullOrWhiteSpace(dimension);
if (string.IsNullOrWhiteSpace(dimension))
{
return BadRequest("Dimension is required and cannot be empty or whitespace.");
}

_logger.LogInformation("Retrieving spectrum history for dimension {Dimension}", Sanitize(dimension));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,19 +74,28 @@ public Task<BalanceResponse> GetBalanceAsync(BalanceRequest request, Cancellatio
Rationale = "Default initial position."
});

double value;
string rationale;
lock (state)
{
value = state.Value;
rationale = state.Rationale;
}

dimensions.Add(new SpectrumDimensionResult
{
Dimension = dimensionName,
Value = state.Value,
LowerBound = Math.Max(0.0, state.Value - 0.1),
UpperBound = Math.Min(1.0, state.Value + 0.1),
Rationale = state.Rationale
Value = value,
LowerBound = Math.Max(0.0, value - 0.1),
UpperBound = Math.Min(1.0, value + 0.1),
Rationale = rationale
});
}

var overallConfidence = _reflexionResults.IsEmpty
var reflexionSnapshot = _reflexionResults.ToArray();
var overallConfidence = reflexionSnapshot.Length == 0
? 0.5
: 1.0 - _reflexionResults.Count(r => r.IsHallucination) / (double)_reflexionResults.Count;
: 1.0 - reflexionSnapshot.Count(r => r.IsHallucination) / (double)reflexionSnapshot.Length;

_logger.LogInformation(
"Balance retrieved with {DimensionCount} dimensions, overall confidence {Confidence}",
Expand Down Expand Up @@ -123,11 +132,15 @@ public Task<OverrideResponse> ApplyOverrideAsync(OverrideRequest request, Cancel
Rationale = "Default initial position."
});

var oldValue = state.Value;
double oldValue;
var now = DateTimeOffset.UtcNow;

state.Value = request.NewValue;
state.Rationale = request.Rationale;
lock (state)
{
oldValue = state.Value;
state.Value = request.NewValue;
state.Rationale = request.Rationale;
}

// Add history entry
var historyEntries = _history.GetOrAdd(request.Dimension, _ => new List<SpectrumHistoryEntry>());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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 () =>
{
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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"); }
Expand All @@ -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 notice

Code scanning / CodeQL

Generic catch clause Note

Generic catch clause.

Copilot Autofix

AI 24 days ago

In general, the issue is fixed by not catching System.Exception blindly. Instead, catch and handle only the exception types you expect (for example, domain-specific exceptions or cancellation-related ones), and either let unexpected exceptions bubble up to global error handling middleware or handle them in a final catch after the specific ones. This preserves diagnostic information and allows more accurate HTTP status codes.

For this specific method, the minimal change without altering intended functionality is to:

  • Add a specific catch for OperationCanceledException (and possibly TaskCanceledException, depending on how cancellation is surfaced) so that request cancellations don’t get logged as orchestration failures and don’t return a misleading 500.
  • Keep a final catch (Exception ex) as a last-resort safety net, which maintains the current behavior for all other exceptions.

Concretely:

  • In src/BusinessApplications/AgentRegistry/Controllers/AgentController.cs, within ExecuteTask, split the existing catch (Exception ex) into two catch blocks:
    • catch (OperationCanceledException) that returns an appropriate status code (commonly 499 or 400/408; since 499 is non-standard and 400/408 are standard, we can choose 400 Bad Request to indicate client-initiated cancel, or 499 if your infra uses it. Given standard ASP.NET Core constants, 400 is safe) and avoids logging it as an error.
    • Followed by the existing catch (Exception ex) block unchanged, preserving the current logging and 500 "SERVICE_UNAVAILABLE" response for all other exceptions.

No new imports are needed, as OperationCanceledException is in System, which is already imported. The change is localized to the ExecuteTask method.

Suggested changeset 1
src/BusinessApplications/AgentRegistry/Controllers/AgentController.cs

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/BusinessApplications/AgentRegistry/Controllers/AgentController.cs b/src/BusinessApplications/AgentRegistry/Controllers/AgentController.cs
--- a/src/BusinessApplications/AgentRegistry/Controllers/AgentController.cs
+++ b/src/BusinessApplications/AgentRegistry/Controllers/AgentController.cs
@@ -196,6 +196,12 @@
                 var response = await _orchestrationPort.ExecuteTaskAsync(request, cancellationToken);
                 return Ok(response);
             }
+            catch (OperationCanceledException)
+            {
+                // The request was canceled (e.g., client disconnected or timeout); do not treat as an internal error.
+                return StatusCode(StatusCodes.Status400BadRequest,
+                    ErrorEnvelope.Create("REQUEST_CANCELLED", "The orchestration task was cancelled."));
+            }
             catch (Exception ex)
             {
                 _logger.LogError(ex, "Error executing orchestrated task.");
EOF
@@ -196,6 +196,12 @@
var response = await _orchestrationPort.ExecuteTaskAsync(request, cancellationToken);
return Ok(response);
}
catch (OperationCanceledException)
{
// The request was canceled (e.g., client disconnected or timeout); do not treat as an internal error.
return StatusCode(StatusCodes.Status400BadRequest,
ErrorEnvelope.Create("REQUEST_CANCELLED", "The orchestration task was cancelled."));
}
catch (Exception ex)
{
_logger.LogError(ex, "Error executing orchestrated task.");
Copilot is powered by AI and may make mistakes. Always verify output.
Comment on lines +188 to +204
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Let request cancellation escape instead of mapping it to 500.

This action now passes a CancellationToken, but catch (Exception) will also catch OperationCanceledException and return SERVICE_UNAVAILABLE. Client disconnects/timeouts will look like server faults.

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
Verify each finding against the current code and only fix it if needed.

In `@src/BusinessApplications/AgentRegistry/Controllers/AgentController.cs` around
lines 188 - 204, The current try/catch around
_orchestrationPort.ExecuteTaskAsync in the AgentController action swallows
OperationCanceledException and maps cancellations to a 500; add a specific catch
for OperationCanceledException (or TaskCanceledException) that rethrows (or lets
the exception propagate) before the generic catch so client
cancellations/timeouts are not translated into SERVICE_UNAVAILABLE; locate the
block containing ExecuteTaskAsync and replace the single generic catch with a
two-tiered catch: catch (OperationCanceledException) { throw; } followed by
catch (Exception ex) { ... } so cancellation escapes.

}

// --- Authority & Consent Endpoints ---
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -374,21 +374,23 @@ public interface IAgentRegistryPort
/// Registers a new agent in the system with compliance metadata.
/// </summary>
/// <param name="request">The registration request containing agent details and compliance claims.</param>
/// <param name="cancellationToken">Token to cancel the operation.</param>
/// <returns>The registered agent with its assigned ID and initial compliance status.</returns>
/// <remarks>
/// This method performs initial validation of the agent's compliance claims against
/// the specified regulatory frameworks. For a full compliance verification, use
/// <see cref="VerifyAgentComplianceAsync"/> after registration.
/// </remarks>
Task<Agent> RegisterAgentAsync(AgentRegistrationRequest request);
Task<Agent> RegisterAgentAsync(AgentRegistrationRequest request, CancellationToken cancellationToken = default);

/// <summary>
/// Retrieves an agent by its ID.
/// </summary>
/// <param name="agentId">The ID of the agent to retrieve.</param>
/// <param name="tenantId">The ID of the tenant that owns the agent.</param>
/// <param name="cancellationToken">Token to cancel the operation.</param>
/// <returns>The agent if found; otherwise, null.</returns>
Task<Agent> GetAgentByIdAsync(Guid agentId, string tenantId);
Task<Agent> GetAgentByIdAsync(Guid agentId, string tenantId, CancellationToken cancellationToken = default);

/// <summary>
/// Updates an existing agent's definition and metadata.
Expand All @@ -409,8 +411,9 @@ public interface IAgentRegistryPort
/// <param name="tenantId">The ID of the tenant that owns the agent.</param>
/// <param name="deactivatedBy">The ID of the user performing the deactivation.</param>
/// <param name="reason">The reason for deactivation.</param>
/// <param name="cancellationToken">Token to cancel the operation.</param>
/// <returns>True if the agent was successfully deactivated; otherwise, false.</returns>
Task<bool> DeactivateAgentAsync(Guid agentId, string tenantId, string deactivatedBy, string reason);
Task<bool> DeactivateAgentAsync(Guid agentId, string tenantId, string deactivatedBy, string reason, CancellationToken cancellationToken = default);

/// <summary>
/// Retrieves agents of a specific type.
Expand Down
Loading
Loading