A community based topic aggregation platform built on atproto

fix(read-path): task 7 re-review on Opus 5 — close the comment-filter leak, gate post.get on the viewer master

Re-review of the read-side visibility predicate (the compensating control for task 6's removed write barrier) on a stronger model. The predicate itself held up under a full surface enumeration; the gaps were all at its edges. LEAK: actor.getComments' ?community= filter selected roots straight from posts with no visibility predicate and no deleted_at filter. An attacker could write a postv2 naming a community that never accepted it, comment on their own hidden post, and have that comment served in full inside a listing the client renders as "this user's comments in community C" — the injection the write barrier used to block, one table over. Now runs the shared predicate with the viewer bound. FAIL-OPEN GATE: the getComments header gate was an optional type assertion whose miss-path served ungated content. Today's wiring satisfied it, so the security property rested on convention — any future decorator implementing only posts.Repository would have turned it off silently. The requirement is now a compile-time dependency (comments.PostReader); the fallback is gone. post.get was hard-bound to an anonymous viewer, so an author could not see their own pending post via permalink while seeing it everywhere else. GetViewsByURIs now threads the viewer. Also: GetProfileStats' hand-copied predicate replaced with the shared one (three demonstrated mutations went uncaught); community postCount was served from a stored column that NOTHING has ever incremented — every community reported 0, including as a sort key — now a live gated subquery, and the dead countAcceptedPostsForCommunity behind `var _ =` is gone; GetByURI renamed to GetRawIndexedRow with a danger banner; removedMarkers batched (N+1 on a caller-controlled URI list) and its lookup errors no longer collapse to notFound. Tests: viewer-DID provenance pinned at T0 for every handler that threads one (each proven to bite), removedMarkers table test, NULL accepted_cid and pending_reacceptance seeded at T1, and the fixture trap that made the intuitive "accept it" call produce an invisible post is fixed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QDvRJ45k6E5KrBARDHUtiM