#1555
Large-room policy: follow-ups from review of the policy stack
Review of the large-room policy stack (#1532–#1535) raised these items. Each one needs a design decision or broader change than fits the stack, so they are tracked here instead of blocking it. The stack's own fixes for contained findings landed in those PRs.
Search and archive access (#1533)
- Archives opened without Setup. An archive upgraded from before room policy can have chat rooms without policy rows.
pkg/archiveSetupnow initializes them, butOpen/OpenSQLitestill accept an archive that skipped Setup. Its chat messages are then hidden from default search. A versioned required migration that publishes the schema marker only after policy bootstrap would close this. - Idle refresh cost on SQLite. The minute loop's refresh-due check scans retained chat history with
julianday(COALESCE(...))filters that cannot use the existing indexes, even when automatic classification is off. Persisting the next activity deadline, or an index over the full relevant population, would avoid the scan. - Slack self-attribution after identifier reassignment. When self identity comes from
sources.identifierwithout a matchingaccount_identitiesrow,SetParticipantIdentifierContextadvancesparticipant_identifier_revision, but refresh admission andConversationPolicyRefreshDueignore it. A quiet room can stay full until cached activity expires.
Embeddings and evaluation (#1534)
- Per-request admission for Jev reranking. Eval checks policy admission once per reranking shape. During per-candidate reranking, a room demoted between requests can still have its text uploaded. Checking admission at the HTTP transport before every request would close it. The Typesafe client installs its own egress-policy transport, so this needs a per-request hook in docbank's Typesafe client rather than a msgvault-side wrapper.
- Legacy PostgreSQL vector schemas. Policy cleanup refuses a vector schema that predates the person, document or visual storage lanes. Without vector generation or provider credentials the migration never runs, so cold vectors stay stored and every cleanup retry fails. Upgrading existing native storage without providers, or reconciling the lanes that exist, would let cleanup finish.
- Embedded-count snapshot.
EmbeddedMessageCountForSnapshotre-reads mutable message state afterScanEmbeddingCoveragecaptured its population. A concurrent worker changingembed_genin between makesembeddings listreport valid vectors as blank. Capturing revisions with the stamped IDs would make the count consistent.
People features (#1535)
- FTS metadata uses raw participant values. Free-text search backfills sender and recipient fields from raw participant addresses, so a full message can match an address supported only by a cold room, while address filters and displayed metadata exclude it. Matching participant metadata separately with eligibility predicates would align them, including for already-indexed rows and later support withdrawal.
- Message moves and people data. Moving a message that supports a fact or brief from a full room into an already-cold room reconciles no people data, because neither room changes tier. Projections, current briefs and semantic inputs stay until another trigger, indefinitely with people sweep off.
- Cold-to-full moves. Envelope context invalidation runs before
conversation_idchanges and selects only currently eligible support, so the newly eligible envelope is missed and other full rooms sharing those participants keep stale embeddings. - Rebuild work after envelope invalidation. Recipient-envelope changes and participant-context invalidation revoke contextual stamps across Beeper rooms that share participants, but they do not append entries to the contextual change journal. Ordinary context-worker runs therefore leave those windows out of semantic search until the periodic backstop, 24 hours by default. Journaling the affected chat scopes alongside the invalidation, in both dialects' trigger schemes, would rebuild them on the next run.
- Bootstrap and contextual vectors. The one-time people bootstrap invalidates people but not contextual messages of rooms that keep their tier. Existing full-room vectors can keep participant text the value-level rules now exclude.
This improves correctness of the opt-in large-room policy for users who enable it.
Another deferred item from review of #1535:
refreshConversationPolicyAndRoster, reusing the roster it fetched. The other importers need the same retain-and-replay step, plus tests that run the full import flow instead of replaying the roster by hand.