🚧 Current source behavior contract-backed; sample-pulse and runtime proof remain open

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 explicit false hides one, owner/admin-controlled from Settings); nav groups are collapsed by default except Intelligence, persisted per user in User.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:

  1. 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.

  2. 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/pulse for the first time, so that I immediately understand how to fill in my daily check-in.
    • On first visit to /intel/pulse after registration, the pulse form shows pre-filled sample answers with a visible “(Sample)” note appended to each answer text. (PulsePage.tsx: isFirstPulse set 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_suffix before 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)

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/connect redirect 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/work shows: 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/business shows: companies, deals (CRM), approvals — feature overview and benefits. (BusinessIntroPage.tsx: feature cards + benefit summary section)
    • /apps/intro/connect shows: 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:

Stateapps_enabled[slug]Who can change itIntro-page action
Blankkey absentany org memberprimary “Activate” button (everyone)
Activetrueowner/admin only”active” status + (admin) “Deactivate” button
Inactivefalseowner/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 from apps_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 sets Organization.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)
  • 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 gets 403. (activate_app: already_managed and role not in (owner, admin) → 403; deactivate_app gated by IsOrgOwnerOrAdmin)
    • On /apps/intro/* for an active app, an “active” status note is shown; owner/admin additionally see a “Deactivate” button. (all 3 intro pages: isActive branch → activated_status + admin Deactivate)
    • Clicking “Deactivate” triggers POST /api/organizations/<id>/apps/<slug>/deactivate/ which sets apps_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)
  • 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_enabled contains "work", the Work nav group renders in the sidebar on every page load. (Sidebar.tsx: appsEnabled.work check 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)

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_enabled JSON 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/chat is 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

MethodPathRoleDescription
POST/api/organizations/<id>/apps/<slug>/activate/any member if blank; owner/admin if already managedEnables 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/adminDisables app for org (apps_enabled[slug]=false). Returns updated organization.
GET/api/organizations/<id>/apps/status/any authenticatedReturns {"apps_enabled": {...}, "all_slugs": ["work", "business", "connect"]}.
GET/api/organizations/<id>/pulse/sample/any authenticatedReturns 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.py with classes TestAppActivate, TestAppsStatus, TestAppRequest. Rows below use original per-case naming; the file is authoritative.

#CaseTypeWhere
1New user sees Intelligence-only sidebarfrontendSidebar.test.tsx
2Activate Work → Work nav group appearse2edescribe("App Activation")
3Blank state: any member can activate; managed app is admin-only (403)backendtests/test_apps.py::TestAppActivate
4POST activate sets apps_enabled[slug]=truebackendtests/test_apps.py::TestAppActivate
5POST deactivate sets apps_enabled[slug]=false (owner/admin only)backendtests/test_apps.py::TestAppDeactivate
6Cross-tenant: org-B activate does not affect org-Abackendtests/test_apps.py
7Sample pulse loads on first visit onlyfrontendPulsePage.test.tsx
8Sample pulse has (Sample) labels, stripped on submitfrontendPulsePage.test.tsx (sample logic is client-side per §11 row 5; no backend test)
9Intro route aliases redirect to their canonical operating surfacese2ecross-cutting-2.spec.ts
10Activation persists across page reloadse2eworkspace-apps.spec.ts
11Sidebar omits the removed Guides group and intro linkse2eworkspace-apps.spec.ts
12Retired intro-page activation control remains absente2eworkspace-apps.spec.ts
13Pulse page loads without errore2eworkspace-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

#ErrorBehavior
1Activate fails (DB error)Toast: “Activation failed. Please try again.” Logged server-side.
2Request fails (email service down)Toast: “Request saved but email could not be sent. Admin will see it on their dashboard.” Notification created anyway.
3Unknown app slug in URL404 with “App not found” message. Valid slugs are work, business, connect.
4Unauthenticated user hits intro pageRedirect to login. Intro pages are org-aware but not public.
5Sample 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 on apps_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.

#ClaimStatusEvidence
1Organization.apps_enabled JSONField on Organization model✅ matchbackend/apps/organizations/models.py line 21
2App activate + deactivate + status endpoints (three-state, role-gated)✅ matchbackend/apps/organizations/views.py (activate_app, deactivate_app, apps_status); tests/test_apps.py
3Sidebar conditional nav group (Work, Business, Connect) based on apps_enabled✅ matchfrontend/src/components/Sidebar.tsx
4Legacy intro aliases redirect to Work, Business, and Connect operating surfaces✅ matchfrontend/src/pages/apps/{Work,Business,Connect}IntroPage.tsx
5Sample pulse logic (first-visit detection, pre-filled answers)✅ matchfrontend/src/pages/intel/PulsePage.tsx
6i18n additions (sidebar group_apps, app_intros namespace, pulse sample keys)✅ matchConfirmed 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 BUILDStrategy 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

  1. 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).

  2. First-visit detection for sample pulse. Server-side flag on OrganizationMember or frontend localStorage? Decision: server-side (first_pulse_loaded boolean on OrganizationMember) so it persists across devices.

  3. 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.

  4. 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.

  5. 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.

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 a Link to its route, with hover/focus affordance (ArrowUpRight on list items, border/shadow on cards).
  • Telegram is dev-only (Sidebar DEV_ONLY_PATHS); ConnectIntroPage now mirrors that via isDevEnvironment() — 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.tsx to reflect the IA restructure. Verified by find 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 at frontend/src/pages/apps/{Work,Business,Connect}IntroPage.tsx. The historical entry is preserved unchanged (per spec policy: never rewrite history). Current locations verified by find 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 explicit false hides a module. New orgs (empty apps_enabled) show Work/Business/Connect immediately. Sidebar gate is now appsEnabled[slug] !== false.
  • Admin hide control moved to Settings. activate_app/deactivate_app are 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). The apps_enabled field and endpoints are retained — only default meaning + entry point changed (no migration).
  • Nav collapsed by default except Intelligence, persisted per user. New User.ui_preferences JSON (sidebar_groups: {<stableGroupKey>: bool}) via PATCH /api/auth/me/preferences/ (merge) + returned on GET /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 the apps_enabled/role logic; they now only explain features & use cases. The routes remain available directly, without a sidebar group.
  • Tests. backend/tests/test_apps.py updated (activate/deactivate now admin-only — member/manager/ viewer get 403, no blank-state self-activation); new backend/tests/test_ui_preferences.py (me exposes ui_preferences, PATCH merges, isolation); frontend/src/test/F57SidebarApps.test.tsx rewritten (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.json locales; the floating dock pod and panel title follow the same key. Route /hub/chat unchanged (no broken links).
  • Icon changed to Sparkles (assistant) from MessageSquare (chat).
  • Updated §2.1 (Intelligence items now include lumi assistant) and §4.2 (Connect items no longer list chat).
  • Tests updated: Sidebar.test.tsx, CommandDock.test.tsx.
  • Commit 5bf4cb53 · reported in streamentry/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 / Connect language 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 (active true or inactive false), 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 obsolete request_app endpoint + /request/ route. activate_app permission relaxed to IsOrgMember with 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 from organizations[].role (matching Sidebar) instead of the unreliable user.role.
  • i18n: removed request_button/requested_button/request_sent; added deactivate_button, deactivation_success, activation_failed, deactivation_failed, activated_status, deactivated_status across 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 (new TestAppDeactivate, rewritten TestAppActivate, removed TestAppRequest); frontend/src/pages/{Work,Business,Connect}IntroPage.tsx; frontend/src/api/endpoints.ts; frontend/src/test/F57SidebarApps.test.tsx; app_intros locale files.

Tests:

  • Backend: tests/test_apps.py 25 passing; suite coverage 99.65%.
  • Frontend: F57SidebarApps.test.tsx 16 passing (added blank-state-member-activate, admin-deactivate, deactivated-no-button); tsc --noEmit clean; app_intros key-parity passing.

Notes:

  • Pre-existing sidebar namespace 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 draft to agreed.
  • Confirmed backend/apps/organizations/models.py Organization model lacks apps_enabled JSONField — 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_enabled JSONField or equivalent, and note it as a backend prerequisite.

Impact:

  • Implementation agents must add apps_enabled JSONField 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.