WebHost.Systems WebHost.Systems/docs/spec/progress/2026-01-24.md
Project: webhost.systems (v1) Focus: Dashboard polish — add Documentation + Account pages; start improving Agents navigation toward a sortable/searchable table Owner: Engineering

Progress Log — 2026-01-24

Project: webhost.systems (v1) Focus: Dashboard polish — add Documentation + Account pages; start improving Agents navigation toward a sortable/searchable table Owner: Engineering

Summary (what changed today)

  • Started Slice B dashboard navigation polish by adding dedicated pages for:

  • Account (identity + tier/usage snapshot; billing placeholder)

  • Documentation (spec-first quick links + local quickstart notes)

  • Began refactoring the dashboard shell so Account and Documentation are no longer “coming soon” placeholders.

  • Improved Agents navigation by replacing the left “agent buttons” list with a sortable/searchable/filterable agents table.

  • Implemented Agents routing as two separate pages using query-string routes (so #... remains available for in-page section anchors):

  • Agents list: ?page=agents

  • Agent detail: ?page=agent&agentId=<agentId>

What shipped today

Dashboard/UI

1) Account page (new)
  • Added a new Account page intended to become the home for:

  • identity info (from Clerk),

  • tier + usage snapshot (from control plane),

  • billing/subscription UI (placeholder for now).

Notes:

  • The page pulls usage using the existing GET /v1/usage/current endpoint via ControlPlaneClient.

  • The page intentionally does not introduce any new server endpoints yet.

2) Documentation page (new)
  • Added a new Documentation page intended to be a “spec-first index”:

  • links to docs/spec and ADRs (via GitHub),

  • quick local env reminders,

  • quick list of endpoints exercised by the dashboard.

Notes:

  • This stays non-normative and points back to the spec set as the source of truth.

3) Sidebar navigation updated
  • Updated the authed sidebar nav so:

  • “Documentation” is a first-class route label (instead of “Docs”).

  • “Account” route renders the new Account page.

  • “Documentation” route renders the new Documentation page.

Agents UI — shipped (agents table)

What shipped

Replaced the left “agent buttons” list with a table-driven Agents sidebar that supports:

  • Search: client-side substring match across name/description/id/status/runtime + active deployment id.

  • Filters:

  • status (draft|ready|deploying|active|error|disabled|deleted)

  • runtime (cloudflare|agentcore|unknown)

  • deployment state (has active deployment / none)

  • Sorting (client-side):

  • name, status, preferredRuntimeProvider, hasActiveDeployment, updatedAtMs, createdAtMs, _id

  • sensible defaults (e.g. updatedAtMs desc, hasActiveDeployment desc)

  • Selection: clicking a row selects the agent (preserving the existing “left list + right detail panels” layout).

Notes / follow-ups

  • This is intentionally dependency-free (no table library).

  • Next UX polish: when the selected agent is filtered out, show a small banner (or adjust the selection policy) so the state doesn’t feel “lost”.

  • Routing follow-up (requested): move to two separate pages:

  • Agents list route (table + create)

  • Agent detail route (deploy/invoke/deployments/telemetry for a selected agent)

Spec/ADR alignment notes

  • ✅ Dashboard improvements are non-normative and remain aligned with:

  • 10_API_CONTRACTS.md (usage endpoint for tier/usage display)

  • 00_MASTER_SPEC.md (dashboard requirements / views)

  • ✅ No changes to the control-plane boundary or security model.

  • ⚠️ Billing UI is still placeholder; webhook-driven tier updates remain a future phase.

Files touched (high-level)

  • apps/web/src/App.tsx — route wiring + nav label updates

  • apps/web/src/components/AccountPage.tsx — new (now rendered)

  • apps/web/src/components/DocsPage.tsx — new (now rendered)

  • apps/web/src/components/AgentsPage.tsx — replaced agent sidebar list with sortable/searchable/filterable table

  • apps/web/src/index.css — added table/control styles to support the Agents table UX

Validation performed

  • Manual UI sanity:

  • navigation switches between Dashboard / Agents / Account / Documentation

  • Agents sidebar table: search/filter/sort works and selection updates the right-side detail view

  • Account page loads usage (when control plane env is configured)

  • Typecheck:

  • npm run typecheck -w apps/web

  • No new server endpoints added.

Known issues / risks

  • Agents table UX is a first pass:

  • selection can become “hidden” if filters/search exclude the selected agent (needs a banner or consistent selection policy)

  • Agents list/detail split is now implemented using query-string routing:

  • list: ?page=agents renders only the table/create UI

  • detail: ?page=agent&agentId=<agentId> renders only deploy/invoke/deployments/telemetry for that agent

  • Hash (#...) is reserved for in-page anchors (section jumps) within each page.

  • Avoid duplicating token-fetch logic across pages long-term:

  • consider extracting a shared useControlPlaneClient() hook now that multiple pages use the same pattern.

Next steps

  • [ ] Agents routing polish (query-string routes):

  • [ ] verify deep links:

  • ?page=agents

  • ?page=agent&agentId=<agentId>

  • [ ] verify list → detail navigation and detail → list back work with browser history

  • [ ] confirm in-page anchors still work on Account/Docs/Agents pages (hash is not used for routing)

  • [ ] Agents table polish:

  • [ ] show a banner when filters/search exclude the selected agent (if/when a combined view exists again), or ensure the detail route drives selection cleanly

  • [ ] move remaining inline table styles toward the CSS classes for consistency

  • [ ] Add a small “agent quick actions” row for the selected agent (copy id, refresh, open deployments section).

  • [ ] Extract a shared useControlPlaneClient() hook to eliminate repetition across Dashboard/Agents/Account.

  • [ ] Start wiring real billing once checkout + webhook flows are implemented (per spec).

Addendum (later on 2026-01-24) — Quick wins landed

Dashboard/UI — shared client hook (DX + consistency)

  • Implemented a shared useControlPlaneClient() hook in the web app to centralize:

  • base URL normalization (VITE_CONTROL_PLANE_URL)

  • Clerk JWT template selection (VITE_CLERK_JWT_TEMPLATE, default "convex")

  • consistent, actionable token-fetch failure messaging

  • Refactored pages to use the shared hook (removes repeated client construction):

  • Dashboard

  • AgentsPage

  • AccountPage

Notes:

  • No behavior changes intended; this is primarily a duplication/consistency win.

  • This aligns with the earlier “avoid duplicating token-fetch logic across pages” follow-up.

Agents UI — quick actions row (small UX win)

  • Added a small “agent quick actions” row in the Agent detail header:

  • Copy agent id

  • Refresh details (agents + deployments + usage/telemetry)

  • Jump link to the Deployments section

Control plane — CORS allowlist + safer SSE errors (security polish)

  • Added an environment-configurable CORS allowlist to the Convex HTTP server:

  • CONTROL_PLANE_CORS_ALLOW_ORIGINS (comma-separated origins)

  • When unset/empty, preserves the previous dev-friendly behavior (reflect Origin).

  • Tightened SSE error envelopes to avoid leaking internal/provider error messages:

  • Keep user-facing messages generic unless the error is one of the explicitly allowlisted codes (e.g. UNAUTHORIZED, LIMIT_EXCEEDED, etc.)

Notes:

  • No API shape changes; these are HTTP-layer response hardening changes.

Control plane — delegated invocation endpoint (prep for workflow runners)

  • Implemented the v1 delegated invocation endpoint (server-to-server, internal):

  • POST /v1/delegated/invoke/:agentId

  • Auth model:

  • HMAC over raw request bytes using WHS_DELEGATION_SECRET

  • required headers:

  • X-WHS-Delegation-Source

  • X-WHS-Delegation-Timestamp (skew window enforced; recommended ±5 minutes)

  • X-WHS-Delegation-Signature: v1=<hex-hmac-sha256(raw_body_bytes)>

  • optional allowlist via WHS_DELEGATION_ALLOWED_SOURCES

  • Delegated user resolution:

  • Added an internal upsert path to resolve delegation.externalUserId (auth subject) into a WHS users row, creating it if missing.

  • Idempotency:

  • Added delegatedInvocationIdempotency table and internal mutations to ensure retries do not double-run/double-charge.

  • Conflict detection: same (userId, agentId, idempotencyKey) with different payload hash returns CONFLICT.

  • Limits + routing behavior remains consistent with the normal invoke gateway:

  • routes only via agents.activeDeploymentId

  • enforces request-limit reservation pre-invoke (ADR-0007)

Agentromatic — WHS Agent node + wiring (next-phase integration prep)

  • Added a WHS-specific node type to Agentromatic’s shared node registry:

  • whs_agent (config schema includes whsAgentId + invocation input placeholders)

  • Added Agentromatic server-side helper(s) to sign and call WHS delegated invocation:

  • uses WHS_CONTROL_PLANE_URL + WHS_DELEGATION_SECRET

  • sends to POST /v1/delegated/invoke/:agentId with the required headers

  • Extended the Phase 1 executeWorkflow stub toward a minimal runner shape:

  • sequentially iterates nodes from the workflow snapshot (still not a DAG engine)

  • attempts delegated WHS invocation for whs_agent (and compatible ai_agent configs) when configured

  • logs results into execution logs for UI debugging

Notes:

  • This is deliberately “thin-slice wiring”: it sets up the integration seams without committing to a full workflow engine yet.

  • The delegated invocation endpoint is explicitly server-to-server and MUST NOT be used from browsers.

Validation performed

  • Typecheck:

  • npm run typecheck -w apps/web

  • npm run typecheck -w apps/control-plane

  • npm run typecheck (Agentromatic workspaces)

Open in the interactive atlas