AgentWorks

AgentWorks documentation

Org Dashboard — design

Status (2026-08-03): historical design record; implementation evolved. The card-based Org dashboard was built, but the two-loop writer model and fixed Pulse sequence below are no longer the current Pulse contract. Read pulse_consolidation.md for current review/fix architecture and workflow_monitoring.md for the current database-native per-workflow Pulse popup. The body below is retained to explain the Org dashboard's card-assembly decisions.

Problem

Today the Org page (frontend/.../org/OrgHtmlPanels.tsx) just embeds two HTML docspulse/goals.html (scorecard) and pulse/org-pulse.html (the CoS daily journal). It's documents to read, not a dashboard, and it's only as fresh as the daily CoS Org Pulse. You can't see, at a glance, "what is the whole org doing, are we meeting our goals, and if not what's being done about it."

Principle

Bottom-up, assembled at view time. Each workflow keeps its own status current as it runs; the Org page assembles those into the live dashboard. No central file that multiple writers share. No separate JSON data model — the agents keep producing readable HTML (see "Data carrier").

Two loops = three axes (the core insight)

Every workflow runs two distinct loops; they answer different questions and own different parts of a card:

Loop Cadence Job Axis Statuses
Pulse per run fix — keep it working (triage → low-risk fix → backup → notify) 🩺 Operational ("is it working?") healthy / bug / critical
Pulse report step per run report — surface spend and elapsed-time telemetry without optimizing 💵 Cost/time ("is it spending sanely?") normal / elevated / missing
Auto-improve scheduled improve — move it toward its goal long-term (experiments, replan, harden, db/KB/learnings) 🎯 Goal progress ("is it winning?") on-track / at-risk / off-goal

"Working but off-goal" is a normal, important state (runs fine, not moving the goal) — the dashboard must surface it. The user's "are we meeting goals + what steps" lives mostly on the 🎯 Auto-improve axis; Pulse is the keep-the-lights-on axis underneath.

Writers & ownership (no shared file)

Many writers (every workflow's two loops + the CoS), so each owns distinct files:

pulse/
  goals.html                    ← CoS Org Pulse (goal scorecard)            [daily]
  org-pulse.html                ← CoS Org Pulse (dated journal)             [daily]
  cards/
    <workflow>.health.html      ← Pulse loop        (🩺 + last fix + trend) [per run]
    <workflow>.cost.html        ← Pulse report step (💵 + spend/time)       [per run]
    <workflow>.progress.html    ← Auto-improve loop (🎯 + improvement)      [per improve run]

A workflow's Pulse only touches *.health.html and *.cost.html; its Auto-improve only touches *.progress.html; the CoS only touches goals.html/org-pulse.html. No two writers ever share a file → no races even when everything runs at once.

The card (assembled per workflow from its fragments)

Substack growth
🩺 Working   (Pulse: no bugs, last fix 3d ago)
💵 Cost ok   (Pulse report: $0.12 / 18k tokens · top spend reviewer)
🎯 Off-goal  (Auto-improve: open-rate flat · experiment "subject lines" running 2/5)
Trend ●●🔴🟢🟢                                          → click: improve.html (full history)

Mechanism — how a loop updates the dashboard

Parallel to notify_user: a tool (e.g. update_org_dashboard) each loop calls with its card fields. Important cadence difference from notify:

Pulse run → improve.html (detailed log)
          → update_org_dashboard(health + cost cards) ← ALWAYS (keeps dashboard live)
          → notify_user(email/whatsapp)          ← ONLY on a decision-worthy change

notify_user is sparing (don't spam); the card updates every run so the dashboard is always current, not "last time something was notable." Auto-improve does the same for its progress card.

Layout

UI — a real dashboard, not embedded HTML

The Org page must render its own React components (status pills, goal groups, filters, sort, triage bar) from the card fieldsnot embed the agents' raw HTML (that's the doc-like, inconsistent surface we're replacing). goals.html/org-pulse.html remain as embedded drill-down docs.

Data carrier (decision: data-attributes; JSON noted as the alternative)

The React UI needs the card fields without a JSON data model. Chosen: data-* attributes on the HTML card fragments — honors "no JSON" (the loops keep writing readable HTML), and the app reads data-status/data-goal/data-updated plus data-field="headline", data-field="metric", and data-field="detail" to render its components, group by attention status, sort, and filter. data-goal is a short distilled label (3-6 words), not the raw success_criteria text — Pulse (card.health.html and card.cost.html, every run) and Auto-improve (card.progress.html, on fire) write it, so the goal chip shows up from the workflow's first run rather than waiting on the less-frequent Auto-improve loop.

<article class="pulse-card" data-workflow="substack" data-axis="health"
         data-status="healthy" data-goal="grow-subs" data-updated="2026-06-30T09:12Z">
  ...visible card content...
</article>

Trade-off (documented): the app parses HTML to extract fields (slightly hacky). If a cleaner feed is preferred later, a tiny per-card JSON ({status, verdict, goal, headline, next}) is the alternative — but that reintroduces the JSON we chose to avoid.

Classification

Split across the three axes (loop-set, not UI-derived):

What stays (drill-downs, unchanged)

Open / future

Implementation notes — corrected architecture (2026-06-30)

The "Writers & ownership" section above assumed ONE shared pulse/cards/ dir. That is wrong: workflows are separate workspaces (each has its own workspacePath; builder/improve.html is per-workflow — see auto_improvement_endpoints.go). A per-workflow loop cannot write into the org/CoS workspace's pulse/. Corrected, simpler model (as wired):