feat(notes): action-item owner, due and context on web, macOS and mobile
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.
| Surface | Changes |
|---|---|
| Web share page | Owner and due date share one line, context gets its own line, and tentative due dates are marked. |
| macOS | Same 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
ActionItemdecodes 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
ActionItemcarries those fields throughfromGeneratedandtoGenerated. 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.pywith 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 nullabledue_certaintyenumerated asconfirmed/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.shafter 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 repositoryOMI_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-mergeOMI_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