Files
waggle-os/docs/redesign-warm-hive/pr3-recon/home.md
Oleg Maslov 0c3e2ead3b
Some checks failed
Installer Smoke / installer-smoke (push) Has been cancelled
moving
2026-09-02 10:10:29 +02:00

24 KiB

PR3 Recon — SCREEN 01 · Home / Cockpit (ship Variation A "Editorial")

Maps the current Home/Cockpit implementation against the warm-Hive design spec for SCREEN 01. Scope: what renders today, the live data contract, the test contract that must keep passing, the Editorial target with exact copy, the gap list, and the reuse/build plan.

Primary files:

  • Current UI: apps/web/src/components/os/apps/HomeCockpit.tsx (the real Home; this is the one PR3 rebuilds)
  • Route wrapper: apps/web/src/routes/HomeRoute.tsx
  • CockpitApp.tsx (apps/web/src/components/os/apps/CockpitApp.tsx) is a separate system-health dashboard (Mission-Control-style), NOT the Home surface — see note in §1.
  • Server contract: packages/server/src/local/routes/home.ts
  • FE types: apps/web/src/lib/types.ts:266-331
  • Test contract: apps/web/src/test/p2-home-desktop.test.tsx
  • Design: docs/design_handoff_waggle_app/SCREENS.md §"01 · Home / Cockpit" + design-files/screens/home.html (view #view-a)

1. Current structure — what HomeCockpit.tsx renders today (section by section)

HomeCockpit is the default /home surface (HomeRoute.tsx:13). It is a single scrolling column, p-6 max-w-3xl mx-auto on the sub-states and max-w-4xl mx-auto on the normal render (HomeCockpit.tsx:580). Data loads in load() (HomeCockpit.tsx:467-497): adapter.getHomeBriefing() then a best-effort adapter.getHomeOvernight(); deferred until useService().connecting settles (cold-load 401 race guard, :499-507).

Render states (root, :509-650):

  1. LoadingCockpitSkeleton (:84-98), data-testid="home-cockpit-loading".
  2. Permission denied (403):514-527, data-testid="home-cockpit-permission-denied".
  3. Load error / offline:531-554, data-testid="home-cockpit-error" + Retry.
  4. First-run emptyFirstRunEmpty (:101-125), data-testid="home-cockpit-empty", "Create your first workspace" CTA.
  5. Normal:579-649, data-testid="home-cockpit".

Normal render, top to bottom:

  • GreetingHeader (:136-159, rendered :581): H1 = briefing.greeting (text-2xl font-display font-bold), sub = formatBriefingDate(briefing.date) rendered as a human date (:130-134). Right side: optional offline "Local only" pill + a static Ctrl+K chip (:153-156). No mono date row, no live dot, no streak chip.
  • Attention banner (:583-599, data-testid="home-cockpit-attention-banner"): only when overnight.failures.length > 0. Honey-tinted role="alert".
  • J08 review banner (:601-625, data-testid="home-cockpit-review-banner"): only when briefing.needsReviewCount > 0; "N imported memories need your review" + Review CTA that dispatches waggle:open-app {appId:'memory', filter:'unreviewed'} (:573-577).
  • RecentWorkspacesPanel (:162-227, rendered :627-632): section head "You were working on" (:174-176), 2-col grid (sm:grid-cols-2). Each card: name + group chip, optional 2-line summary, lastActive relative time + pendingCount "pending" warning, a "Continue →" button (:208-215), and a WorkspaceActionsMenu kebab (G1 rename/archive/delete). Card body click → onOpenDesktop. Returns null when 0 cards.
  • OvernightPanel (:230-288, rendered :637, suppressed when offline): section head "Overnight", a 3-counter grid (Memories consolidated / Artifacts created / Automations completed), then a failures block (deep-links each failure to the Automation Center logs, :271-280). Hidden when no activity.
  • SuggestedActionsPanel (:321-343, rendered :639): section head "Suggested next actions", wrap of pill buttons from briefing.suggestedActions, each → onContinue(a.workspaceId, a.sessionId).
  • UpNextPanel (:291-318, rendered :643): section head "Up next", list of up to 6 items (event/task/schedule icons). Returns null when 0 items.
  • QuickCapturePanel (:346-434, rendered :645): "Quick capture" — a 4-kind segmented selector (note/task/link/file) + text input + Capture button → adapter.quickCapture(). Not in the Editorial design.
  • ActiveModelsTile (:437-448, rendered :647): tiny "Active models: …" chips row when briefing.activeModels present.

Styling today: uses shadcn semantic classes (bg-secondary/30, border-border/30, text-muted-foreground) and inline var(--sem-intelligence) / --sem-attention / --sem-work / --sem-healthy / --sem-risk semantic tokens. Layout is dense and utilitarian (10-13px text, rounded-xl, compact panels) — the opposite of the Editorial spec's calm, large-type, story-led 920px column.

Note on CockpitApp.tsx: despite the name, this is the system-health/ops dashboard (System Health, Cost, Memory Weaver, Cron, Connectors, Audit Trail, ComplianceDashboard) on a 30s refresh. It is not the Home surface and not in scope for SCREEN 01 — it corresponds to "Mission Control" (SCREENS §16) / "surfaces" (§08). Listed in the task only to disambiguate; PR3 Home work happens entirely in HomeCockpit.tsx.


2. Data contract — HomeBriefing shape + feeding routes (real vs mocked)

Types (FE mirror apps/web/src/lib/types.ts:266-331; server home.ts:43-95)

interface HomeBriefing {
  greeting: string;            // server builds via buildTimeAwareGreeting + personalizeGreeting (home.ts:401-407)
  userName?: string;           // from IdentityLayer on personal mind (home.ts:261-270); optional
  date: string;                // now.toISOString() (home.ts:412) — RAW ISO, FE must humanize
  recentWorkspaces: RecentWorkspaceCard[];
  suggestedActions: SuggestedAction[];
  upNext: UpNextItem[];
  activeModels?: string[];     // OPTIONAL — server briefing NEVER populates it (no producer in home.ts)
  isFirstRun: boolean;         // ranked.length === 0 (home.ts:418)
  needsReviewCount?: number;   // J08 unreviewed personal-mind frames (home.ts:382-389)
}

interface RecentWorkspaceCard {
  id; name; group;             // group = workspace.group string
  summary?;                    // state.recentDecisions[0].content (home.ts:314-316)
  lastActive: string;          // ISO; ranking timestamp
  pendingCount: number;        // state.pending.length + state.blocked.length
  continueSessionId?;          // NOTE: server never sets it (home.ts card builder omits) → Continue has no session seed
}

interface SuggestedAction { label; workspaceId; sessionId?; kind; }  // kind always 'next-action' from server
interface UpNextItem { id; label; workspaceId?; at?; kind: 'event'|'task'|'schedule'; }
                       // server only ever emits kind:'schedule' from cron; 'event'/'task' are type-only
                       // server NEVER sets `at` → UpNextPanel's time column is always empty today

interface OvernightSummary {       // GET /api/home/overnight (separate call)
  consolidated; artifactsCreated; automationsCompleted;
  failures: OvernightFailure[];    // {id,label,automationId?,error,at} from cron execution history
  window?: { from; to };
}

Routes feeding Home

Route Method Adapter Source / realness
/api/home/briefing GET adapter.getHomeBriefing() (adapter.ts:612-615) Real. Server-side cross-workspace fan-out (home.ts:257-423); personal-only (A2), excludes team/archived. Reuses buildWorkspaceState, buildTimeAwareGreeting, buildUpcomingSchedules, IdentityLayer, cron store, audit DB.
/api/home/overnight GET adapter.getHomeOvernight(since?) (adapter.ts:617-621) Real. Audit-event counts (memory_write → consolidated; file-write tool_call → artifacts) + cron execution history → automations/failures (home.ts:425-503).
/api/quick-capture POST adapter.quickCapture() (adapter.ts:623-627) Real. Used by QuickCapturePanel (not in Editorial layout).

Real vs mocked / missing, per design need

  • REAL: greeting, userName, date (ISO), recentWorkspaces (name/group/summary/lastActive/pendingCount), suggestedActions, upNext (schedule labels only), overnight counters + failures, needsReviewCount.
  • NOT produced by the server (treat as absent):
    • continueSessionId — never set → "Continue" can't target a session (already a known gap, HomeRoute.tsx:16-19).
    • UpNextItem.at (time) and event/task kinds — never set; only schedule from cron.
    • activeModels — typed-optional, no producer; tile never shows from the real route.
  • NO DATA ANYWHERE (must mock for the Editorial design):
    • 🔥 streak chipgrep across packages/server/src and apps/web/src finds no streak field. Habit-loop spec (SCREENS §15) says the streak lives on Home but it is unimplemented. Needs-mock (or a follow-up backend field).
    • Overnight "story" sentence + run chips with labels ("Teardown drafted · 9 competitors") — server gives raw counts, not a composed narrative or per-run labels. The narrative must be composed client-side from OvernightSummary (counts + failures), and the first "Teardown drafted" run chip has no backing field (would need the top suggestedAction/workspace summary as a proxy) → partial mock.
    • Workspace card hex avatar glyph / "agent live" badge — no glyph/live field; derive glyph from name[0], and there is no per-card live-agent flag → derive / mock badge.

3. Test contract — apps/web/src/test/p2-home-desktop.test.tsx

These assertions constrain the rebuild (the describe('HomeCockpit (P2)') block, :73-129). All must keep passing. The harness renders HomeCockpit bare with mocked adapter, useService ({connecting:false, connected:true}), offline=false, and a stub ShellContext (:38-46).

Required behaviors / DOM contract:

  1. Root testid — after load, screen.getByTestId('home-cockpit') must exist (:81). Keep data-testid="home-cockpit" on the normal-render root.
  2. J08 review banner (:84-98) — with needsReviewCount:3, data-testid="home-cockpit-review-banner" renders text containing "3 imported memories need your review", and data-testid="home-cockpit-review-cta" click dispatches exactly waggle:open-app with detail { appId:'memory', filter:'unreviewed' }. Keep the banner, its copy pattern, the CTA testid, and the event payload.
  3. Banner omitted at 0/undefined (:100-103) — no home-cockpit-review-banner when needsReviewCount is undefined.
  4. Up next omitted when empty (:105-111) — no home-cockpit-upnext when upNext is [] or undefined.
  5. Up next present with items (:113-118) — home-cockpit-upnext contains the item label ("Weekly digest") when ≥1 item.
  6. Human date (:120-128) — the raw ISO (2026-06-11T07:42:13.512Z) must not appear; the date must render via new Date(iso).toLocaleDateString(undefined, {weekday:'long', month:'long', day:'numeric'}). Keep formatBriefingDate semantics; the mono date row must humanize, not print ISO.

The mocked briefing factory (:57-67) defines the minimum shape the component must tolerate: {greeting, userName:'Marko', date, recentWorkspaces:[], suggestedActions:[], upNext:[], isFirstRun:false, needsReviewCount:0}. The Editorial rebuild must still render home-cockpit with all-empty arrays (no crash on empty overnight/workspaces).

Implication: PR3 may freely restyle and re-lay-out, but must preserve these testids + behaviors: home-cockpit, home-cockpit-review-banner, home-cockpit-review-cta (+ event payload), home-cockpit-upnext (present/absent rules), and human-date rendering. The up next empty/present rule means the Editorial "Up next" (if kept) stays conditional. Other testids (home-cockpit-overnight, home-cockpit-suggested, home-cockpit-continue-*, quickcapture testids) are not asserted in this file — they can be renamed/removed if their features are reshaped.


4. Design spec (Editorial / Variation A) — target sections + exact copy

From SCREENS.md §01 (ship = Variation A) and home.html #view-a (:157-186). Layout: single centered column, max-width 920px, 46px top padding (home.html:31). Sections top to bottom:

4.1 Greeting (home.html:158-161)

  • Mono date row (--honey, uppercase, .1em tracking) with a live dot (.dot-live, --healthy) on the left, and a right-aligned 🔥 streak chip (pill, --honey-wash bg, --honey-line border, --honey text).
  • H1 Hanken 600, clamp(34px,5vw,52px), line-height 1.02; the keyword "ahead" is honey (em, not italic; --honey).
  • Exact copy: date "Friday · June 14 · 8:42" (compose from briefing.date); streak "🔥 12-day streak"; H1 = "Good morning, Mara." / "You're ahead of yesterday."
    • Maps to: greeting → briefing.greeting (real, already personalized server-side; the design's literal "Good morning, Mara." is buildTimeAwareGreeting's output). "You're ahead of yesterday" second line + the honey "ahead" → needs-mock / composed (no "ahead vs yesterday" signal exists). Date → real (briefing.date, reformatted with time). Streak → needs-mock.

4.2 Overnight hero card (home.html:163-172)

  • Radius --r-xl, gradient --surface → --surface-2, --shadow, soft honey radial glow top-right (::after). Mono eyebrow with --intel dot.
  • Eyebrow (exact): "While you slept".
  • Story line (exact): Hanken 600, clamp 21→28px, max-width 30ch; honey key numbers (b): "Waggle finished the Q2 competitor teardown, folded 14 new memories into the hive, and ran into one snag worth a look."
  • Run chips (exact, status dot + label):
    • "Teardown drafted · 9 competitors" (dot --healthy)
    • "14 memories consolidated" (dot --intel)
    • "2 artifacts created" (dot --work)
    • "1 export failed" (dot --risk)
    • Maps to: counters are real (overnight.consolidated, .artifactsCreated, failures.length). The composed sentence + the "Teardown drafted · 9 competitors" label are needs-mock/composed (no narrative producer; no per-run "9 competitors" field). Build the sentence from counts + failures client-side; degrade gracefully when overnight is null/empty (must still render home-cockpit).

4.3 "Pick up where you left off" (home.html:174-175, 287-299)

  • Section head = mono uppercase --text-dim with trailing hairline rule (.sec-h).
  • 2-col grid (.ws-grid) of .ws-card: hex avatar (honey gradient, glyph = first letter), title (16px, 650), time (mono --text-dim, e.g. "2h ago"), summary (13.5px --text-muted), footer = "Continue →" (honey) + optional status badge (pend honey-wash "3 to review" / live healthy-wash "agent live"). Hover: honey border + translateY(-3px) + --shadow.
    • Maps to: realrecentWorkspaces[].name, .summary, formatRelative(lastActive), .pendingCount (→ "N to review"/"N pending"). Hex glyph = derive from name[0]. "agent live" badge → no field, mock/omit. Section head copy must change "You were working on" → "Pick up where you left off".

4.4 "Waggle suggests" (home.html:177-178, 301-306)

  • Section head "Waggle suggests". Stacked .move rows: tinted icon tile (--*-wash bg), title (14.5px 600) + sub (12.5px --text-muted), arrow that slides on hover.
  • Exact sample copy (design data): "Review the competitor teardown" / "9 competitors · ready since 02:40 · ~6 min read"; "Send the board update" / "Draft built from this week's work in Q2 Board Deck"; "Confirm 3 imported memories" / "From Tuesday's pricing call — Waggle wants your sign-off".
    • Maps to: title → real suggestedActions[].label. The sub-line has no backing field (server emits label only, kind:'next-action') → needs-mock/derive (e.g. workspace name + relative time). Section head copy "Suggested next actions" → "Waggle suggests". Rows become stacked (not pills).

4.5 Ask bar (home.html:180-185)

  • Full-width pill (.ask), honey "+" icon left, text input (placeholder "Start something new — "draft the board update from this week's work"…""), mono "⌘K" hint, honey round send button ("→"). Focus → honey border + glow.
    • Maps to: net-new on Home. No current "ask bar" on HomeCockpit. Wire send → onContinue/new-chat or open ⌘K (PR2 CommandCenter). This replaces the current QuickCapturePanel as the primary input affordance. The "+" can keep a quick-capture role, but the design's primary intent is "start a task".

4.6 Not in Editorial (drop or relocate)

  • Quick capture segmented panel — replaced by the ask bar; the adapter.quickCapture API can be kept behind the "+" icon or dropped from Home.
  • Active models tile — not in Editorial (and route never populates it). Drop.
  • Attention/review banners — not literally in the Editorial mock, but the J08 review banner is test-locked (keep it; can be styled as a run-chip-adjacent attention row or kept above the hero). The overnight-failure attention banner maps naturally onto the "1 export failed" run chip + (optionally) Variation C's risk alert pattern.

5. Gap list

Design section Current state Gap Data available? Severity
920px centered column, 46px top pad max-w-4xl mx-auto p-6 dense Re-layout to 920px / pt-[46px], larger type scale n/a (layout) MED
Mono date row + live dot Plain text-sm sub under H1, no dot Add mono uppercase honey date row + .dot-live date = real; format with weekday+time MED
🔥 streak chip (right pill) Absent Add streak chip No data — needs-mock or new backend field HIGH
H1 with honey "ahead" keyword H1 = raw greeting, no honey span Two-line H1; honey-span a keyword greeting real; "ahead of yesterday" composed MED
Overnight hero card (gradient + glow) OvernightPanel = 3 plain counters + failures, suppressed offline, hidden when empty Rebuild as hero card w/ eyebrow + story + run chips; must still render when empty/null counts real; story + run-chip labels composed/mock HIGH
Run chips (4, status-dotted) Counter tiles only Render chips w/ status dots; map counts 3 of 4 real (consolidated/artifacts/failed); "Teardown drafted · 9 competitors" mock MED
"Pick up where you left off" head "You were working on" Copy + .sec-h hairline style n/a (copy) LOW
Workspace cards: hex avatar + 16px title + Continue→ + badge Cards w/ name+group chip, Continue button, kebab Add hex avatar (glyph from name), restyle; keep Continue + kebab name/summary/time/pending real; glyph derived; "agent live" mock/omit MED
"Waggle suggests" stacked rows w/ sub-line "Suggested next actions" pills, label-only Copy + restyle to .move rows; add sub-line label real; sub-line composed/mock MED
Ask bar (pill, +/⌘K/send) None on Home (QuickCapture panel instead) Net-new ask bar; wire to chat/⌘K n/a (action wiring) MED
continueSessionId for targeted resume Plumbed but server omits Continue lands at chat root, not a session Not produced by home.ts LOW
Quick capture panel Present Remove/relocate behind "+" n/a LOW
Active models tile Present, never populated Remove from Home route never sets activeModels LOW
J08 review banner (test-locked) Present + correct Preserve testids/copy/event while restyling needsReviewCount real (keep)
Overnight-failure attention banner Present Reconcile with "1 export failed" run chip failures real LOW

Gap count (distinct design-vs-impl gaps): 11 (excludes the two "keep as-is" test-locked rows and the layout-only re-layout row counted once).


6. Reuse + build

6.1 Reuse (already shipped in PR1 — do NOT recreate)

All warm-Hive tokens the design references already exist in apps/web/src/index.css and apps/web/src/waggle-theme.css:

  • Surfaces/lines/text: --bg --bg-2 --surface --surface-2 --line --line-soft --line-strong --text-2 --text-muted --text-dim (index.css:149-153).
  • Honey + washes: --honey --honey-bright --honey-deep --honey-wash --honey-line --honey-glow (index.css:99,155-159).
  • Semantics + washes: --work --intel --healthy --attention --risk + each *-wash (index.css:161-169).
  • Radii: --r:12 --r-lg:18 --r-xl:26 (index.css:177-179).
  • Shadows: --shadow --shadow-sm --shadow-lg --shadow-pop (index.css:171-174).
  • Fonts: Tailwind font-display / font-sans → Hanken Grotesk, font-mono → JetBrains Mono (tailwind.config.ts:87-90). Hanken/JetBrains @import already in index.css.
  • Utilities: .hex clip-path (waggle-theme.css:140), .comb honeycomb bg (:143-146), .dot-live + @keyframes breathe (index.css:428-435), .kbd/mono helper (waggle-theme.css:161).
  • Existing component primitives: WorkspaceActionsMenu (kebab CRUD — keep on cards), formatRelative / formatBriefingDate (HomeCockpit.tsx:69-81,130-134), the adapter trio (getHomeBriefing/getHomeOvernight/quickCapture), useOfflineStatus, useService().connecting race guard, shadcn ui/ (button/input/card available if wanted), lucide icons, waggle:open-app deep-link shim. ⌘K lives in CommandCenter.tsx (PR2) — wire the ask-bar "⌘K" hint / send to it.

6.2 Net-new (build for Editorial)

  • Mono date row with live dot + 🔥 streak chip (streak value mocked/constant until a backend field exists — flag in code).
  • Honey-keyword H1 (two-line; second line composed; honey-span helper).
  • Overnight hero card — a new presentational component: eyebrow "While you slept" + composed story sentence (from OvernightSummary counts + failures) + 4 run chips. Must render gracefully when overnight is null/empty (no crash; the test renders with getHomeOvernight → null).
  • Run-chip component (status dot + label).
  • Hex-avatar workspace card restyle (.ws-card look: hex glyph, hover lift, "Continue →", badge).
  • .move suggestion row restyle (icon tile + title + sub + sliding arrow); compose sub-line from workspace/time.
  • Ask bar (pill input + honey "+" + "⌘K" hint + honey send) — replaces QuickCapture as the primary input; wire send to chat/⌘K.
  1. Keep HomeCockpit.tsx as the component (route + props + load() + all five render states + the test-locked testids/banners stay). Restyle/replace only the normal render body (:579-649) and the sub-state shells to the warm Editorial look.
  2. Container: swap max-w-4xl mx-auto p-6 → a centered max-w-[920px] mx-auto px-8 pt-[46px] pb-20 wrapper; optionally drop a .comb honeycomb layer behind it.
  3. Extract small presentational subcomponents within the file (or co-located home/ dir, per CLAUDE.md "many small files"): GreetingHeader (rework), OvernightHero (new, replaces OvernightPanel), RunChip, WorkspaceCard (hex restyle of the existing card), SuggestRow (restyle of SuggestedActionsPanel), AskBar (new). Keep UpNextPanel conditional (test 4/5) or fold it into the dash — simplest is to retain it below suggestions to satisfy the present/absent tests.
  4. Compose, don't fetch: the overnight story sentence and run-chip labels are derived client-side from the existing OvernightSummary + top suggestedAction /workspace summary. Mark the streak and "ahead of yesterday" as explicit // TODO(backend): no data source yet constants so the mock is honest.
  5. Preserve behavior: J08 banner (copy + home-cockpit-review-cta event payload), human-date, home-cockpit root testid, home-cockpit-upnext present/absent rule.
  6. Dark-first (per BUILD-PLAN §6); verify light via the existing ratchet (light-mode-tokens.test.ts). Gates: tsc -p apps/web/tsconfig.app.json 0, npm run test (FE) green incl. p2-home-desktop.test.tsx, lint clean.

Honesty flags for the rebuild: (a) streak has no data anywhere — it is a pure mock until a backend field lands; (b) the overnight narrative + "9 competitors" run-chip and the suggestion sub-lines are composed/mocked, not server-provided; (c) continueSessionId is still omitted server-side so "Continue" lands at chat root.