shipped and contract-backed (web + KMP mobile; G2/G5 gates pending; Contract Graph migration 2026-07-14)

Spec: F70 — Chaos Import → Boss Brief (the wedge)

Status: shipped and contract-backed (web + KMP mobile; G2/G5 gates pending; Contract Graph migration 2026-07-14)

This is the wedge. Per competitive-strategy-v2.md and field-tests.md §20, the single highest-priority surface in Lumi is the zero-setup first session: a contractor owner pastes one messy Zalo/Excel/screenshot/voice-note and gets today’s owner brief in under 10 minutes, before configuring anything. Every other feature in the registry is downstream of this working. Field-tests calls this “the only product spec allowed before evidence.” This spec is the contract for that flow.

Validation-gated, not velocity-gated. This is a draft design tied to field-test gates G2 (first-session value) and G5 (extraction quality). It is built concierge-first (manual-assisted) and automated only after repeated demand appears (field-tests §6). It does not get marked shipped until G2 passes with real owners.

Contract Graph migration note (2026-07-14)

F70 is now declared in contracts/features/F70.cue. The contract owns the authenticated paste-to-brief API, shared faithfulness boundary, evidence-bearing items, tenant isolation, role-gated save, and mutation safety. The shipped web/KMP source remains partial as a product claim because G2/G5 live validation and target-runtime evidence are not inferred from repository tests.

1. Problem & CEO Job

See clearly + trust the signal. The owner of a Vietnam interior fit-out / renovation / small M&E SME runs the business on Zalo/WhatsApp threads, Excel sheets, and memory. Their day’s real work — who owes what, which customer needs a callback, what is late, what risk is brewing, what must be confirmed with a client or supplier — is scattered across chat and spreadsheets and never assembled in one place. Today, to get any value from Lumi, the owner must first create an org, add projects, add staff, and learn a system. That cold-start tax is fatal: a non-technical owner’s patience runs out long before a blank workspace becomes useful. The strategy’s hard constraint is time-to-first- value under 10 minutes (strategy v2 §7.3).

Without this feature, Lumi asks the owner to “set up your whole workspace first” — the exact failure mode field-tests §1.3 lists as a stop condition. With it, the owner’s very first action produces a brief built from their own chaos, with every line traceable to the message it came from. The competitor here is not Asana or Notion — it is the owner’s own Zalo + Excel + memory. Lumi wins by turning that chaos into structure faster and more reliably than the owner can by hand.

2. User Stories & Acceptance Criteria

  • As an owner (first session, no projects/staff yet), I want to paste one messy work sample and get a structured “today” brief, so that I see value before configuring anything.

    • Given a freshly registered owner with an empty workspace, when they paste a Zalo thread and submit, then a Boss Brief renders within the same session with at least the sections Việc cần xử lý / Cần chốt với bên ngoài / Rủi ro / Tin nhắn nên gửi populated from the input.
    • Given the input contains an explicit promise (“anh gửi báo giá thứ 5”), when extraction runs, then a commitment item appears with the assignee, the due date, and a citation of the source line.
    • Given the input is too short/empty/unreadable, when submitted, then the owner gets a guided “send a richer sample” state — never an invented brief.
  • As an owner, I want every brief item to cite its source line, so that I trust it.

    • Given any AI-produced item, when it renders, then it links to an EvidenceSource quoting the exact text/row/message it was derived from, with an honest confidence badge.
    • Given an item the AI is unsure about, when it renders, then it is marked uncertain rather than asserted, and is never created as a hard commitment without owner confirmation.
  • As an owner, I want to correct or reject extracted items, so that the brief is mine.

    • Given an item with a wrong assignee/date, when the owner edits it, then the correction persists and is logged (feeds F15 feedback / extraction-quality loop).
    • Given a junk item, when the owner dismisses it, then it disappears and is not re-suggested.
  • As an owner, I want a clear next step after the brief, so that value compounds.

    • Given a useful brief, when the owner taps invite staff, then they get a paste-into-Zalo invite link (F02) — this is the Loop 1 handoff.
    • Given a brief item needing an external sign-off, when the owner taps send confirmation, then they enter the F71 inter-company confirmation flow (Loop 2) — never a dead end.

3. Scope Boundaries

  • In:
    • Single-input ingest: pasted chat text, screenshot (OCR’d to text), pasted Excel rows, voice-note transcript, or a typed note. (Reuses F32 Signal Inbox ingest + F34 uploads for images/audio.)
    • One-shot extraction → a first Boss Brief assembled in the session (reuses F08 extraction and F12 brief assembly).
    • Accept / correct / reject extracted items; save brief into the workspace.
    • Upgrade path CTAs: invite staff (F02 / Loop 1), send confirmation (F71 / Loop 2).
    • Concierge fallback: a manual-assisted path for the pre-automation validation phase (field-tests §6).
  • Out:
    • Full CRM / project / task setup (F04, F05, F27, F50) — these come after the wedge proves value, not before.
    • The recurring daily pulse ritual (F07) — the wedge is the cold start; the pulse is the steady-state habit it graduates into.
    • Source connectors (F44) — paste-first; connectors are a later convenience.
    • Multi-sample batch import / bulk migration — explicitly out (one messy sample is the point).

4. Data Model

Reuses the existing spine; adds one lightweight grouping model.

  • Reused: SignalInboxItem (the raw pasted/uploaded input), EvidenceSource (provenance for every produced item), and the produced drafts Commitment / Risk / DecisionRequest / Task, assembled into a Brief + BriefItem set.
  • New — ChaosImport (UUIDModel, organization FK — required, never null):
    • created_by → OrganizationMember
    • input_kind ∈ {chat_paste, excel_paste, screenshot, voice_note, typed_note}
    • raw_signal → FK to SignalInboxItem
    • generated_brief → FK to Brief (nullable until extraction completes)
    • status ∈ {received → extracting → briefed → saved} | failed
    • is_concierge (bool) — true when a human assisted extraction during validation
    • Status transitions update backend/DIAGRAMS.md state machine.
  • Zero-setup without breaking tenancy. “No setup” is a UX promise, not a tenancy hole. On first sign-up the wedge auto-provisions a provisional Organization (named from the pasted content, renameable later) so that every ChaosImport and produced item is tenant-scoped from line one (Rule 1). The owner never fills an org-creation form; the org exists silently. See §12 — this needs an ADR.

5. API Contract

MethodPathAuth/RoleRequestResponseErrors
POST/api/chaos-import/Authenticated member; org auto-provisioned if none{input_kind, content, upload_id?}201 {chaos_import_id, status}400 empty/oversized input; 415 unreadable; 429 rate-limited
GET/api/chaos-import/{id}/Member of owning org—200 {status, brief{...items[] with evidence + confidence}}404 cross-org; 409 still extracting
POST/api/chaos-import/{id}/items/{item_id}/correct/Member of owning org{field, value}200 {item}404 cross-org
POST/api/chaos-import/{id}/items/{item_id}/dismiss/Member of owning org—204404 cross-org
POST/api/chaos-import/{id}/save/Owner/Admin—200 {brief_id} (persists drafts into the workspace)403 role

Contract Graph ownership: the shipped detail route GET /api/chaos-import/{id}/ is assigned to ChaosImportViewSet.retrieve with a registered backend proof. This records source/test ownership only; field-validation, browser, native, and production evidence remain separate gaps.

  • Tenant scoping: every query filtered by organization_id=self.request.organization_id; the produced Brief/items inherit the same org. No endpoint accepts a cross-org id.
  • AI output: every produced item links an EvidenceSource quoting the source line. Confidence is honest: 0.6–0.8 for keyword extraction, 0.85–0.90 for LLM, never 1.0, never hardcoded (Rule 2). Items below a confidence floor render as uncertain suggestions, not created objects.
  • Hallucination guard: the extraction prompt + post-filter must never assert a commitment/date/ assignee absent from the source. A produced item with no evidence span is dropped, not shown.

6. UI / UX

  • Route: /intel/import (first-run, authenticated import) and reachable later as Import from the Brief/Today surfaces. (The public marketing /start landing is unchanged.)
  • Page component: ChaosImportPage → ChaosImportInput (textarea + paste/upload affordances) and BossBriefResult (the rendered brief).
  • States to design: empty (the paste prompt with vertical-specific placeholder copy), loading (extraction in progress — honest skeleton, not a fake spinner), populated (the brief with evidence badges), low-signal (“gửi mẫu chi tiết hơn”), error, no-permission.
  • shadcn components: Textarea, Card, Button, Badge (confidence), Skeleton, Tabs (input kinds). Check frontend/src/components/ui/ first; no raw Tailwind for UI (Rule 4).
  • i18n namespace: chaosImport — keys added to both en/ and vi/ (Rule 3), Vietnamese written first per F18. Placeholder copy uses owner vocabulary, not SaaS vocabulary (“Dán đoạn Zalo / Excel đang rối ở đây” — not “Paste your data”).
  • Mobile-first: the paste, the result, accept/correct, and both CTAs must work well on a phone (strategy §7.3); follow DESIGN.md + TASTE.md pre-flight.
  • No abstract AI UI: the result is “việc cần xử lý / khách cần chốt / rủi ro / tin nhắn nên gửi”, not “AI output”. The word “AI” does not appear in the owner-facing brief.

7. Test Plan

#CaseTypeWhere
1Paste chat → brief with ≥1 evidence-linked commitmentbackendbackend/tests/test_api.py::TestChaosImport
2Cross-tenant isolation — import + brief scoped to owning org onlybackendbackend/tests/test_multitenancy.py
3Hallucination guard — no item created without an evidence spanbackendbackend/tests/test_api.py::TestChaosImportEvidence
4Role — only Owner/Admin can save a brief into the workspacebackendbackend/tests/test_api.py
5Low/empty input → guided state, never an invented briefbackendbackend/tests/test_api.py
6First-run flow: register → paste → brief → invite-staff CTAe2efrontend/tests/e2e/lumi.spec.ts describe("Chaos Import wedge")
7Correct an assignee → persists + logged to feedback loope2esame

Backend coverage ≥99%; new E2E describe block required. Extraction quality is additionally measured against the field-tests §15 benchmark (100 real samples) — the G5 gate, tracked separately from unit tests.

8. Current Behavior

The authenticated paste-to-brief orchestration is shipped across the web and KMP surfaces. A tenant member submits a sufficiently rich sample, run_chaos_import() captures it, extracts grounded proposals, materializes evidence-linked operational items, assembles a Boss Brief, and exposes correction, dismissal, save, invite, and F71 handoff paths. The current product boundary is honest: the authenticated path requires an existing organization, while provisional-org anonymous cold start, concierge UI, G2 first-session value, G5 real-corpus accuracy, and target-runtime execution remain open validation work.

9. Error States

  • Extraction failure → user-visible chaosImport.error_extract (“Chưa đọc được mẫu này — thử dán đoạn khác giúp tôi”); offer the concierge fallback; log full context server-side; the input is kept so the owner can retry without re-pasting.
  • Low confidence / ambiguous → items render under uncertain; never auto-created; no invented dates or assignees.
  • Oversized / unsupported input → chaosImport.error_too_large / error_unsupported; suggest splitting or a different sample kind.
  • Rate limited (F20) → chaosImport.error_rate with retry timing.
  • Never silently swallow an extraction error or fabricate a brief to fill the screen.

10. Diagrams & Docs Touched

  • frontend/DIAGRAMS.md: add /intel/import to the route tree + first-run flow.
  • backend/DIAGRAMS.md: ChaosImport state machine; request lifecycle for the import → extract → brief pipeline.
  • GLOSSARY.md: add Chaos Import, Boss Brief, wedge, time-to-first-value.
  • DIAGRAMS.md: AI pipeline — show the paste→brief path as the cold-start entry.
  • ADR: provisional-org auto-create on first session (see §12).

11. Implementation Audit (2026-06-14 — Updated 2026-06-23)

LLM extraction live; validation gate still open. The wedge now exists as a working authenticated web flow with LLM-powered smart extraction in real mode:

  • ChaosImport model (UUIDModel, org FK, status machine, is_concierge) + migration
  • run_chaos_import() service orchestrating: capture_signal → keyword extraction (Commitment/Risk/DecisionRequest each with EvidenceSource) → Brief/BriefItem assembly
  • ChaosImportViewSet with POST create, GET retrieve, correct_item, dismiss_item, save actions
  • Tenant-scoped via IsOrgMember; save requires IsOrgOwnerOrAdmin
  • Hallucination guard: items only created when keyword patterns match; items with no evidence span never produced
  • Confidence honest: 0.65 keyword, never 1.0, never hardcoded
  • Low-signal input → ValueError(“low_signal”) → HTTP 400 guided state (no invented brief)
  • Cross-tenant isolation: all querysets filtered organization_id=request.organization_id
  • Frontend /intel/import surface shipped in frontend/src/pages/intel/ChaosImportPage.tsx with: paste tabs, guided low-signal/error/loading states, evidence-cited brief items, per-item correct + dismiss actions, save action, invite-staff CTA, and F71 send-confirmation handoff via SendConfirmationDialog
  • Boss Brief result now groups extracted items into the owner-facing sections promised in §2 / §6: Việc cần xử lý, Cần chốt với bên ngoài, Rủi ro, plus Tin nhắn nên gửi suggestions instead of one flat mixed list
  • Frontend route and sidebar wiring shipped: frontend/src/routes/routeTree.ts, frontend/src/components/Sidebar.tsx, frontend/src/App.tsx
  • Unit coverage shipped: frontend/src/test/ChaosImportPage.test.tsx
  • Browser E2E shipped: frontend/tests/e2e/lumi.spec.ts describe("Chaos Import wedge")
  • Deferred per scope decision: provisional-org auto-create on first session — still requires ADR (see §12). POST endpoint requires valid X-Organization-ID like every other endpoint.
  • LLM extraction (real mode): run_chaos_import() now calls the configured LLM provider with a structured extraction prompt (verbatim evidence spans required, hallucination guard enforced, confidence clamped to [0.85, 0.90]). Extracts task items in addition to commitment/risk/decision. Graceful keyword fallback on outage or unparseable response.
  • F86 (2026-06-24): the chaos LLM + keyword hallucination guards now route through the shared apps/ai/faithfulness.py::assert_grounded (verbatim span + amount/date/owner cross-check; honest band); produced EvidenceSource rows carry evidence_span + grounding_verified. See F86.
  • Still not built: concierge-assisted extraction UI and the anonymous/no-org cold-start promised by the draft wedge ideal. Screenshot/voice-note capture now belongs to and is shipped through F87.
  • Acceptance is the G2 gate (real owners want a second use), not a passing test suite alone.

2026-06-25 — Mobile (Compose Multiplatform shared module) implementation

Built the wedge on the shared KMP module so it ships on Android / iOS / Mac from one codebase, mirroring the web behavioral reference and the existing screen/screen-model/repository trio convention (modeled on signalinbox). Scope was android/ only — no backend/frontend changes; backend API unchanged.

  • DTOs (shared/.../api/LumiDtos.kt): added ChaosImportResponse, ChaosImportBriefResponse, ChaosImportItemResponse, ChaosImportEvidenceResponse, plus ChaosImportCreateRequest / ChaosImportCorrectRequest / ChaosImportSaveResponse. The chaos brief-item shape is distinct from the daily BriefItemResponse (carries description / subject_type / subject_id and evidence content_text), matching backend/apps/chaos/serializers.py exactly (verified against that file). Per ADR-025 all DTOs live in shared.
  • API client (shared/.../api/LumiApiClient.kt): added the 5 calls — chaosImportCreate (POST), chaosImportDetail (GET), chaosImportCorrectItem, chaosImportDismissItem (204 → no body parse), chaosImportSave — each carrying Authorization + X-Organization-ID via the shared header helpers.
  • Repository (shared/.../presentation/chaosimport/ChaosImportRepository.kt): ChaosImportRepository interface + DefaultChaosImportRepository forwarding to the client (test-swappable, mirrors SignalInboxRepository).
  • Screen model (ChaosImportScreenModel.kt): ChaosImportUiState holds input content + ChaosInputKind tabs (chat_paste / excel_paste / typed_note), submit, 2s status polling while received/extracting (interval injectable for tests), brief item list, optimistic dismiss, correct + save mutations, low-signal (briefed with zero items → guided state, never an invented brief), and failed/error-key state with honest error mapping (too_large / rate / extract).
  • Compose screen (shared/.../ui/chaosimport/ChaosImportScreen.kt): mirrors the web sections — Action items / Needs external confirmation / Risks / Messages to send — using the same buildSections/buildSuggestedMessages grouping logic; per-item confidence badge (High/Medium/Low + %), verbatim evidence citation, dismiss + save, and an honest skeleton (LumiSkeletonList, not a bare spinner) during extraction. Built entirely from shared Lumi primitives/tokens (no raw colors).
  • Navigation: ChaosImportNavKey + entry<ChaosImportNavKey> and a "Chaos Import" workspace-row route in app/.../ui/LumiApp.kt; model wired in SharedKmpViewModel; tile added to the Intelligence section of WorkspaceLaneScreen.kt; makeChaosImportViewController factories added to the iOS and macOS MainViewController.kt.
  • i18n: chaos_import_* keys added to both values/strings.xml (en) and values-vi/strings.xml (vi, written to match the web copy / owner vocabulary).
  • Tests (shared/src/commonTest/.../chaosimport/ChaosImportScreenModelTest.kt): 11 cases incl. paste→brief success with evidence carried through, polling received→extracting→briefed transition, low-signal guided state, backend failed → error key, oversized→error_too_large, optimistic dismiss, save, correct, and start-over.
  • Verification — BLOCKED in this environment. ./gradlew :shared:compileKotlinJvm (and therefore :shared:jvmTest, which runs commonTest) fails at the Gradle configuration phase because the Android Gradle / com.android.lint:9.0.0 plugin cannot be fetched — the agent proxy returns 403 for dl.google.com:443 (confirmed via $HTTPS_PROXY/__agentproxy/status), and no AGP artifacts are cached locally (project never built here). This is an environment network restriction, not a code defect; the compile never starts. Code was instead statically reviewed against the existing signalinbox trio and the verified component signatures (LumiPageTitle, LumiSectionHeader, LumiCard, LumiStatusBanner, LumiBadge, LumiFilterChip, LumiMultilineField, LumiPrimaryButton, format-arg stringResource). A reviewer with Google-repo access must run ./gradlew :shared:jvmTest to confirm green.
  • iOS / Mac shell wiring ✅ done (verified 2026-07-02): ios/Lumi/UI/MainTabView.swift has case chaosImport (line 259), a KMPComposeView(screen: .chaosImport, ...) render case (line 720–721), and the workspace-row routing case "Chaos Import": .push(.chaosImport) (line 799). ios/Lumi/KMP/SharedScreenBridge.swift also has the full .chaosImport branch dispatching makeChaosImportViewController. ios/LumiMac/KMP/SharedScreenBridge.swift matches on macOS. On-device verification pending (build toolchain not available in agent env) — the routing code is present and code-complete.

12. Open Questions / Decisions

  • Provisional org on first session — auto-create a silent org vs a true anonymous/ephemeral trial? This is a structural tenancy decision and requires an ADR before implementation (cross-reference F01 §2 org-setup gate and F02). Deferred per fleet-C scope. The backend POST endpoint requires a valid X-Organization-ID like every other endpoint. If the frontend lacks one, it must obtain one first (register → create org → redirect). The no-org path needs the ADR.
  • Screenshot OCR — which service/model, and does OCR text become its own EvidenceSource?
  • Voice-note transcription — source and language handling (defer to F37).
  • Confidence floor for auto-create vs uncertain-suggestion — tune against the G5 benchmark.

13. Bug / Change Log

2026-07-28 — Native low-signal feedback is not an application error

The shared mobile submit path treats only the canonical 400 low-signal response as expected domain feedback. It still renders the guided extraction recovery state, but does not forward that handled outcome to Sentry. Other 400 responses and unexpected failures remain reportable.

2026-07-02 — Spec drift fix: all 9 §2 ACs marked shipped

Type: Docs

Summary:

  • Updated status from draft to shipped (web + KMP mobile; G2 gate pending).
  • Marked all 9 §2 acceptance criteria [x] — each confirmed implemented per the §11 Implementation Audit (2026-06-14 through 2026-06-25 entries).
  • No code changed; documentation only.

Evidence mapping:

  • AC1 (paste → sections): §11 — “Boss Brief result now groups extracted items into the owner-facing sections promised in §2 / §6”
  • AC2 (commitment + citation): §11 — “LLM extracts … verbatim evidence spans required, hallucination guard enforced”
  • AC3 (low-signal guided state): §11 — “ValueError(‘low_signal’) → HTTP 400 guided state (no invented brief)”
  • AC4 (EvidenceSource on every item): §11 — “items only created when keyword patterns match; items with no evidence span never produced”
  • AC5 (uncertain items visible, not hidden): §11 — “confidence badges with % shown” / KMP screen: “per-item confidence badge (High/Medium/Low + %)”
  • AC6 (correction persists): §11 — “correct_item action on ViewSet + frontend”
  • AC7 (dismiss → gone): §11 — “dismiss_item action” / KMP: “optimistic dismiss”
  • AC8 (invite-staff CTA): §11 — “invite-staff CTA” explicitly shipped on /intel/import
  • AC9 (F71 handoff): §11 — “F71 send-confirmation handoff via SendConfirmationDialog”

Still open: G2 field-test gate (real owners), provisional-org ADR, screenshot/voice-note upload path, concierge-assisted extraction UI.

2026-06-23 — LLM extraction path (truly smart wedge)

Type: Enhancement

Summary:

  • Replaced pure keyword extraction with an LLM-first extraction path in run_chaos_import() when LLM_PROVIDER != 'mock'.
  • The LLM call uses build_system_prompt(member, "extraction") + a structured prompt demanding verbatim evidence_span quotes per item.
  • Supports all four entity types the wedge can surface: task, commitment, risk, decision — not just the three keyword-detectable types.
  • Honest confidence: all LLM-reported confidence values are clamped to [0.85, 0.90]; never 1.0, never fabricated.
  • Hallucination guard: items are kept only when evidence_span is a verbatim substring of the source — unsourced items are dropped with a warning log.
  • Graceful fallback: LLMProviderError, malformed JSON, or an empty LLM item list all fall back to deterministic keyword extraction — never a dead screen, never a fabricated brief.
  • Mock mode (LLM_PROVIDER=mock) is unchanged and remains the test default.
  • 6 new TestChaosImportLLM tests covering: multi-item extraction with tasks, confidence clamping, hallucination guard, provider outage fallback, malformed JSON fallback, empty-items fallback.
  • Full suite: 3870 passed, chaos coverage 97%.

2026-06-18 — Boss Brief sections rendered as owner-facing groups

Type: UX correction

Summary:

  • Changed the populated /intel/import Boss Brief result from one flat mixed list into the sectioned owner view promised by the spec: Việc cần xử lý, Cần chốt với bên ngoài, Rủi ro, and Tin nhắn nên gửi.

Reason:

  • The backend extraction and first-run loop already existed, but the rendered brief still buried the wedge’s core promise inside an undifferentiated list. That was weaker than §2’s acceptance contract and less useful for a contractor owner scanning the page on a phone.

Impact:

  • Existing extracted items are now grouped by action / external sign-off / risk intent using the current item metadata; no API contract changed.
  • Suggested confirmation-ready items now also surface again under Tin nhắn nên gửi, which is intentional duplication of the action surface, not a second extracted object.

Tests:

  • pnpm --dir frontend exec vitest run src/test/ChaosImportPage.test.tsx --pool=forks --poolOptions.forks.maxForks=1
  • pnpm --dir frontend exec tsc --noEmit

Notes:

  • This improves web implementation honesty. It does not close the G2 validation gate or the deferred provisional-org ADR.

2026-06-15 — Frontend route namespaced

Frontend route namespaced: /start (authenticated import) -> /intel/import (app-wide IA restructure; public marketing /start and API paths unchanged).

2026-06-14 — Spec created (wedge crystallized from strategy v2)

Type: Feature

Summary:

  • Created F70 to give the strategy’s wedge — “Chaos Import → Boss Brief” — a single home. Previously its components were scattered across F32/F08/F34/F12/F52 with no zero-setup first-session flow.

Reason:

  • competitive-strategy-v2.md and field-tests.md §20 name this the single highest-priority surface and “the only product spec allowed before evidence.” The spec set had no entry for it — the largest gap between strategy and specs. Scope change authorized by the session goal (read strategy → upgrade specs to serve the mission).

Impact:

  • New draft feature F70 in the registry at status 🔲 Not started. Tenancy-sensitive (provisional org) and validation-gated (G2/G5). No code shipped by this spec.

Tests:

  • Test plan defined (§7); not yet implemented.

Notes:

  • Build concierge-first. Do not mark shipped until field-test G2 passes with real contractor owners.

2026-06-15 — Backend implementation (fleet-C r29, agent C)

Type: Implementation

Summary:

  • Built apps/chaos Django app: model, service, views, serializers, URLs, migration, 16 tests.
  • Registered app in config/settings/base.py, URLs in config/urls.py under /api/chaos-import/.

Scope decision obeyed:

  • Authenticated member with EXISTING org only. No provisional-org auto-create (deferred, requires ADR).
  • POST requires X-Organization-ID. No anonymous/zero-org path.

Reason:

  • fleet-C task scope: backend only. Frontend agent B handles the /start surface.
  • Provisional org needs an ADR before implementation — spec §12 acknowledges this.

Impact:

  • Registry status: Backend ✅, Frontend 🔲, E2E 🔲, iOS 🔲, Android 🔲, Mac 🔲.
  • Content > 100K chars → HTTP 400 chaosImport.error_too_large.
  • Empty/too-short content → HTTP 400 chaosImport.error_low_signal. No ChaosImport or Brief created.
  • Cross-tenant retrieve/save/dismiss/correct → HTTP 404 (not 403 — no existence confirmation).
  • save action → HTTP 403 for non-Owner/Admin roles.

Tests:

  • 16 new tests in tests/test_chaos.py covering: paste→brief with evidence-linked items, hallucination guard, confidence never 1.0, role-based save, low-signal rejection, cross-tenant isolation (retrieve/save/dismiss/correct).
  • Full gate: 2620 tests passed, 99.19% coverage.

Notes:

  • Backend does not touch frontend/. All cross-edit requests noted in fleet-C report.

2026-06-17 — Web completion slice (owner control loop + real F71 handoff)

Type: Implementation

Summary:

  • Finished the missing owner-control loop on the existing /intel/import surface: extracted brief items can now be corrected, dismissed, and saved from the page itself.
  • Replaced the dead-end confirmation CTA with the actual F71 SendConfirmationDialog, wired from the real extracted subject ids.
  • Tightened the wedge browser tests to match the shipped extractor contract and locator surface.

Reason:

  • The route and page already existed, but the surface still broke two core promises in §2: the owner could not truly correct/reject extracted items from the wedge page, and the “send confirmation” CTA did not enter the F71 loop.

Impact:

  • Registry truth changed from backend-only to web-implemented: Backend ✅, Frontend ✅, E2E ✅ while overall feature status remains validation-gated until G2.

Tests:

  • backend/.venv/bin/python -m pytest tests/test_chaos.py -q
  • pnpm --dir frontend exec vitest run src/test/ChaosImportPage.test.tsx
  • pnpm --dir frontend exec tsc --noEmit
  • pnpm --dir frontend exec playwright test tests/e2e/lumi.spec.ts -g "Chaos Import wedge" --project=chromium

Notes:

  • This does not resolve the provisional-org ADR or make F70 field-validated. It makes the shipped web implementation honest.