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
useQueriesindependence — 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).
- Given the CEO opens
- 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 ≤ todayandstatus = 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).
- Given My Tasks section, then it shows tasks where
- 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=opendecisions 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).
- Given Open Decisions section, then it shows
- JTBD (CEO): “When Today shows At-Risk Commitments, I need commitments with
due_date ≤ today + 3dandstatus=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_riskis 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).
- Given At-Risk Commitments section, then it fetches open commitments (
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:
| Method | Path | Min role | Notes |
|---|---|---|---|
| GET | /api/briefs/today/ | member | Latest Brief summary (briefsApi.today() — dedicated today_brief view) |
| GET | /api/tasks/?status=todo&my_tasks=true&due_before=<today> | member | My Tasks — todo items (query 1 of 2) |
| GET | /api/tasks/?status=in_progress&my_tasks=true&due_before=<today> | member | My Tasks — in-progress items (query 2 of 2); both results merged in useTodayData |
| GET | /api/decisions/?status=open | member | Open Decisions (org-scoped) |
| GET | /api/commitments/?status=open | member | At-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=me | member | Pinned 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
Sharebutton in thePageHeader.actionsslot (rendered only for executive roles — owner/admin/manager — and only once a real brief exists for today, i.e. notnot_generated/null) executes the exact same share flow as the F12BriefPageShare button: builds the share artifact via the shareduseBriefSharehook, 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/brieffirst 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, sameAbortErrorno-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
Share2icon button alongside the card content. Clicking it builds a focused single-decision artifact via the newfrontend/src/hooks/useDecisionShare.tshook — same fallback chain, sameAbortErrorno-fallback, sameshare_copied/share_errortoasts from thebriefnamespace 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 viabuildReferralUrl(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
Share2icon button alongside the card content. Clicking it builds a focused single-risk artifact via the newfrontend/src/hooks/useRiskShare.tshook — same fallback chain, sameAbortErrorno-fallback, sameshare_copied/share_errortoasts from thebriefnamespace 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), riskdescriptionline (when populated), and the same generic-or-referral footer embedded with the CEO’s F59 referral link viabuildReferralUrl(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
Share2icon button alongside the card content. Clicking it builds a focused single-commitment artifact via the newfrontend/src/hooks/useCommitmentShare.tshook — same fallback chain, sameAbortErrorno-fallback, sameshare_copied/share_errortoasts from thebriefnamespace 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),⏰ Overdueline (derived state —due_date < todayDate, both required), and the same generic-or-referral footer embedded with the CEO’s F59 referral link viabuildReferralUrl(referralCode?.code). Rendered only for open commitments (c.status === 'open'— the Today query already filtersstatus='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(addsshare_brief_button); the share artifact text and toast strings for the whole-brief share remain in thebriefnamespace where they live today. The per-decision, per-risk, and per-commitment share artifacts reuse thebriefnamespace’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 thedecisionsnamespace (share_button,share_header,share_header_nodate,share_recommend,share_decide_by), five risk-specific keys in therisksnamespace (share_button,share_header,share_header_nodate,share_severity,share_type), and six commitment-specific keys in thecommitmentsnamespace (share_button,share_header,share_header_nodate,share_owner,share_due,share_overdue).
7. Test Plan
| # | Case | Type | Where |
|---|---|---|---|
| 1 | Each section loads correctly (tasks, decisions, commitments) | backend | covered by respective feature tests |
| 2 | Per-section failure independence (one section fails, others render) | frontend | TodayPage.test.tsx::test_section_independent_failure |
| 3 | Empty state for each section | frontend | TodayPage.test.tsx::test_section_empty_states |
| 4 | Announcement banner shows + hides on ack | e2e | describe("Today") > test_pinned_announcement_banner_acks |
| 5 | Today landing page loads all sections | e2e | describe("Today") > test_today_loads |
| 6 | Share-brief button renders in PageHeader.actions once today’s brief exists (executive role) | frontend | TodayPage.test.tsx::renders Share today's brief once a brief is generated |
| 7 | Share-brief button absent when brief is not_generated, null, or still loading | frontend | TodayPage.test.tsx::no Share button until today's brief is generated |
| 8 | Share-brief button absent for non-executive members | frontend | TodayPage.test.tsx::no Share button for non-executive members |
| 9 | Share-brief click uses the native share sheet when available + embeds the CEO’s referral link | frontend | TodayPage.test.tsx::Share opens native share sheet with the referral-footer artifact |
| 10 | Share-brief click falls back to clipboard + success toast when no navigator.share | frontend | TodayPage.test.tsx::Share falls back to clipboard when no native share sheet |
| 11 | Per-decision share artifact builder: dated/undated header, title-only, title+recommended, title+decide-by, full ordering, generic vs referral footer | frontend | decisionShare.test.ts (7 deterministic cases) |
| 12 | Per-decision Share button renders inside every open Decision card (executive section), absent on decided/cancelled decisions | frontend | TodayPage.test.tsx::renders per-decision Share button on open decisions only |
| 13 | Per-decision Share opens native share sheet with the decision artifact + referral footer; clipboard fallback + success toast when no navigator.share | frontend | TodayPage.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 |
| 14 | Per-risk share artifact builder: dated/undated header, title, severity line, risk-type line, description line, full ordering, generic vs referral footer | frontend | riskShare.test.ts (8 deterministic cases) |
| 15 | Per-risk Share button renders inside every open Risk card (executive section), absent on resolved/dismissed risks | frontend | TodayPage.test.tsx::renders per-risk Share button on open risks only |
| 16 | Per-risk Share opens native share sheet with the risk artifact + referral footer; clipboard fallback + success toast when no navigator.share | frontend | TodayPage.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 |
| 17 | Per-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 footer | frontend | commitmentShare.test.ts (11 deterministic cases) |
| 18 | Per-commitment Share button renders inside every open Commitment card on Today (executive section), absent on done/cancelled commitments | frontend | TodayPage.test.tsx::renders per-commitment Share button on open commitments only |
| 19 | Per-commitment Share opens native share sheet with the commitment artifact + referral footer; clipboard fallback + success toast when no navigator.share | frontend | TodayPage.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
| # | Error | Response | Notes |
|---|---|---|---|
| ES1 | My Tasks section load failure | Section shows t('error_load_tasks') with retry button; other sections unaffected | Fixed 2026-06-14 |
| ES2 | Open Decisions section load failure | Section shows t('error_load_decisions') with retry button; other sections unaffected | Fixed 2026-06-14 |
| ES3 | At-Risk Commitments section load failure | Section shows t('error_load_commitments') with retry button; other sections unaffected | Fixed 2026-06-14 |
| ES4 | Brief summary section load failure | Section shows t('error_load_brief') with retry button; other sections unaffected | Fixed 2026-06-14 |
| ES5 | Announcement banner load failure | Banner silently absent (non-critical); no error toast | Graceful degradation |
| ES6 | Ack announcement failure | toast.error(t('error_ack')) — banner remains; user can retry | No E2E test |
| ES7 | No Brief generated yet today | Brief section shows “Chưa có Brief hôm nay — nhấn để tạo” CTA → calls generate | test_today_brief_section_no_brief |
| ES8 | All sections empty simultaneously | Each section renders its positive empty state; no blank page | Fixed 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.
| # | Claim | Status | Evidence |
|---|---|---|---|
| 1 | /work/today route exists; TodayPage.tsx is the urgency surface (not the cold-start default — see §1 notice re F91) | ✅ match | frontend/DIAGRAMS.md route tree; frontend/src/pages/work/TodayPage.tsx |
| 2 | Four sections (Brief, My Tasks, Open Decisions, At-Risk Commitments) load independently | ✅ match | useQueries — each section has its own query; fixed 2026-06-14 |
| 3 | Per-section failure independence — one section fails, others render | ✅ match | Fixed 2026-06-14; test_section_independent_failure |
| 4 | All four sections show correct empty states (not blank) | ✅ match | Fixed 2026-06-14; test_section_empty_states; positive copy e.g. “Không có quyết định đang chờ 🎉“ |
| 5 | Pinned announcement banner: max 1, hides on ack | ✅ match | TodayPage.tsx banner logic; test_pinned_announcement_banner_acks (E2E) |
| 6 | My Tasks: due_date ≤ today, status=open, assigned_to=me — max 10 | ✅ match | API filter params; frontend hard-cap slice to 10 |
| 7 | Cross-team task visibility: org-scope wider than project-team-scope (tasks visible beyond project membership) | ⚠️ gap | My Tasks uses org FK + assigned_to; F22 project team membership not checked; tasks from non-member projects visible |
| 8 | Open 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 |
| 9 | Frontend hard-cap (tasks: 10, others: 5) with overflow count | ✅ match | useTodayData exposes `totalTasks |
| 10 | ”Xem thêm” links navigate to feature pages correctly | ✅ match | Navigation confirmed; test_today_loads E2E covers link presence |
| 11 | Visual QC: non-executive members do not land on an executive Today surface with org-wide decisions, risks, and commitments | ✅ match | 2026-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 |
| 12 | Visual QC: Today first viewport stays a triage surface instead of drifting into a chart dashboard | ✅ match | 2026-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=openorg-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, addflagged_for_userorassigned_to_userfiltering instead of front-end-only reshaping. - Overflow count (row 9): Resolved 2026-07-06 —
useTodayDatanow exposes total counts; TodayPage renders “{{visible}} of {{total}}” via theoverflow_counti18n 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.cuedeclaration 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 dedicatedtoday_briefview (backend/apps/briefs/views.py~45) viabriefsApi.today()infrontend/src/api/endpoints.tsline 218. There is no?date=todayquery param path.GET /api/tasks/?due_date__lte=today&status=open&assigned_to=me→ two separate queries.frontend/src/hooks/queries/useToday.tsissues?status=todo&my_tasks=true&due_before=<today>and?status=in_progress&my_tasks=true&due_before=<today>independently; results are merged inuseTodayData. Thestatus=openandassigned_to=meparams do not exist.GET /api/commitments/?at_risk=true→GET /api/commitments/?status=open.CommitmentViewSet.get_querysetonly handlesstatus,due_after,due_before— there is noat_riskfilter. The frontend determines at-risk display presentation-side.
- §2 AC: “shows commitments with
due_date ≤ today + 3dORstatus=at_risk” was wrong. The backend returnsstatus=opencommitments; 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.tsxto reflect the IA restructure that moved pages into subdirectories. Verified byfind 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
Share2icon 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 newuseCommitmentSharehook (see F09 §6 for the helper/hook implementation — F09 owns the commitment-share spec; F13 owns the surface). -
No new keys in the
todaynamespace. Six new keys land in thecommitmentsnamespace (share_button,share_header,share_header_nodate,share_owner,share_due,share_overdue) and propagate to all 62 locales vianode scripts/propagate-new-i18n-keys.mjs. Shared footer + toasts continue to come from thebriefnamespace — 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.actionsuntouched; per-decision Share button untouched; per-risk Share button untouched; per-item brief share on/intel/briefuntouched; 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 inTodayPage.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
briefnamespace, sameAbortErrorno-fallback, same F59 referral footer — four share surfaces, one viral loop, one source of truth for the loop’s branding. -
Added a ghost-variant
Share2icon 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 newuseRiskSharehook (see F10 §6 for the helper/hook implementation — F10 owns the risk-share spec; F13 owns the surface). -
No new keys in the
todaynamespace. Five new keys land in therisksnamespace (share_button,share_header,share_header_nodate,share_severity,share_type) and propagate to all 62 locales vianode scripts/propagate-new-i18n-keys.mjs. -
Behavior-preserving for every existing path: whole-brief share button on Today’s
PageHeader.actionsuntouched; per-decision Share button untouched; per-item brief share on/intel/briefuntouched; 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 inTodayPage.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/todayevery 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
Share2icon button to every open Decision card on/work/today(rendered only ford.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 newuseDecisionSharehook — same fallback chain, sameAbortErrorno-fallback, sameshare_copied/share_errortoasts asuseBriefShare/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 thebriefnamespace’s existingshare_footer/share_footer_referralkeys (already translated in all 62 locales — same footer copy as the brief share, one source of truth). - Five new i18n keys in
decisionsnamespace (share_button,share_header,share_header_nodate,share_recommend,share_decide_by) in en + vi, propagated to all 60 other localedecisions.jsonfiles as English placeholders. No new artifact text added tobriefnamespace — 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=openendpoint 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 infrontend/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
Sharebutton inTodayPage’sPageHeader.actionsslot, 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 asBriefPage— 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.tshook soBriefPageandTodayPageshare one source of truth — the two surfaces can never drift on share semantics (samebuildBriefShareText, same try/catch, sameAbortErrorno-fallback, sameshare_copied/share_errortoast keys from thebriefnamespace, same generic-footer fallback when the referral query is still in flight). - New i18n key (
share_brief_button) added totodayen+vi and propagated to all 60 other localetoday.jsonfiles. 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 withBriefPage, 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/briefonly.
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.tsxrefactored from singleuseQuery+ conditional renders touseQueriesarray.- 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.tsxnow 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.tsxfrontend/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.tsxnow 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.tsxfrontend/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.