Skip to content

feat(activity): add dated contact activity - #599

Open
salmonumbrella wants to merge 1 commit into
kenn-io:mainfrom
salmonumbrella:dated-activity-contact-state-publication
Open

feat(activity): add dated contact activity#599
salmonumbrella wants to merge 1 commit into
kenn-io:mainfrom
salmonumbrella:dated-activity-contact-state-publication

Conversation

@salmonumbrella

Copy link
Copy Markdown
Contributor

What changed

  • Adds portable dated activity projection for message, chat, and meeting evidence, with source-aware dates and owner-relative direction.
  • Materializes contact recency, cadence, daily aggregates, and authored daily notes for bounded person and day queries.
  • Adds REST, OpenAPI, generated client, and msgvault activity build surfaces with SQLite and PostgreSQL parity.
  • Keeps projection work and contact state consistent across identity changes, message mutations, deletions, and cache builds.

Why

People and relationship views need reliable recency and cadence without scanning the full archive on every request. The archive also contains mixed date quality and source-specific ownership signals, so this needs one durable, explainable projection instead of separate query-time heuristics.

Usage

Run the incremental projection:

msgvault activity build

Force a complete archive backstop scan when needed:

msgvault activity build --backstop

The scheduled incremental job uses the [activity] configuration and runs at 17 * * * * by default.

Refs #534

@roborev-ci

roborev-ci Bot commented Aug 11, 2026

Copy link
Copy Markdown

roborev: Combined Review (5b1246f)

Code review found two medium-severity correctness issues in activity projection freshness and counterpart attribution.

Medium

  • internal/store/activity_queries.go:386 — Sender attribution can be lost

    Counterpart resolution ignores messages.sender_id when an imported chat message has no from recipient. This can omit the sender and misclassify outgoing activity as inbound. A mention recipient can also incorrectly suppress the conversation-member fallback.

    Suggested fix: Add a synthetic from counterpart from sender_id when no explicit from row exists. Ensure non-addressing rows such as mentions do not disable membership fallback. Test sender-only and sender-plus-mention message shapes.

  • internal/store/activity_views.go:739 — Contact freshness ignores pending projection work

    Freshness checks only consider dirty and identity revisions for existing rows, while missing rows inspect already-projected direct links. Queued message or recipient changes—and newly bound people—can therefore return stale or empty contact data with stale=false until projection runs.

    Suggested fix: Include projection-queue and reconciled-epoch state in freshness decisions, or mark potentially affected people dirty when work is queued and clear that state only after acknowledgement.


Reviewers: 2 done | Synthesis: codex, 13s | Total: 17m32s

@salmonumbrella
salmonumbrella force-pushed the dated-activity-contact-state-publication branch from 5b1246f to 24f9ea4 Compare August 11, 2026 03:41
@roborev-ci

roborev-ci Bot commented Aug 11, 2026

Copy link
Copy Markdown

roborev: Combined Review (24f9ea4)

The PR needs changes for two medium-severity activity projection consistency issues.

Medium

  • internal/store/activity_queries.go:421 — A lone from recipient incorrectly suppresses conversation-member fallback. Channel importers such as Slack store the sender in message_recipients while keeping the audience in conversation_participants, causing inbound channel messages to become observed and outbound messages to have no linked counterparts. Suppress fallback only when explicit addressee rows (to, cc, or bcc) exist, and add coverage for a channel message containing both a from row and conversation members.

  • internal/activity/projector.go:133max_direct_counterparts affects persisted evidence classification but is neither recorded nor reconciled. Changing it leaves existing events classified under the old policy while new events use the new value, producing inconsistent contact counts and cadence until a manual rebuild. Persist the threshold as projection metadata and trigger a complete reconciliation when it changes.


Reviewers: 2 done | Synthesis: codex, 12s | Total: 15m20s

@salmonumbrella
salmonumbrella force-pushed the dated-activity-contact-state-publication branch from 24f9ea4 to fa8c429 Compare August 11, 2026 04:25
@roborev-ci

roborev-ci Bot commented Aug 11, 2026

Copy link
Copy Markdown

roborev: Combined Review (fa8c429)

Changes need revision: three medium-severity attribution and classification inconsistencies were identified.

Medium

  • internal/store/messages.go:1016, internal/store/migrations.go:21 — The new envelope-authoritative attribution rule changes derived identity_is_from_me, but reconciliation remains gated by the already-applied message_attribution_provenance_v2 migration. Upgraded archives may retain legacy is_from_me=true values when a non-owner From envelope should make them false, causing message APIs to disagree with cache and activity derivations. Add a versioned migration that refreshes attribution with the new predicate and advances the account-identity/cache revision.

  • internal/store/messages.go:755upsertMessageSQL treats every matching email identifier as ownership, while the canonical fallback accepts email identifiers only when the participant has no primary email. UpsertMessage and granular recipient APIs can therefore persist attribution inconsistent with activity/cache results because they skip PersistMessage’s final canonical refresh. Mirror the primary-email guard in the upsert logic and ensure granular From-recipient replacement performs a final refreshMessageAttributionWith, or migrate callers to the atomic persistence path.

  • internal/activity/classify.go:47 — Classification considers only the first from counterpart a sender despite support for multiple distinct From participants. If a later From participant is the owner, the message may be classified as inbound based on participant-ID ordering, while additional authors receive addressed/co-presence roles. Track all From counterparts as senders, classify as outbound when any From counterpart is an owner, and classify as inbound only when ownership appears exclusively among non-From counterparts.


Reviewers: 2 done | Synthesis: codex, 14s | Total: 18m10s

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant