shipped

Spec: F52 — Onboarding & Activation Flow

Status: shipped

1. Problem & CEO Job

Job: Decide fast — but not before the CEO reaches a first Brief. The current register → create-org → empty-Today flow has no guided path, demo data, first-Brief promise, or activation metric; a CEO who signs up at 8 PM and sees no tasks or team can simply leave.

Strategy v2 (2026-06-14) defines onboarding as the F70 wedge, not an industry-picker wizard. The target owner runs on Zalo and Excel. Conversion is pasting their own mess — a Zalo thread, Excel export, or screenshot — and receiving a useful, evidence-cited Boss Brief about their company in under 10 minutes, with no setup or blank workspace. Demo data is only a fallback. Activation is a Brief that identifies a real commitment, risk, or decision from that input, not industry-templated filler; before the paste→brief loop is friction, after it is retention.

The industry dropdown → invite team → demo Brief path below is the as-built v1, including activation as the first demo Brief viewed. It remains documented behavior, not the target: the F70 own-data-first realignment is a §11 implementation-audit gap, not a shipped change.

2. User Stories & Acceptance Criteria

Guided Onboarding Wizard

  • As a new CEO, I want a 3-step onboarding wizard after organization creation, so that Lumi is configured for my business before I see an empty dashboard.
    • Step 1 — “What kind of business?”: select industry from dropdown (Manufacturing, Retail, Services, Tech/SaaS, Construction, Trading/Import-Export, Education, Healthcare, Other). Selection pre-loads demo teams, sample project, and pulse templates.
    • Step 2 — “Invite your team”: input email addresses (comma-separated) or skip. Emails queued for invitation on submit. Shows “3 invitations will be sent.”
    • Step 3 — “Your first brief”: shows a loading animation (“Lumi is preparing your morning brief…”) while the backend generates a demo brief with sample data for the selected industry. ~5 seconds.
    • Onboarding can be skipped entirely with one “Skip, take me to Lumi” link at the bottom. Skippers still get demo data (just silently generated).

Demo Data Scaffolding

  • As a new CEO, I want to see a realistic brief immediately, so that I understand what Lumi does without inviting my team or creating tasks.
    • Demo data includes: 1 sample project (industry-appropriate), 3–5 tasks (some overdue, some due today), 2–3 demo commitments, 1–2 demo risks, 1 demo decision. (Confirmed 2026-07-02: demo_data.py templates verified — manufacturing template: 1 project, 3 tasks with due_days_from_now of -1/0/3 covering overdue+today+future, 2 commitments, 1 risk, 1 decision. generate_demo_data() creates all items with is_demo=True. §11 Rows 7+8 ✅.)
    • Demo data is clearly marked: “Demo” badge on all demo-generated items, with a dismissible banner: “This is sample data to show you how Lumi works. It’ll disappear when your team adds real data.”
    • CEO can dismiss all demo data with one click: “Remove sample data” in the banner. This deletes all demo items.
    • Demo data is generated server-side as a Celery task on org creation (no blocking the registration flow).

First-Day Brief

  • As a new CEO, I want a brief that’s ready within 60 seconds of signup, so that I don’t leave before seeing the product’s main value.
    • Brief generation for new orgs is immediate (triggered on org creation, not waiting for the 15-minute beat).
    • Demo brief shows: decisions pending (2–3), overdue commitments (2–3), risks (1–2), with realistic industry-specific language.
    • CEO can interact with demo brief items: assign, dismiss, comment. These work on real database objects — there’s no “demo mode” that breaks functionality.

Activation Tracking

  • As a product team, I want to know which new signups reach activation, so that I can measure onboarding effectiveness.
    • Activation event: “Brief viewed” — tracked when a user opens the Brief page for the first time and there’s at least one item.
    • Tracked via PostHog (already integrated): onboarding_step_completed events for each wizard step, activation_completed event on first brief view, demo_data_dismissed on banner dismissal.
      • Backend instrumentation (F85): the activation event this story referenced as a PostHog placeholder is now the self-hosted ActivationEvent ledger — first brief view emits brief_viewed via metrics.emit() (F85 §5.1). F85’s ActivationEvent is the source of truth for the activation/retention gates; PostHog, if used, is an optional downstream mirror.
    • Dashboard metric (F43 Admin HQ): signup → activation rate, average time to activation, % who skip onboarding.
    • No PII in activation events — industry selection is fine (aggregate), but never track individual data.

Welcome Bot Message

  • As a new CEO, I want a welcome message from Lumi in chat, so that I know the AI assistant is active and how to use it.
    • On first visit to /hub/chat, Lumi sends an automated welcome message: “Chào mừng đến với Lumi AI Workspace! Tôi là trợ lý AI của bạn. Bạn có thể hỏi tôi về: tình hình công ty, quyết định đang chờ, rủi ro khách hàng, hoặc cam kết trễ hạn. Hãy thử hỏi: ‘Hôm nay tôi cần quyết gì?’” (localized per user language).
    • Welcome message appears only once (per org). Flag stored on Organization: welcome_message_sent.

Quick Actions on Empty States

  • As a new CEO landing on Today with no team members, I want the empty state to drive the next action, so that I don’t stare at a blank page.
    • Empty Today: “Chưa có gì cần chú ý hôm nay.” + CTA “Xem Brief mẫu” → navigates to Brief.
    • Empty Brief (shouldn’t happen with demo data, but just in case): “Brief của bạn đang được chuẩn bị. Sẽ sẵn sàng trong ít phút.” + “Mời team để có dữ liệu thật” → navigates to Teams.
    • Empty Projects: “Dự án đầu tiên của bạn” → create project form inline.
    • Empty Tasks: “Tạo task đầu tiên” → create task form inline.

3. Scope Boundaries

  • In:
    • 3-step onboarding wizard (industry → invite team → demo brief).
    • Demo data generation (industry-specific, dismissible, non-blocking).
    • Immediate brief generation on org creation.
    • Activation tracking (PostHog events + Admin HQ metric).
    • Welcome bot message on first chat visit.
    • Quick-action empty states.
  • Out:
    • A/B testing onboarding flows — track metrics first, experiment later.
    • Personalized onboarding paths (different flows for different roles) — CEO-focused flow only.
    • Video tutorials or interactive product tours — text + demo data is faster to ship.
    • Post-activation email drip campaign (covered by F35 notification spec if needed).
    • Industry-specific pulse questions (v1 uses standard 6 questions for all industries).
    • Invitation scheduling or bulk CSV import — comma-separated email input only.

4. Data Model

4.1 Organization Model Changes

# Organization — new fields
industry = models.CharField(max_length=50, blank=True, choices=[
    ("manufacturing", "Manufacturing"),
    ("retail", "Retail"),
    ("services", "Services"),
    ("tech_saas", "Technology / SaaS"),
    ("construction", "Construction"),
    ("trading", "Trading / Import-Export"),
    ("education", "Education"),
    ("healthcare", "Healthcare"),
    ("other", "Other"),
])
onboarding_completed = models.BooleanField(default=False)
demo_data_generated = models.BooleanField(default=False)
welcome_message_sent = models.BooleanField(default=False)

4.2 Demo Data Model (Temporary)

Demo data uses the same real models (Project, Task, Commitment, Risk, DecisionRequest, Brief, BriefItem) — no separate demo table. Distinction is via a flag:

# Task, Commitment, Risk, DecisionRequest — new field
is_demo = models.BooleanField(default=False)

On “Remove sample data,” all items with is_demo=True in the org are soft-deleted (or hard-deleted if no team interactions yet).

4.3 Demo Data Templates

Stored as a Python dict in backend/apps/onboarding/demo_data.py — one template per industry:

DEMO_TEMPLATES = {
    "manufacturing": {
        "project": "Triển khai dây chuyền sản xuất mới",
        "tasks": [
            {"title": "Kiểm tra thiết bị dây chuyền", "priority": "high", "due_days_from_now": -1},
            {"title": "Đào tạo công nhân vận hành", "priority": "medium", "due_days_from_now": 3},
            {"title": "Chạy thử nghiệm batch đầu tiên", "priority": "critical", "due_days_from_now": 0},
        ],
        "commitments": [
            {"title": "Giao 500 đơn vị sản phẩm cho đối tác ABC", "status": "pending"},
            {"title": "Hoàn thành báo cáo chất lượng ISO", "status": "overdue"},
        ],
        "risks": [
            {"title": "Nguyên liệu từ nhà cung cấp XYZ chậm 5 ngày", "severity": "high"},
        ],
        "decisions": [
            {"title": "Phê duyệt tăng ngân sách nguyên liệu 15% cho Q3"},
        ],
    },
    # ... one template per industry
}

4.4 No New Models Beyond What’s Listed

No separate OnboardingStep or DemoData model. Simplicity.

5. API Contract

MethodPathRoleDescription
GET/api/organizations/current/onboarding/any memberReturns onboarding status: {onboarding_completed, demo_data_generated, industry}
PATCH/api/organizations/current/onboarding/any memberSaves industry selection. Body: {industry: str}. If {skipped: bool} included, also sets onboarding_completed=True.
POST/api/organizations/current/onboarding/complete/ownerMarks onboarding_completed=True. Body: {industry: str, skip: bool} (optional).
POST/api/organizations/current/demo-data/ownerTriggers demo data generation synchronously. Idempotent — returns existing if already generated.
DELETE/api/organizations/current/demo-data/ownerDeletes all demo data (is_demo=True items) from Task, Commitment, Risk, DecisionRequest. Returns {demo_data_generated: false, deleted: {tasks, commitments, risks, decisions}}.

The complete_onboarding endpoint also auto-triggers demo data generation after org creation via generate_demo_data().

6. UI / UX

6.1 Onboarding Wizard

┌─ Welcome to Lumi AI Workspace ──────────────────────────────────┐
│                                                                  │
│  Step 1 of 3                                                     │
│                                                                  │
│  What kind of business do you run?                               │
│                                                                  │
│  ┌──────────────────────────────────────────────────────────┐   │
│  │ Select your industry...                              ▾   │   │
│  └──────────────────────────────────────────────────────────┘   │
│                                                                  │
│  We'll set up sample data that matches your world.               │
│                                                                  │
│  ┌──────────────────────────────────────────────────────────┐   │
│  │                 Continue →                               │   │
│  └──────────────────────────────────────────────────────────┘   │
│                                                                  │
│  Skip, take me to Lumi                                          │
└──────────────────────────────────────────────────────────────────┘

6.2 Post-Onboarding: Today with Demo Banner

┌─ Today ─────────────────────────────────────────────────────────┐
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ 📋 This is sample data showing what Lumi can do.             │ │
│ │ When your team adds real data, it'll replace these items.    │ │
│ │ [Remove sample data]                              [Dismiss]  │ │
│ └──────────────────────────────────────────────────────────────┘ │
│                                                                  │
│ 3 Decisions · 2 Overdue · 1 Risk                                 │
│                                                                  │
│ ⚠️ Phê duyệt tăng ngân sách...        [Approve] [Snooze]        │
│ [Demo]                                                            │
│                                                                  │
│ 📋 Hoàn thành báo cáo chất lượng ISO   [Resolve] [Snooze]       │
│ [Demo] · 3 days overdue                                          │
│                                                                  │
│ 🔴 Nguyên liệu XYZ chậm 5 ngày          [Acknowledge]           │
│ [Demo] · High severity                                           │
└──────────────────────────────────────────────────────────────────┘

6.3 i18n

New namespace: onboarding. Keys:

  • onboarding.step1_title, onboarding.step1_subtitle, onboarding.industry_label
  • onboarding.step2_title, onboarding.step2_subtitle, onboarding.step2_skip
  • onboarding.step3_title, onboarding.step3_loading, onboarding.step3_done
  • onboarding.skip, onboarding.back, onboarding.continue
  • onboarding.industry_manufacturing, onboarding.industry_retail, … (one per choice)
  • onboarding.demo_banner, onboarding.demo_banner_remove, onboarding.demo_banner_dismiss
  • onboarding.welcome_chat_message

Both en and vi updated. Industry labels localized.

7. Test Plan

Coverage note (2026-06-14): Onboarding tests live in backend/tests/test_onboarding.py with classes TestOrganizationFields, TestIsDemo, TestDemoDataTemplates, TestDemoDataGeneration, TestOnboardingProgressEndpoint, TestCompleteOnboardingEndpoint, TestGenerateDemoDataEndpoint, TestDismissDemoDataEndpoint. Tenant isolation is in test_multitenancy.py. Rows below use original per-case naming; the file is authoritative.

#CaseTypeWhere
1Onboarding wizard: select industry → invite team → demo brief generatede2edescribe("Onboarding")
2Onboarding wizard: “Skip, take me to Lumi” skips wizard but still generates demo datae2esame block
3Onboarding wizard: back button preserves selected industrye2esame block
4Demo data appears on Today with [Demo] badgese2esame block
5Dismiss demo data banner removes all demo itemse2esame block
6”Remove sample data” deletes is_demo=True items only; real data preservedbackendtests/test_onboarding.py
7Demo brief has industry-appropriate content per templatebackendtests/test_onboarding.py::TestDemoDataTemplates
8Demo data generation idempotent — second call returns existing databackendtests/test_onboarding.py
9Activation event fires on first brief view (PostHog)unitfrontend PostHog event test
10Welcome bot message appears only once per orgbackendtests/test_onboarding.py
11Welcome bot message localized per user languagebackendtests/test_onboarding.py
12Quick-action empty states render correct CTAs — Today, Brief, Projects, Taskse2edescribe("Empty States")
13Skip onboarding still generates demo data (industry=“other”)backendtests/test_onboarding.py
14Invited member joining existing org skips founder-style onboardingbackendtests/test_onboarding.py
15POST /api/organizations/current/onboarding/complete/ sets onboarding_completed=Truebackendtests/test_onboarding.py
16DELETE /api/organizations/current/demo-data/ removes only is_demo=True itemsbackendtests/test_onboarding.py
17Cross-tenant: org A’s demo data never visible to org Bbackendtests/test_multitenancy.py
18Pilot banner on Today shows correct days remaininge2edescribe("Today")
19Demo data items respect role permissions (viewer cannot delete)backendtests/test_onboarding.py
20GET /api/organizations/current/onboarding/ returns status for any memberbackendtests/test_onboarding.py::TestOnboardingProgressEndpoint::test_get_onboarding_returns_status
21PATCH /api/organizations/current/onboarding/ saves industrybackendtests/test_onboarding.py::TestOnboardingProgressEndpoint::test_patch_onboarding_saves_industry
22PATCH /api/organizations/current/onboarding/ with skipped: true marks completedbackendtests/test_onboarding.py::TestOnboardingProgressEndpoint::test_patch_onboarding_saves_skipped
23POST /onboarding/complete/ with industry in body saves industry + completesbackendtests/test_onboarding.py::TestCompleteOnboardingEndpoint::test_complete_onboarding_with_industry
24Cross-tenant: org B cannot patch org A’s onboarding statebackendtests/test_onboarding.py::TestOnboardingProgressEndpoint::test_cross_org_patch_blocked
25PATCH response includes updated onboarding statebackendtests/test_onboarding.py::TestOnboardingProgressEndpoint::test_patch_onboarding_returns_updated_status
26Organization field defaults — onboarding_completed, demo_data_generated, welcome_message_sentbackendtests/test_onboarding.py::TestOrganizationFields

7.2 As-Built E2E (T3-C R11)

#CaseTypeWhere
E1DemoBanner renders on Today page for demo-seeded orgse2edescribe("Onboarding (F52)") in lumi.spec.ts
E2Today empty state shows “View sample brief” CTA when no datae2edescribe("Onboarding (F52)") in lumi.spec.ts
E3Onboarding page is accessible and renders step contente2edescribe("Onboarding (F52)") in lumi.spec.ts
E4GET /api/organizations/current/onboarding/ returns status objecte2edescribe("Onboarding (F52)") in lumi.spec.ts
27is_demo field defaults on Task, Commitment, Risk, DecisionRequestbackendtests/test_onboarding.py::TestIsDemo

8. Current Behavior

The shipped v1 path is register → create/join org → web onboarding wizard → assistant-first destination. The wizard persists industry/invitation/completion state and the onboarding API can generate and dismiss organization-scoped demo objects. The repository has unit and API proof plus registered browser proof IDs; browser execution, native post-auth behavior, and deployed activation metrics remain explicit gaps.

The v1 wizard is not the product’s target activation wedge. F70 owns the future paste-your-own-chaos to useful own-data brief path; F52 does not claim that path is shipped. Welcome-message delivery, asynchronous Celery generation, and the activation funnel also remain open according to §11.

9. Error States

#ScenarioUser-visibleInternalLogRetry
1Onboarding step API fails (POST /onboarding/complete/)Toast: “Could not save onboarding progress. Try again.”Server error loggedERROR “onboarding_complete_error”User retries via button
2Demo data generation Celery task failsBanner on Today: “Demo data couldn’t be generated. [Try again]“Task failure loggedERROR “demo_data_generation_failed”Manual “Try again” button
3Demo data deletion fails mid-way (partial delete)Toast: “Some demo data could not be removed. [Retry]“Partial delete logged; remaining is_demo=True items retriedERROR “demo_data_partial_delete”Manual retry
4Welcome bot message send fails (chat service unavailable)Silently skipped — no user impactLogged but no user retry neededWARNING “welcome_message_failed”Retry on next chat visit
5Non-owner calls onboarding endpoints403, redirect to TodayLogged for auditINFO “onboarding_permission_denied”n/a
6Organization not found in API call404404 response, loggedERROR “onboarding_org_not_found”n/a
7Demo data already generated, POST called again200 with existing data (idempotent)No-op, return existing stateDEBUG “demo_data_already_exists”n/a
8Invalid industry value in POST body400 with field validationValidation errorWARNING “onboarding_invalid_industry”User corrects form
9Demo data and onboarding API called for org that was never onboarded400: “Organization not in onboarding state”Check failsWARNING “onboarding_not_in_progress”n/a

10. Diagrams & Docs Touched

  • frontend/DIAGRAMS.md activation funnel
  • backend/DIAGRAMS.md onboarding state or event model if implemented
  • F24 and F30 specs where onboarding hands off into billing or mobile

11. Implementation Audit

Contract Graph reconciliation (2026-07-15)

The shipped evidence is narrower than the original Status: shipped label. The backend onboarding progress/completion and demo-data endpoints, deterministic demo templates, frontend wizard tests, and the registered Playwright journey are now contract-owned. The current behavior section above is stale where it says that no wizard or demo data exists; executable code and tests are the authority for that v1 surface. The strategy section is still authoritative about the unresolved product direction: F70 paste-your-own-chaos and a useful own-data brief are not delivered by this wizard. Synchronous generation, activation/welcome instrumentation, browser execution, native runtime, live provider/PostHog, Redis, Sentry, and production metrics remain explicit gaps. No proof is inferred from collection alone.

Edge Cases Catalog (formerly §9, 2026-06-14; 11 items): invited member skips founder wizard, activation event fires once only, pre-selected paid plan copy acknowledgement, wizard reopened with pre-filled industry, demo data generation race condition on page load, demo/real data coexistence on dismiss, non-owner 403 gate, welcome message per-org flag not per-language, multi-tab server-as-source-of-truth, demo generation failure retry path, org deleted during Celery task. Full catalog in git history.

#ClaimStatusEvidence
1GET + PATCH /api/organizations/current/onboarding/ (status + industry save)✅ matchE2E tests E4, §7 tests #20–22; T3 BL entry
2OnboardingPage.tsx (3-step wizard: industry Select, invite textarea, loading spinner) at frontend/src/pages/settings/OnboardingPage.tsx✅ matchT3 BL (2026-06-13); E2E test E3; src/test/OnboardingPage.test.tsx 13 tests (grep-confirmed 2026-07-02: 13 it() calls)
3Empty state CTAs (Today “View sample brief”, Projects inline form, Tasks inline form)✅ matchT3-D BL (2026-06-13); E2E tests E1, E2
4onboarding i18n namespace (en + vi: step labels, industry names, banner keys)✅ matchT3 BL (2026-06-13): keys added to en/vi onboarding.json
5Demo banner on Today page (dismissible, “Remove sample data” button)✅ matchT3-D BL (2026-06-13); E2E test E1
6Organization.onboarding_completed, demo_data_generated, welcome_message_sent model fields✅ matchbackend/apps/organizations/models.py L35-37 confirmed 2026-07-02: all three fields present as BooleanField(default=False). The prior “not grep-confirmed” caveat is resolved.
7is_demo = BooleanField(default=False) on Task, Commitment, Risk, DecisionRequest✅ matchGrep-confirmed 2026-07-02: backend/apps/work/models.py:196 (Task), backend/apps/operations/models.py L59 (Commitment), L112 (Risk), L156 (DecisionRequest). The original “0 hits” audit was incorrect.
8backend/apps/onboarding/ app (demo templates dict + generate_demo_data function)✅ matchDirectory confirmed 2026-07-02: backend/apps/onboarding/ contains apps.py, demo_data.py (DEMO_TEMPLATES dict, 9 industries), tasks.py (generate_demo_data() — plain function, not yet a Celery task; PR-2 note in file says Celery wrap is deferred), urls.py, views.py. App is populated and registered.
9POST /api/organizations/current/onboarding/complete/ and DELETE /demo-data/ endpoints✅ matchConfirmed 2026-07-02: backend/apps/onboarding/urls.py registers both — onboarding/complete/ → views.complete_onboarding (L69, POST) and demo-data/ → views.demo_data (L100, GET/POST/DELETE dispatch). Both endpoints exist and are wired.
10Welcome bot message on first /hub/chat visit (welcome_message_sent guard)⚠️ gapNot mentioned in any BL entry
11PostHog activation events (onboarding_step_completed, activation_completed, demo_data_dismissed)⚠️ gapNot mentioned in any BL entry
12[Strategy v2 realignment — TO BUILD, not shipped] Onboarding’s first action is the F70 Chaos Import (paste a messy Zalo thread / Excel export / screenshot → Boss Brief from the owner’s OWN data, <10 min, zero setup), not the generic industry-dropdown → demo-brief wizard. The activation event should be “owner gets a useful brief from THEIR OWN pasted chaos” (a brief item recognizably lifted from their input), not “first brief seen” from industry-templated demo data. Concrete flow change: (a) replace/precede §2 Step 1–3 wizard with the F70 paste surface as the primary post-registration screen; (b) demo data (§2 Demo Data Scaffolding) demotes to a fallback for owners with nothing to paste; (c) redefine the §2 Activation Tracking event from demo-data first-brief to own-data first-brief.🔲 gap — TO BUILDStrategy v2 §9.1/§7.2 + registry doctrine rules 4–5 (Positioning doctrine, strategy v2 2026-06-14); F70 is the wedge. Not built — current code ships the generic wizard (rows 1–5). §4 data model / §5 API / §7 tests unchanged by this note.

12. Open Questions / Decisions

  1. Demo data quality: Should demo data be hardcoded or LLM-generated per industry? Decision: hardcoded templates. LLM generation at signup time is slow, expensive per user, and stochastic. Curated templates are predictable and load instantly.
  2. Industry impact on pulse questions: Should pulse questions differ per industry? Decision: not in v1. The 6 standard questions work for all industries. Industry-specific questions are a v2 feature after measuring whether standard questions have adequate response rates.
  3. Onboarding wizard skippable: Should the wizard be mandatory or skippable? Decision: skippable. Forcing a wizard increases signup abandonment. The demo data still generates in the background — the CEO just doesn’t pick an industry.
  4. Industry field migration: Organization.industry exists as free-text (CharField). Should this spec add choices validation at the model level, or keep it loose? Decision: enforce choices at the serializer level (DRF ChoiceField), not at the model. Backward compatible with existing orgs that have free-text industry values.

13. Bug / Change Log

2026-07-16 — Contract Graph demo-data POST ownership checkpoint

The exact shipped route POST /api/organizations/current/demo-data/ is now declared under F52 through demo_data, reusing the existing F52.AC002.HAPPY, F52.AC002.IDEMPOTENT, and F52.AC002.ERROR runner-native proofs. This closes inventory ownership only; backend execution, browser, native, live-provider, Redis, Sentry, production, F70, and async-generation gaps remain.

2026-07-16 — Contract Graph onboarding-completion ownership checkpoint

The exact route POST /api/organizations/current/onboarding/complete/ is now declared under F52 through complete_onboarding, retaining the existing F52.AC001.COMPLETE, COMPLETE_ERROR, and ROLE_DENY proofs. This closes inventory ownership only; guarded backend execution remains F52.GAP008 and no browser, native, provider, Redis, Sentry, or production evidence is inferred.

2026-07-02 — Docs: upgrade falsely-flagged ⚠️ rows in §11 to ✅; correct test count; flip confirmed AC

Type: Documentation Fix

Summary:

  • §11 Row 2: test count corrected 7 → 13. frontend/src/test/OnboardingPage.test.tsx has 13 it() calls (grep-confirmed 2026-07-02). Path confirmed as frontend/src/pages/settings/OnboardingPage.tsx.
  • §11 Row 6 (⚠️ partial → ✅): Organization.onboarding_completed, demo_data_generated, welcome_message_sent are present at backend/apps/organizations/models.py L35-37. The prior “not grep-confirmed” caveat is resolved.
  • §11 Row 7 (⚠️ gap → ✅): is_demo = BooleanField(default=False) confirmed on all four models — backend/apps/work/models.py:196 (Task) and backend/apps/operations/models.py L59 (Commitment), L112 (Risk), L156 (DecisionRequest). The original audit’s “0 grep hits” was wrong.
  • §11 Row 8 (⚠️ gap → ✅): backend/apps/onboarding/ exists and is populated: demo_data.py (DEMO_TEMPLATES dict, 9 industries), tasks.py (generate_demo_data() — plain function; Celery wrap is deferred per PR-2 note in file), urls.py, views.py, apps.py.
  • §11 Row 9 (⚠️ gap → ✅): both endpoints registered in backend/apps/onboarding/urls.py — complete_onboarding (views.py:69, POST .../onboarding/complete/) and demo_data (views.py:100, DELETE .../demo-data/).
  • §2 AC “Demo data includes: 1 sample project…” → [x]: manufacturing template in demo_data.py has 1 project + 3 tasks (due_days_from_now -1/0/3 = overdue/today/future) + 2 commitments + 1 risk + 1 decision. generate_demo_data() creates all items with is_demo=True. The AC is met.
  • Remaining [ ] ACs (11 of 12) are unchanged: demo data as Celery task (Row 8 notes it is still a plain function), immediate brief generation, demo brief content, CEO interaction with demo items, all four PostHog/activation ACs, welcome bot message appearance, Empty Brief CTA, and the F70 strategy v2 realignment (Row 12 “TO BUILD”).
  • No code changes.

2026-07-02 — Docs: correct stale file paths (batch 2)

Type: Documentation Fix

Summary:

  • frontend/src/pages/OnboardingPage.tsx (§13 entry 2026-06-14 + PR-3 plan) — page moved to frontend/src/pages/settings/OnboardingPage.tsx during the settings IA restructure. Both occurrences corrected.
  • frontend/src/test/OnboardingWizard.test.tsx (PR-3 plan) — file was never created; wizard tests were consolidated into the existing frontend/src/test/OnboardingPage.test.tsx (7 tests). Annotated as not created.
  • frontend/src/test/EmptyStateCTAs.test.tsx (PR-4 plan) — file was never created; empty-state coverage distributed across page-level test files. Annotated as not created.
  • frontend/src/test/PostHogEvents.test.tsx (PR-5 plan) — file was never created; PostHog activation tracking is an open ⚠️ gap (§11 Row 11). Annotated as not created.
  • No code changes.

2026-07-02 — Spec drift fix: checked shipped ACs in §2

Type: Documentation

Summary:

  • 9 of 21 [ ] ACs in §2 flipped to [x] — confirmed shipped against §11 audit rows 1–5 (all ✅).
  • 12 ACs remain [ ]: demo data scaffolding backend (§11 Row 8 ⚠️ gap), immediate brief generation (Row 9 ⚠️ gap), is_demo field (Row 7 ⚠️ gap), activation tracking/PostHog (Row 11 ⚠️ gap), welcome bot message (Row 10 ⚠️ gap), Empty Brief CTA (not confirmed in Row 3 evidence), and the strategy v2 F70 realignment (Row 12, explicitly “TO BUILD”).
  • No code changes.

2026-06-15 — Frontend route namespaced

Frontend route namespaced: /chat -> /hub/chat and the post-onboarding redirect target /today -> /work/today (app-wide IA restructure; API paths unchanged).

2026-06-14 — Fix: “Skip, take me to Lumi” appeared stuck

Type: Bug Fix

Summary:

  • handleSkip in frontend/src/pages/settings/OnboardingPage.tsx awaited the heavy POST /organizations/current/demo-data/ (and then the onboarding PATCH) before calling navigate('/today?welcome=1'). While demo-data generation ran, the page stayed on the onboarding screen, so Skip looked broken.
  • Changed Skip to navigate immediately and run the skipped: true PATCH and demo-data generation as background fire-and-forget calls (errors still swallowed, as before). Both requests are still sent; they no longer block the redirect.

Reason:

  • “Skip, take me to Lumi” must be instant — blocking on demo generation contradicts the activation intent (time-to-first-value, no waiting).

Impact:

  • Frontend behavior only. No §4 data model, §5 API, or Status change. handleSkip is now synchronous (fire-and-forget).

Tests:

  • Existing src/test/OnboardingPage.test.tsx “skip link triggers demo generation and navigates to today” still passes (9/9); asserts both generateDemo and navigation to /today?welcome=1. tsc --noEmit clean.

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

Type: Documentation Change

Summary:

  • §1 reframed: the real onboarding is the F70 wedge (paste your own messy Zalo/Excel/screenshot → evidence-cited Boss Brief from the owner’s OWN company, <10 min, zero setup), not the generic industry-dropdown wizard. Honest activation event = “owner gets a useful brief from THEIR OWN pasted chaos”, not “first brief seen” from demo data. The existing wizard is noted as as-built v1, no longer the target.
  • §11 added gap row 12 documenting the concrete flow change (F70 paste as primary first action; demo data demoted to fallback; activation event redefined to own-data) as TO BUILD — not shipped.

Reason:

  • Align with competitive-strategy-v2.md §9.1/§7.2 (accept the mess, owner’s own data converts) and the registry Positioning doctrine (strategy v2, 2026-06-14) rules 4 (time-to-first-value <10 min, no blank workspace) and 5 (wedge = F70).

Impact:

  • Positioning prose only in §1. The behavior/UI/activation 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:

  • §2 wizard, demo-data scaffolding, and the §14 implementation-plan appendix are left intact as the documented as-built v1; the realignment supersedes them as the target via the §11 gap, not by rewriting them.

2026-06-14 — Spec restructured to TEMPLATE.md canonical order (§1–§13) + prior BL entries merged

Type: Documentation Change

Summary:

  • §9 Edge Cases (11 items, 9.1–9.11) absorbed into §11 Implementation Audit note.
  • §10 Error States → §9, §11 Diagrams & Docs Touched → §10, §12 Implementation Audit (grep-verified Exists/Missing) → §11 4-column table (11 rows, ✅/⚠️ per BL evidence).
  • §14 Open Questions / Decisions → §12, §15 Bug / Change Log → §13.
  • §13 Implementation Plan (non-standard, 5 PRs) → §14 Implementation Plan Appendix.

Reason:

  • Align with TEMPLATE.md canonical section order per SP fleet restructuring pass.

Impact:

  • Spec authors and future implementation agents.

2026-06-13 — Spec refinement: numbered edge cases, detailed error matrix, expanded test plan

Type: Documentation Change

Summary:

  • Replaced §9 loose prose with 11 numbered edge cases (9.1–9.11) covering invited members, activation reminders, plan pre-selection, race conditions, demo/real data coexistence, role gates, language changes, multi-tab, generation failure, and org deletion scenarios.
  • Replaced §10 with 9-row detailed error matrix (user-visible/internal/log/retry).
  • Expanded §7 test plan from 9 to 19 test cases, adding coverage for idempotency, locale, member onboarding skip, role permissions, and cross-tenant demo data isolation.
  • Updated spec status from draft to agreed.

Reason:

  • Spec needed hardening per implementation audit. Edge cases were loose prose, test plan was thin, error states lacked structured detail.

Impact:

  • Spec authors and future implementation agents.

Tests:

  • Spec-only change — no code tests affected.

2026-06-13 — Implementation audit + implementation plan hardened

Type: Documentation Change

Summary:

  • Replaced §12 high-level audit (“Not built. Requires 1,2,3,4”) with grep-verified detailed Implementation Audit listing every missing file/path and three existing design constraints.
  • Added §13 implementation plan with 5 ordered PRs: PR-1 (backend models + onboarding app + demo templates), PR-2 (API endpoints), PR-3 (frontend wizard), PR-4 (demo banner + empty states + welcome message), PR-5 (activation tracking + pilot banner). Each PR has tests-first list, backend models/endpoints, frontend routes/components, i18n namespace, and verification gate.
  • Added edge case §9.11 (org deleted during demo generation), §9.4 (industry selection previously made), §9.10 (demo data generation fails).
  • Added Open Question §14 item 4 (industry field migration strategy — serializer-level enforcement, not model-level).
  • Verified spec aligns with TEMPLATE structure and MVP_SCOPE.md registry entry.

Reason:

  • Per fleet task D-r04: specs must be build-ready with concrete implementation audits and PR-scoped plans.

Impact:

  • Spec authors and future code rounds executing F52.

Tests:

  • Spec-only change — no code tests affected.

2026-06-13 — T3 fleet: onboarding wizard steps 1–3 implemented

Type: Feature

Summary:

  • Replaced <select> element with shadcn <Select> component (base-ui).
  • Added step progress dots indicator (steps 1–2 only, step 3 is full-screen).
  • Replaced Card-based step 3 with full-screen auto-loading spinner + “Lumi is preparing your morning brief…” text.
  • Step 2: replaced <Input> with <textarea>, added email count display.
  • Step 2 “Finish setup” now auto-triggers demo generation + API update, transitions to step 3 spinner, then redirects to /today after ~5s via setTimeout.
  • Added i18n keys: next, finish, select_industry, emails_placeholder, invitations, brief_loading.
  • Updated test file (7 tests, removed stale step-3 button tests, added loading-spinner and email-count tests).

Reason:

  • Per task B-r10: implement steps 1–3 with shadcn Select, step dots, textarea + invitation count, and step 3 auto-loading animation.

Impact:

  • OnboardingPage.tsx, en/vi onboarding.json, OnboardingPage.test.tsx, MVP_SCOPE.md.

Tests:

  • src/test/OnboardingPage.test.tsx — 7 tests passing, updated assertions for new UI.
  • Frontend gate: tsc --noEmit (0 errors), pnpm vitest run src/test/OnboardingPage.test.tsx (7/7 pass).

2026-06-13 — T3-D R10: Empty States implemented

Type: Feature

Summary:

  • TodayPage.tsx: isEmpty guard — when tasks/commitments/risks/decisions all empty, shows “Nothing needs your attention today.” + “View sample brief” CTA → /brief.
  • ProjectsPage.tsx: dashed <Card> with “Create your first project” + Plus button opening inline form.
  • TasksPage.tsx: dashed <Card> with “Create your first task” + Plus button opening inline form.
  • i18n keys added to en/today.json + vi/today.json: empty_state_text, view_sample_brief.
  • i18n keys added to en/projects.json + vi/projects.json: empty_state_text.
  • i18n keys added to en/tasks.json + vi/tasks.json: empty_state_text.

Files: TodayPage.tsx, ProjectsPage.tsx, TasksPage.tsx, locale files for today/projects/tasks.

14. Implementation Plan (Appendix)

PR-1: Backend foundation — Organization fields + onboarding app

Tests-first (create backend/tests/test_onboarding.py):

  • TestOrganizationFields::test_onboarding_completed_default — field is False for new orgs
  • TestOrganizationFields::test_demo_data_generated_default — field is False for new orgs
  • TestOrganizationFields::test_welcome_message_sent_default — field is False for new orgs
  • TestDemoDataTemplates::test_all_industries_have_templates — DEMO_TEMPLATES dict has entries for all 9 industries
  • TestDemoDataTemplates::test_industry_template_structure — each template has project/tasks/commitments/risks/decisions
  • TestDemoDataGeneration::test_generates_demo_data — Celery task creates Task/Commitment/Risk/DecisionRequest with is_demo=True
  • TestDemoDataGeneration::test_idempotent — second call returns existing, no duplicate
  • TestDemoDataGeneration::test_industry_other_default — no industry specified → uses “other” template
  • TestDemoDataGeneration::test_org_deleted_during_generation — task exits silently if org gone (check .exists())

Backend:

  • Migration: add onboarding_completed, demo_data_generated, welcome_message_sent to Organization
  • Migration: add is_demo = BooleanField(default=False) to Task, Commitment, Risk, DecisionRequest
  • New app backend/apps/onboarding/:
    • demo_data.py — DEMO_TEMPLATES dict (9 industries, ~30 lines per template)
    • tasks.py — generate_demo_data Celery task. Creates 1 sample project, 3–5 tasks, 2–3 commitments, 1–2 risks, 1 decision. All with is_demo=True. Sets Organization.demo_data_generated = True on success.
    • Wire into apps/onboarding/apps.py ready()
  • Register in config/settings/base.py INSTALLED_APPS

Gate: cd backend && DJANGO_SETTINGS_MODULE=config.settings.test python -m pytest tests/test_onboarding.py -x --cov=apps --cov=core --cov-fail-under=99 --tb=short

PR-2: Backend API endpoints

Tests-first (add to backend/tests/test_onboarding.py):

  • TestOnboardingComplete::test_owner_can_complete — POST sets onboarding_completed=True
  • TestOnboardingComplete::test_non_owner_gets_403 — admin/manager/member/viewer get 403
  • TestOnboardingComplete::test_invalid_industry_400 — industry not in choices → 400
  • TestDemoDataGenerate::test_owner_can_generate — POST triggers Celery task
  • TestDemoDataGenerate::test_non_owner_403 — 403 for non-owner
  • TestDemoDataDelete::test_deletes_only_is_demo_items — real items preserved
  • TestDemoDataDelete::test_partial_delete_can_retry — if some items fail, remaining is_demo=True items are cleaned
  • TestActivationStatus::test_returns_status — onboarding_completed + demo_data_generated + team_count + brief_count
  • TestDemoDataIsolation::test_cross_tenant_403 — org B cannot delete org A’s demo data
  • TestWelcomeMessage::test_only_once — POST sets welcome_message_sent, second call is no-op
  • TestWelcomeMessage::test_non_owner_403

Backend:

  • onboarding/views.py — OnboardingCompleteView, DemoDataGenerateView, DemoDataDeleteView, ActivationStatusView, WelcomeMessageView
  • onboarding/urls.py — routes:
    • POST /api/organizations/<id>/onboarding/complete/
    • POST /api/organizations/<id>/demo-data/generate/
    • DELETE /api/organizations/<id>/demo-data/
    • GET /api/organizations/<id>/activation-status/
    • POST /api/organizations/<id>/welcome-message/
  • Register URLs in project root urls.py
  • Wire auto-generation: after org creation (in OrganizationViewSet.create), call generate_demo_data.delay(org_id, industry)

Gate: same backend gate as PR-1 + targeted E2E for owner-only endpoints

PR-3: Frontend — Onboarding Wizard (3-step)

Tests-first:

  • frontend/src/test/OnboardingWizard.test.tsx (not created — wizard tests were consolidated into frontend/src/test/OnboardingPage.test.tsx):
    • renders step 1 industry selection
    • navigates to step 2 on Continue
    • step 2 shows input field + skip
    • step 3 shows loading animation
    • skip button skips wizard, triggers demo generation
    • back button preserves industry selection
    • api error shows toast with retry
  • frontend/tests/e2e/lumi.spec.ts — describe("Onboarding"):
    • completes 3-step wizard flow
    • skip button generates demo data silently
    • wizard not shown for invited members

Frontend:

  • frontend/src/pages/settings/OnboardingPage.tsx — 3-step wizard: industry dropdown → invite team → “preparing brief” loading animation. Skip link at bottom.
  • Route /onboarding in App.tsx — displayed after org creation, before /today
  • Guard: skip if onboarding_completed=True (redirect to /today)
  • frontend/src/i18n/locales/en/onboarding.json + vi/onboarding.json

Gate: cd frontend && tsc --noEmit && pnpm test

PR-4: Frontend — Demo banner, empty state CTAs, welcome message

Tests-first:

  • frontend/src/test/TodayPage.test.tsx — demo banner renders with dismiss, “Remove sample data” deletes
  • frontend/src/test/ChatPage.test.tsx — welcome message appears on first visit, not on subsequent
  • frontend/src/test/EmptyStateCTAs.test.tsx (not created — empty-state CTA coverage is distributed across page-level test files: TodayPage.test.tsx, BriefPage.test.tsx) — each page (Today/Brief/Projects/Tasks) shows correct CTA content
  • E2E: existing describe("Onboarding") extended — demo badge visible, banner dismiss works, welcome message appears in chat

Frontend:

  • TodayPage.tsx — demo banner (dismissible), “Remove sample data” button, pilot countdown banner
  • BriefPage.tsx, TodayPage.tsx, ProjectsPage.tsx, TasksPage.tsx — F52-specified empty state CTAs
  • ChatPage.tsx — check welcome_message_sent, call POST /welcome-message/ to mark, show welcome content
  • i18n: extend onboarding namespace with banner/welcome-message keys

Gate: cd frontend && tsc --noEmit && pnpm test && make e2e

PR-5: Activation tracking + pilot banner wiring

Tests-first:

  • frontend/src/test/PostHogEvents.test.tsx (not created — PostHog activation-tracking is a ⚠️ gap per §11 Row 11; no test file exists) — events fire at correct moments
  • backend/tests/test_onboarding.py — pilot expiry calculated correctly from Subscription.expires_at
  • E2E: pilot banner shows correct days, yellow warning at 48h

Frontend:

  • PostHog onboarding_step_completed event on each wizard step
  • PostHog activation_completed on first brief view with ≥1 item
  • PostHog demo_data_dismissed on banner dismissal
  • Pilot banner component in TodayPage.tsx — reads Subscription.expires_at, shows countdown

Gate: full test suite + E2E

The exact GET /api/organizations/current/onboarding/ read surface is registered to onboarding_progress with F52.AC001.PROGRESS_API; its repository proof does not establish deployed tenant, browser, native, live-provider, or production activation evidence. The exact PATCH /api/organizations/current/onboarding/ mutation is separately registered to onboarding_progress with F52.AC001.PATCH; its repository proof does not establish backend runner, browser, native, live-provider, or production activation evidence.