← Back to Dashboard
Build Log · Internal
How the Dashboard Works
A running log of what's been built, how data flows in, what each piece of logic actually does, and what we still need to wire up. Update this as we go.
What the Dashboard Is
The Client Health Dashboard is a live snapshot of every HelloSciCom client we currently work with. Each card shows hours used vs. retainer this month, the hourly rate (with retainer discount), a link to the contract, the creative lead, and the status of next month's retainer. Cards are clickable for a detail/drill-down view.
Goal: at a glance, everyone on the team can answer "are we on track with this client, and is the client looped in?"
Where the Data Comes From
- Hours used (this month): Pulled live from Avaza via the Avaza REST API. A Personal Access Token (
AVAZA_API_KEY) stored in ~/.openclaw/openclaw.json authenticates a script at ~/.openclaw/scripts/refresh-dashboard-from-avaza.py. That script aggregates entries by customer + project and PUTs to the dashboard data API. Scheduled by macOS LaunchAgent com.helloscicom.avaza-dashboard-refresh — runs every hour 9 AM–7 PM ET Mon–Fri, plus noon ET Sat/Sun.
- Retainer (this month + next month): Stored in KV. Updated by the creative lead asking Potoo, or directly via the dashboard's signoff toggle.
- Hourly rates, contract URLs, creative leads, goals: Stored in KV. Seeded from a default object in
functions/api/dashboard-data.js, then overridden by anything saved to KV.
- Signoff state ("client informed"): Stored per-client in KV under
currentMonthSignoff. Toggled directly from the dashboard.
- Under-trigger dismissal: Stored per-client in KV under
currentMonthUnderDismissed. Silences the "running low" warning for the rest of the current month.
Storage: Cloudflare KV namespace CRM_DATA, key dashboard:v1. Reads via GET /api/dashboard-data, writes via PUT with header X-Potoo-Secret.
The Two Pieces of Logic That Drive Color
1. Countdown to the 15th (next month's retainer)
Each month, by the 15th, creative leads need to confirm next month's retainer with their clients. The countdown box on each card is a gentle nudge to make that happen.
- Hidden 1st–7th of the month (too early to nag).
- Visible 8th–15th, ticking down with a "days left" counter. Last 3 days highlight in amber.
- After the 15th: if still unconfirmed, the box goes red and reads OVERDUE.
- Once a client is marked confirmed for next month, the countdown is replaced with a calm "Confirmed" line until the next cycle.
- Potoo emails creative leads on the 10th with a suggested email to send to each client. (Cron job pending — see Roadmap.)
2. Health bar color (hours used vs. retainer)
The bar fills with the percentage of retainer hours used. The colors mean:
- Purple (default): Healthy. Anything under 80% used and not in either trigger condition.
- Amber: 80–100% used, no triggers fired. Heads up but not urgent.
- Red — Over: Hours have exceeded the retainer. The "Client informed?" pill appears. Bar stays red until the creative lead marks the client as informed, then it returns to green/healthy.
- Red — Running Low: Within the last 5 days of the month, and less than 60% of retainer hours have been used, and the retainer is at least 10 hours. A "What's going on?" pill appears with a dismiss option. Designed to under-trigger — most creative leads plan hours their own way and we don't want to nag.
Why the under-trigger is conservative: Sarah's note in the build meeting — err on the side of not triggering. Creative leads plan hours their own way and we don't know what's "normal" for each client. The 60% / 5-days / 10-hrs floor combo means this fires rarely, and there's a one-click dismiss for the rest of the month if it's a false alarm. Tunable in dashboard.html → computeUnderTrigger().
The "Client informed of over/under?" toggle
Only appears when a trigger has fired (over or under). The most important thing is always communication to the client — going over or under is fine as long as they know. Once toggled on:
- The bar returns to a healthy (green/purple) state.
- The pill flips from "client not informed" → "acknowledged".
- State is saved per-client per-month in KV.
On the 1st of each month, signoff and dismissal state reset automatically (handled by the data layer).
Files & Endpoints
- Main page:
frontend/dashboard.html
- This page:
frontend/dashboard-notes.html
- Drill-down detail page:
frontend/dashboard-detail.html
- Drill-down router:
frontend/functions/dashboard/[[catchall]].js
- Data API:
frontend/functions/api/dashboard-data.js · GET for reads, PUT with X-Potoo-Secret: potoo-dashboard-2026 for writes. Powers hours, retainers, signoff state.
- Detail API:
frontend/functions/api/dashboard-detail.js · GET/PUT same secret. Powers the per-client detail pages (contacts, activity log, scope/objectives, working notes).
- Refresh script:
~/.openclaw/scripts/refresh-dashboard-from-avaza.py · Avaza→KV sync; LaunchAgent com.helloscicom.avaza-dashboard-refresh runs hourly business hours + weekend noon.
- KV store: namespace
CRM_DATA · dashboard:v1 (main data) · dashboard:detail:v1 (detail pages).
- Shared nav:
frontend/partials/nav.html · injected into every page via scripts/inject-nav.py.
What I Need from Willa / Sarah
Data still to fill in
- Client contacts + recent activity for the skeleton detail pages: Poley, Cobblestone, JLS, talkSTEM, IEEE. Outreach emails sent to Andrew + Hebba 2026-06-02; replies expected by end of week.
- RWJF Comedy Camp contacts + recent thread — Andrew's covering this (info-request email sent 2026-06-02).
- Carnegie: Jeanine's email — named in contract Appendix B; Sarah asked why we need it (answered: building a stakeholder contact list per client). Low priority.
- Per-client tone notes — one-line vibe per client so future auto-drafted emails sound right.
Recently confirmed
- Hourly rates set: JLS, talkSTEM, RWJF ($200/hr each), Carnegie ($130/hr flat-fee).
- Creative leads assigned: RWJF (Sarah + Andrew), JLS (Andrew), talkSTEM (Andrew + Hebba), Cobblestone (Hebba), IEEE (Hebba), Carnegie (Sarah + Willa).
- Contract URLs added: Poley, Carnegie.
- RWJF grant context: $200K total / $113K HSC labor, full Comedy Camp ownership map + budget structure on the detail page (provided by Sarah 2026-06-03).
Changelog
2026-06-04
v2 (current) — Nav refactor, confirm flow, internal-strip scoping
Site-wide top navigation replaces the per-page hamburger menus. Built as a single partial (partials/nav.html) injected into every page via scripts/inject-nav.py. Structure: Chat · Clients ▾ (Dashboard, CRM, Prospects, Research) · Potoo Corner ▾ (Health, Mind, Skills) · North Star. Desktop hover-bridge dropdowns; mobile collapses to a left-side hamburger.
Standardized content widths to 1200px (chat at 1000px). All pages now feel desktop-first; mobile is responsive.
Next-month retainer confirm flow: unconfirmed cards now show "Suggested: XX hrs" in greyed italic + a clickable [unconfirmed] pill. Click → modal asks "Has next month been confirmed? For how many hours?" → on yes, PUTs new hours + confirmed=true + timestamp to KV; bar flips to confirmed state immediately.
Internal Hours strip moved inside the Retainer tab (was visible under both tabs).
Memory sync: retainer confirmations now flow into Potoo's heartbeat → MEMORY.md so I track the history of when each client confirmed each month.
2026-06-02 → 2026-06-03
Per-client detail pages
Built KV-backed detail page system (functions/api/dashboard-detail.js, key dashboard:detail:v1). Each client gets a profile with headline meta, contacts, latest activity, scope/objectives, working notes, and a "data needed from CL" reminder banner. Card layout adapts to clientType: "flat-rate" | "retainer".
Carnegie detail page fully populated (5 Carnegie contacts, 6 SOW sections, hosts, contract terms). RWJF skeleton built; filled in 2026-06-03 with Sarah's grant context: $113K HSC labor of $200K total, full Comedy Camp ownership map, 4-bucket budget structure, contractor lines (with $24K/$20K flagged for revision), 12-month timeline.
Info-request emails sent to Andrew (JLS, talkSTEM, RWJF), Hebba (Cobblestone, talkSTEM, IEEE), Sarah (RWJF + Carnegie). Sarah replied 2026-06-03; Andrew + Hebba pending (deadline: end of week).
2026-06-02
Two-tab split + flat-rate cards
Dashboard split into Monthly Retainer tab (Poley, Cobblestone, JLS, talkSTEM, IEEE) and Flat-rate / Project tab (RWJF, Carnegie). Flat-rate cards show $-spent / $-budget with progress bar (dollars-aware), event date / grant context, and lifetime hours.
Refresh script gained LIFETIME_START per-client filtering + Phase C lifetime aggregation. Carnegie scoped to Oct-2026 contract only ($-spent now reads 4% of $8K, not 263% of all-time work).
2026-05-22
RWJF Comedy-Camp invoice automation
Monthly cron c3ce752e-9518-4fb5-8590-d8960ed9e95a fires 12:15pm ET on the 1st: pulls RWJF (a.k.a. "Comedy Camp") hours per contractor from Avaza, multiplies by $200/hr, writes into the Labor_Detail tab of the Invoice Tracker (rows 7–11, Apr-26 → Mar-27 columns). Confirmed working April-2026 trial with Mattheus + Eli.
2026-05-14 · later same day
Build Log truth pass
Caught and corrected stale language on this page and on the dashboard: hours are not email-parsed — they sync live from Avaza via the existing REST API integration (LaunchAgent + Python script described above). Roadmap item "build the Avaza integration" reframed as "add a verifier pass to the existing sync."
2026-05-14
v2 — Smarter color logic, countdown windowing, build-log page
Countdown box now hides outside the 8th→15th window (and shows "OVERDUE" after the 15th if unconfirmed). The "Client informed?" pill no longer appears by default — it only surfaces when an over-trigger or under-trigger fires. Added a new under-trigger (≤5 days left + <60% used + retainer ≥10 hrs) with a one-click dismiss for the rest of the month. Created this page and moved all build notes here from the bottom of the dashboard.
earlier
v1 — Live Avaza hours
Hours pull directly from Avaza (current month, all entries). Sub-client roll-ups working for Poley and JLS. Retainer-tier rate discounts (-$20 at 10 hrs, -$30 at 20, -$40 at 40, -$50 at 60) applied to displayed rate. Sticky header + drill-down detail pages live.
v0
v0 — Draft / Internal Tool
Initial cards, seed data, KV-backed signoff toggle.
Roadmap
- Cron: 10th-of-the-month email to creative leads — auto-generated draft per client for the lead to forward, asking the client to confirm next month's retainer. Blocked on: per-client tone notes.
- Cron: daily over/under check — when a trigger fires, email the creative lead with suggested client-facing language and a link to mark "informed".
- Avaza sync verifier (double-up) — add a second independent pass via
/api/TimesheetSummary grouped by project, compare totals to the entry-level sum, refuse the update if drift > ±0.05 hrs.
- Detail page coverage — finish filling Poley, Cobblestone, JLS, talkSTEM, IEEE, RWJF detail pages with contacts + activity logs once Andrew/Hebba reply.
- Sub-creative-lead emails — for Poley sub-clients (Cloudflare, Twilio, Google), route the 10th-of-month email to the right sub-lead.
- Build a "past clients" section — for Kate Barr, OpenAI, DCF, Honda (paused, not defunct — see MEMORY.md). Surface them in outreach/sales context but keep off the main grid.
Done since v2
- ✓ Potoo health monitor live at /health.
- ✓ Two-tab split: Monthly Retainer vs. Flat-rate / Project.
- ✓ Flat-rate cards (RWJF, Carnegie) with dollar-based budgets + lifetime hours.
- ✓ Per-client detail pages with KV-backed profile system (contacts, activity, scope, notes).
- ✓ Lifetime hours aggregation in refresh script (Carnegie scoped to Oct-2026 contract only).
- ✓ Sitewide shared navigation (top bar + mobile hamburger).
- ✓ Next-month retainer confirm flow (click "unconfirmed" → modal → confirm hours → KV write).
- ✓ Internal Hours strip scoped to Retainer tab only.
This page is internal to HelloSciCom. Last meaningful update: 2026-06-04.