Skip to content

Console UX: eliminate CLI dependency, fix project-recovery dead end, never show an empty dashboard#1

Open
archdex-art wants to merge 3 commits into
masterfrom
feat/console-onboarding-and-recovery
Open

Console UX: eliminate CLI dependency, fix project-recovery dead end, never show an empty dashboard#1
archdex-art wants to merge 3 commits into
masterfrom
feat/console-onboarding-and-recovery

Conversation

@archdex-art

Copy link
Copy Markdown
Owner

Summary

Three fixes to the Web Console's first-run and repeat-login experience, in commit order:

1. f09f522 — Eliminate CLI dependency for setup and MCP registration

  • Setup tab (new default view after signup/login/project-switch): shows the real project_id + api_key for the active project, a ready-to-paste Python SDK snippet, and a curl healthcheck — the Console previously minted credentials but never showed them to the user, forcing everyone into curl/CLI to retrieve their own key.
  • In-UI MCP server registration — Registry tab now has a "Register server" form posting directly to POST /v1/mcp/servers, replacing the agentmesh mcp register <manifest.yaml> CLI-only requirement.
  • run.sh — one command (./run.sh) brings up the stack, health-waits on the Query API, then opens the Console. stop/reset for teardown.

2. 0772dda — Fix existing-project login dead end

Root cause of users getting stuck on repeat login: clicking an existing project in the picker only expanded a note saying the key "can't be recovered, use the CLI" — no actual action. First-time signup worked; every subsequent login into an existing project was a wall.

  • New POST /v1/auth/projects/{id}/rotate-key (ownership-checked, revokes the old key and mints one fresh key, keeping the "exactly one active key per project" invariant).
  • Fixed a second bug found while wiring it: the route 404'd at Go's stdlib ServeMux because only the exact /v1/auth/projects pattern was registered, not the /v1/auth/projects/ subtree — needed for path params.
  • ProjectPicker's dead note replaced with a working "Use this project" button.

3. 53a48be — Run Demo / Generate Sample Data

Addresses "never show an empty dashboard": a brand-new project has zero traffic, so every view (Trace DAG, Cost Dashboard, Replay, Anomaly Detector) was empty until real SDK traffic arrived.

  • New services/collector/internal/demo package + POST /v1/demo/seed (Collector :4318, a plain-HTTP door since browsers can't speak the real OTLP/gRPC ingestion path at :4317): generates realistic, fully-linked traces through the exact same write+publish pipeline real spans use.
  • Four scenarios: default (happy path), tool_failure, cost_spike, loop (the last two exercise Cost Dashboard outliers and the Anomaly Detector's loop tracker).
  • Console: "Run Demo" replaces the bare empty-traces state; a persistent "Generate Sample Data" section on Setup adds a scenario picker + 1/10/100/1,000-trace bulk generation.
  • Bug caught during test authoring and fixed: demo traces could land with timestamps in the future for longer scenarios (cost_spike's ~14s of summed span durations exceeded the fixed start offset) — fixed by shifting each trace so it always ends at exactly "now".

Verification

  • go build/go vet/gofmt clean across all touched services; npx oxlint + vite build clean for the console.
  • New Go unit tests for services/collector/internal/demo (scenario structural invariants + full HTTP contract) — all passing, zero regressions.
  • Every flow verified live in a real browser against the running Docker Compose stack: signup → Setup shows real credentials → MCP registration with no CLI → logout/login → existing-project recovery via rotate-key → Run Demo → Trace DAG/Replay/Cost Dashboard all populated correctly across all four scenarios.

Base note

This branch is rebased off db537eb (pre-session state); master was reset to that same commit before opening this PR, per an explicit request to make this session's work reviewable as a normal PR rather than having landed via direct pushes.

- Setup tab (new default landing view after login/signup): shows the
  current project's real ID + API key + a ready-to-paste Python SDK
  snippet and curl healthcheck, so 'how do I use this outside the
  browser' never requires the CLI or re-deriving credentials.
- MCP Registry: in-UI 'Register server' form posts directly to
  POST /v1/mcp/servers, replacing the 'agentmesh mcp register
  <manifest.yaml>' CLI-only requirement.
- run.sh: one-command startup (docker compose up --build, health-wait
  on the Query API, then opens the Console). 'run.sh stop'/'reset' for
  teardown.

Verified live end-to-end in a real browser: sign up -> Setup screen
shows real project_id/api_key/snippet -> Registry tab registers a
real MCP server with no CLI calls.
Root cause of the blank/stuck screen users hit on repeat login: the
ProjectPicker let you click an existing project, but clicking it only
expanded a note saying the key 'can't be recovered here, use the CLI'
— there was no button, no action, nothing that actually let you into
that project. First-time signup worked (new project + key minted
inline); every subsequent login into an existing project was a wall.

Fix:
- New POST /v1/auth/projects/{id}/rotate-key endpoint (auth.go +
  project_provisioning.go): mints a fresh API key for a project the
  caller owns and revokes the previous one, keeping the 'exactly one
  active key per project' invariant createProjectAndKey establishes.
  Ownership is checked against projects.owner_user_id before any
  mutation.
- cmd/main.go: registered the missing '/v1/auth/projects/' SUBTREE
  mux pattern alongside the existing exact '/v1/auth/projects' match
  — Go's stdlib ServeMux only routes .../projects/<id>/rotate-key to
  a subtree registration, so without this the new endpoint 404'd at
  the router before ever reaching the handler (caught by live
  end-to-end testing, not unit tests, since the unit suite talks to
  the handler directly).
- ProjectPicker (AuthGate.tsx): existing-project note replaced with a
  working 'Use this project' button wired to the new endpoint, with
  loading state and an explicit warning that it rotates the key.

Verified live: signed up, created a project, logged out, logged back
in, clicked the existing project, hit 'Use this project' -> landed in
the Console on the SAME project_id with a fresh key; confirmed in
Postgres the old key is revoked and exactly one key is active.
Addresses the onboarding gap: a brand-new project has zero traffic, so
every view (Trace DAG, Cost Dashboard, Replay, Anomaly Detector) was
empty until a user wired up the real SDK. One click now populates a
project with realistic, internally-consistent traces through the exact
same write+publish path real ingestion uses.

- services/collector/internal/demo (new package): Generate() builds a
  complete trace (root span + children, real parent/child linkage,
  timestamps ending at 'now') for one of four scenarios —
  default (research-assistant happy path), tool_failure (a tool call
  errors, the agent retries and recovers), cost_spike (one LLM call
  processes an unbatched full corpus — an outlier on the Cost
  Dashboard), loop (the same tool call repeats 4x with no progress —
  the exact shape the Anomaly Detector's loop tracker watches for).
- POST /v1/demo/seed (handler.go): API-key authenticated (same
  X-AgentMesh-API-Key convention as every other endpoint), writes via
  the Collector's existing SpanWriter and fans out via SpanPublisher —
  a demo trace is indistinguishable downstream from a real one.
- New HTTP surface on the Collector (:4318, cmd/main.go) alongside its
  existing OTLP/gRPC receiver (:4317): a browser can't speak gRPC, so
  this is the one plain-HTTP door into span ingestion, deliberately
  separate from the real SDK ingestion path.
- Console: RunDemoPanel — 'Run Demo' one-click CTA replaces the bare
  'No traces found' empty state (TraceList), plus a persistent
  'Generate Sample Data' section on the Setup tab with a scenario
  picker and 1/10/100/1,000-trace bulk generation (chunked into <=50
  per request, matching the server's per-request cap).

Bug caught during test authoring and fixed: Generate()'s doc comment
promised timestamps 'ending at now', but the builder's fixed -2s start
offset only covered the shortest scenario — cost_spike's summed span
durations (~14s) pushed its last span's EndTime into the future.
Fixed by shifting the whole trace uniformly so it always ends at
exactly 'now', verified against a live ClickHouse query (freshly
generated trace ends -8s from now, not +14s).

Tests: services/collector/internal/demo/{scenario,handler}_test.go —
unit tests for every scenario's structural invariants (single root,
correct parent linkage, span.Validate() passing, non-decreasing
timestamps) and the full HTTP contract (auth, role check, CORS,
count-clamping, writer/publisher wiring, error paths). go build/vet/
test and gofmt all clean.

Verified live end-to-end in a real browser against the running Docker
stack: signed up, created a project, clicked Run Demo from the Setup
tab -> Traces list populated -> opened the Trace DAG (5 real spans,
correct costs/tokens) -> Time-Travel Replay -> Cost Dashboard reflected
the same spend. Ran all four scenarios and confirmed distinct,
correct shapes (cost_spike: $2.19/128k tokens; tool_failure and loop:
both surfaced as Error status with the right span/error counts).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant