Review posture

Review target: 00-final-recommendation.md, checked against 0106 and the fixed founder constraints. Decisions already made are treated as constraints; the questions below concern their consequences and implementation viability. An item marked BLOCKER must be resolved or pass a spike before feature implementation starts.

1. Contradictions & Stale Content

1.1 Zero is decided, but the synthesis still describes an engine bake-off

1.2 Zero status and capability descriptions are stale or internally inconsistent

1.3 Offline scope contradicts itself

1.4 The corpus alternates between Zero and an app-built second sync engine

1.5 Hot/cold history is described in mutually exclusive ways

1.6 Other corpus contradictions

2. Zero Hard Questions

2.1 What exactly is the syncable dataset?

BLOCKER

"messages" is not a dataset boundary. 00 §5 defines one messages table, while §16 promises a bounded hot publication. Specify the exact published tables, columns, partitions, expected row counts, average row widths, growth rate, retention, and three-year size.

2.2 Can zero-cache be operated to the product's SLO?

2.3 How many query windows can the system afford?

2.4 How are revoked rows actually removed?

2.5 Does the client working set fit a Discord-scale UI?

2.6 Is React Native support production-ready for this lifecycle?

2.7 How does auth refresh work on long-lived sockets?

2.8 What is the exact durable message-outbox boundary?

3. Data Model Open Questions

3.1 Tenancy and database topology

3.2 Message identity, ordering, and partitioning

3.3 Replies and threads

3.4 Reactions and aggregates

3.5 Read state and unread summaries

3.6 Attachments and blobs

3.7 Search and unsynced history

4. AuthZ Open Questions

4.1 Capability manifest shape

4.2 Permission precedence rules

The platform uses layered, order-sensitive capability algebra stored as human-readable capability_key + effect rows. Evaluate one channel in exactly one server, with explicit inputs only, in this order:

  1. Start with server.default_permissions.
  2. Apply every applicable server-role override in TRW rank order.
  3. Apply applicable user-attribute overrides. Attributes are a first-class cross-server user dimension: subscription tiers, staff cohorts, and similar platform-wide facts can modulate permissions inside any server/channel.
  4. Apply channel-level overrides in order: channel default, applicable channel roles in rank order, then applicable channel attributes.
  5. Apply the timeout/moderation mask (ALLOW_IN_TIMEOUT) after ordinary grants.
  6. Apply the bounded server-owner/platform-privileged safe bypass. No bypass defeats account disable, ban, server suspension, legal hold, or hard entitlement/quota gates.
  7. Apply required entitlements after the permission result. A permission grant cannot manufacture a paid entitlement; feature flags may remove access but never grant it.

Use one shared pure-TypeScript calculator on client and backend. It accepts the server defaults, rank-ordered roles, user attributes, channel overrides, moderation state, owner/privileged state, typed configuration, entitlement facts, and manifest version as explicit values; it must not reach through a global singleton such as appClient.attributes.get. Unknown capability keys fail closed.

4.3 Entitlement propagation through synced queries

4.4 Guests and anonymous access

4.5 Moderation and retroactive visibility

5. Product/UX Open Questions

5.1 Jump-to-message under query-driven hydration

5.2 Unread badges without syncing all messages

5.3 Typing and presence transport

5.4 Notification routing

5.5 Course progress and video position

6. Delivery Sequencing

Spike 0 — freeze the architecture contract (1–2 days)

Spike 1 — prove Bun/Zero integration and history topology (week 1)

Spike 2 — zero-cache operations, migration, and DR (week 1–2)

Spike 3 — authorization and revocation correctness (week 2)

Spike 4 — query fan-out and hot-channel load (week 2–3)

Spike 5 — React Native lifecycle and working set (week 3)

Spike 6 — durable message outbox fault matrix (week 3–4)

Spike 7 — auth refresh and account isolation (week 4)

Spike 8 — hot/archive jump, unread, and search UX (week 4–5)

Spike 9 — message-list anchoring and media growth (week 5)

Spike 10 — ephemeral events, notifications, and course progress (week 5–6)

Sequencing gate

Do not begin Stage 1 feature breadth until Spikes 1–7 pass. Spikes 8–10 may overlap only after the underlying topology, authz, operations, RN, and outbox contracts are stable. A failed threshold triggers a written founder decision on scope/SLO/topology; it does not silently revive an eliminated sync engine or defer the problem to Stage 4.

7. Top 10 Decisions for the Founder

1. Adopt an explicit hot/archive history boundary

Recommendation: publish a physically bounded hot-message dataset to Zero and serve older history through an authorized read-only history/search API, starting with a 90-day target adjusted by the capacity spike. The tradeoff is losing Zero's single-abstraction story for cold jumps; the benefit is avoiding an architecture that crosses Zero's current ~100 GB guidance with no migration plan.

2. Accept and document the revocation limit of offline-readable data

Recommendation: promise removal within 2 seconds P95 while connected and on the next successful sync while offline; offer memory-only/short-retention mode for high-sensitivity servers. Instant offline reading and immediate remote erasure cannot both be guaranteed.

3. Use layered ordered-role and attribute permission algebra

Decision: store human-readable capability_key + effect rows and evaluate server defaults → rank-ordered server roles → user attributes → channel default/rank-ordered roles/attributes → timeout mask → bounded owner/privileged bypass, with entitlements gated afterward. Attributes remain the first-class cross-server dimension for subscription tiers, staff and similar platform-wide facts. Optional bitset caches are derived only. Port the calculator once to pure TypeScript with explicit inputs, typed configuration instead of magic IDs, fail-closed unknown keys, explainable results, and property tests.

4. Make the message outbox write-ahead and message-only

Recommendation: allow a message to be queued even when already offline, persist it before any Zero mutation, and acknowledge only from an authoritative server receipt/canonical row. Maintains a small encrypted local command system and rejection UX; avoids lost/duplicated sends without reopening generic offline mutation support.

5. Use shared-schema tenancy, not schema-per-tenant

Recommendation: start with one shared Postgres schema and explicit server_id, then introduce measured tenant-cohort cells rather than per-tenant schemas. Schema-per-tenant is incompatible with sane migrations, Zero publications, and cross-server users at this stage.

6. Choose dot-plus-mentions unread semantics

Recommendation: show an unread dot from tail/read cursors and exact mention counts; do not promise exact global message counts or durable offline-read reconciliation in v1. Avoids O(messages × members) projections and another offline-write exception.

7. Keep product memberships entirely app-owned

Recommendation: use Better Auth only for identity/session/provider concerns; app tables own invitations, servers, memberships, owners, roles, and all access lifecycle. Prevents dual membership sources and authorization drift.

8. Fund Zero operations as a first-class subsystem

Recommendation: self-host Zero self-hosted and staff explicit ownership for replication-manager, view-syncers, NVMe, Litestream, logical slots, CVR/Change DBs, upgrades and failover. The operating budget includes dedicated platform-engineering ownership and support escalation.

9. Use a separate ephemeral WebSocket plane

Recommendation: run typing and presence over a Bun-compatible WebSocket gateway with Redis/Valkey TTL state; reserve LiveKit transport for active live rooms. Putting ephemeral fan-out into Zero/Postgres would create write amplification, stale presence, and pointless replay.

10. Keep course progress online-authoritative and coarse-grained

Recommendation: sync coarse progress rows, send video position about every 15 seconds through an API, and require connectivity for completion/reward transitions. Preserves the message-only offline exception and prevents high-frequency Zero mutations or client-granted rewards.