feat(notes): action-item owner, due and context on web, macOS and mobile

GI
10d ago
+5184-196

What

Meeting-note action items now use the same metadata and the section name Action items on web, macOS and mobile. Tentative dates display as Due ~Wed, Oct 7 and expose “tentatively due …” to accessibility; confirmed and unspecified dates keep the existing presentation. Unknown owner, due date and context are omitted.

SurfaceChanges
Web share pageOwner and due date share one line, context gets its own line, and tentative due dates are marked.
macOSSame metadata and due certainty; “Action items” heading; status control creates/completes or reopens the linked task with the item, and Add to Tasks preserves due_at. Cache encoding keeps owner, due date/certainty and context. “Try with Apps” starts collapsed.
Mobile“Action items” section with metadata and certainty; check-off creates one completed task or completes the linked task, and unchecking reopens it; row menu adds an open task with in-flight, retry and linked-task states, or opens the transcript at the source segment when available.

The native apps show “You” for the reader’s own items (capture_owner == "user"); the public share page keeps the owner’s name. Email-shaped owner names are omitted. The separate task list continues to say “Tasks”.

The web Markdown export for agents is unchanged and retains Owner: Unknown / Due: Unknown; shared-export.test.mjs pins that behavior.

Data

The wire already carries due_certainty, target_task_id and source_segment_ids; this change retains them in the client models and their caches. The public share projection now includes optional due_certainty, which is represented in the checked-in app-client OpenAPI schema and generated TypeScript clients. Flutter Structured.toJson() writes full action-item objects while its decoder still accepts legacy description strings; macOS Structured.encode(to:) preserves owner, due date/certainty and context.

  • macOS ActionItem decodes and encodes due certainty, target task ID and source segment IDs. These fields survive the JSON summary cache round trip, and legacy rows without them still decode.
  • Flutter ActionItem carries those fields through fromGenerated and toGenerated. Task creation uses a stable idempotency key for each extracted item, so repeated toggles reuse the same task.
  • Mobile strings were added through scripts/l10n.py with translations for all 49 locales.

Verification

  • Web: cd web/frontend && npm test && npm run test:share-http — both passed (185 unit tests and the HTTP share test).
  • Backend: BACKEND_UNIT_TEST_FILE_LIST=/tmp/omi-lane-a-backend-tests.txt bash backend/test.sh — 13 passed for the selected share projection test file. backend/.venv/bin/python backend/scripts/export_openapi.py --surface app-client --write docs/api-reference/app-client-openapi.json — regenerated the schema with nullable due_certainty enumerated as confirmed / tentative; the four generated TypeScript clients were refreshed from that schema and the generator check passes.
  • macOS: cd desktop/macos && ./scripts/dev-feedback.py --once swift 'ActionItemRowMetadataTests' — 9 passed, including tentative date, ActionItem and Structured cache round trips, and collapsed disclosure tests. Local Xcode toolchain; not visually verified in a running app.
  • Flutter focused tests: flutter test test/backend/structured_sections_test.dart test/widgets/conversation_action_items_section_test.dart test/providers/siri_task_mutation_test.dart — 31 passed, covering conversation-cache round trips, legacy cache decoding, metadata adapters, tentative presentation/accessibility, menu actions, and repeated complete/reopen/add operations.
  • Localization: python3 app/scripts/l10n.py check — 49 locales, 3685 keys.
  • Mobile UX contract ratchet: python3 .github/scripts/check_mobile_ux_contract.py --changed-files <changed app/lib files> --base origin/main — passed INV-UI-3.
  • bash app/test.sh after merging the remote cache fix: 4731 passed, 0 failed. scripts/pr-preflight --pr-body-file <this file>: all 53 selected checks passed in 666.74 seconds before the remote cache, OpenAPI schema and generated client updates. Pre-merge repository OMI_PR_BODY_FILE=<this file> make preflight: all 53 checks passed in 289.89 seconds before the remote cache, OpenAPI schema and generated client updates. Post-merge OMI_PR_BODY_FILE=<this file> make preflight: all 53 checks passed in 344.38 seconds before the final OpenAPI artifact and generated TypeScript client updates.

No running macOS app or physical device was used.

Product invariants affected

  • INV-MEM-1: not affected. The path glob matches memory components, but this change touches no memory tiers, collections or access policy.
  • INV-TASK-1: task creation and completion are used only to connect extracted action items to the existing task store; task bucket and pagination behavior are unchanged.
  • INV-TASK-2: not affected. Conversation extraction never writes a task; an extracted item becomes a task only through the reader's explicit check-off or Add-to-Tasks gesture, and the canonical promoter keeps that path suggestion-only.

Failure-Class: FC-mirrored-model-omits-new-member
Line-Count-Exception: desktop/macos/Desktop/Sources/Stores/TasksStore.swift | 3713 -> 3730 | reason: createTask accepts an initial completed state so extracted action items can be checked off without creating an open task first; toggleTask now reports whether the completion mutation took effect so conversation summaries only mirror toggles that reached the backend.
Line-Count-Exception: backend/routers/conversations.py | 2266 -> 2277 | reason: set_action_item_status now scopes its standalone-task mirror to the row's durable target_task_id when present instead of flipping every task that shares the description, keeping sibling tasks with equal wording independent.

🤖 Generated with Claude Code