Skip to content

fix(dashboard): graph renders edges + harness turns attribute correctly#90

Merged
thenotoriousllama merged 1 commit into
mainfrom
fix/dashboard-graph-edges-and-harness-turns
Jun 23, 2026
Merged

fix(dashboard): graph renders edges + harness turns attribute correctly#90
thenotoriousllama merged 1 commit into
mainfrom
fix/dashboard-graph-edges-and-harness-turns

Conversation

@thenotoriousllama

@thenotoriousllama thenotoriousllama commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator

Two dashboard-truthfulness bugs, fixed via the Bee Army (typescript-node + harness-integration, security→quality close-out). Both verified.

Item 1 — graph showed "16068 nodes, 0 edges"

The dashboard's parseSnapshot read rec.edges, but the codebase snapshot is NetworkX node-link JSON whose edges live under links (source/target) — so the edge array was always empty while nodes rendered fine.

  • Replaced the loose reader with a typed snapshotToGraphView mapper over the Snapshot contract (linksedges{from,to,kind}), so the field-name bug can't silently recur (tsc checks it).
  • Resolved the latent GET /api/graph double-registration (a counts-only codebase handler vs the dashboard full-view handler) that flapped built:false. GET /api/graph is now a single handler reading the freshest local snapshot — the same file POST /api/graph/build writes — so the just-shipped "Build graph" button's re-read shows edges immediately (no DeepLake eventual-consistency wait). Dead fetchGraphView/parseSnapshot removed.
  • Live dogfood: GET /api/graph went 0 → 3,550 edges after a build (e.g. Badge.jsx → external:react (imports)).

Item 2 — every harness showed "0 turns captured"

Captured sessions were written with agent="" (no shim stamped the harness identity), so harness-api's COUNT(*) GROUP BY agent matched none of the canonical names.

  • All six shims now stamp meta.agent with their canonical token (claude-code/codex/cursor/hermes/pi/openclaw) at the shared createShim.normalize seam.
  • Fixed a latent OpenClaw defect: deriveMeta was clobbering agent (harness provenance) with the per-user name — it now sets only agent_id, keeping the openclaw/alice split correct.
  • Existing agent="" rows are not backfilled (no destructive migration); new captures attribute correctly. eventCount:0 in fetchSessionsView is a separate deferred read change (it does not feed the Harnesses page).

Close-out

Security — PASS, zero remediations: no path traversal (graph dir keys on daemon-local git identity, never request headers), scope/RBAC/local-gate preserved, inert-text render (no dangerouslySetInnerHTML), the agent token is hardcoded (not forgeable / no injection), provenance-vs-per-user split correct. One non-blocking Low (the dir sanitizer permits ./.. but repo is daemon-local → not reachable; flagged as a follow-up).
Quality — PASS: both fixes real, complete, and locked by behavior-asserting tests (typed mapper links→edges; single-handler regression both ways; six-harness stamp parameterized; openclaw/alice split; seeded GROUP BY attribution + a nameagent rename guard).

Gate

npm run ci2855 passed, 6 skipped (the sources/api.test.ts load-flake didn't surface). build / audit:sql / audit:openclaw green. Zero deletions, nothing under assets/.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Resolved dashboard graph displaying zero edges when building codebases
    • Fixed harness attribution metrics incorrectly showing zero activity across all harnesses
    • Corrected API route collision preventing proper graph data delivery
  • Documentation

    • Added QA verification report for graph and harness fixes
    • Added security audit report validating fixes and metadata handling
  • Tests

    • Extended test coverage for graph edge mapping and display
    • Added harness identity attribution test suite and metadata persistence validation

… the right harness

Two dashboard-truthfulness bugs.

ITEM 1 — graph showed 16068 nodes but 0 edges. The dashboard's parseSnapshot
read `rec.edges`, but the codebase snapshot is NetworkX node-link JSON whose
edges live under `links` (source/target) — so edges were always empty while
nodes rendered. Replaced the loose reader with a TYPED snapshotToGraphView mapper
over the Snapshot contract (links → edges{from,to,kind}), so the field-name bug
can't silently recur. Also resolved the latent GET /api/graph double-registration
(a counts-only codebase handler vs the dashboard full-view handler) that flapped
built:false: GET /api/graph is now a single handler reading the freshest LOCAL
snapshot — the same file POST /api/graph/build writes — so the "Build graph"
button's re-read shows edges immediately with no DeepLake eventual-consistency
wait. Dead fetchGraphView/parseSnapshot removed. Live: GET /api/graph 0 → 3550 edges.

ITEM 2 — every harness showed 0 turns captured. Captured sessions were written
with agent="" because no shim stamped the harness identity, so harness-api's
COUNT(*) GROUP BY agent matched none of the canonical names. All six shims now
stamp meta.agent with their canonical token (claude-code/codex/cursor/hermes/pi/
openclaw) at the shared createShim.normalize seam. Also fixed a latent OpenClaw
defect: deriveMeta was clobbering `agent` (harness provenance) with the per-user
name — it now sets only agent_id, keeping the openclaw/alice split correct.
Existing agent="" rows are not backfilled (no destructive migration); new captures
attribute correctly. eventCount:0 in fetchSessionsView is a separate deferred read
change (it does not feed the Harnesses page).

Security: PASS, zero remediations (no path traversal — graph dir keys on daemon-
local git identity, not request headers; scope/RBAC/local-gate preserved; inert-
text render; agent token hardcoded, not forgeable; provenance/per-user split
correct). Quality: PASS — both fixes real + test-locked.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Two dashboard-truthfulness fixes: (1) GET /api/graph ownership is transferred exclusively to mountGraphApi via a new snapshotToGraphView helper mapping Snapshot.linksGraphView.edges; duplicate handler and dead parse helpers removed from mountDashboardApi. (2) All six harness shims now stamp meta.agent with their canonical token at the normalize seam; OpenClaw's deriveMeta is corrected to set only agentId. QA and security audit reports are added.

Changes

Graph Edge Routing Fix

Layer / File(s) Summary
snapshotToGraphView mapper and GET /api/graph handler
src/daemon/runtime/codebase/api.ts
Exports snapshotToGraphView converting Snapshot.nodes/links (NetworkX convention) into GraphView nodes/edges with label fallback; GET /api/graph now returns built:false empty state or full built:true GraphView instead of count-based metadata.
Remove graph handler and dead code from mountDashboardApi
src/daemon/runtime/dashboard/api.ts
Deletes exported fetchGraphView, removes DASHBOARD_GROUPS.graph route registration, strips SnapshotGraph interface and parseSnapshot helper; module docs declare mountGraphApi as sole owner of GET /api/graph.
Assembly and CONVENTIONS routing docs
src/daemon/runtime/assemble.ts, src/daemon/runtime/dashboard/CONVENTIONS.md
Updates assemble.ts step comments for mountDashboard and mountGraph to assert single-owner rule; updates CONVENTIONS.md to document empty fallback shape and remove fetchGraphView from shared data-source fetcher list.
Graph API and routing-collision tests
tests/daemon/runtime/codebase/api.test.ts, tests/daemon/runtime/dashboard/api.test.ts
Adds pre-build empty state, post-build full GraphView shape, snapshotToGraphView unit, and routing-collision regression tests; dashboard suite asserts 501 when only mountDashboardApi is mounted and removes /api/graph from VIEW_PATHS.

Harness Attribution Stamping Fix

Layer / File(s) Summary
Harness token constant, normalize stamping, and OpenClaw derivation fix
src/hooks/openclaw/shim.ts, src/hooks/normalize.ts, src/hooks/index.ts
Adds OPENCLAW_HARNESS = "openclaw" constant and re-exports it; normalize() stamps meta.agent = spec.harness; openclawDeriveMeta now sets only agentId from namespaced session IDs; openclawExpandBatch stamps agent: OPENCLAW_HARNESS on batch-expanded inputs.
Harness identity, capture persistence, and attribution tests
tests/hooks/harness-identity-stamp.test.ts, tests/hooks/openclaw/shim.test.ts, tests/daemon/runtime/capture/capture-handler.test.ts, tests/daemon/runtime/dashboard/harness-api.test.ts
New harness-identity-stamp suite parameterizes all six shims and verifies THE_SIX token completeness; OpenClaw shim tests updated for agentId/agent separation; capture handler tests assert sessions.agent column persistence per canonical token; harness API tests validate buildHarnessStatuses attribution with seeded/unseeded agent rows.

QA and Security Audit Reports

Layer / File(s) Summary
QA and security audit documents
library/qa/dashboard/2026-06-23-graph-edges-harness-turns-qa.md, library/qa/dashboard/2026-06-23-graph-edges-harness-turns-security.md
QA report covers both fix validations, scorecard, DoD gates, plan-item traceability, and file inventory with PASS verdict. Security audit covers per-category findings, verification gates, cross-cutting catalog results, a low-severity L-1 defense-in-depth note, and residual risk assessment.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • legioncodeinc/honeycomb#67: Implements the original mountGraphApi/GET /api/graph endpoint that this PR reasserts as the sole owner, making the route-collision fix directly downstream of that work.
  • legioncodeinc/honeycomb#72: Touches the same harness telemetry path—shim meta.agent stamping, OpenClaw derivation, and SQL GROUP BY agent for per-harness activity—which this PR corrects and regression-locks.
  • legioncodeinc/honeycomb#89: Adds the UI "Build graph" flow that calls POST /api/graph/build then re-hydrates via GET /api/graph, directly depending on the full GraphView response shape this PR establishes.

Poem

🐇 Two bugs had gone astray,
The edges: nowhere, turns: zero they'd say.
I mapped the links, I stamped the agent right,
Collision resolved — one owner, one sight!
The graph is full, the harness counts now true,
Hippity-hop, the dashboard's fresh and new! 🌿

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely summarizes the two main fixes: graph edge rendering and harness session turn attribution. It directly reflects the core changes across the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/dashboard-graph-edges-and-harness-turns

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
tests/daemon/runtime/capture/capture-handler.test.ts (1)

291-297: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert agent/agent_id persistence with column-aware checks, not raw substring presence.

These toContain(...) checks can be satisfied by the serialized message JSON alone, so they may pass even if the sessions.agent or sessions.agent_id mapping regresses. Please assert against the actual INSERT column-value mapping (column-position-aware parsing of the INSERT statement) to avoid false positives.

Also applies to: 310-311

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/daemon/runtime/capture/capture-handler.test.ts` around lines 291 - 297,
The current assertions using toContain("claude-code") and toContain("alice") are
checking for substring presence anywhere in the SQL statement, which is
insufficient because these values could appear in the serialized message JSON or
other parts of the INSERT statement rather than in the actual agent and agent_id
columns. Parse the INSERT statement in a column-aware manner to extract the
actual column-value mappings for the sessions table, then verify that
"claude-code" is specifically mapped to the agent column and "alice" is
specifically mapped to the agent_id column, rather than just checking for raw
substring presence.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@tests/daemon/runtime/capture/capture-handler.test.ts`:
- Around line 291-297: The current assertions using toContain("claude-code") and
toContain("alice") are checking for substring presence anywhere in the SQL
statement, which is insufficient because these values could appear in the
serialized message JSON or other parts of the INSERT statement rather than in
the actual agent and agent_id columns. Parse the INSERT statement in a
column-aware manner to extract the actual column-value mappings for the sessions
table, then verify that "claude-code" is specifically mapped to the agent column
and "alice" is specifically mapped to the agent_id column, rather than just
checking for raw substring presence.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b97192d1-7d03-484f-b160-577302e1ede5

📥 Commits

Reviewing files that changed from the base of the PR and between b9fb5a5 and 16c4165.

📒 Files selected for processing (15)
  • library/qa/dashboard/2026-06-23-graph-edges-harness-turns-qa.md
  • library/qa/dashboard/2026-06-23-graph-edges-harness-turns-security.md
  • src/daemon/runtime/assemble.ts
  • src/daemon/runtime/codebase/api.ts
  • src/daemon/runtime/dashboard/CONVENTIONS.md
  • src/daemon/runtime/dashboard/api.ts
  • src/hooks/index.ts
  • src/hooks/normalize.ts
  • src/hooks/openclaw/shim.ts
  • tests/daemon/runtime/capture/capture-handler.test.ts
  • tests/daemon/runtime/codebase/api.test.ts
  • tests/daemon/runtime/dashboard/api.test.ts
  • tests/daemon/runtime/dashboard/harness-api.test.ts
  • tests/hooks/harness-identity-stamp.test.ts
  • tests/hooks/openclaw/shim.test.ts

@thenotoriousllama thenotoriousllama merged commit 5cbfe22 into main Jun 23, 2026
15 checks passed
@thenotoriousllama thenotoriousllama deleted the fix/dashboard-graph-edges-and-harness-turns branch June 23, 2026 11:37
@github-actions github-actions Bot locked and limited conversation to collaborators Jun 23, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant