feat(agent-team): WebUI makeover — branching pipeline map + click-through task history #55
No reviewers
Labels
No labels
app
bug
ci
compliance
content
dependencies
docs
documentation
duplicate
enhancement
github_actions
good first issue
help wanted
infra
invalid
javascript
needs-triage
python
question
tests
wontfix
No milestone
No project
No assignees
1 participant
Due date
No due date set.
Dependencies
No dependencies set.
Reference: adam/orchestrator#55
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "feature/agent-team-webui-makeover"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What
A full makeover of the agent-team status dashboard (the LAN page on R720
:8770), delivering three asks:graph.py+ oneNODE_METAentry makes it appear, no coordinates.treeprocesses; the model supports multiple trees off theintakecoordinator.…plus an overall visual makeover: a React/Vite SPA replacing the inline HTML/SVG string.
How
Backend
db/schema.py: newtask_transitionstable (schema v3, additive migration +assert_task_transitions_readystartup check).db/transitions.py: fail-soft, idempotentTransitionRecorder(one open row per thread; no double-count under LangGraph resume replay; terminal-close fillsexited_at).graph.py:build_graphgains an injectedtransition_recorder; afunctools.wraps'd_instrumentwrapper records each node entry while preserving the node signature so LangGraph still injectsRunnableConfig(guards the prior config-injection bug). Wired atcoordinator.py.topology.py: derives the map fromcompiled.get_graph()+ aNODE_METAdisplay sidecar (label/agent/tree/kind/gated); classifies edges spine/branch/loopback.dashboard.py: new read-only FastAPI app (0.0.0.0:8770) serving/api/state(contract preserved + per-node live state),/api/topology,/api/task/{thread_id}(validated^[A-Za-z0-9_-]{1,64}$, timeline+cost join, partial fallback), and the built SPA. Kept separate from the authedapi.pyto preserve that security boundary.status_page.py: trimmed to the/api/statedata layer; inline HTML/SVG renderer + stdlib server retired.Frontend (
web/, new): React 18 + Vite 5 + TS + React Flow + dagre (all pinned, no CDN). Dark three-pane layout: summary top bar, filterable task list, auto-laid pipeline map, task-history drawer. Polls/api/stateevery 4s.dist/gitignored — built on the Mac, rsynced (box Node is too old for Vite 5+).Ops:
agent-team-status.serviceExecStart →dashboard.serve(uvicorn).Tests
test_transitions.py(idempotency, terminal close, fail-soft, B4 signature-preservation, end-to-end graph drive),test_topology.py(meta coverage, trees, edge kinds),test_dashboard.py(endpoints, validation, partial, cost join).test_status_page.pyreworked to the data layer.Gates
cross_reviewer) on the_instrumentwrapper,db/transitions.py, and/api/task— no BLOCKs; FIX/NIT findings applied (commit6951bf2)./sh-security-review(mandatory agentic pass for the new untrusted-input surface) — outstanding, run before merge./sh-deploy-r720— deploy-before-merge: Hyper-V snapshot → ledger backup BEFORE rsync → whole-package rsync +web/dist→ restart → asserttask_transitionsexists → post-deploy asset check. Confluence (host 20054017 + runbook 22609921) + project memory updates land with the deploy.Notes
main(verified: prior dashboard/#47-#49 already merged; thefeat/ws-activation-wiringassumption in project memory was stale).@ -0,0 +266,4 @@"edges": [],"error": type(exc).__name__,}return JSONResponse(payload, headers={"Cache-Control": "no-store"})CodeQL / Information exposure through an exception
Stack trace information flows to this location and may be exposed to an external user.
Show more details
/sh-security-review— PASS (no block)High-recall detector fan-out (injection, authz, secrets/data-exposure, web-client/XSS, logic — 5 fresh-context adversarial detectors) + a proof-or-kill verifier over the new surface (
dashboard.py,db/transitions.py,graph._instrument,db/schema.py,topology.py,status_page.py, and the React SPA).Gate:
confirmed_critical=0,confirmed_high=0→ PASS.Confirmed (1, low) — FIXED in
a632a7d:/api/stateembeddedstr(exc)of a sqlite/OS error into the payload, leaking the absolute DB path / table names to the unauthenticated LAN surface. Now returnstype(exc).__name__only (matching thetask_detailhardening). Applied the same to/api/topology(SEC-DASH-003).Verified safe (refuted / not request-reachable):
^[A-Za-z0-9_-]{1,64}$, anchored, used only as a bind param.dangerouslySetInnerHTML/innerHTML;encodeURIComponenton the id.mode=ro; the writableTransitionRecorderis never imported by the dashboard (RO-dashboard / RW-coordinator separation holds);/api/taskIDOR is by-design within the documented LAN-only/unauth threat model (no privilege tier, secrets excluded)._instrumentadds no new crash path (recorder calls are fail-soft;GraphInterruptpropagates correctly).Unverified / accepted tech-debt (downgraded by the verifier — latent, not request-reachable):
BEGIN IMMEDIATE): latent race; no concurrent same-thread writer exists in the current wiring (ResumeWorker single-flight; start_task completes before any resume). Tracked as hardening.