feat: ship agent-first mission control v0.2.57
This commit is contained in:
@@ -0,0 +1,347 @@
|
||||
# Nexus Agent-first and Performance V2 — implementation and acceptance
|
||||
|
||||
**Milestone date:** 2026-07-30
|
||||
**Final documentation review:** 2026-07-30
|
||||
**Scope:** local combined working tree; no commit, push, deployment or VPS write
|
||||
**Outcome:** core architecture implemented locally; production provisioning,
|
||||
live OpenClaw acceptance and measured load gates remain blocked or unproven
|
||||
|
||||
## Executive verdict
|
||||
|
||||
Nexus now has a coherent durable path for owner-approved agent proposals,
|
||||
PostgreSQL-backed workflow events, typed frontend contracts and an efficient
|
||||
Task Board read model. The implementation materially reduces duplicate
|
||||
requests and broad refreshes for the migrated domains.
|
||||
|
||||
This is not a production-readiness or performance-budget claim:
|
||||
|
||||
- productive OpenClaw writes remain intentionally disabled until a pinned
|
||||
OpenClaw release officially supports an external Nexus or generic operator
|
||||
Client ID;
|
||||
- no real OpenClaw pairing, agent creation or
|
||||
`Nexus -> OpenClaw -> OpenAI -> Nexus` write flow was performed;
|
||||
- the controlled Playwright server is synthetic;
|
||||
- Docker/Testcontainers, Toxiproxy, k6, Promptfoo live evaluation,
|
||||
`EXPLAIN (ANALYZE, BUFFERS)` and the browser p95 budget require separate,
|
||||
isolated acceptance runs.
|
||||
|
||||
## Implemented architecture
|
||||
|
||||
### One typed API contract
|
||||
|
||||
- ASP.NET Core emits OpenAPI 3.1 to
|
||||
`backend/openapi/Nexus.Api.json`.
|
||||
- `openapi-typescript` generates
|
||||
`frontend/src/api/generated/schema.d.ts`; `openapi-fetch` is the typed
|
||||
transport for migrated endpoints.
|
||||
- CI regenerates the schema and rejects a diff.
|
||||
- `ProblemDetails` and `ValidationProblemDetails` are the common HTTP error
|
||||
shape. The server attaches `traceId`; the frontend uses one error adapter.
|
||||
- `EntityRefDto`, `OperationResultDto`, `DomainEventDto` and
|
||||
`TaskBoardPageDto` are explicit public contracts rather than view-model
|
||||
inference.
|
||||
|
||||
### Server-state migration
|
||||
|
||||
TanStack Vue Query owns the migrated server state:
|
||||
|
||||
- Task Board;
|
||||
- projects and project-scoped tasks;
|
||||
- agent proposals and create options;
|
||||
- Nexus activity and notifications;
|
||||
- OpenClaw overview, agents, runs, cron and models;
|
||||
- agent details and configuration reads; and
|
||||
- owner-only Memory, Docs, Incidents and Security reads.
|
||||
|
||||
The Query-key factory makes multiple consumers share one request. Background
|
||||
refresh keeps previous visible data. Pinia still owns authentication, command
|
||||
and Iris modal state, setup/wizard workflow state, local drafts and shared
|
||||
mutation facades; it does not duplicate canonical runtime collections.
|
||||
|
||||
After route and error-state parity checks, the legacy operations/task/
|
||||
notification/dashboard server-state stores, static agent inventory and
|
||||
mappers, duplicate `liveSync.ts`/`live-sync.ts` modules and the old generic
|
||||
live-service reader were removed. The backend compatibility endpoints remain
|
||||
temporarily available, but no active frontend consumer uses them as its
|
||||
canonical cache.
|
||||
|
||||
Memory, Docs and Incidents no longer use host workspace mounts. Their
|
||||
owner-only services read OpenClaw workspace content through confined RPC,
|
||||
return source-agent and workspace-path provenance and expose no arbitrary
|
||||
workspace write path.
|
||||
|
||||
### PostgreSQL workflow and event backbone
|
||||
|
||||
Migration `20260730224500_AddAgentProvisioningAndBoardIndexes` adds:
|
||||
|
||||
- `AgentProposals`;
|
||||
- `AgentProvisionRequests`;
|
||||
- `OperationClaims`; and
|
||||
- `OutboxEvents`.
|
||||
|
||||
For migrated Nexus-owned mutations, domain state and outbox event are stored in
|
||||
the same EF transaction. The registered OpenClaw mutation claim store also uses
|
||||
PostgreSQL `OperationClaims` plus a database transaction and lock; the former
|
||||
JSONL path is exposed only for discovery of an immutable legacy archive and is
|
||||
not read or appended. The background worker:
|
||||
|
||||
- leases pending work with `FOR UPDATE SKIP LOCKED`;
|
||||
- uses a bounded single-process wake-up channel plus database polling for
|
||||
restart recovery;
|
||||
- publishes content-minimized events to subscriber queues of 64 entries;
|
||||
- retains at least 24 hours and at least 10,000 global sequences; and
|
||||
- exposes at most 512 replay deltas per reconnect.
|
||||
|
||||
`GET /api/v1/events?afterSequence=` resumes by global sequence. A missing or
|
||||
expired range produces `resync_required`; the browser refreshes affected Query
|
||||
domains rather than silently accepting a gap. The existing OpenClaw event
|
||||
projection remains a separate sanitized Runtime adapter during migration.
|
||||
|
||||
### Authenticated SSE hub
|
||||
|
||||
The frontend uses one fetch-based `AuthenticatedSseHub` with:
|
||||
|
||||
- bearer authentication and the existing refresh path;
|
||||
- `eventsource-parser`;
|
||||
- abort and subscriber lifecycle;
|
||||
- heartbeat detection;
|
||||
- jittered reconnect; and
|
||||
- a 256 KiB maximum parser buffer.
|
||||
|
||||
Task events normally reconcile one card through
|
||||
`GET /api/v1/tasks/{id}/board-card`. Other events batch small Query-domain
|
||||
invalidations. The Dashboard legacy SSE path was hardened for cursor
|
||||
continuation and cancellation but remains a compatibility surface.
|
||||
|
||||
## Agent proposal and provisioning flow
|
||||
|
||||
### Shared flow
|
||||
|
||||
The manual `/agents/new` UI and Iris use the same durable service:
|
||||
|
||||
```text
|
||||
local form draft -> awaiting_approval -> provisioning
|
||||
-> ready | partial | failed | in_doubt
|
||||
-> rejected
|
||||
```
|
||||
|
||||
New APIs:
|
||||
|
||||
- `GET /api/v1/openclaw/agents/create-options`
|
||||
- `GET|POST /api/v1/openclaw/agent-proposals`
|
||||
- `GET /api/v1/openclaw/agent-proposals/{proposalId}`
|
||||
- `POST /api/v1/openclaw/agent-proposals/{proposalId}/approve`
|
||||
- `POST /api/v1/openclaw/agent-proposals/{proposalId}/reject`
|
||||
- `POST /api/v1/openclaw/agent-proposals/{proposalId}/retry`
|
||||
|
||||
Proposal lists use a stable `(CreatedAt, Id)` keyset cursor, including equal
|
||||
timestamps. Owner mutations require an Idempotency Key, correlation ID,
|
||||
optimistic proposal revision and authenticated actor.
|
||||
|
||||
### Iris boundary
|
||||
|
||||
Iris receives only:
|
||||
|
||||
- `nexus_propose_agent`; and
|
||||
- `nexus_get_agent_proposal`.
|
||||
|
||||
Both MCP tools return structured content and stable state/error information.
|
||||
They cannot approve a proposal or call OpenClaw. Tool annotations describe
|
||||
read-only, destructive, idempotent and open-world behavior but are not used as
|
||||
authorization.
|
||||
|
||||
### Provisioning safety
|
||||
|
||||
After explicit owner approval, Nexus rechecks:
|
||||
|
||||
- local `ManagementEnabled`;
|
||||
- official external Client-ID support;
|
||||
- normalized endpoint and TLS trust;
|
||||
- current capability hash and required advertised methods;
|
||||
- `operator.admin`;
|
||||
- proposal revision; and
|
||||
- idempotency claim.
|
||||
|
||||
The workspace root is server-controlled and checked against live OpenClaw
|
||||
configuration. Nexus calls `agents.create` at most once. After a possible
|
||||
dispatch timeout it records `in_doubt` and permits only read-only inventory
|
||||
reconciliation before another action. A successful create is not `ready` until
|
||||
`agents.list` confirms it. Approved standard files are written through
|
||||
`agents.files.set` and read back; a later file failure becomes `partial` and
|
||||
does not trigger automatic deletion.
|
||||
|
||||
The production button remains disabled because OpenClaw `2026.7.1` does not
|
||||
register the required external Nexus/generic operator identity. Nexus does not
|
||||
impersonate CLI, Control UI or `gateway-client/backend`.
|
||||
|
||||
## Task Board V2
|
||||
|
||||
`GET /api/v1/tasks/board?doneLimit=50&doneCursor=` returns:
|
||||
|
||||
- every non-Done card on the initial page;
|
||||
- the newest 50 Done cards by default;
|
||||
- an opaque `(UpdatedAt, Id)` keyset cursor for further Done pages; and
|
||||
- a stable board revision.
|
||||
|
||||
The repository uses `AsNoTracking` and direct DTO projection. Child counts and
|
||||
latest activity are correlated scalar projections rather than N+1 reads. The
|
||||
initial path uses at most three SQL statements; a Done continuation skips the
|
||||
active-card query and uses two. The migration adds state, partial Done,
|
||||
activity, child and agent-workflow indexes.
|
||||
|
||||
The Vue client uses `useInfiniteQuery`, deduplicates Done cards and keeps all
|
||||
active columns mounted for drag-and-drop. Moves are optimistic and roll back on
|
||||
failure. Create, update, move and domain events fetch only the affected
|
||||
`board-card`; full invalidation is a recovery path. Dashboard, sidebar,
|
||||
Command Palette and Task Board use the same Query key instead of parallel board
|
||||
loads.
|
||||
|
||||
No claim is made that the p95 budgets are met until the recorded
|
||||
1,000-task/10,000-activity dataset, k6 run, SQL trace/plans and repeated browser
|
||||
timing run have been executed.
|
||||
|
||||
## Cross-page result navigation
|
||||
|
||||
- `EntityRefDto` contains type, ID and optional label but no backend-generated
|
||||
URL.
|
||||
- The frontend resolver maps agent, proposal, project, task, run, cron,
|
||||
incident, document, notification and event-stream references to registered
|
||||
routes.
|
||||
- Iris and proposal mutations can show `OperationResultCard` with primary and
|
||||
affected entities plus operation/trace metadata.
|
||||
- Projects now have a real `/projects` index and
|
||||
`GET /api/v1/projects/{id}/tasks`; Project Detail no longer loads unrelated
|
||||
tasks.
|
||||
- Activity and Notifications use typed, authenticated domain queries and
|
||||
navigate to available related entities.
|
||||
|
||||
The 2026-07-31
|
||||
[structured operation-results follow-up](../../2026-07-31/operation-results-deep-links/IMPLEMENTATION_AND_ACCEPTANCE.md)
|
||||
closed the remaining task, project, notification, cron, config, approval,
|
||||
session and agent-file mutation gap. A global result tray now exposes the
|
||||
typed references and the addressed target surfaces consume their deep-link
|
||||
selection.
|
||||
|
||||
## Resilience and observability
|
||||
|
||||
- `Microsoft.Extensions.Http.Resilience` protects safe OpenClaw reads with a
|
||||
10-second attempt timeout, 30-second total budget, bounded jittered retries
|
||||
and a circuit breaker.
|
||||
- Management mutations and Chat/Run have no automatic retry; Gateway
|
||||
WebSocket reconnect remains inside the connector.
|
||||
- OpenTelemetry instruments ASP.NET Core, HttpClient, Npgsql, Task Board,
|
||||
Gateway RPC, proposals/provisioning, outbox and SSE-related metrics.
|
||||
- OTLP export is opt-in through configuration; no extra production container
|
||||
is introduced.
|
||||
- `web-vitals` reports an allow-list of metric, value, rating, route name,
|
||||
build version, live mode and correlation ID to
|
||||
`POST /api/v1/telemetry/browser`.
|
||||
- Redaction removes URL queries/full URLs, SQL text, exception messages/stacks
|
||||
and content-bearing attributes. Prompts, chat text, Markdown, tool arguments,
|
||||
credentials, headers and entity names are not telemetry.
|
||||
|
||||
## Dependency decisions
|
||||
|
||||
Implemented and pinned:
|
||||
|
||||
- `@tanstack/vue-query` `5.101.4`
|
||||
- `eventsource-parser` `3.1.0`
|
||||
- `openapi-fetch` `0.17.0`
|
||||
- `openapi-typescript` `7.13.0`
|
||||
- `web-vitals` `5.3.0`
|
||||
- Playwright `1.62.0`
|
||||
- Testcontainers PostgreSQL/Toxiproxy `4.13.0` in the test project only
|
||||
- OpenTelemetry `1.17.0`
|
||||
- `Microsoft.Extensions.Http.Resilience` `10.0.0`
|
||||
|
||||
`ModelContextProtocol.AspNetCore` remains pinned to `1.4.1`. The
|
||||
`scripts/qa/test-mcp2-compatibility.ps1` candidate mode copies the project to a
|
||||
temporary directory and may probe `2.0.0`, but it cannot open the upgrade gate
|
||||
without isolated live OpenClaw negotiation. The local isolated `2.0.0`
|
||||
compile/test probe is green; production deliberately remains on `1.4.1`.
|
||||
|
||||
Not introduced: Redis, NATS, Kafka, RabbitMQ, Temporal, Hangfire, Quartz,
|
||||
GraphQL, SignalR, RxJS, direct OpenAI Agents/Responses orchestration, OPA,
|
||||
OpenFGA, pgvector or a second production service.
|
||||
|
||||
## Automated verification
|
||||
|
||||
The following non-live checks were completed on 2026-07-30:
|
||||
|
||||
| Gate | Result |
|
||||
|---|---|
|
||||
| .NET 10 / MCP `1.4.1` baseline | **Passed: 360; failed: 0; skipped: 5; total: 365** |
|
||||
| Isolated MCP `2.0.0` candidate copy | **Passed: 360; failed: 0; skipped: 5**; compatibility probe only |
|
||||
| MCP production pin/static markers | **Passed**; production project remains `1.4.1` |
|
||||
| Promptfoo wrapper `-ValidateOnly` | **Passed**; configuration only, no live evaluation |
|
||||
| PowerShell AST parse for all QA scripts | **Passed** |
|
||||
| `node --check` for k6, QA mock and Promptfoo provider | **Passed** |
|
||||
| Frontend typecheck | **Passed**; full Vue application and E2E TypeScript projects |
|
||||
| Frontend unit tests | **Passed: 12 files, 28 tests** |
|
||||
| Frontend production build | **Passed: 1,984 modules transformed** |
|
||||
| Playwright controlled contract suite | **Passed: 24 tests** across all 20 core routes and 375/768/1024/1440/1920 px |
|
||||
| Production dependency audit | **Passed**; no known vulnerabilities after pinning PostCSS `8.5.18` |
|
||||
| OpenAPI generation repeatability | **Passed**; checked-in backend contract and generated TypeScript schema were stable |
|
||||
|
||||
The five skips are three explicitly gated PostgreSQL/Testcontainers
|
||||
provisioning tests, one Toxiproxy test and one PostgreSQL operation-claim
|
||||
concurrency test. Docker CLI was available but its daemon was not running.
|
||||
They were not executed and must not be described as passed integration
|
||||
evidence. The Playwright server is a controlled local contract fixture, not a
|
||||
real OpenClaw.
|
||||
|
||||
## Not executed or not proven
|
||||
|
||||
The following acceptance evidence was not produced by this milestone:
|
||||
|
||||
1. real pairing or scope upgrade against Bao's OpenClaw;
|
||||
2. a live agent create, file write or recovery mutation;
|
||||
3. a complete `Nexus -> OpenClaw -> OpenAI -> Nexus` run;
|
||||
4. Docker-backed PostgreSQL/Toxiproxy evidence when the environment gate or
|
||||
Docker daemon is unavailable;
|
||||
5. k6 p95 thresholds on a verified 1,000-task/10,000-activity fixture;
|
||||
6. `EXPLAIN (ANALYZE, BUFFERS)` and independent SQL-statement-count evidence;
|
||||
7. repeated browser `navigation -> cards visible` p95 evidence;
|
||||
8. live Promptfoo accuracy/injection evaluation against an isolated Nexus and
|
||||
test OpenClaw;
|
||||
9. live MCP 2.0 `tools/list`, Streamable HTTP and down-level negotiation; and
|
||||
10. production OTLP or `pg_stat_statements` activation.
|
||||
|
||||
The repository contains guarded scripts and tests for several of these checks.
|
||||
Static syntax/config validation proves only the artifacts, not their external
|
||||
systems or thresholds.
|
||||
|
||||
## Residual work and release gates
|
||||
|
||||
1. Obtain and pin official external Nexus/generic-operator Client-ID support.
|
||||
2. Run read-only pairing and inventory acceptance, then a separately approved
|
||||
disposable management-write sequence.
|
||||
3. Run Docker integration, Toxiproxy, k6, SQL-plan, repeated browser and
|
||||
Promptfoo release gates with archived sanitized evidence.
|
||||
4. Enable `pg_stat_statements` only through a separately approved PostgreSQL
|
||||
maintenance window because it requires server configuration/restart.
|
||||
5. Reconsider virtualization, pgvector/QMD and external policy engines only
|
||||
after measurements or multi-operator/tenant requirements justify them.
|
||||
|
||||
## Primary code and evidence pointers
|
||||
|
||||
- `backend/openapi/Nexus.Api.json`
|
||||
- `backend/Data/Migrations/20260730224500_AddAgentProvisioningAndBoardIndexes.cs`
|
||||
- `backend/Services/AgentProposalService.cs`
|
||||
- `backend/Services/DomainEventStreamService.cs`
|
||||
- `backend/Repositories/TaskRepository.cs`
|
||||
- `frontend/src/api/queryClient.ts`
|
||||
- `frontend/src/api/taskBoard.ts`
|
||||
- `frontend/src/services/sseHub.ts`
|
||||
- `frontend/src/services/domainEvents.ts`
|
||||
- `frontend/e2e/agent-proposals.e2e.ts`
|
||||
- `frontend/e2e/route-smoke.e2e.ts`
|
||||
- `frontend/e2e/task-board.e2e.ts`
|
||||
- [QA automation and evidence boundaries](../../../QA_AUTOMATION.md)
|
||||
- [Agent-first target contract](../../../AGENT_FIRST_MISSION_CONTROL.md)
|
||||
- [OpenClaw Attach & Adopt evidence](../openclaw-attach-adopt/IMPLEMENTATION_AND_ACCEPTANCE.md)
|
||||
|
||||
## External compatibility reference
|
||||
|
||||
- [OpenClaw `2026.7.1` client-ID registry](https://github.com/openclaw/openclaw/blob/v2026.7.1/packages/gateway-protocol/src/client-info.ts)
|
||||
- [OpenClaw Gateway protocol](https://github.com/openclaw/openclaw/blob/v2026.7.1/docs/gateway/protocol.md)
|
||||
Reference in New Issue
Block a user