feat: add Agent Lens for subagent traces - #12
Conversation
|
@yearth Looks cool. I'll take some time to try this feature and share my feedback. |
|
Thanks for the work on Agent Lens. I verified the PR in a fresh worktree, exercised the feature in the browser, and reviewed the adapter, server, caching, contract, packaging, and UI paths. The core interaction works well, but I think the following issues are worth addressing. I have grouped them by area and included the concrete failure mode and code location. 1. AgentGraph correlation correctness1.1 Legacy Codex spawn output can produce duplicate and incorrectly failed nodesLocation
Failure mode A single legacy spawn can appear as two rows: one failed/unavailable launch node and one derived/available child node. Non-empty output is not sufficient evidence that the launch failed; it may simply be a different success format. Suggested fix
Acceptance case A legacy Codex session containing one task_name spawn should produce exactly one usable AgentNode. 1.2 Claude launch matching should use a stable call identityLocation internal/adapter/claudecode/agents.go:101-113 stores matched launches in map[*claudeAgentLaunch]bool. Failure mode If the same callID is read from more than one transcript, the two parsed values have different pointer identities. One can be matched to the real artifact while the other is emitted as a ghost unavailable row. Suggested fix Track matched launches by callID, or by a stable composite identity if additional ownership context is required. Acceptance case The same callID appearing across transcript inputs should still produce only one node. 1.3 Depth-first sorting is required for nested agentsLocation
Failure mode Both comparators sort by depth before launch order. For a graph Main -> A, B and A -> A1, the UI receives A, B, A1. Because indentation is the only hierarchy cue, A1 visually appears under B. Suggested fix Return a stable preorder traversal: each parent immediately followed by its complete subtree, with siblings ordered by launchSeq and then stable label/ID fallbacks. The two adapters can share this ordering helper. Acceptance case At every nesting depth, each subtree remains contiguous in the returned AgentGraph and in the panel. 2. Refresh consistency and caching2.1 Rescan leaves child trace data staleLocation
Failure mode After a running subagent grows from, for example, 21 to 30 events, Rescan can update the row to “30 events” while entering the lens still displays the cached 21-event trace. If the repository layout changed, the cached child trace can also retain file IDs assigned against the old citymap. Suggested fix
Acceptance case After a child trace or repository tree changes, one Rescan updates the graph count, lens events, and file-to-building mapping together. 2.2 AgentGraph is rebuilt for every API requestLocation
Impact On the local corpus, a 66 MB session took roughly 0.5 seconds per graph build. Concurrent requests repeated the same parsing work, adding CPU time and increasing RSS. This is currently acceptable for small sessions but scales poorly for long traces and nested teams. Suggested fix
Acceptance case Repeated and concurrent requests with the same fingerprints should perform one graph build; a changed input should automatically rebuild it. 3. Contract and packaging completeness3.1 AgentGraph has no mirrored JSON SchemaLocation
Suggested fix Add schema/agent-graph.schema.json covering version 1, node kinds and statuses, trace availability, link quality/method, parent/depth fields, and optional metadata. Add a contract test that validates representative exported graphs. 3.2 The embedded frontend was not regeneratedLocation
Failure mode The feature works through the Vite dev server, but a normally built mindwalk binary can continue serving the old UI without Agent Lens. Suggested fix
Acceptance case mindwalk serve and mindwalk serve --dev should expose the same Agent Lens functionality. 4. UI feedback and discoverability4.1 Child-trace errors and Retry are rendered below the entire listLocation
Observed behavior At 1280x720, the panel had 583 px of visible height while the 12-row list was 1,275 px tall. When loading a top-row agent failed, the row spinner disappeared but the error and Retry appeared more than 1,300 px below the top. To a sighted user, the click appeared to do nothing. Loading itself is not silent—the clicked row already shows a spinner and “Loading trace…”. The real gap is failure recovery. Suggested fix
4.2 The feature entry points are hard to discover and the list is too tallLocation
Suggested fix
A separate Dock status dot and a lens-cycling keyboard shortcut do not seem necessary for this pass. 4.3 A zero-event child trace displays the wrong empty stateLocation web/src/ui/Timeline.tsx:420 displays “Select a session to start the walk” whenever there is no current event. Failure mode A valid selected child trace with zero events is presented as though no session were selected. Suggested fix Distinguish “no session selected” from “selected trace has no events.” The latter could say: “No recorded activity for this agent.” Suggested validation set
The core design remains strong: adapter ownership boundaries are clean, child traces are authorized through the root graph, link quality is explicit, and the lens/playhead behavior is solid. The items above are mainly about making that model reliable across legacy data, refreshes, larger sessions, nested graphs, and the packaged binary. |
|
@cosmtrek Thanks for the detailed review. I have addressed all ten items in the latest revision. Correlation correctness
Refresh and caching
Contract and packaging
UI and discoverability
Validation now includes the focused acceptance cases, the full Go suite, race tests, Playwright 15/15, production build/static parity, real Codex browser QA, and a passing CI run. One evidence gap remains: the historical Claude missing-parent artifact used during discovery is no longer available locally, so that exact artifact is covered by deterministic fixtures rather than a fresh live replay. |
Why
Mindwalk currently replays one normalized session trace at a time. When a root Codex or Claude session launches subagents, their work is stored in separate session artifacts, so the root view cannot answer a basic question: where did each subagent spend its attention?
This PR adds an Agent Lens V1. It keeps Main and every child as independent traces and lets the user choose one observation lens at a time. It deliberately does not merge actors into a synthetic timeline or infer lifecycle state that the source logs do not provide.
User flow
Missing, failed, derived, and zero-event children remain visible as explicit states instead of silently disappearing.
What changed
1. Versioned agent graph contract
AgentGraph/AgentNodemodel with stable actor IDs.Tracecontract actor-scoped; graphs reference traces instead of embedding or merging them.2. Adapter-owned session correlation
spawn_agentcall/output metadata with childsession_metafields such as parent thread ID, agent ID, depth, role, and nickname.subagents/artifacts and sidecar metadata, preserving exact vs derived quality.3. Root-scoped API and ownership checks
Adds:
GET /api/sessions/{root}/agentsGET /api/sessions/{root}/agents/{agent}/traceChild traces are loaded on demand. The server verifies graph ownership before returning a child trace, including ambiguous duplicate Codex root IDs, so a node cannot be fetched through a different root session. Missing or unavailable traces use explicit conflict/error responses rather than guessed fallback data.
4. Agent Lens UI
5. Viewport-level agent details
6. Async and export safety
Review guide
The easiest review order is:
internal/model/agent.gointernal/adapter/codex/agents.goandinternal/adapter/claudecode/agents.gointernal/server/server.goweb/src/App.tsxandweb/src/ui/AgentsPanel.tsxweb/e2e/agent-lens.spec.tsThe adapter tests are intentionally fixture-heavy because each source format owns its linking semantics. The browser suite uses the production React/API path with deterministic fixtures for races and unavailable states.
Deliberate V1 boundaries
Testing
go test ./... -count=1go test -race -count=1 ./internal/adapter/... ./internal/servernpm --prefix web run buildnpm --prefix web run test:e2e— 15/15make testgit diff --checkThe browser cases cover truthful rows, Main/child playhead memory, a zero-event child, report-to-Main navigation, rapid stale-response switching, failed child retry, export locking, viewport-level detail placement, dock overflow protection, pinned long-content scrolling, and all supported dismissal paths.
Manual QA
tmc-pc-v2root containing 2,149 files, 834 events, and six subagent entries.Known evidence gap
The historical real Claude missing-parent artifact used during discovery is no longer present on the local machine. That state is covered by deterministic adapter, API, and browser fixtures, but the exact original live artifact could not be revalidated.
Related to #9.