Skip to content

feat(messages): add pinned channel messages - #1990

Merged
hmans merged 13 commits into
mainfrom
hmans/pinned-message-ideas
Aug 11, 2026
Merged

feat(messages): add pinned channel messages#1990
hmans merged 13 commits into
mainfrom
hmans/pinned-message-ideas

Conversation

@hmans

@hmans hmans commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Why

Channel members need a durable place for announcements, decisions, and other useful messages without copying their content elsewhere. Room managers should be able to curate that list with the existing room-management authority, while message edits, retractions, room access, and mixed client/server versions remain safe.

Closes #1982.

What changed

  • Added channel-only pin and unpin facts, current pin state in the Room Timeline projection and snapshot, and authorization-fenced manager commands. Pin mutations require both current membership and effective room.manage; archived rooms reject changes.
  • Added additive RoomService list/create/delete RPCs and exposed canonical pin state on the existing Message resource, so timeline, singular message, and BatchGetMessages responses share one authoritative shape.
  • Hydrated pin-list responses through a focused assembler with request-scoped decryption caching, batched reactions, and bounded concurrent message work. Related message authors and participants are resolved by ID through the canonical batched user API and shared client cache.
  • Delivered pin changes through the existing server_state_upsert realtime operation. The retained client store refreshes canonical resources, applies message edits immediately, removes retractions, and scrubs content on access loss or room eviction.
  • Added a channel Pins sidebar with automatic pagination, the exact canonical room-message component without truncation, whole-message navigation, normal message actions, retry states, and a device-local unseen dot on the header pin icon.
  • Ordered pins by durable event sequence and returned a stable opaque latest-pin marker that survives unpins, avoiding replica-clock ordering errors and offline-unpin false positives without exposing a pin-operation identity or storage coordinates.
  • Added complete localized copy, the Pinned Messages feature record, architecture inventory updates, a user guide, generated API reference updates, and the 0.5.0 release-note entry.

Compatibility and security

  • Classification: additive public API, realtime, persisted-event, and disposable-snapshot changes; no breaking fields or migration are introduced.
  • Older clients continue to understand server_state_upsert and ignore its unknown nested pin field. Newer clients gate pinned messages at server version 0.5.0-0, so they do not call the new RPCs on older servers.
  • Pin writes use room and authorization OCC fences. Nonmembers, direct-message participants, callers without room.manage, and managers of archived rooms cannot mutate pins. Client plaintext mirrors are reset on authorization loss, room removal, projection reset, and realtime-retention eviction.
  • Pin mutations and realtime changes update the retained room store immediately; late mutation responses are epoch-fenced, and only an explicit positive access grant reopens the store.
  • Device-local seen markers are scoped to the authenticated viewer and purged on room access loss, preventing both room-sensitive persistence and cross-account state inheritance.
  • Room Timeline diagnostics account for both active-pin and latest-pin indexes so operational memory estimates remain complete.

Test plan

  • mise codegen-proto
  • mise test-cli
  • mise test-frontend
  • mise lint
  • mise license-check
  • git diff --check
  • Chrome DevTools MCP: confirmed BatchGetMessages returns authoritative pinned state; a freshly hydrated row showed Unpin; unpinning immediately changed the action to Pin; re-pinning restored the fixture; and the network contained normal message hydration plus list/create/delete pin calls with no pin-specific batch endpoint. A stable reload reported no console warnings or errors in DevTools.

@hmans
hmans force-pushed the hmans/pinned-message-ideas branch from 1870245 to e94ad90 Compare August 11, 2026 16:01
@hmans
hmans merged commit 2b3b187 into main Aug 11, 2026
16 checks passed
@hmans
hmans deleted the hmans/pinned-message-ideas branch August 11, 2026 17:37
hmans added a commit that referenced this pull request Aug 11, 2026
…-0-details

* origin/main:
  feat(messages): add pinned channel messages (#1990)

# Conflicts:
#	apps/docs-website/src/content/docs/releases/0-5-0.mdx
#	cli/internal/pb/chatto/core/v1/event.pb.go
#	cli/internal/pb/chatto/core/v1/projection_snapshots.pb.go
#	cli/internal/pb/chatto/realtime/v1/realtime.pb.go
hmans added a commit that referenced this pull request Aug 11, 2026
* origin/main:
  feat(messages): add pinned channel messages (#1990)
  docs(events): document integration and logging contracts (#2004)
  fix(events): guard single-run lifecycles (#2003)
  fix(events): add bounded subject reads (#2002)
  fix(events): validate snapshot bindings (#2001)
  fix(events): require pointer projections (#2000)
  fix(events): make nil loggers safe (#1999)
  ci: run pull request workflows for stack bases (#1998)
  feat(auth): add server invite links (#1983)
  fix(i18n): restore regional locale fallback chains (#1987)
  feat(rooms): allow flexible Unicode names (#1986)
  feat(rooms): add slow mode (#1980)
  chore(agent): add session wrap-up skill (#1981)
  fix(workers): harden durable recovery (#1978)
  feat(admin): report durable worker health (#1979)
  perf(core): defer server member detail hydration (#1977)
  refactor(core): use durable consumers for cleanup (#1974)
  fix(core): make user key shredding crash-safe (#1973)
  test(e2e): add large-server performance CI (#1976)
  feat(events): add durable pull-worker execution (#1972)

# Conflicts:
#	apps/frontend/messages/de-AT/rbac.json
#	apps/frontend/messages/de-CH/rbac.json
#	apps/frontend/src/lib/components/chat/adminNav.test.ts
#	apps/frontend/src/lib/components/chat/adminNav.ts
#	apps/frontend/src/routes/chat/[serverId]/manage/+layout.svelte
#	cli/internal/core/user_auth_projection.go
#	cli/internal/core/user_projection.go
#	cli/internal/core/user_projection_snapshot_test.go
#	cli/internal/core/users.go
#	cli/internal/evtstream/subjects.go
#	cli/internal/pb/chatto/core/v1/event.pb.go
#	cli/internal/pb/chatto/core/v1/models.pb.go
#	cli/internal/pb/chatto/core/v1/projection_snapshots.pb.go
#	cli/internal/pb/chatto/core/v1/user_events.pb.go
#	docs/adr/INDEX.md
#	docs/architecture/interfaces.md
#	docs/fdr/INDEX.md
#	proto/chatto/core/v1/event.proto
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Pinned Messages

1 participant