feat(dashboard): standalone "Why" causal-lineage debugging page - #71
Open
saadamjad wants to merge 13 commits into
Open
feat(dashboard): standalone "Why" causal-lineage debugging page#71saadamjad wants to merge 13 commits into
saadamjad wants to merge 13 commits into
Conversation
Adds a ready-made, shareable causal-lineage tool at /dashboard/debugging/why — the fastest path from "I found a bad event in the logs" to "here's its full history." - User provides only an event_id (no parent_id — the backend's recursive WHY query walks the parent chain server-side). Optional depth field. - Deep-linkable: ?event_id=&depth= auto-runs on load, so a trace can be pasted straight into a Slack thread or bug ticket. - CloudWatch-Insights-style: every AgentEvent field is shown per node (event_id, parent_id, agent, event type, full-precision timestamp, session_id, sequence_no) plus the full data payload as expandable JSON. - Chain renders root-first: first node badged "Origin", searched event badged at the bottom (both on the same node when chain_length === 1). - Error nodes flagged with a red badge using the same "error"/"fail" heuristic the backend already uses in memory.py's has_errors. - Copy affordances: per-field, per-node JSON, whole-chain JSON, and a "Copy link" for sharing — all clipboard calls wrapped in try/catch. - parent_id links to a jump-scroll when the ancestor is in the loaded chain; plain text when depth cut it off. "Trace this instead" re-runs WHY from any ancestor. - Client-side UUID validation before hitting the API; distinct inline error states (invalid id vs not-found vs network). - New "Why" entry in DashboardShell nav (desktop + mobile). getWhyChain gains an optional depth param (backward compatible; the existing agents/[id] call site is unaffected). No backend changes, no schema changes, no new env vars. KB updated (§17.3 + new §19.3a). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
`next build` (the real gate) rejects unescaped ' in JSX text via react/no-unescaped-entities — "you're" → "you're". Verified with a production Docker build of the dashboard against a live local stack. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reframes the Why page around the actual debugging goal: paste an error's
event_id, get the root cause and the full story of how it happened.
- Single input only (the error event_id). Removed the confusing "depth"
field — the client always requests max depth (50) so the full lineage
is captured without the user thinking about it.
- Root-cause summary banner: computes the earliest error/fail event in
the chain as the root cause and narrates the story ("began with X, ran
N steps, failing at step K — <event>: <message>"), with at-a-glance
stats (steps, time span, agent, session) and a "traced N parent links"
caption that explains how the lineage walk works.
- Sequential CloudWatch-style timeline: numbered step dots joined by a
connector rail, per-step +Δ time delta, and markers — Origin, Root
cause, Error (cascading), You searched this. Every event field stays
visible; data JSON expands per step (errors/searched open by default).
- No-error chains show a neutral summary instead of a forced badge.
- Keeps deep-linking, UUID validation, distinct error states, per-field
and per-node copy, parent_id jump-links, and "trace this instead".
Verified: production build compiles clean (Docker), page serves 200, all
refined logic ships in the bundle, depth removed, and the seeded error
chain (user_message → planning → tool_call → tool_error) traces correctly
via the live API. Docs updated (KB §19.3a).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The step output was crowded — every step dumped five full-UUID rows at full density. Reworked into progressive disclosure so the story reads cleanly top-to-bottom and full detail is one click away. UX: - Collapsed step is now a clean 3-line summary: event type + role badges, a one-line "what happened" (error message, or a compact key/value preview of data), and a subtle meta line — agent · seq · timestamp · +Δ. All the supporting values the user asked to always see stay visible. - The noisy bits (full event_id / parent_id / session_id, raw data JSON, copy + "Trace from here") move into an expandable Details panel. The whole header is the toggle. Error and searched steps auto-expand. - Softer, more consistent palette and spacing; JSON is height-capped and scrolls instead of stretching the card. Edge cases hardened (all verified against a running stack): - Root cause = the EARLIEST error in the chain, so cascading downstream errors don't mask the true origin (verified: db_error root cause with a later retry_failed shown as a downstream Error). - chain_length === 1 → the single step carries Origin + You searched this. - No-error chain → neutral summary, no forced badge. - Friendly, actionable error copy for not-found (404) / network / expired session instead of raw API text; invalid UUID blocked client-side. - Safe date parsing (no "Invalid Date"); missing/empty data, null session/parent, and huge payloads all render without breaking layout. - parent_id jump now also expands the target step; clipboard still guarded. Verified: Docker production build compiles clean; all scenarios (main error chain, single root, clean chain, multi-error) serve 200 and derive the correct banner + per-step badges. Mobile-responsive (stacked meta, 1-col detail grid). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Moves "Why" out of the flat top-level nav into a dedicated Debugging section, so debugging tools have a clear home (and room to grow — at / search / memory-diff can slot in later). - DashboardShell nav now supports NavLink | NavGroup; the desktop sidebar renders a "Debugging" section label (Bug icon) with "Why" indented under it. Clicking Why opens /dashboard/debugging/why exactly as before. - The mobile bottom bar can't nest, so it flattens groups to their leaf links (mobileLinks) — Why still appears directly there. Verified: production build compiles clean; the Debugging section + Why link render in the sidebar and the Why page still loads (HTTP 200). KB §19.3a nav note updated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds a second, optional Parent ID input to the Why page and a matching guard on the backend, for callers who want an explicit "only trace if the parent matches" assertion (useful mental model for multi-agent / fleet setups). Honest scope, reflected in the UI copy: tenant isolation is already the real boundary (why is WHERE event_id=$1 AND tenant_id=$2, and event_id is a globally-unique UUID), so this guard is an integrity assertion, not an access control. It's enforced server-side so it isn't bypassable. - Backend (core/api/events.py::why): optional `parent_id` query param. When present it's UUID-validated and checked against the searched event's real parent_event_id; mismatch → 400. Omitted → unchanged behavior (backward compatible; SDK + agents/[id] call site unaffected). - lib/api.ts: getWhyChain gains an optional parentId → ?parent_id=. - Page: second "Parent ID — optional integrity check" input; deep-linkable (?event_id=&parent_id=); client-side UUID check; friendly inline messages for mismatch / invalid-uuid; "Trace from here" now carries the target node's own parent_id so re-traces keep the guard (origins re-trace blank). Left optional on purpose so root/origin events (no parent) stay traceable. Verified live against the locally-built API: baseline 200, correct parent 200, wrong parent 400, invalid uuid 400, root+parent 400, root alone 200; dashboard production build compiles clean and all input combos load. Docs updated (KB §17.3 + §19.3a). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
main's "centralized exceptions" refactor (#28) dropped the direct HTTPException import from core/api/events.py in favor of services.exceptions helpers. This branch predated it, so after merging main the new parent_id guard still called HTTPException → ruff F821 in CI (which lints the PR merged into main). Convert the two parent_id 400s to bad_request() and import it alongside not_found(). No behavior change (bad_request → HTTPException 400). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
UI/UX pass on the Why page based on review feedback:
- Inputs no longer presume an error. "Error event ID" → "Event ID";
empty state and header copy are neutral ("trace any event's causal
history") — root-cause language now appears only when an error is
actually detected (the summary badge/banner remains data-driven).
- Proper field labels ("Event ID", "Parent ID · optional") above each
input instead of cramming everything into placeholders that vanish on
type; shorter, clearer placeholders.
- Consistent input styling — both fields now share the same background,
border, and green focus ring (previously mismatched #111/#0d0d0d and
different focus colors), for a cleaner, calmer form.
- Primary input (Event ID) is paired with the Trace action; the optional
Parent ID sits below with a one-line helper, replacing the long
multi-sentence paragraph.
- Button label simplified "Trace root cause" → "Trace".
Verified: production build compiles clean; page loads (200) and the
parent_id guard still enforces (wrong parent → 400).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Label-only rename of the debugging tool — clearer than the abstract
"Why" for a nav item. The route stays /dashboard/debugging/why so
already-shared deep links and bookmarks keep working, and the SDK method
(db.why()), API path (/v1/events/{id}/why), and getWhyChain client keep
the "why" identifier.
- DashboardShell nav label + page <h1> → "Causal Trace"
- KB §19.3a heading updated with a note that the route is unchanged
Verified: production build compiles clean; sidebar + header show "Causal
Trace"; deep link /debugging/why?event_id=…&parent_id=… still loads (200).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…dashboard-why-page
Three verified UX-clarity fixes from the debugging-system review (each
confirmed against the live API):
- Search: the page swallowed the API's "embeddings not configured" 400
and showed "No results" — misleading. Now surfaces a distinct setup
state linking to Settings → Embeddings, plus separate network/other
error states (was: silent empty).
- Time Travel: for agents that don't emit STATE_SET/STATE_DELETE events,
the API returns state = {_last_event: …}. The UI now detects this and
explains it ("no key/value state to rebuild — showing the most recent
event instead") rather than dumping a lone _last_event that looks
broken; also handles the empty-state case.
- Behavior/baseline: the warming-up and no-baseline states now explain
what drift tracking does and when it activates, instead of only echoing
the raw "need N sessions" message.
No API/route/schema changes. Dashboard production build compiles clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Full structured report (Phases 1–5, 15 deliverables): per-feature review (purpose, user journey, backend flow, inputs, outputs), architecture analysis, UX review, prioritized gap analysis, new-feature specs (Debugging Hub, Impact Trace, Error Explorer, Ask ZizkaDB), API/DB/ security recommendations, comprehensive testing plan, risks, and a High/Medium/Low roadmap. Grounded in live verification against the running stack. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Adds a standalone, shareable "Why" debugging page at
/dashboard/debugging/why— a ready-made causal-lineage query so a team using ZizkaDB to log their agents can go from "I found a bad event in the logs" to "here's its full history" without knowing the API by hand.It wraps the existing
GET /v1/events/{id}/whyendpoint (already used as a tab inside the agent detail page) into its own agent-agnostic, deep-linkable screen.Key behaviors
parent_idneeded — the backend's recursiveWHYquery walks the parent chain server-side. Optionaldepthfield (default 10, API caps at 50).?event_id=<uuid>&depth=<n>auto-runs on load, so a trace can be pasted straight into Slack or a bug ticket.AgentEventfield is shown per node —event_id,parent_id,agent,eventtype, full-precisiontimestamp,session_id,sequence_no— plus the fulldatapayload as expandable JSON. Nothing trimmed.chain_length === 1)."error"/"fail"gets a red Error badge — same heuristic the backend already uses forhas_errorsinmemory.py.parent_idbecomes a jump-scroll link when the ancestor is in the loaded chain (plain text when depth cut it off); "Trace this instead →" re-runs WHY from any ancestor.Files
dashboard/app/dashboard/debugging/why/page.tsxdashboard/lib/api.tsgetWhyChaingains optionaldepthparam (backward compatible)dashboard/components/DashboardShell.tsxdashboard/DASHBOARD_KNOWLEDGE_BASE.mdNo backend changes, no schema changes, no new env vars.
How to test
Then, against a running local stack (
bash scripts/setup-local.sh), seed some causally-linked events (run anexamples/agent), grab anevent_id, and visit/dashboard/debugging/why?event_id=<id>.Edge cases covered
chain_length === 1) — single node marked as both Origin and searched.parent_idbeyond the loaded depth — rendered as plain text, not a broken link.cancelledguard prevents stale overwrites.Checklist
tsc --noEmitclean,next lintcleannpm run build(compiling — slow in this environment; will confirm)allow_origins=["*"]combinations — N/A (dashboard-only)/v1/renamesDASHBOARD_KNOWLEDGE_BASE.mdupdated in the same PR🤖 Generated with Claude Code