Files
buzz/migrations/0056_personal_read_state.sql
ART5 dceafd3ee7 relay: count buzz/v1 unread forward from the read position
Count unread forward from each context's position, capped at 99 (advertised
as unread_cap). Thread read rows now mean membership: ingest writes them on
reply, mention, DM, or a reply to a thread you started, replacing the
parent lookup and giving unread_thread_count. Accounts start caught up at
the actor's first intent (personal_read_accounts.started_at); joining starts
a channel caught up. Drops the retention horizon, account, status fields,
latest_message_complete, threads.complete and context unknown.

Docs (docs/buzz-v1-read-state.md) not yet updated.

Signed-off-by: ART5 <a7397738d0f05003030d4487431db3096f2f48e09df21eb15f91f3a9aecf5f0c@buzz.block.builderlab.xyz>
2026-10-06 17:07:45 -07:00

38 lines
1.8 KiB
SQL

-- Private accessory read progress. Never included in Nostr event queries.
-- A frontier is the relay arrival time (events.received_at) of the message a
-- context was read through, never signed event time, which the sender chooses.
-- An empty root_id covers only the channel timeline; a root-specific frontier
-- covers that thread, without inheritance.
-- threads_through_timestamp is the only cross-context cut: an explicit
-- whole-channel read that also covers every thread in that channel.
-- started_at is the actor's first read intent and the floor of every
-- position: until then nothing counts, and from then the actor starts caught
-- up. Ingest creates accounts for thread membership without starting them.
CREATE TABLE personal_read_accounts (
community_id UUID NOT NULL REFERENCES communities(id),
actor BYTEA NOT NULL CHECK (octet_length(actor) = 32),
started_at TIMESTAMPTZ,
PRIMARY KEY (community_id, actor)
);
CREATE TABLE personal_read_frontiers (
community_id UUID NOT NULL,
actor BYTEA NOT NULL,
channel_id UUID NOT NULL,
root_id BYTEA NOT NULL DEFAULT ''::bytea CHECK (octet_length(root_id) IN (0, 32)),
through_timestamp TIMESTAMPTZ NOT NULL,
-- Whole-channel cut covering every thread; channel rows only.
threads_through_timestamp TIMESTAMPTZ
CHECK (threads_through_timestamp IS NULL OR root_id = ''::bytea),
PRIMARY KEY (community_id, actor, channel_id, root_id),
FOREIGN KEY (community_id, actor)
REFERENCES personal_read_accounts (community_id, actor) ON DELETE CASCADE,
FOREIGN KEY (community_id, channel_id)
REFERENCES channels (community_id, id) ON DELETE CASCADE
);
SELECT attach_community_write_fence('personal_read_accounts');
SELECT attach_community_write_fence('personal_read_frontiers');