Spec: F57 — Workspace Apps & Blank State Onboarding
Current V2 mobile shell:
Lumi·Work·Business·Hub.
Status: 🚧 Current source behavior contract-backed; sample-pulse and runtime proof remain open
⚠️ Activation model superseded by ADR-022 (2026-06-22). The original “blank-state onboarding” — modules hidden until activated from the intro pages — has been reversed. The current behavior: modules are on by default (
apps_enabled[slug]absent ⇒ enabled; only an explicitfalsehides one, owner/admin-controlled from Settings); nav groups are collapsed by default except Intelligence, persisted per user inUser.ui_preferences; and the former intro-page route aliases now redirect to the corresponding operating surfaces: Work →/work/today, Business →/deals, and Connect →/intel/chat. They remain registered for bookmark compatibility but are no longer documentation destinations or exposed through a sidebar group. The §2/§6 activate/deactivate-flow stories below are kept for history but no longer describe shipped behavior — read ADR-022 and the 2026-07-16 §13 checkpoint for the truth. F57’s discovery doctrine survives.
1. Problem & CEO Job
Jobs: See clearly + Decide fast.
A brand-new CEO who just registered and created an org lands on a fully populated sidebar showing all 5 nav groups (Work, Intelligence, Business, Connect, Admin). Most items lead to empty pages — no projects, no deals, no companies, no messages, no announcements. This is overwhelming, not helpful. The CEO doesn’t know where to start.
Existing F52 (Onboarding) creates demo data for the Brief/Today experience, but the sidebar still shows everything. It doesn’t guide discovery step by step.
This spec solves two problems:
-
Blank state onboarding — After registration, show only the Intelligence module (pulse, brief, commitments, risks, decisions); other nav groups are hidden until activated. Strategy v2 (2026-06-14): the blank state’s first action should be the F70 Chaos Import → Boss Brief — paste a messy Zalo thread / Excel export / screenshot and get an evidence-cited brief from the owner’s own data — not the pulse ritual. Pasting your own chaos is the zero-setup wedge that earns the first “this knows my company” moment in under 10 minutes (registry doctrine rules 4–5). The daily Pulse is the steady-state habit the wedge graduates into — the recurring capture ritual the owner adopts after the paste→brief loop has proven value, not the cold-start they face on day one. The first-visit sample pulse (below) remains a teaching aid for that steady-state ritual; it is not the blank state’s lead action.
-
Workspace Apps as a nav group — Replace the hidden Work/Business/Connect/Admin groups with an “Apps” nav group containing three intro pages: Work, Business, Connect. These are landing pages that explain what the app does. Admins can click “Activate” to enable the full nav menu for that app group. Non-admin members click “Request” to notify an admin.
Mobile shell alignment note (2026-08-05): F57 still describes web/sidebar activation language. Mobile should not mirror these labels literally. Under F79, the activated product is organized into Lumi · Work · Business · Hub; Pulse and Company Hub are nested inside that shell. F57’s app-group idea remains useful as enablement and discovery doctrine, but not as the final mobile tab naming.
2. User Stories & Acceptance Criteria
Blank State — Intelligence-Only Sidebar
- As a new CEO, I want to see only the Intelligence module in the sidebar after registration, so that I focus on what matters first: submitting my pulse and seeing my brief.
- After registration + org creation, the sidebar shows exactly one nav group: “Intelligence” with items (pulse, brief, commitments, risks, decisions, follow-ups, inbox, lumi assistant). All other nav groups (Work, Business, Connect, Admin) are hidden. (Sidebar.tsx: Work/Business/Connect gated by appsEnabled; Intelligence always visible; new users start with empty apps_enabled)
- The Admin nav group only appears for owner/admin roles even after activation. (Sidebar.tsx:
...(isAdminOrOwner ? [...] : [])conditional)
Sample Pulse
- As a new CEO, I want to see a pre-filled sample pulse when I open
/intel/pulsefor the first time, so that I immediately understand how to fill in my daily check-in.- On first visit to
/intel/pulseafter registration, the pulse form shows pre-filled sample answers with a visible “(Sample)” note appended to each answer text. (PulsePage.tsx:isFirstPulseset when pulses.length === 0; sampleAnswers pre-fill from pulse.json locale keys) - The sample answers are editable — the CEO can modify them before submitting. (standard controlled textareas)
- A banner or tooltip explains: “This is a sample to show you how Lumi works. Edit the answers or write your own, then submit.” (PulsePage.tsx line 113: sample_banner locale key)
- Submitting the sample-modified pulse creates a real PulseEntry with extraction (F08) and evidence. The “(Sample)” note is stripped on submit. (PulsePage.tsx handleSubmit: strips
sample_suffixbefore calling pulsesApi.submit) - On subsequent visits to
/intel/pulse, no sample data is shown — only the user’s real submissions (or an empty form for the new day). (list.length > 0 → isFirstPulse = false)
- On first visit to
Route-only App Aliases
- As a CEO following an old app-intro link, I want to reach the real operating surface, so that the bookmark remains useful without creating a parallel documentation destination.
-
/apps/intro/work,/apps/intro/business, and/apps/intro/connectredirect to/work/today,/deals, and/intel/chat, respectively. (routeTree.ts + redirect-destination E2E coverage) - The sidebar does not render a Guides/Apps group or links to the intro pages. (Sidebar.tsx + Sidebar/F57 regression coverage)
- When an app is activated, the corresponding nav group (Work, Business, or Connect) appears in the sidebar with its sub-items. (Sidebar.tsx: appsEnabled.work/business/connect conditional)
-
App Intro Pages (historical, superseded)
The stories below record the retired page implementation. The route aliases now redirect as specified above; these checked boxes are historical implementation evidence, not current acceptance criteria.
- As a CEO, I want a beautiful intro page for each app, so that I understand what the module does before I activate it.
-
/apps/intro/workshows: hero section with app name + tagline, feature overview (projects, tasks, recurring tasks, horizon), screenshots or mockups, benefit summary for an SME CEO. (WorkIntroPage.tsx: title+tagline, feature icon cards, feature list, benefit summary section with why_heading + work_benefit_1/2/3) -
/apps/intro/businessshows: companies, deals (CRM), approvals — feature overview and benefits. (BusinessIntroPage.tsx: feature cards + benefit summary section) -
/apps/intro/connectshows: hub announcements, messages (F29 internal chat), telegram, chat (Ask Lumi) — feature overview and benefits. (ConnectIntroPage.tsx: feature cards + benefit summary section) - Each intro page is fully localized (en + vi). (app_intros.json in both en/ and vi/ with all keys including benefit_1/2/3 and why_heading; 60 other locales use English fallback)
- Each intro page has an empty-state illustration or icon per feature listed. (feature icon cards use Lucide icons: CalendarDays, FolderOpen, CheckSquare, etc.)
-
Activate / Deactivate Flow (three states)
Each app slug has three states in Organization.apps_enabled:
| State | apps_enabled[slug] | Who can change it | Intro-page action |
|---|---|---|---|
| Blank | key absent | any org member | primary “Activate” button (everyone) |
| Active | true | owner/admin only | ”active” status + (admin) “Deactivate” button |
| Inactive | false | owner/admin only | (admin) “Activate” button · (non-admin) “deactivated” message |
-
As a new member viewing an un-activated (blank) app, I want to activate it myself, so that I don’t have to wait for an admin to turn on a module no one has touched yet.
- On
/apps/intro/*, when the app is in the blank state (slug absent fromapps_enabled), every authenticated member sees a primary “Activate” button regardless of role. (all 3 intro pages:isBlank || isAdmin→ Activate button) - Clicking “Activate” triggers
POST /api/organizations/<id>/apps/<slug>/activate/which setsOrganization.apps_enabled[<slug>] = true. (appsApi.activate → organizations/views.py activate_app) - On success, the corresponding nav group (Work/Business/Connect) appears in the sidebar immediately. A success toast confirms activation. (handleActivate calls useAuthStore.getState().loadOrganization() after success)
- On
-
As an owner/admin, I want exclusive control over an app once it has been activated, so that a member cannot silently toggle a module the org now depends on.
- Once the slug is present in
apps_enabled(active or inactive), only owner/admin may activate/deactivate it; any other role gets403. (activate_app:already_managed and role not in (owner, admin)→ 403; deactivate_app gated byIsOrgOwnerOrAdmin) - On
/apps/intro/*for an active app, an “active” status note is shown; owner/admin additionally see a “Deactivate” button. (all 3 intro pages:isActivebranch → activated_status + admin Deactivate) - Clicking “Deactivate” triggers
POST /api/organizations/<id>/apps/<slug>/deactivate/which setsapps_enabled[<slug>] = false; the nav group disappears on success. (appsApi.deactivate → organizations/views.py deactivate_app → loadOrganization) - On
/apps/intro/*for an inactive app, owner/admin see an “Activate” button to re-enable; non-admins see a “deactivated — ask your admin” message. (intro pages:isBlank || isAdmin→ Activate; else deactivated_status)
- Once the slug is present in
Nav Group Activation Persistence
- As a CEO who activated Work, I want the Work nav group to persist in the sidebar across sessions, so that I don’t have to re-activate.
- When
Organization.apps_enabledcontains"work", the Work nav group renders in the sidebar on every page load. (Sidebar.tsx:appsEnabled.workcheck reads from organization loaded by authStore) - Multiple apps can be enabled independently — activating Work does not auto-activate Business or Connect. (each app slug is independently set in apps_enabled JSON)
- The intro pages remain available by direct route after activation (so existing deep links continue to work). (routes remain registered in
routeTree.ts)
- When
3. Scope Boundaries
- In:
- Intelligence-only sidebar after registration (conditional nav rendering).
- Sample pulse on first visit to
/intel/pulse(frontend-only or backend-flagged). - Direct-route app intro pages (Work, Business, Connect) with descriptions, features, screenshots.
- Three-state activation: blank (any member activates) → active/inactive (owner/admin only toggle).
- Activate + Deactivate buttons + backend endpoints.
Organization.apps_enabledJSON field for persisting activated apps.- Both en + vi localization for all new UI.
- Out:
- Non-admin Request/notification flow (superseded — any member can activate a blank app directly).
- Per-member app visibility (activation is org-wide).
- Trial/gating by subscription plan (activation is admin choice, not plan-based in v1).
- App store or marketplace — these are Lumi’s own modules, not third-party.
- Custom app intro content per organization (single canonical intro per app).
- Request email retry or follow-up workflow (single email is enough for v1).
4. Data Model
4.1 Organization Model Changes
# Organization — new field
apps_enabled = models.JSONField(default=dict, blank=True)
# Example value: {"work": true, "business": false, "connect": false}
No new models. apps_enabled is a simple JSON dict keyed by app slug.
4.2 App Slugs
APPS = {
"work": {
"name": "Work",
"name_vi": "Công việc",
"nav_group": "group_work",
"items": ["today", "horizon", "projects", "tasks", "recurring"],
},
"business": {
"name": "Business",
"name_vi": "Kinh doanh",
"nav_group": "group_business",
"items": ["companies", "deals", "approvals"],
},
"connect": {
"name": "Connect",
"name_vi": "Kết nối",
"nav_group": "group_connect",
"items": ["hub", "messages", "telegram"],
},
}
Admin group is always available to owner/admin roles and is not gated by an app.
2026-06-21 (feedback #18):
chat(“Lumi Assistant”, formerly “Chat Lumi”) moved out of the gated Connect app group into the always-on Intelligence group — the AI operating assistant should be visible from day one, not hidden behind Connect activation. Route/hub/chatis unchanged. See §13.
4.3 Request Model (optional — can use existing Notification model)
If a dedicated model is preferred:
class AppRequest(UUIDModel):
organization = ForeignKey(Organization, on_delete=CASCADE)
requested_by = ForeignKey(OrganizationMember, on_delete=CASCADE)
app_slug = CharField(max_length=20)
created_at = DateTimeField(auto_now_add=True)
notified_admins = BooleanField(default=False)
5. API Contract
| Method | Path | Role | Description |
|---|---|---|---|
| POST | /api/organizations/<id>/apps/<slug>/activate/ | any member if blank; owner/admin if already managed | Enables app for org (apps_enabled[slug]=true). Returns updated organization. 403 if a non-admin tries to toggle an already-managed app. |
| POST | /api/organizations/<id>/apps/<slug>/deactivate/ | owner/admin | Disables app for org (apps_enabled[slug]=false). Returns updated organization. |
| GET | /api/organizations/<id>/apps/status/ | any authenticated | Returns {"apps_enabled": {...}, "all_slugs": ["work", "business", "connect"]}. |
| GET | /api/organizations/<id>/pulse/sample/ | any authenticated | Returns a sample pulse payload with pre-filled answers marked as is_sample: true. Called once on first /intel/pulse visit. |
Tenant scoping: All endpoints scoped to request.organization_id.
6. UI / UX
6.1 Sidebar — Intelligence-Only Blank State
After registration, the sidebar renders:
┌─ Lumi AI Workspace ─────────────────────────────────────┐
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ 🎯 Intelligence │ │
│ │ │ │
│ │ 📡 Pulse │ │
│ │ 📄 Brief │ │
│ │ 🔗 Commitments │ │
│ │ ⚠️ Risks │ │
│ │ 🌿 Decisions │ │
│ │ 🔔 Follow-ups │ │
│ │ 📥 Inbox │ │
│ └──────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ ⚙️ Admin │ │
│ │ │ │
│ │ 👥 Teams │ │
│ │ 👤 Invite │ │
│ │ ⚙️ Settings │ │
│ │ 🔌 Sources │ │
│ └──────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────┘
After activating Work:
│ ┌──────────────────────────────────────────────────┐ │
│ │ 💼 Work │ │
│ │ │ │
│ │ 📅 Today │ │
│ │ 🗓️ Horizon │ │
│ │ 📁 Projects │ │
│ │ ✅ Tasks │ │
│ │ 🔄 Recurring │ │
│ └──────────────────────────────────────────────────┘ │
Admin group always shows for owner/admin roles (not gated by an app).
6.2 Sample Pulse
┌─ Daily Pulse ───────────────────────────────────────────┐
│ │
│ 📋 This is a sample to show you how Lumi works. │
│ Edit the answers or write your own, then submit. │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Hôm nay anh/chị làm gì? │ │
│ │ ┌────────────────────────────────────────────────┐ │ │
│ │ │ Họp với nhà cung cấp về đơn hàng tháng 7 │ │ │
│ │ │ (Sample) │ │ │
│ │ └────────────────────────────────────────────────┘ │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Có gì cần quyết định? │ │
│ │ ┌────────────────────────────────────────────────┐ │ │
│ │ │ Chọn nhà cung cấp nguyên liệu A hay B │ │ │
│ │ │ (Sample) │ │ │
│ │ └────────────────────────────────────────────────┘ │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ ... (4 more questions with sample answers) │
│ │
│ [Submit Pulse] │
└──────────────────────────────────────────────────────────┘
6.3 App Intro Page (example: Work)
┌─ Lumi AI Workspace ───────────────────────────────────────┐
│ /apps/intro/work │
│ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ 💼 Work │ │
│ │ │ │
│ │ Quản lý công việc, dự án và deadline — tất cả │ │
│ │ trong một nơi. │ │
│ │ │ │
│ │ ┌───┐ ┌───┐ ┌───┐ ┌───┐ │ │
│ │ │📅 │ │📁 │ │✅ │ │🔄 │ │ │
│ │ │Tod│ │Pro│ │Tas│ │Rec│ │ │
│ │ │ay │ │jec│ │ks │ │urr │ │ │
│ │ └───┘ └───┘ └───┘ └───┘ │ │
│ │ │ │
│ │ • Today — Xem tất cả việc cần chú ý hôm nay │ │
│ │ • Projects — Quản lý dự án theo tiến độ │ │
│ │ • Tasks — Theo dõi công việc từng người │ │
│ │ • Recurring — Tác vụ lặp lại hàng ngày/tuần/tháng │ │
│ │ • Horizon — Deadline sắp tới trong tuần │ │
│ │ │ │
│ │ Dành cho CEO: biết ai đang làm gì, việc gì trễ, │ │
│ │ dự án nào cần chú ý — không cần mở Slack hay │ │
│ │ email để hỏi. │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────┐ │ │
│ │ │ Activate Work Module │ │ │
│ │ └──────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ (Non-admin: [Request Admin to Activate]) │ │
│ └───────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────┘
6.4 i18n
Legacy sidebar namespace keys group_apps / group_guides remain for locale compatibility;
neither key is used to render a sidebar group.
New namespace: app_intros. Keys per app:
app_intros.work.title,app_intros.work.tagline,app_intros.work.features.today, etc.app_intros.business.title,app_intros.business.tagline, …app_intros.connect.title,app_intros.connect.tagline, …
Shared keys:
app_intros.activate_button(“Activate” / “Kích hoạt”)app_intros.deactivate_button(“Deactivate” / “Vô hiệu hóa”)app_intros.activation_success(”activated successfully!” / “Đã kích hoạt !”) app_intros.deactivation_success(”has been deactivated.” / ” đã bị vô hiệu hóa.”) app_intros.activation_failed/app_intros.deactivation_failed(error toasts)app_intros.activated_status(“This app is active for your organization.” / …)app_intros.deactivated_status(“This app has been deactivated. Ask your admin to enable it again.” / …)
Pulse namespace keys:
pulse.sample_note(“(Sample)” / “(Mẫu)”)pulse.sample_banner(“This is a sample… Edit or write your own, then submit.” / “Đây là mẫu…”)
Both en and vi updated for all new keys.
7. Test Plan
Coverage note (2026-06-14): App-activation tests live in
backend/tests/test_apps.pywith classesTestAppActivate,TestAppsStatus,TestAppRequest. Rows below use original per-case naming; the file is authoritative.
| # | Case | Type | Where |
|---|---|---|---|
| 1 | New user sees Intelligence-only sidebar | frontend | Sidebar.test.tsx |
| 2 | Activate Work → Work nav group appears | e2e | describe("App Activation") |
| 3 | Blank state: any member can activate; managed app is admin-only (403) | backend | tests/test_apps.py::TestAppActivate |
| 4 | POST activate sets apps_enabled[slug]=true | backend | tests/test_apps.py::TestAppActivate |
| 5 | POST deactivate sets apps_enabled[slug]=false (owner/admin only) | backend | tests/test_apps.py::TestAppDeactivate |
| 6 | Cross-tenant: org-B activate does not affect org-A | backend | tests/test_apps.py |
| 7 | Sample pulse loads on first visit only | frontend | PulsePage.test.tsx |
| 8 | Sample pulse has (Sample) labels, stripped on submit | frontend | PulsePage.test.tsx (sample logic is client-side per §11 row 5; no backend test) |
| 9 | Intro route aliases redirect to their canonical operating surfaces | e2e | cross-cutting-2.spec.ts |
| 10 | Activation persists across page reloads | e2e | workspace-apps.spec.ts |
| 11 | Sidebar omits the removed Guides group and intro links | e2e | workspace-apps.spec.ts |
| 12 | Retired intro-page activation control remains absent | e2e | workspace-apps.spec.ts |
| 13 | Pulse page loads without error | e2e | workspace-apps.spec.ts |
8. Current Behavior
Shipped. Organization.apps_enabled (JSONField) gates module visibility: absent means enabled and an
explicit false hides a module; only owner/admin may toggle it. Activation/deactivation/status
endpoints live in backend/apps/organizations/views.py. The sidebar renders Work/Business/Connect
nav groups conditionally on apps_enabled (frontend/src/components/Sidebar.tsx). The legacy
/apps/intro/{work,business,connect} aliases redirect to /work/today, /deals, and /intel/chat
and are not listed in the sidebar. The sample-pulse acceptance cases remain open as recorded in the
Contract Graph; they are not claimed shipped here.
9. Error States
| # | Error | Behavior |
|---|---|---|
| 1 | Activate fails (DB error) | Toast: “Activation failed. Please try again.” Logged server-side. |
| 2 | Request fails (email service down) | Toast: “Request saved but email could not be sent. Admin will see it on their dashboard.” Notification created anyway. |
| 3 | Unknown app slug in URL | 404 with “App not found” message. Valid slugs are work, business, connect. |
| 4 | Unauthenticated user hits intro page | Redirect to login. Intro pages are org-aware but not public. |
| 5 | Sample pulse already exists for today | /intel/pulse shows the real submission (read-back), not the sample form. |
10. Diagrams & Docs Touched
frontend/DIAGRAMS.md— sidebar nav structure updated to show conditional rendering based onapps_enabled.backend/DIAGRAMS.md— state machine for Organization.apps_enabled (simple JSON field, no complex states).docs/specs/F52-onboarding-activation.md— cross-reference that blank state onboarding complements demo-data generation.
11. Implementation Audit
Edge Cases Catalog (formerly §9, 2026-06-14; 7 items): all-apps active sidebar scroll, admin demotion keeps org toggle, new members inherit enabled apps, idempotent re-activate (200), sample pulse skipped for existing orgs, first-visit flag prevents re-show, request cooldown 24h. Full catalog in git history.
| # | Claim | Status | Evidence |
|---|---|---|---|
| 1 | Organization.apps_enabled JSONField on Organization model | ✅ match | backend/apps/organizations/models.py line 21 |
| 2 | App activate + deactivate + status endpoints (three-state, role-gated) | ✅ match | backend/apps/organizations/views.py (activate_app, deactivate_app, apps_status); tests/test_apps.py |
| 3 | Sidebar conditional nav group (Work, Business, Connect) based on apps_enabled | ✅ match | frontend/src/components/Sidebar.tsx |
| 4 | Legacy intro aliases redirect to Work, Business, and Connect operating surfaces | ✅ match | frontend/src/pages/apps/{Work,Business,Connect}IntroPage.tsx |
| 5 | Sample pulse logic (first-visit detection, pre-filled answers) | ✅ match | frontend/src/pages/intel/PulsePage.tsx |
| 6 | i18n additions (sidebar group_apps, app_intros namespace, pulse sample keys) | ✅ match | Confirmed by registry en/ + vi/ locale files |
| 7 | [Strategy v2 realignment — TO BUILD, not shipped] Blank state leads with F70 Chaos Import → Boss Brief (paste a messy Zalo/Excel/screenshot → evidence-cited brief from the owner’s own data) as the first action, not “submit your first pulse”. UI change: the Intelligence-only blank state surfaces the F70 paste affordance as its primary CTA on first load; the sample pulse demotes to a teaching aid for the steady-state ritual the wedge graduates into. | 🔲 gap — TO BUILD | Strategy v2 §9.1/§7.2 + registry Positioning doctrine (strategy v2, 2026-06-14) rules 4–5 (zero-setup, no blank workspace, wedge = F70). Not built — current code leads the blank state with the sample-pulse flow (rows 1, 5). §4 data model / §5 API / §7 tests unchanged by this note. |
12. Open Questions / Decisions
-
Sample pulse content. What should the sample answers say? Decision in v1: generic Vietnamese SME CEO answers (meeting with suppliers, need to decide on materials, etc.). Could be industry-customized in future (ties to F52 industry selection).
-
First-visit detection for sample pulse. Server-side flag on
OrganizationMemberor frontend localStorage? Decision: server-side (first_pulse_loadedboolean onOrganizationMember) so it persists across devices. -
Legacy app-intro route discoverability. Decision: retain the aliases for bookmark compatibility, but redirect them to
/work/today,/deals, and/intel/chat; do not expose a Guides/Apps sidebar group. -
Admin group always visible. Should Admin always show for admin/owner even before activation? Decision: yes. Admin (Teams, Invite, Settings, Sources) is not a “module” — it’s org management and should always be accessible.
-
Activation vs subscription gating. Should app activation require a certain plan? Decision: not in v1. Any org can activate any app. Future billing integration can gate by plan if needed.
13. Bug / Change Log
2026-07-15 — Remove redundant Guides menu
Type: UI / Information architecture change
Summary:
- Removed the sidebar’s Guides/Hướng dẫn group and its Work, Business, and Connect intro links.
- Kept
/apps/intro/{work,business,connect}registered as direct routes for existing bookmarks and deep links. - Updated Sidebar unit tests, F57 coverage, workspace E2E coverage, and the frontend navigation diagram.
2026-07-10 — Guide pages deep-link every mentioned feature (feedback #56)
Type: Enhancement (tester feedback lumi-feedback#56)
Summary:
- The three intro/guide pages (
frontend/src/pages/apps/{Work,Business,Connect}IntroPage.tsx) described features without linking to them — users read the guide, then had to hunt for the screen. Every feature mentioned (icon card + features-list bullet) is now aLinkto its route, with hover/focus affordance (ArrowUpRight on list items, border/shadow on cards). - Telegram is dev-only (Sidebar
DEV_ONLY_PATHS);ConnectIntroPagenow mirrors that viaisDevEnvironment()— outside dev the telegram entry is neither mentioned nor linked. - Tests:
frontend/src/test/apps/IntroPagesLinks.test.tsx(4 cases — link presence per page, dev-only gating both ways).
2026-07-02 — Correct stale page paths in §11 (batch-1 spec audit)
Type: Documentation Fix
Summary:
- §11 row 5: Updated
frontend/src/pages/PulsePage.tsx→frontend/src/pages/intel/PulsePage.tsxto reflect the IA restructure. Verified byfind frontend/src -name PulsePage.tsx=frontend/src/pages/intel/PulsePage.tsx. - Historical note: the 2026-06-22 change log entry records
frontend/src/pages/{Work,Business,Connect}IntroPage.tsx; those files now live atfrontend/src/pages/apps/{Work,Business,Connect}IntroPage.tsx. The historical entry is preserved unchanged (per spec policy: never rewrite history). Current locations verified byfind frontend/src -name '*IntroPage.tsx'=frontend/src/pages/apps/.
2026-06-22 — Reverse activation gate: modules default-on, nav collapsed-by-default, intro pages → docs (ADR-022)
Type: UX / Architecture change (reverses this spec’s core “blank-state activation” model).
Why. The activation gate made a new CEO decide before they could see, and forced the intro pages to be both a feature explainer and an on/off CTA. Per the owner’s call (and ADR-022) we drop the gate: everything is available by default, the nav stays calm via collapse (not hiding), and the intro pages become pure documentation.
What changed.
- Modules default-on.
Organization.apps_enabled[slug]semantics flip: absent ⇒ enabled; only an explicitfalsehides a module. New orgs (emptyapps_enabled) show Work/Business/Connect immediately. Sidebar gate is nowappsEnabled[slug] !== false. - Admin hide control moved to Settings.
activate_app/deactivate_appare now owner/admin-only (show/hide); member self-activation removed. A new Modules card in/settings(owner/admin) is the visibility control (SettingsPage.tsx→appsApi.activate/deactivate). Theapps_enabledfield and endpoints are retained — only default meaning + entry point changed (no migration). - Nav collapsed by default except Intelligence, persisted per user. New
User.ui_preferencesJSON (sidebar_groups: {<stableGroupKey>: bool}) viaPATCH /api/auth/me/preferences/(merge) + returned onGET /api/auth/me/. Sidebar groups carry stable keys (work/business/…), never translated labels. - Intro pages → documentation.
/apps/intro/{work,business,connect}drop the activate/deactivate CTA and theapps_enabled/role logic; they now only explain features & use cases. The routes remain available directly, without a sidebar group. - Tests.
backend/tests/test_apps.pyupdated (activate/deactivate now admin-only — member/manager/ viewer get 403, no blank-state self-activation); newbackend/tests/test_ui_preferences.py(me exposesui_preferences, PATCH merges, isolation);frontend/src/test/F57SidebarApps.test.tsxrewritten (default-on, collapse-default, expand persistence, admin-hide, Guides) + intro pages now assert no CTA. - Docs. ADR-022 records the decision; ADR index + this banner updated. Backend gate green (3437 passed, 98.53%); frontend tsc + design-tokens clean.
2026-06-21 — Lumi Assistant moved to Intelligence + renamed from “Chat Lumi” (feedback #18)
Type: UI / IA Change
Summary:
- Moved the assistant nav item from the gated Connect group to the always-on
Intelligence group (
Sidebar.tsx), so the AI operating assistant is visible from first run rather than hidden behind Connect activation. - Renamed “Chat Lumi” → “Lumi Assistant” (vi: “Trợ lý Lumi”) across
sidebar.json+dock.jsonlocales; the floating dock pod and panel title follow the same key. Route/hub/chatunchanged (no broken links). - Icon changed to
Sparkles(assistant) fromMessageSquare(chat). - Updated
§2.1(Intelligence items now include lumi assistant) and§4.2(Connect items no longer listchat). - Tests updated:
Sidebar.test.tsx,CommandDock.test.tsx. - Commit
5bf4cb53· reported instreamentry/lumi-fb#18. - Follow-up: ~38 non-en/vi locales that already translated the old label keep their translation until the translation workflow localizes the new name; the Connect intro page (§6.3 family) still lists “chat” as a Connect feature and should be revisited.
2026-06-15 — Frontend route namespaced
Frontend route namespaced: /pulse -> /intel/pulse (app-wide IA restructure; API paths unchanged).
2026-06-18 — Clarify F57 is not the mobile tab naming contract
Type: Documentation Change
Summary:
- Added a mobile-shell alignment note in §1 clarifying that F57’s
Apps/Work/Business/Connectlanguage remains useful for enablement and discovery, but does not define the final mobile tab names. - Pointed mobile shell ownership to F79.
Reason:
- Without this note, F57 and F79 would give different answers about mobile information architecture.
Impact:
- Future activation/onboarding work can still reuse F57 doctrine without accidentally reintroducing the wrong mobile tab labels.
Tests:
- None. Spec-only change.
2026-06-15 — Three-state activation; Request flow replaced by self-serve Activate
Type: Behavior Change
Summary:
- App activation is now self-serve in the blank state: any org member may activate an app that has never been turned on (slug absent from
apps_enabled). Once stored (activetrueor inactivefalse), the app is admin-managed — only owner/admin may activate/deactivate it (non-admin →403). - Added
POST /api/organizations/<id>/apps/<slug>/deactivate/(owner/admin only) and removed the obsoleterequest_appendpoint +/request/route.activate_apppermission relaxed toIsOrgMemberwith an in-view managed-state role check. - Intro pages (
Work/Business/Connect) now render state-appropriate actions: blank/inactive-admin → “Activate”; active → “active” note + admin “Deactivate”; inactive non-admin → “deactivated, ask your admin” message. Role is read fromorganizations[].role(matching Sidebar) instead of the unreliableuser.role. - i18n: removed
request_button/requested_button/request_sent; addeddeactivate_button,deactivation_success,activation_failed,deactivation_failed,activated_status,deactivated_statusacross the 13 parity-checked locales (en + vi translated, others English-fallback).
Reason:
- Remove the admin bottleneck for discovering empty modules while keeping admin control once a module is in use. The Request→notify-admin flow was a stub (no email) and is no longer needed.
Impact:
backend/apps/organizations/{views,urls}.py;backend/tests/test_apps.py(newTestAppDeactivate, rewrittenTestAppActivate, removedTestAppRequest);frontend/src/pages/{Work,Business,Connect}IntroPage.tsx;frontend/src/api/endpoints.ts;frontend/src/test/F57SidebarApps.test.tsx;app_introslocale files.
Tests:
- Backend:
tests/test_apps.py25 passing; suite coverage 99.65%. - Frontend:
F57SidebarApps.test.tsx16 passing (added blank-state-member-activate, admin-deactivate, deactivated-no-button);tsc --noEmitclean;app_introskey-parity passing.
Notes:
- Pre-existing
sidebarnamespace key-parity failures (dev_only/dev_only_hint) are unrelated to this change.
2026-06-14 — Strategy v2 realignment (positioning)
Type: Documentation Change
Summary:
- §1 problem #1 (blank state) reframed: the blank state leads with the F70 Chaos Import → Boss Brief (paste your own mess → evidence-cited brief), not “submit your first pulse”. The pulse is restated as the steady-state habit the wedge graduates into; the first-visit sample pulse is a teaching aid for that ritual, not the lead action.
- §11 added gap row 7 documenting the blank-state UI change (F70 paste as primary first-load CTA; sample pulse demoted) as TO BUILD — not shipped.
Reason:
- Align with competitive-strategy-v2.md §9.1/§7.2 and the registry Positioning doctrine (strategy v2, 2026-06-14) rules 4 (zero-setup, no blank workspace) and 5 (wedge = F70; Pulse is steady-state).
Impact:
- Positioning prose only in §1. The UI change is recorded as a §11 audit gap to fix — not claimed as built. No §4 data model, §5 API, §7 test plan, §11 prior audit-verdict rows, or Status change.
Tests:
- None (spec-only).
Notes:
- The Intelligence-only sidebar, sample-pulse behavior, and Apps activation flow are left intact as as-built v1; the realignment changes only which action leads the blank state, tracked via the §11 gap.
2026-06-14 — Spec restructured to TEMPLATE.md canonical order (§1–§13)
Type: Documentation Change
Summary:
- §9 Edge Cases (7 items) absorbed into §11 Implementation Audit note.
- §12 Implementation Audit converted from 3-column table + Implementation Order to canonical 4-column table (6 rows, all ✅).
- Sections renumbered: §10 ES → §9, §11 Diags → §10, §12 Audit → §11, §13 OQ → §12, §14 BL → §13.
Reason:
- Align with TEMPLATE.md canonical section order (§1–§13) per SP fleet restructuring pass.
Impact:
- Spec authors and future implementation agents.
2026-06-14 — Status change draft→agreed; added backend prerequisite for apps_enabled field
Type: Documentation Change
Summary:
- Changed spec status from
drafttoagreed. - Confirmed
backend/apps/organizations/models.pyOrganizationmodel lacksapps_enabledJSONField — documented as backend prerequisite in §12. - No spec content changed beyond section-level hardening.
Reason:
- Per T4-G round 2 task: verify Organization model has
apps_enabledJSONField or equivalent, and note it as a backend prerequisite.
Impact:
- Implementation agents must add
apps_enabledJSONField migration before F57 endpoints can work.
Tests:
- Spec-only change — no code tests affected.
2026-07-16 — Contract Graph activation ownership checkpoint
The exact shipped route POST /api/organizations/current/apps/{slug}/activate/ is now owned by
F57 through activate_app, with runner-native proof F57.AC001.ACTIVATE covering the owner/admin
allow path. This closes inventory ownership only. Backend execution, deployed tenant/authorization,
browser, accessibility, localization, persistence-runtime, and the existing sample-pulse gap remain
explicitly open.
The paired POST /api/organizations/current/apps/{slug}/deactivate/ route is also now declared
through deactivate_app, with F57.AC001.DEACTIVATE covering the owner/admin allow path. This is
additional inventory ownership and proof registration, not runtime or production evidence.
2026-07-16 — Contract Graph migration slice
The 2026-06-13 implementation audit was stale: F57 is not spec-only. Executable source and tests
now prove the ADR-022 behavior shipped in the repository: modules are default-on unless explicitly
hidden, visibility changes are owner/admin-only, sidebar expansion is persisted per user with
Intelligence expanded by default, direct intake is a collapsed disclosure, and the three legacy
intro routes redirect to real operating surfaces. The canonical contract is
contracts/features/F57.cue; the old audit is replaced by a current evidence matrix.
The sample-pulse acceptance cases remain skipped and are not claimed green. Browser execution, deployed authorization/tenant isolation, accessibility, localization, and persistence runtime remain explicit gaps with owners and reproduction commands in the contract.
The exact GET /api/organizations/current/apps/status/ read surface is registered to apps_status
with F57.AC001.APPS_STATUS_API; its repository test does not establish deployed tenant,
authorization, browser, accessibility, localization, or persistence runtime evidence.