shipped

Spec: F13 — Today Dashboard

Status: shipped · Audited: 2026-06-12

1. Problem & CEO Job

Assistant-first direction (F91, 2026-07-02): The statement “Today is that page” refers to Today as the urgency triage surface once the user is in the workspace — not as the cold-start post-login landing. Per F91 Pillar F, the post-login default surface is the AI assistant (/intel/chat) at F91 P5 (behind a per-user setting until then). Today (/work/today) is an urgency surface the assistant surfaces and deep-links into; it is not the cold-start default after F91 P5 ships. F91 is WIP (P1 built; assistant-as-home lands at P5). F91 is the authority on the post-login landing; this spec owns Today’s content and behavior once navigated to.

Land in the right place first. The CEO opens Lumi. Where should they land? Not in a project list, not in a notification inbox — in a page that answers “what does my company look like right now, and what do I need to do today?” in under ten seconds. Today is that page: today’s Brief, today’s my-tasks, today’s open decisions, today’s at-risk commitments. Four sections, each independently loadable, each a direct link to the surface it summarizes.

What it is NOT: not a news feed, not a dashboard with charts, not a summary of everything. Today surfaces the CEO’s personal agenda — the decisions waiting for them, their own tasks due today, the risks they haven’t resolved — not the org’s full activity stream. The distinction matters: if Today shows everything, it loses the focus that makes it the right landing page.

2. User Stories & Acceptance Criteria

  • JTBD (CEO): “When I open Lumi first thing in the morning, I need a single screen that tells me what happened and what I need to do today — so I can triage in 2 minutes before meetings start. Today my morning is scrolling back through Zalo threads to reconstruct what happened overnight, then opening an Excel file to check who owes what. By the time I’ve pieced it together, the day has already started without me. Lumi’s Today answers ‘what does my company look like right now?’ in one screen — on the very first run it shows the F70 Boss Brief synthesized from the chaos I pasted in, so the screen is useful before I’ve set anything up.”
    • Given the CEO opens /work/today (or is redirected there on login), then all four sections load independently: Brief summary, My Tasks, Open Decisions, At-Risk Commitments.
    • Given any section fails to load, then that section shows its own error state; the other three sections still render (per-section useQueries independence — fixed 2026-06-14).
    • Given all four sections have no content today, then each shows an empty state (e.g., “Không có quyết định đang chờ 🎉”), not a blank page (fixed 2026-06-14).
  • JTBD (CEO): “When I see a pinned announcement on Today, I need one tap to acknowledge it — so I’m not prompted about it again. One announcement banner at most — Today is not a notification center.”
    • Given a pinned, unacknowledged announcement, then Today shows it as a banner above the four sections (max 1).
    • Given the CEO taps “Đã đọc ✓” on the banner, then it disappears immediately (ack via F28 endpoint, banner hides on success).
  • JTBD (CEO): “When Today shows My Tasks, I need to see today’s and overdue tasks only — not my full task list. The full list belongs in the Tasks page. Today is a triage surface, not a management surface.”
    • Given My Tasks section, then it shows tasks where due_date ≤ today and status = open, assigned to the current user.
    • Given more than 10 tasks meet the filter, then the section shows 10 and a “Xem thêm” link with an overflow count (e.g. “10 of 15”) — resolved 2026-07-06 (§11 row 9).
  • JTBD (CEO): “When Today shows Open Decisions, I need to see only decisions assigned to me or flagged for my attention — not all org decisions. The full list is in the Decisions page.”
    • Given Open Decisions section, then it shows status=open decisions for the org (current: org-scoped, not CEO-specific — see §11 row 7).
    • Given more than 5 decisions, then section shows 5 and a “Xem thêm” link with an overflow count (e.g. “5 of 6”) — resolved 2026-07-06 (§11 row 9).
  • JTBD (CEO): “When Today shows At-Risk Commitments, I need commitments with due_date ≤ today + 3d and status=at_risk — not all commitments. If everything is on-track, I want to see that (‘Tất cả cam kết đang đúng tiến độ 🎉’).”
    • Given At-Risk Commitments section, then it fetches open commitments (status=open) and determines at-risk display presentation-side: commitments overdue or due within 3 days are highlighted as at-risk; status=at_risk is not a backend filter value.
    • Given all commitments on track, then section shows the positive empty state.
    • Given more than 5 at-risk commitments, then section shows 5 and “Xem thêm” link with an overflow count (e.g. “5 of 7”) — resolved 2026-07-06 (§11 row 9).

3. Scope Boundaries

  • In: Today landing page (/work/today), four sections (Brief summary, My Tasks, Open Decisions, At-Risk Commitments), pinned announcement banner, per-section independent load.
  • Out: custom section configuration, widget library, activity feed, full task management (Tasks page is F17), CEO-personal decision filtering (scoped to org for now — §11 row 7).

4. Data Model

No new models. Today reads from: briefs.Brief (latest for org), work.Task (user’s own open, due today), operations.DecisionRequest (open, org-scoped), operations.Commitment (at-risk/overdue), hub.Announcement (pinned + unacked).

5. API Contract

Today makes 4–5 parallel API calls, one per section:

MethodPathMin roleNotes
GET/api/briefs/today/memberLatest Brief summary (briefsApi.today() — dedicated today_brief view)
GET/api/tasks/?status=todo&my_tasks=true&due_before=<today>memberMy Tasks — todo items (query 1 of 2)
GET/api/tasks/?status=in_progress&my_tasks=true&due_before=<today>memberMy Tasks — in-progress items (query 2 of 2); both results merged in useTodayData
GET/api/decisions/?status=openmemberOpen Decisions (org-scoped)
GET/api/commitments/?status=openmemberAt-Risk Commitments — backend returns all open commitments; at-risk display (overdue or due ≤ today+3d) is determined presentation-side
GET/api/announcements/?pinned=true&unread=mememberPinned announcement banner

6. UI / UX

  • /work/today (TodayPage.tsx). Clean landing page — not a dashboard with charts. Mobile-first: each section card stackable.
  • Section states: loading skeleton → content | empty state (positive copy) | error state (retry button).
  • Announcement banner: above sections, dismisses on ack.
  • “Xem thêm” links navigate to the relevant feature page.
  • Share today’s brief (growth / distribution-object discoverability). A discreet outline Share button in the PageHeader.actions slot (rendered only for executive roles — owner/admin/manager — and only once a real brief exists for today, i.e. not not_generated / null) executes the exact same share flow as the F12 BriefPage Share button: builds the share artifact via the shared useBriefShare hook, opens the native share sheet on mobile (→ Zalo/WhatsApp), copies to clipboard on desktop, embeds the CEO’s F59 referral link in the footer when available. The CEO who lands on Today every morning no longer needs to navigate to /intel/brief first to forward — meeting them on their highest-traffic surface closes the discoverability gap on the proven viral loop. The hook (frontend/src/hooks/useBriefShare.ts) is the single source of truth so the two surfaces can never drift on share semantics (same toast keys, same AbortError no-fallback, same generic-footer fallback).
  • Per-decision Share on every Decision card (growth / atomic distribution-object). Each open Decision card in the Decisions section renders a ghost-variant Share2 icon button alongside the card content. Clicking it builds a focused single-decision artifact via the new frontend/src/hooks/useDecisionShare.ts hook — same fallback chain, same AbortError no-fallback, same share_copied / share_error toasts from the brief namespace as the whole-brief and per-item-brief shares, so the three share surfaces can never drift. The artifact shape is decision-specific: Lumi · Decision · {date} header, decision title, → Recommend: {recommended_option} line (when populated), ⏰ Decide by: {decision_needed_by} line (when set), and the same generic-or-referral footer (”— Made with Lumi · join free: {url}”) embedded with the CEO’s F59 referral link via buildReferralUrl(referralCode?.code). Rendered only for open decisions (d.status === 'open') — decided/cancelled decisions are not forwarded. The button stays inside the executive-gated Decisions section, so members never see it (matches the existing role gate on the section itself). This extends the Notion-/Calendly-/Figma-class atomic-share distribution-object pattern from the brief surface to the highest-traffic CEO landing surface where decisions actually live every morning: the CEO’s most natural forward gesture (“@cofounder, decide this”) becomes a one-tap branded, attributed doorway instead of a screenshot or retyped Zalo message.
  • Per-risk Share on every Risk card (growth / atomic distribution-object). Each open Risk card in the Risks section renders a ghost-variant Share2 icon button alongside the card content. Clicking it builds a focused single-risk artifact via the new frontend/src/hooks/useRiskShare.ts hook — same fallback chain, same AbortError no-fallback, same share_copied / share_error toasts from the brief namespace as the whole-brief, per-item-brief, and per-decision shares, so the four share surfaces can never drift. The artifact shape is risk-specific: Lumi · Risk · {date} header, risk title, ⚠ Severity: {severity} line (always rendered), 🏷 Type: {risk_type} line (when populated), risk description line (when populated), and the same generic-or-referral footer embedded with the CEO’s F59 referral link via buildReferralUrl(referralCode?.code). Rendered only for open risks (r.status === 'open') — resolved/dismissed risks are not forwarded. The button stays inside the executive-gated Risks section, so members never see it (matches the existing role gate on the section itself). Risks are the second-most-natural CEO forward gesture after decisions (“@head_of_finance, this is yours”) — the triage-forward gesture complements the decision triage-back gesture. See F10 §6 for the helper/hook details (single source of truth — F10 owns the risk-share spec; F13 owns the surface).
  • Per-commitment Share on every Commitment card (growth / atomic distribution-object). Each open Commitment card in the Commitments section renders a ghost-variant Share2 icon button alongside the card content. Clicking it builds a focused single-commitment artifact via the new frontend/src/hooks/useCommitmentShare.ts hook — same fallback chain, same AbortError no-fallback, same share_copied / share_error toasts from the brief namespace as the whole-brief, per-item-brief, per-decision, and per-risk shares, so the five share surfaces can never drift. The artifact shape is commitment-specific: Lumi · Commitment · {date} header, commitment title, 👤 Owner: {owner_name_raw} line (when populated), 📅 Due: {due_date} line (when populated), ⏰ Overdue line (derived state — due_date < todayDate, both required), and the same generic-or-referral footer embedded with the CEO’s F59 referral link via buildReferralUrl(referralCode?.code). Rendered only for open commitments (c.status === 'open' — the Today query already filters status='open', and the gate keeps pattern-fidelity with the per-decision / per-risk shares against any future loosening). The button stays inside the executive-gated Commitments section, so members never see it (matches the existing role gate on the section itself). Commitments are the accountability-forward CEO gesture (“@head_of_sales, you committed Friday — what’s the status?”) — completes the four-member share family across every Today item type (brief, decision, risk, commitment). See F09 §6 for the helper/hook details (single source of truth — F09 owns the commitment-share spec; F13 owns the surface).
  • i18n: today (adds share_brief_button); the share artifact text and toast strings for the whole-brief share remain in the brief namespace where they live today. The per-decision, per-risk, and per-commitment share artifacts reuse the brief namespace’s footer (share_footer, share_footer_referral) and toast (share_copied, share_error) keys — same footer copy + toast strings as the whole-brief share, so one copy edit lands on all five surfaces — and add five decision-specific keys in the decisions namespace (share_button, share_header, share_header_nodate, share_recommend, share_decide_by), five risk-specific keys in the risks namespace (share_button, share_header, share_header_nodate, share_severity, share_type), and six commitment-specific keys in the commitments namespace (share_button, share_header, share_header_nodate, share_owner, share_due, share_overdue).

7. Test Plan

#CaseTypeWhere
1Each section loads correctly (tasks, decisions, commitments)backendcovered by respective feature tests
2Per-section failure independence (one section fails, others render)frontendTodayPage.test.tsx::test_section_independent_failure
3Empty state for each sectionfrontendTodayPage.test.tsx::test_section_empty_states
4Announcement banner shows + hides on acke2edescribe("Today") > test_pinned_announcement_banner_acks
5Today landing page loads all sectionse2edescribe("Today") > test_today_loads
6Share-brief button renders in PageHeader.actions once today’s brief exists (executive role)frontendTodayPage.test.tsx::renders Share today's brief once a brief is generated
7Share-brief button absent when brief is not_generated, null, or still loadingfrontendTodayPage.test.tsx::no Share button until today's brief is generated
8Share-brief button absent for non-executive membersfrontendTodayPage.test.tsx::no Share button for non-executive members
9Share-brief click uses the native share sheet when available + embeds the CEO’s referral linkfrontendTodayPage.test.tsx::Share opens native share sheet with the referral-footer artifact
10Share-brief click falls back to clipboard + success toast when no navigator.sharefrontendTodayPage.test.tsx::Share falls back to clipboard when no native share sheet
11Per-decision share artifact builder: dated/undated header, title-only, title+recommended, title+decide-by, full ordering, generic vs referral footerfrontenddecisionShare.test.ts (7 deterministic cases)
12Per-decision Share button renders inside every open Decision card (executive section), absent on decided/cancelled decisionsfrontendTodayPage.test.tsx::renders per-decision Share button on open decisions only
13Per-decision Share opens native share sheet with the decision artifact + referral footer; clipboard fallback + success toast when no navigator.sharefrontendTodayPage.test.tsx::per-decision Share opens native sheet with the referral-footer artifact / per-decision Share falls back to clipboard when no native share sheet
14Per-risk share artifact builder: dated/undated header, title, severity line, risk-type line, description line, full ordering, generic vs referral footerfrontendriskShare.test.ts (8 deterministic cases)
15Per-risk Share button renders inside every open Risk card (executive section), absent on resolved/dismissed risksfrontendTodayPage.test.tsx::renders per-risk Share button on open risks only
16Per-risk Share opens native share sheet with the risk artifact + referral footer; clipboard fallback + success toast when no navigator.sharefrontendTodayPage.test.tsx::per-risk Share opens native sheet with the referral-footer artifact / per-risk Share falls back to clipboard when no native share sheet
17Per-commitment share artifact builder: dated/undated header, title, owner line (present/absent), due line (present/absent), overdue derivation (past/future/null today), full ordering, generic vs referral footerfrontendcommitmentShare.test.ts (11 deterministic cases)
18Per-commitment Share button renders inside every open Commitment card on Today (executive section), absent on done/cancelled commitmentsfrontendTodayPage.test.tsx::renders per-commitment Share button on open commitments only
19Per-commitment Share opens native share sheet with the commitment artifact + referral footer; clipboard fallback + success toast when no navigator.sharefrontendTodayPage.test.tsx::per-commitment Share opens native sheet with the referral-footer artifact / per-commitment Share falls back to clipboard when no native share sheet

8. Current Behavior

Shipped. /work/today (TodayPage.tsx) renders four sections using useQueries for independent parallel loading. Per-section failure independence fixed 2026-06-14 (previously one section failure blocked the others). Empty states for all four sections now rendered correctly (also fixed 2026-06-14 — previously, some sections showed blank on empty).

Pinned announcement banner: max 1 pinned, unacked announcement shows above sections. Disappears immediately on “Đã đọc ✓” tap.

Frontend hard-caps: My Tasks shows max 10, decisions/commitments show max 5, with “Xem thêm” links. Overflow count now displayed (e.g., “10 of 23”) when total exceeds the cap — resolved 2026-07-06 (§11 row 9).

Cross-team task visibility gap: My Tasks shows org-scoped tasks assigned to current user (assigned_to=me + org FK filter). If the org uses project-level team visibility (F22), tasks from projects the user is not a member of may still appear (org-scope is wider than project-scope). Tracked in §11 row 7 and as an open question (§12).

9. Error States

#ErrorResponseNotes
ES1My Tasks section load failureSection shows t('error_load_tasks') with retry button; other sections unaffectedFixed 2026-06-14
ES2Open Decisions section load failureSection shows t('error_load_decisions') with retry button; other sections unaffectedFixed 2026-06-14
ES3At-Risk Commitments section load failureSection shows t('error_load_commitments') with retry button; other sections unaffectedFixed 2026-06-14
ES4Brief summary section load failureSection shows t('error_load_brief') with retry button; other sections unaffectedFixed 2026-06-14
ES5Announcement banner load failureBanner silently absent (non-critical); no error toastGraceful degradation
ES6Ack announcement failuretoast.error(t('error_ack')) — banner remains; user can retryNo E2E test
ES7No Brief generated yet todayBrief section shows “Chưa có Brief hôm nay — nhấn để tạo” CTA → calls generatetest_today_brief_section_no_brief
ES8All sections empty simultaneouslyEach section renders its positive empty state; no blank pageFixed 2026-06-14

10. Diagrams & Docs Touched

frontend/DIAGRAMS.md route tree → /work/today entry · GLOSSARY (Today Dashboard).

11. Implementation Audit (2026-07-13)

The canonical contract is contracts/features/F13.cue. The current code-vs-doc audit is F13-today-audit.md. The contract deliberately claims the web route and unit-testable UI states, while leaving browser runtime evidence as a blocking gap and making no native Today parity claim.

#ClaimStatusEvidence
1/work/today route exists; TodayPage.tsx is the urgency surface (not the cold-start default — see §1 notice re F91)✅ matchfrontend/DIAGRAMS.md route tree; frontend/src/pages/work/TodayPage.tsx
2Four sections (Brief, My Tasks, Open Decisions, At-Risk Commitments) load independently✅ matchuseQueries — each section has its own query; fixed 2026-06-14
3Per-section failure independence — one section fails, others render✅ matchFixed 2026-06-14; test_section_independent_failure
4All four sections show correct empty states (not blank)✅ matchFixed 2026-06-14; test_section_empty_states; positive copy e.g. “Không có quyết định đang chờ 🎉“
5Pinned announcement banner: max 1, hides on ack✅ matchTodayPage.tsx banner logic; test_pinned_announcement_banner_acks (E2E)
6My Tasks: due_date ≤ today, status=open, assigned_to=me — max 10✅ matchAPI filter params; frontend hard-cap slice to 10
7Cross-team task visibility: org-scope wider than project-team-scope (tasks visible beyond project membership)⚠️ gapMy Tasks uses org FK + assigned_to; F22 project team membership not checked; tasks from non-member projects visible
8Open Decisions: status=open, org-scoped only (not CEO-personal)⚠️ spec drift§2 JTBD says “assigned to me or flagged for my attention”; current: all open org decisions; no per-user decision assignment filter
9Frontend hard-cap (tasks: 10, others: 5) with overflow count✅ matchuseTodayData exposes `totalTasks
10”Xem thêm” links navigate to feature pages correctly✅ matchNavigation confirmed; test_today_loads E2E covers link presence
11Visual QC: non-executive members do not land on an executive Today surface with org-wide decisions, risks, and commitments✅ match2026-06-18 visual QC: TodayPage.tsx renders a member-safe Today variant with personal due tasks while executive summary sections stay owner/admin/manager-only; TodayPage.test.tsx; frontend/screenshots/15-member-view/01-member-today-dashboard.png
12Visual QC: Today first viewport stays a triage surface instead of drifting into a chart dashboard✅ match2026-06-18 pass 2: removed the analytics chart row so /work/today leads with decisions, risks, tasks, and commitments as specced; TodayPage.test.tsx; frontend/screenshots/03-today/01-today-dashboard.png

Summary: The shipped route, composition, role boundary, and UI states match the current code. Cross-team task visibility and org-scoped decisions remain documented product decisions; browser runtime proof is explicitly tracked in the Contract Graph rather than inferred from source.

Edge Cases Catalog (formerly §11, 2026-06-12; 11 items): Key items integrated: section failure independence (E1→row 3), empty states (E2→row 4), banner ack failure (E3→ES6), overflow hard-cap (E4→row 9), Brief not yet generated (ES7). Remaining edge cases (E5 concurrent section load, E6 stale cache on re-navigate, E7 task due_date boundary midnight, E8 decision needs_by same as today, E9 pinned announcement deleted mid-session, E10 mobile one-hand reach, E11 slow connection skeleton behavior) — tracked in git history (2026-06-12 changelog entry).

12. Open Questions / Decisions

  • Cross-team task visibility (row 7): Today’s My Tasks is intentionally org-scoped — keeps the filter simple and ensures the CEO sees their full task load regardless of project team membership. Risk: if F22 project team permissions are strict, this may show tasks from projects the user formally cannot access. Decision needed before F22 permissions are enforced: either (a) Today filter respects project team membership, or (b) Today is explicitly org-scoped and F22 is documented as not applying to personal task views. Leaning toward (b) — the CEO’s task view should be total, not bounded by project membership rules they likely defined themselves.
  • CEO-personal decision filter (row 8): status=open org-scoped is correct for the CEO (they own all decisions). The 2026-06-18 member-view polish now suppresses this executive queue on non-executive Today surfaces, but the backend contract is still org-scoped; if member-specific decision work lands later, add flagged_for_user or assigned_to_user filtering instead of front-end-only reshaping.
  • Overflow count (row 9): Resolved 2026-07-06 — useTodayData now exposes total counts; TodayPage renders “{{visible}} of {{total}}” via the overflow_count i18n key when items exceed the hard-cap. No longer tracked in POLISH.md.

13. Bug / Change Log

2026-07-13 — Contract Graph ownership and proof alignment

Type: Documentation and proof-traceability change

Summary:

  • Added the canonical contracts/features/F13.cue declaration for /work/today, its real implementation symbols, role boundary, dependencies, and proof dimensions.
  • Attached stable F13 proof IDs to the existing Today Vitest and browser tests, adding direct i18n and accessibility assertions where the contract requires them.
  • Replaced the stale implementation audit paths (frontend/src/pages/TodayPage.tsx, App.tsx, and the old test filenames) with the current route tree, page, hook, and test paths.
  • Kept browser runtime evidence explicitly blocked in the contract; no Playwright result is claimed by source inspection or unit tests.

2026-07-02 — Landing/home alignment: Today is an urgency surface, not the cold-start landing (F91 Pillar F)

Type: Documentation Change — landing/home scope alignment

Summary:

  • Added §1 notice clarifying that Today (/work/today) is the urgency triage surface within the workspace, not the cold-start post-login landing. Per F91 Pillar F, the post-login default surface is the AI assistant (/intel/chat) at F91 P5 (behind a per-user setting until then). “Today is that page” in §1 refers to Today as the right surface for triage once the user is in the workspace — F91 is the authority on what loads at cold start.
  • §11 row 1 annotated to clarify “TodayPage.tsx is the urgency surface” rather than the post-login default.
  • F91 is WIP (P1 built; assistant-as-home default lands at P5). The interim post-login default is Pulse (F79 tab 1). Today’s content contract (Brief, Tasks, Decisions, At-Risk Commitments) is unchanged.

Reason:

  • F13 was written before F91 existed. The §1 framing implies Today is the cold-start destination; leaving that claim uncorrected creates a spec conflict with F91 Pillar F’s assistant-as-home direction.

Impact:

  • Documentation only. No code, API, or behavior changed.

2026-07-02 — Fix §5 API endpoints and §2 AC status=at_risk claim (batch-1 spec audit)

Type: Documentation Change

Summary:

  • §5 API table: three rows were wrong.
    • GET /api/briefs/?date=today → GET /api/briefs/today/. Today uses the dedicated today_brief view (backend/apps/briefs/views.py ~45) via briefsApi.today() in frontend/src/api/endpoints.ts line 218. There is no ?date=today query param path.
    • GET /api/tasks/?due_date__lte=today&status=open&assigned_to=me → two separate queries. frontend/src/hooks/queries/useToday.ts issues ?status=todo&my_tasks=true&due_before=<today> and ?status=in_progress&my_tasks=true&due_before=<today> independently; results are merged in useTodayData. The status=open and assigned_to=me params do not exist.
    • GET /api/commitments/?at_risk=true → GET /api/commitments/?status=open. CommitmentViewSet.get_queryset only handles status, due_after, due_before — there is no at_risk filter. The frontend determines at-risk display presentation-side.
  • §2 AC: “shows commitments with due_date ≤ today + 3d OR status=at_risk” was wrong. The backend returns status=open commitments; presentation-side logic highlights those overdue or due within 3 days as at-risk.

Impact:

  • Documentation only. No code, API, or behavior changed.

2026-07-02 — Correct stale page path in §11 row 1 (batch-1 spec audit)

Type: Documentation Fix

Summary:

  • §11 row 1: Updated frontend/src/pages/TodayPage.tsx → frontend/src/pages/work/TodayPage.tsx to reflect the IA restructure that moved pages into subdirectories. Verified by find frontend/src -name TodayPage.tsx = frontend/src/pages/work/TodayPage.tsx.

2026-06-29 — F90 field exceptions stay on the existing Brief → Today control loop

Type: Product Integration Decision

Summary:

  • F90 now projects grounded unresolved field exceptions into the generated Brief.
  • Today does not add a second dedicated field-exception strip in this slice; executives keep consuming that signal through the existing Brief/Today loop instead of learning another summary surface.

Reason:

  • The narrow SaaS wedge is still one executive queue. Repeating the same field blocker in a new Today-only module would create product debt faster than it creates clarity.

Impact:

  • Today inherits the new field-service signal through the existing Brief handoff and stays a cleaner launchpad instead of becoming a second field dashboard.

2026-06-22 — Growth/atomic-distribution: per-commitment Share on every Commitment card on Today

Type: Feature (growth / atomic distribution-object — additive)

  • Completes the four-member atomic-share family across every Today item type: brief (#270/#350), decision (#398), risk (#407), and now commitment. Commitments are the accountability-forward CEO gesture (“@head_of_sales, you committed Friday — status?”) — the volume play in the share series (higher daily frequency than risks, lower stakes per share than decisions).

  • Added a ghost-variant Share2 icon button to every open Commitment card on Today (c.status === 'open' — done/cancelled commitments never expose the share affordance). Section stays executive-gated (no member exposure). Click triggers the new useCommitmentShare hook (see F09 §6 for the helper/hook implementation — F09 owns the commitment-share spec; F13 owns the surface).

  • No new keys in the today namespace. Six new keys land in the commitments namespace (share_button, share_header, share_header_nodate, share_owner, share_due, share_overdue) and propagate to all 62 locales via node scripts/propagate-new-i18n-keys.mjs. Shared footer + toasts continue to come from the brief namespace — single source of truth so a copy edit lands on all five share surfaces at once.

  • Behavior-preserving for every existing path: whole-brief share button on Today’s PageHeader.actions untouched; per-decision Share button untouched; per-risk Share button untouched; per-item brief share on /intel/brief untouched; Commitment card’s existing overdue rail + due line + owner display untouched (the card body becomes a flex row to accommodate the button — visual layout otherwise unchanged).

  • Tested in commitmentShare.test.ts (11 deterministic helper cases) and three new component cases in TodayPage.test.tsx (button presence on open commitments only, native share with the referral artifact, clipboard fallback + success toast).

  • Growth metric this bets on: shares per active CEO per week. With decision (triage-back), risk (triage-forward), and commitment (accountability-forward) all atomic-shareable on Today plus the whole-brief share, every CEO forward gesture the day produces now has a one-tap branded doorway. Secondary: referral-attributed signups per week downstream of more shares.

Verification:

  • frontend/src/test/commitmentShare.test.ts (11 cases) — all green.
  • frontend/src/test/TodayPage.test.tsx (3 new cases, 28 total) — all green.
  • ./node_modules/.bin/tsc --noEmit — 0 errors.
  • node scripts/check-design-tokens.mjs — clean.
  • pnpm validate-i18n — parity green across all 62 locales.
  • Full pnpm test — 10042 passed, 16 skipped, 0 failures.

2026-06-22 — Growth/atomic-distribution: per-risk Share on every Risk card on Today

Type: Feature (growth / atomic distribution-object — additive)

  • Extends the atomic-share family (F12 per-item brief share #381 + Today per-decision share #398) to the Risks section, the second-most-natural CEO forward gesture after decisions (“@head_of_finance, this is yours”). Same hook pattern, same fallback chain, same toast keys from the brief namespace, same AbortError no-fallback, same F59 referral footer — four share surfaces, one viral loop, one source of truth for the loop’s branding.

  • Added a ghost-variant Share2 icon button to every open Risk card on Today (r.status === 'open' — resolved/dismissed risks never expose the share affordance). Section stays executive-gated (no member exposure). Click triggers the new useRiskShare hook (see F10 §6 for the helper/hook implementation — F10 owns the risk-share spec; F13 owns the surface).

  • No new keys in the today namespace. Five new keys land in the risks namespace (share_button, share_header, share_header_nodate, share_severity, share_type) and propagate to all 62 locales via node scripts/propagate-new-i18n-keys.mjs.

  • Behavior-preserving for every existing path: whole-brief share button on Today’s PageHeader.actions untouched; per-decision Share button untouched; per-item brief share on /intel/brief untouched; Risk card’s existing severity badge + risk_type label + severity rail untouched.

  • Tested in riskShare.test.ts (8 deterministic helper cases) and three new component cases in TodayPage.test.tsx (button presence on open risks only, native share with the referral artifact, clipboard fallback + success toast).

  • Growth metric this bets on: shares per active CEO per week. Risks are triage-forward (“you own this fire”) versus decisions which are triage-back (“weigh in on this”). The two together cover the highest-frequency CEO forward gestures the day produces.

Verification:

  • frontend/src/test/riskShare.test.ts (8 cases) — all green.
  • frontend/src/test/TodayPage.test.tsx (3 new cases) — all green.
  • ./node_modules/.bin/tsc --noEmit — 0 errors.
  • node scripts/check-design-tokens.mjs — clean.
  • pnpm validate-i18n — parity green across all 62 locales.
  • Full pnpm test — 10028/10044 pass (16 skipped, 0 failures).

2026-06-22 — Growth/atomic-distribution: per-decision Share on every Decision card on Today

Type: Feature (growth / atomic distribution-object — additive)

  • The brief-share viral loop (F12 whole-brief share #270 + referral footer #287 + reward nudge #310 + image card #310 + ?ref= register conversion #321 + Today PageHeader share affordance #350 + per-item brief share #381) was complete on the brief surface. But the CEO’s most natural daily forward gesture is not a brief item — it’s a pending decision (“@cofounder, decide this; your call by Friday”), and decisions live on /work/today every morning. A CEO who wanted to forward a single decision either screenshotted (lossy, no referral footer), retyped it in Zalo (friction, brand-invisible), or shared the whole brief (a multi-section memo when they only needed one decision). This run closes that atomic-share gap on the highest-traffic CEO surface.
  • Added a ghost-variant Share2 icon button to every open Decision card on /work/today (rendered only for d.status === 'open'; decided/cancelled decisions are not forwarded). The button sits inside the existing executive-gated Decisions section, so members never see it. Click triggers the new useDecisionShare hook — same fallback chain, same AbortError no-fallback, same share_copied / share_error toasts as useBriefShare / useBriefItemShare, so the three share surfaces can never drift on semantics.
  • Added pure frontend/src/lib/decisionShare.ts — buildDecisionShareText(decision, today, t, referralUrl?). Decision- specific artifact: Lumi · Decision · {date} header, decision title, → Recommend: {recommended_option} line (when populated), ⏰ Decide by: {decision_needed_by} line (when set), generic-or-referral footer using the brief namespace’s existing share_footer / share_footer_referral keys (already translated in all 62 locales — same footer copy as the brief share, one source of truth).
  • Five new i18n keys in decisions namespace (share_button, share_header, share_header_nodate, share_recommend, share_decide_by) in en + vi, propagated to all 60 other locale decisions.json files as English placeholders. No new artifact text added to brief namespace — the per-decision share reuses the brief footer + toast keys so a future copy edit lands on three share surfaces at once.
  • No backend change, no migration, no auth/tenancy/AI change, no diagram change → no scope change (Rule 6 clear; F13 + F11 + F59 all already ✅). No new API surface — the Today page already fetches decisions via the org-scoped /api/decisions/?status=open endpoint and the share runs client-side on data already on screen.
  • Tested in frontend/src/test/decisionShare.test.ts (7 deterministic helper cases — dated/undated header, title-only / title+recommended / title+decide-by / full ordering, generic vs referral footer) and three new cases in frontend/src/test/TodayPage.test.tsx (button presence on open decisions only, native share with referral artifact, clipboard fallback + success toast).
  • Growth metric this bets on: shares per active CEO per week (same metric the F12 share run series opened). Hypothesis: per-decision shares from Today will be the highest-conversion forward category because the recipient is implicated in the answer — they’re not just being shown a problem, they’re being asked to weigh in.

2026-06-22 — Growth/discoverability: surface the brief-share affordance on Today

Type: Feature (growth / distribution-object discoverability — additive)

  • The brief-share viral loop (F12 text share #270 + referral footer #287 + reward nudge #310 + image card #310 + ?ref= register conversion #321) was complete but invisible to the CEO who lives on Today — Today is the highest-traffic CEO surface (§1) yet the share affordance only existed on /intel/brief. A CEO who never navigates to the brief page on a given day never forwards. This run closes that discoverability gap by surfacing the proven share action on the morning landing page itself.
  • Added a discreet outline Share button in TodayPage’s PageHeader.actions slot, rendered only for executive roles (owner/admin/manager — matches the existing executive-view gate on decisions/risks/commitments) and only once a real brief is generated for today (status !== 'not_generated'). Click triggers the same share flow as BriefPage — native share sheet on mobile (→ Zalo/WhatsApp), clipboard fallback on desktop, F59 referral link embedded in the footer when the code resolves, all-clear state when no live items.
  • Extracted the share orchestration into a new frontend/src/hooks/useBriefShare.ts hook so BriefPage and TodayPage share one source of truth — the two surfaces can never drift on share semantics (same buildBriefShareText, same try/catch, same AbortError no-fallback, same share_copied / share_error toast keys from the brief namespace, same generic-footer fallback when the referral query is still in flight).
  • New i18n key (share_brief_button) added to today en+vi and propagated to all 60 other locale today.json files. No share artifact text added — Today inherits every locale fix the brief-share run series shipped via the shared hook.
  • No new artifact, no backend change, no migration, no auth/tenancy change, no diagram change → no scope change (Rule 6 clear; F12 + F13 both already ✅). The useBriefToday() query on Today shares TanStack Query cache with BriefPage, so a CEO who opens Today then Brief makes one fetch, not two.
  • Tested in frontend/src/test/TodayPage.test.tsx: button presence by brief state (loading / not_generated / generated), role-gating (executive-only, no member leak), native share + referral artifact, clipboard fallback + success toast.
  • Growth metric this bets on: shares per active CEO per week (same metric F12 #270 opened). Hypothesis: surfacing on Today 2–3× more forwards/week per CEO than on /intel/brief only.

2026-06-15 — Frontend route namespaced

Frontend route namespaced: /today -> /work/today (app-wide IA restructure; API paths unchanged).

2026-06-14 — Strategy v2 realignment (positioning)

Type: Documentation Change

Summary:

  • Reframed the §2 Today JTBD away from US-SaaS comparison (“In Notion, there is no landing page… In ClickUp, I land in my task inbox”) to the real first-market competitor — the owner scrolling Zalo threads + opening Excel + memory — and connected first-run Today to the F70 Boss Brief.

Reason:

  • competitive-strategy-v2.md + the registry Positioning doctrine (v2): the competitor is Zalo/Excel/owner memory, not US SaaS.

Impact:

  • Positioning only. No change to API, data model, tests, or status.

Tests:

  • None (documentation).

Notes:

  • The body reframe shipped in commit 58ef5d574; this changelog entry was added separately after a concurrent revert dropped it.

2026-06-14 — Restructure to TEMPLATE.md section order; expand §11 audit (SP fleet)

Type: Documentation Change

Summary:

  • Numbered “Current Behavior” as §8 (was unnumbered between §7 and §8 Diagrams).
  • §8 Diagrams → §10; §9 Implementation Audit → §11 (10-row table); §11 Edge Cases (11 items) absorbed into §11 note; §12 Error States (ES1-ES8) → §9; §10 Open Questions → §12; §13 Bug Log stays.
  • §11 audit: 10 rows, 7 ✅, 3 ⚠️ gaps (cross-team visibility, org-scoped decisions drift, overflow count).

2026-06-14 — Fix per-section independent failure and empty states (useQueries)

Type: Bug Fix

Summary:

  • TodayPage.tsx refactored from single useQuery + conditional renders to useQueries array.
  • Each section now has its own query — one failure does not block others.
  • Empty states added for all four sections with positive Vietnamese copy.
  • ES1–ES4 updated; row 3 and row 4 in §11 confirmed ✅.

2026-06-18 — Visual QC: role-aware Today for member accounts

Type: UX Fix

Summary:

  • The member-view screenshot showed a sales member landing on the same executive Today surface as the CEO, including org-wide decisions, risks, and commitments.
  • That made the page noisy and misleading for non-executive users, and it also exposed that Today tasks were being fetched as a generic org list instead of a personal due-work queue.
  • TodayPage.tsx now renders a role-aware member variant: non-executive users see only their personal due tasks, while executive summary sections stay reserved for owner/admin/manager roles.

Verification:

  • frontend/src/test/TodayPage.test.tsx
  • frontend/screenshots/15-member-view/01-member-today-dashboard.png

2026-06-18 — Visual QC: remove dashboard charts from Today

Type: UX Fix

Summary:

  • The Today spec is explicit that this page is not a dashboard with charts, but the first viewport still spent a large block on analytics cards that pushed the actionable sections lower and made the landing feel less focused.
  • TodayPage.tsx now removes that chart row entirely, returning the page to its intended role as a fast triage surface for decisions, risks, tasks, and commitments.

Verification:

  • frontend/src/test/TodayPage.test.tsx
  • frontend/screenshots/03-today/01-today-dashboard.png

2026-06-12 — Added Edge Cases (E1–E11) and Error States (ES1–ES8)

Type: Documentation Change

2026-06-12 — Audited §2 ACs against implementation

Type: Doc Fix / Verified

Summary:

  • Per-section failure independence: gap found, documented. Empty state for all sections: gap found, documented. Both subsequently fixed 2026-06-14.

14. Success Metrics (Appendix)

  • Today open rate = session start rate: if CEOs consistently land on Today and see useful content in < 10 seconds, the open rate for Today will equal the rate at which they open Lumi at all. A drop in Today open rate relative to session starts signals the page isn’t earning the landing.
  • Section engagement rate ≥ 60% on active days: share of Today sessions where the CEO taps into at least one section (Brief, tasks, decisions, commitments). Today is a launchpad; a CEO who opens it and immediately navigates away without tapping anything is not using it as intended.
  • Mean time to first tap < 8 seconds on mobile: Today should be scannable before a morning coffee is finished. Target: all four sections visible (collapsed) and the CEO has tapped one within 8 seconds of the page loading.