Collaborative community
What does the full collaborative social community platform require, what exists, and what is still missing? This document is the requirements-and-evidence record for that active ob
What does the full collaborative social community platform require, what exists, and what is still missing? This document is the requirements-and-evidence record for that active objective. It extends the earlier MVP scope. Passing the HTTP contracts does not complete the objective; editor synchronization, cursor overlays, conflict recovery and complete community journeys are still required. No hosted promotion or real social email campaign has been performed for this work.
Status: active work. Persistent social, notification, membership, presence and edit APIs pass SQLite and PostgreSQL fixtures; profile, discussion and collection UI is being qualified. See the table below and implementation status.
Entities#
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#FAFAF7', 'primaryTextColor': '#1C1C1B', 'primaryBorderColor': '#1C1C1B', 'lineColor': '#64645F', 'fontFamily': 'DM Sans, Inter, system-ui, sans-serif'}}}%%
flowchart LR
ACCOUNT["Account<br/>profile · picture · links · achievements"]
PROJECT["Project (draft)<br/>members · roles · edits · sessions"]
RELEASE[("Release / catalog entry<br/>immutable, public or not")]
COLLECTION["Collection<br/>private · unlisted · public"]
THREAD["Discussion thread<br/>anchored · public or team-only"]
INBOX["Notifications<br/>center · preferences · deliveries"]
ACCOUNT -->|follows| ACCOUNT
ACCOUNT -->|likes · bookmarks| RELEASE
ACCOUNT -->|owns| COLLECTION --> RELEASE
ACCOUNT -->|invited viewer / commenter / editor| PROJECT
PROJECT -->|publishes| RELEASE
THREAD -.anchored to.-> RELEASE
THREAD -.anchored to.-> PROJECT
ACCOUNT -->|receives| INBOX
ACCOUNT -.blocks · reports.-> ACCOUNT
classDef source fill:#F0442E,stroke:#1C1C1B,color:#FAFAF7
classDef shared fill:#DCEBDD,stroke:#1C1C1B,color:#1C1C1B
classDef host fill:#DED8F0,stroke:#1C1C1B,color:#1C1C1B
class RELEASE source
class ACCOUNT,PROJECT,COLLECTION,THREAD shared
class INBOX hostMutable social data never enters a portable Toy or an immutable release package.
Requirements and evidence#
| Requirement | Implemented | Remaining work and qualification |
|---|---|---|
| Profiles | Public pages for all accounts, display name, bio, eight HTTPS links, normalized picture upload/removal, public work, real earned achievements | Consistent account/header avatars; real multi-account profile/avatar browser journey passed |
| Collections and liked work | Private/unlisted/public named collections, explicit ordered membership, public collection pages, optional public Likes tab; bookmarks remain private | Full collection pagination/reordering UI and browser journeys |
| Engagement | Follows/unfollows, likes/unlikes, following feed, and posts: product reviews, threaded comments and replies with mentions, likes, helpful votes, pins, publisher responses, edit history, removal/resolution and durable anchors | Competing UI writes and full moderation/block/report browser journeys; review → response → reply → like → pin browser journey |
| Shared drafts | Viewer/commenter/editor invitations, acceptance, roles/removal, shared project listing and enforced access | Workspace invitation/membership controls and live draft entry |
| Concurrent editing | Validated historical authoring batches, independent-field merging, atomic revisions, conflict rejection and author-specific Undo | Connect editor pending edits, conflict review, reconnect, Redo and two-browser journeys |
| Cursors and sessions | Account-bound tab identities, bounded cursor/selections, expiry and authorized reads | Workspace cursor/selection overlays and cleanup/reconnect journeys |
| Collaborative notes | Public versus team-only anchored threads with read/comment permissions | Editor selection/reveal and team discussion UI |
| Notifications | Unified notification center with read/dismiss state, an event × channel preference matrix with locked required events, deduplication, collapsing and deep links | Bell with unread count and ETag polling; filters, grouped rows, optimistic actions; see notifications and localization |
| Email and push channels | Leased delivery for every channel, verified recipients, preference/visibility checks at delivery, bounded retries, digests, SES/Resend, web push, bounce suppression and scoped one-click unsubscribe | Injected-transport tests; operator delivery/bounce monitoring and hosted qualification remain. Native push is planned only |
| Community controls | Blocking (read-time post filter), reports with kind: post, configured moderator report queue, post removal, restore and verified-badge clearing | Moderator queue view and complete blocking/reporting browser journeys |
Identity and access#
SQLite migration 9 and PostgreSQL migration 10 add community, membership, session and edit records. Server-owned projects.parent_release records actual fork provenance separately from editable source for the remixer achievement. Achievements derive from real public work and contributions; no accounts or awards are invented.
| Rule |
|---|
| Bookmarks stay separate from explicit public likes |
| Private collections and liked work are not exposed unless the owner chooses |
| Public collections show visitors only public Toys; later visibility changes withdraw them |
| Blocks prevent interaction; they cannot make anonymously readable public URLs secret |
| Invitations reveal inviter, project identity and role before acceptance, never draft source |
| Every draft, team, session and edit operation checks accepted membership |
| Only owners manage other members; members may leave; publishing and visibility remain owner actions |
| Revocation and blocking deny subsequent reads, edits, notes and presence, including old edit retries |
Shared editing#
The API accepts a durable request key, an existing base project version and a shared authoring batch. It applies the batch to that admitted historical source, merges independent fields against the latest source, validates the complete result and commits through the existing applyBatch project writer. Scalar and array collisions reject with COLLABORATION_CONFLICT; invalid graph dependencies reject atomically. Toy identity stays stable. The authenticated account supplies the audit actor.
Undo reverses only the author's recorded change against the latest source, preserving unrelated peer work. A conflicting reversal rejects; an edit cannot be undone twice; Undo itself is recorded. Edit audit rows reference existing revision snapshots instead of duplicating whole source documents.
| Presence limit | Value |
|---|---|
| Expiring sessions per account | 8 |
| Invited members per project | 32 |
| Selected identities per message | 100, with view/scope and bounded coordinates |
| Unchanged-state polls | Omit source |
| Rate buckets | Presence and edits are separate |
Presence stays outside Toys and audio callbacks.
Posts: reviews, comments and replies#
One posts table (SQLite migration 11, PostgreSQL 12) carries reviews, comments and replies, replacing the foundation comment table in place. services/api/src/posts.ts owns the rules; posts-store.ts owns storage and database triggers keep thread shape, depth, identity and the three-pin limit.
| Kind | Subjects | Rules |
|---|---|---|
review | product | Rating 1–5, optional 120-character title and 8 KiB Markdown; one per account and product (a second save edits it); not by publisher members or the project owner; verified email or one day of account age required |
comment | product, project, version, storefront, docs_page | 1 byte to 8 KiB Markdown; anchors and team-only notes stay on project subjects with the rules below |
reply | Inherited from the root | Depth ≤ 4; a reply to a depth-4 post continues on its parent with a quoted @handle. A publisher response is the one flagged reply per review |
| Rule | Behavior |
|---|---|
| Verified owner | Frozen at creation from an active product:<id> billing grant only; free claims and plays do not verify; refunds never rewrite it; moderators can clear it |
| Reviewed version | Chosen by the author or suggested from the latest installation, else the selected library release; an edit after a newer version is published shows "Updated after version X" with the prior rating |
| Rating summary | rating_summary is recomputed in the transaction of every review write, removal, restore or moderation and exposed on listings as ratingSummary; library items no longer carry ratings |
| Edits and history | Every content edit stores the previous body, rating and title in the immutable post_revisions, readable by the author and moderators |
| Removal | Author removal clears the text; subject owners (project owner, editing publisher member) remove comments and replies but not reviews; moderators remove anything. Tombstones remain while visible replies need them |
| Blocks | Read-time filter in both directions; replies, reactions, mentions and notifications between blocked accounts are refused or dropped |
| Pins | Up to three roots per subject by the subject manager or a moderator, rendered first |
| Notifications | reply, mention, publisher_response, review_on_your_product, comment_on_your_work and pinned commit with the post; deliveries are planned by the notification service |
The web PostFeed renders Reviews (rating summary histogram filters, ReviewComposer, helpful sort) and Discussion tabs, threads with collapse below depth 2, reactions, pins, publisher responses, edit/remove/report and moderator actions. It replaces the Toy page discussion and editor team notes, and renders directly on the public React profile, collection and product pages inside the shared shell.
Notifications and operation#
Community events (follow, like, comment, reply, mention, invitation, collaboration edit) are written by notify() in the same transaction as the event. It drops self-notifications and blocked actors, deduplicates per account and collapses unread repeats with the same group key (for example likes on one Toy) into one row with a count. One NotificationRunner delivers every channel: it acquires a database lease, re-checks the verified address, current preferences, blocks and resource access, then calls the provider outside transactions. Retries keep provider identity; two runners cannot claim the same live lease. SES delivery is at-least-once across a crash after acceptance; exactly-once email is not claimed. Six attempts bound retries and rows retain delivery and failure evidence.
Email and push link to the authenticated app rather than embedding private source or comment text, and push bodies never name people or projects. Unsubscribe tokens are random, stored hashed, scoped to one category or all optional email, and can never disable required account, security or purchase notices. The unsubscribe page never mutates on GET; RFC 8058 one-click uses POST. The design and routes are in notifications and localization.
TUNETOYS_MODERATOR_ACCOUNT_IDS explicitly configures durable account IDs for report review. No administrator credential is seeded. PostgreSQL runtime privileges include the new tables. Offline import includes durable social data and orders threaded posts by insertion to preserve parent foreign keys. Cursor sessions are transient.
Verification#
| Check | Covers |
|---|---|
services/api/test/community.test.mjs | Real HTTP/SDK profiles, interactions, project discussion and team notes through posts, reports, visibility, roles, concurrent edits, author Undo, revocation and immutable packages |
services/api/test/posts.test.mjs | Review eligibility and uniqueness, verified-owner freeze, legacy reviews, summaries, helpful/rating sorts, publisher responses, edit history, depth, pins, blocks, moderation and notifications |
services/api/test/community-mail.test.mjs, notifications.test.mjs | Injected delivery: leases, retries, digests, suppression, web push encryption, preferences, center routes and scoped unsubscribe without sending real mail or push |
| Both on SQLite and disposable PostgreSQL | The first database checkpoint passed 58 PostgreSQL tests and the complete root npm run check |
npm run test:community-ui -w @tunetoys/web (after a browser build) | Profile/avatar editing, follows, likes/replies, private-to-public collections, notifications/read state and anonymous 390px discovery, without page errors or horizontal overflow |
The goal additionally requires every remaining workspace, cursor, conflict, reconnect and moderation item in the requirements table to be implemented and verified before completion.
Implementation#
- services/api —
community.ts,community-routes.ts,community-access.ts,community-pages.ts,notifications.ts,notification-runner.ts,posts.ts,posts-routes.ts,posts-store.ts - services/api/migrations — community and collaboration migrations
- packages/authoring —
collaboration.tsmerge and author Undo - apps/web —
CommunityApp.tsx,CollaborationPanel.tsx,PostFeed.tsx,ReviewComposer.tsx,RatingSummary.tsx,CreatorProfilePage.tsx,CollectionPage.tsx
Related#
- Discovery and public pages — creator pages and public routes
- Platform entities and API responsibilities
- Executable HTTP contract
- Authoring for people and agents — the single-author revision model this extends
- MVP scope — the authorization that added this objective