Stop automatic cards in Chat on macOS and mobile

Merged
GI
11d ago
+680-4491

Chat no longer receives automatic goal/task/meeting-notes entries. Opening Chat, finishing a capture/meeting, changing tasks/goals, and releasing a deferral no longer insert goal/task/question/meeting-note cards. Existing automatic cards are hidden by provenance. Agent tool blocks and renderers stay available.

Producer inventory

ProducerEntry pathChange
Daily goal/task openerchat_first._daily_opener_blocks / _maybe_persist_daily_opener during materialize fetchDeleted preparation and persistence
Rich/sparse cold start_maybe_persist_cold_start -> proactive_engine -> server intentDeleted producers
Sparse script continuationkernel appendNextColdStartSequenceQuestion after an answer terminalizesDeleted automatic continuation; user answers remain
Task/goal mutation judgmentaction_items/goals post-commit wakes -> proactive_engineDeleted wakes and engine
Capture arrivalfinalizer -> persist_capture_arrival_intentDeleted arrival write
Desktop meeting notesmeeting_receipt -> persist_receipt_intent, from-segments, finalized processing, deferred enrichment, repair/backfillRetain audit receipt and notes; remove Chat intent and repair writes
Deferred question resurfacingdeferral outbox -> /v1/chat/deferrals -> release_due_deferrals -> materializationAcknowledge retired outboxes without queue persistence or resurfacing
Proactive delivery/v1 and /v2/chat/materialize-prompts, Swift presence/foreground/meeting coordinator, API driver, kernel materialize operationsBackend returns an empty compatible response; remove runner/driver; kernel rejects without a journal write
macOS proactive notification copyFloatingControlBarWindow notification journal admissionKeep TTL-bounded context for explicit follow-ups; no Chat write or automatic answer row
Mobile daily-summary pushFCM -> MessageProvider.addMessage/history/cacheSuppress automatic day_summary history; notification/recap renderer remains
Empty-history greetingGET /v2/messages initial_message_util fallbackHistory reads return an empty list and never synthesize a greeting

meeting_notes_wiring/process_conversation prepare canonical notes and meeting context; they do not independently journal cards. Mobile has no proactive materializer: it consumes mirrored backend rows and cached/pushed messages. The old task/goal/question enum values now decode as text, so preserve the original wire kind for legacy filtering.

Deleted versus filtered

Deleted the opener/cold-start preparation, proactive engine and wake hooks, capture/meeting arrival persistence and repair, queue adapter, Swift fetch/ack/materialize driver, kernel automatic continuation, and tests asserting retired behavior. Kept released endpoint and receipt shapes, stored historical records, canonical meeting receipts, tools, and all rich renderers. No feature flags or historical-data mutation.

Filter markers: chatFirstIntentId/chatFirstIntentSource, cold-start producer/script subject, proactive_notification origin or an AI-turn notification continuity key, legacy task/goal/question kinds, and day_summary. Backend filtering runs before visible pagination; macOS filters the shared journal projection; mobile filters server history, older pages, cache, and push ingestion. Old mobile caches discarded provenance; a cache-format version causes a canonical history refresh before those rows can render. Ordinary rich-block types alone never hide a reply.

History visibility

Automatic rows do not consume the offset readers' reported-row scan budget, including automatic rows also marked reported. They are crossed in batches of at most 100 until the visible page fills or the collection ends. The released list response has no scan-continuation field, so another arbitrary automatic-row ceiling would still look like EOF to mobile; retain the existing reported-row allowance without capping automatic prefixes. Clean pages continue reading only the requested rows.

Cache-aligned history counts the same visibility predicate using a scoped projection of reported, sender, metadata, message_source, type, and content_blocks. Serialized provenance cannot be excluded by an aggregate. This adds projected document reads across the scope for the exact count, but preserves the existing 10–17 visible-message append epochs without a migration or a new durable counter. The subsequent history read requests only that visible limit; redundant reported overfetch is removed.

The notification: continuity prefix hides AI turns only. User follow-ups and cold-start answers with sequence metadata remain visible in backend history, macOS projection, and mobile cache replay. The meeting banner's failure comment now points to saved conversations and notes, without adding a UI fallback.

Product invariants affected

  • INV-CHAT-1: one canonical journal; the shared projection filters automatic provenance across surfaces without another timeline
  • INV-NAV-1: retained explicit notification follow-up and Chat destinations
  • INV-AGENT-*: retain owner/capability/run fences for user-requested tools and block appends
  • INV-6: preserve user-turn continuity and rich resources; update the retired notification-copy rule in the component guide

Failure-Class: none
This implements David's 2026-10-04 product ruling removing automatic Chat entries, rather than a new inference or rollout flag.

Validation

Affected checks:

  • Backend, from backend/: BACKEND_PYTEST_WORKERS=4 BACKEND_UNIT_TEST_FILE_LIST=../.agent-brief/no-auto-chat-review-files.txt ./test.sh tests/unit/test_get_messages_reported_pagination.py tests/unit/test_chat_message_count.py tests/unit/test_chat_messages_pagination.py: 23 passed. The selector contains those three file paths; positional arguments alone do not narrow this runner.
  • Named-session history regression, from backend/: BACKEND_PYTEST_WORKERS=4 BACKEND_UNIT_TEST_FILE_LIST=../.agent-brief/no-auto-chat-session-test-files.txt ./test.sh tests/unit/test_chat_session_app_identity.py: 6 passed, including the unchanged Clear Chat greeting test. The selector contains that one file path. Empty named-session GETs preserve app/session scoping, return [], and call neither the greeting helper nor message persistence. A search of the backend tests found no other positive GET-empty-history greeting assertion.
  • Flutter, from app/: flutter test test/unit/server_message_content_blocks_test.dart: 12 passed.
  • Swift, from desktop/macos/: xcrun swift build --build-tests --package-path Desktop, followed by xcrun swift test --skip-build --package-path Desktop --filter KernelTurnRecordedProjectionTests: build passed; 41 tests passed.

Dense-prefix regressions use 1,201 automatic rows before real messages for ordinary offset, app, and cache-aligned reads. Cache count tests retain session/app fences and exclude automatic/reported rows. All three platforms assert user notification continuity and cold-start answer visibility while preserving the AI notification filter.

Manual macOS E2E flows were not launched. No production endpoints, credentials, Firestore, app bundles, deployment, or CI monitoring. Clear Chat, initial-message routes, and integration messages are outside this change's follow-up scope.

Line-Count-Exception: backend/database/chat.py | 1524 -> 1549 | Filter automatic provenance before pagination while preserving user replies and rich blocks

šŸ¤– Generated with Claude Code