Commit Graph
71 Commits
Author SHA1 Message Date
Chris Troutner ace703831f Harden profile-last-post with mutation-killing tests
Add unit tests for the profileRecency backfill and recent-profiles read
path that kill the language-mutation survivors: status-tip confirmation
(no tip, exact tip), exclusion of addresses without a profile, replies,
and polls, malformed addrPostHeight entries, missing optional stores,
seen/height defaulting, stale-record removal, and the height/seen/address
comparator tie-breakers. Tighten the indexer recency tests the same way
and assert the set-profile text and max-size boundary.

Refresh the mutate4javascript manifests and write the gherkin-mutator
acceptance mutation metadata for the changed features.

By architect.
2026-09-20 15:29:24 -07:00
Chris Troutner 6b8da07981 Refactor profile-last-post: split recency logic, add property tests
Split backfillProfileRecency and establishProfileRecency into small
helpers so every analyzed function scores CRAP 6 or below, without
changing behavior. Extract the post recency-qualification check in
handlePost.

Add seeded property tests covering backfill conservation and
idempotence, the addrPostHeight key round trip, recent-profile read
ordering and pagination, and the indexer's recency convergence.

By refactorer.
2026-09-20 14:45:37 -07:00
Chris Troutner bdf2c3015e Implement profile ordering by last post
Indexer: maintain a profileRecency index keyed by address with the newest
confirmed qualifying post (top-level 0x6d02 or topic message 0x6d0c).
Replies and poll creations do not qualify, and mempool (unconfirmed) posts do
not write the index. Setting a profile after posting establishes recency from
the address's existing addrPostHeights, excluding replies and polls.

DB: GET /profile/recent now orders profiles by their most recent qualifying
post (height desc, seen desc, address asc) from profileRecency plus per-page
profile lookups, so it no longer scans addrPostHeights or sorts the whole
profiles store. Add a profileRecency backfill library and CLI that rebuilds
the index from addrPostHeights, posts, postParents, polls, and status,
ignoring unconfirmed entries and removing stale records.

Add focused unit tests, acceptance fixtures, and regex step handlers.

By coder.
2026-09-20 14:36:08 -07:00
Chris Troutner 63107855a2 Harden recent profile identity with property tests
Add seeded property tests for the new identity join and table view model:
profile-path round trips, display-name fallback, account mapping, table
row shape, DB identity-field independence, missing-store null behavior,
and page-only join with conserved order and pagination.

Cover the account-cell click handler so RecentProfileAccount reaches 100%
unit coverage (CRAP <= 2).

Tests only; no production behavior changed.

By refactorer.
2026-09-20 13:00:03 -07:00
Chris Troutner 9fb092e845 Implement recent profile identity join and Account column
DB: GET /profile/recent joins each profile's display name (names store)
and avatar URL (profilePics store) by address, reporting null when a
record is absent. The join runs on the requested page only and leaves the
profile order and pagination unchanged.

Client: the /profile/recent table gains a leftmost Account column showing
the display name and avatar, both linking to the profile, with a
truncated-address fallback when the name is absent and an identicon
fallback when the avatar is absent. The existing columns are preserved.

Add focused unit tests, acceptance fixtures, and regex step handlers for
both components.

By coder.
2026-09-20 12:46:12 -07:00
Chris Troutner 5653ecef9d Refactor following feed cap: share acceptance seeding, add cap properties
Extract putPost/putFollowEdge seeding helpers in the psf-memo-db acceptance
handlers and route the following-feed capped/mixed fixtures and the
many-top-level-posts fixture through them, removing repeated store/key writes.

Add property tests for the capped total, newest-first page conservation, and
the bounded eligible scan, plus unit coverage for the remaining PostQuery
constructor guards and static key helpers (post-query.js statements 94.5% ->
99.4%).

CRAP stays at or below 6, src/adapters/post-query.js remains at 40 mutation
sites with its manifest intact, and the DRY tool reports no duplicates in the
changed source file.

By refactorer.
2026-09-20 08:32:43 -07:00
Chris Troutner db216949ec Implement following feed capped scan
By coder.
2026-09-20 08:19:27 -07:00
Chris Troutner c9728cc301 Review notifications query performance: harden defaults, refresh mutation manifests
Add unit coverage for the notification height/seen defaults, the seen
sort tie-break, the missing-status cutoff, and the config flags; refresh
the language mutation manifests and the soft Gherkin acceptance mutation
manifests for the three affected feature files.

By architect.
2026-09-18 10:46:29 -07:00
Chris Troutner 45a7b34fad Refactor notifications query: cut CRAP, share test double, add membership property
Reduce the notifications-query collector cyclomatic complexity below the CRAP
threshold by extracting prefix-range, txid-fallback, height-selection, and
actor-suppression helpers, and by sharing one per-post index scan between the
like and reply collectors. Reuse the shared FakeDb in the unit and property
suites instead of two local LevelDB doubles, cover every not-found branch, and
add a property test asserting returned notifications map to exactly one
generated interaction.

By refactorer.
2026-09-18 10:19:26 -07:00
Chris Troutner f45d63cd70 Implement notifications query performance
Bound GET /posts/notifications/:addr to the viewer's activity in a
configurable block window (NOTIFICATION_BLOCK_WINDOW, default 25000):
read the viewer's posts from addrPostHeights, prefix-scan postLikes and
postChildren for those posts only, and read follows from the new
followeeHeights index. The global likes, postChildren, and follows stores
are no longer full-scanned, and pagination.total counts only in-window
notifications.

Add the indexer write of a followeeHeights entry on every follow and
unfollow, the DB followee-index backfill utility and CLI, the
followeeheight /level route, the notification window config, unit,
property, and acceptance coverage, and developer docs.

By coder.
2026-09-18 09:57:42 -07:00
Chris Troutner 3f05488da0 Review topic metadata: harden default-value coverage
- Add DB unit tests covering a summary missing postCount, a post missing
  blockHeight in listTopics/getTopicPostTxids ordering, listTopics' default
  limit/offset, and topicRecencyKey's missing-height fallback. These kill the
  remaining language-mutation survivors in the changed DB sources.
- Refresh the mutate4javascript manifests for the changed sources and commit
  the gherkin-mutator manifests/stamps written into the three topic-metadata
  features.
- No production behavior changes; the review adds tests and tool-written
  metadata only.

By architect.
2026-09-18 06:29:01 -07:00
Chris Troutner dd11964174 Refactor topic metadata: cut CRAP, share test doubles, harden property tests
- Replace the ternary follower delta in recordTopicFollow with
  Number(isActive) - Number(wasActive), dropping its cyclomatic complexity
  from 8 to 6 (CRAP 8.0 -> 6.0). No behavior change.
- Extend the indexer topic property test to conserve lastSeen (newest post
  time) and followerCount (each address's final active follow state)
  alongside postCount/lastHeight, with varying post seen times.
- Extend the DB backfill and topic-query property tests to conserve lastSeen
  and followerCount; generated follows now include unfollows.
- Add a client topic-metadata property test covering relativeTime bucket
  classification, pluralization, monotonicity, and getLastSeenLabel
  consistency, and cover ensureTopicRoom's existing-summary path plus
  recordTopicFollow's legacy-follower-count fallback.
- Share one in-memory DB double per component (indexer test/support/
  memory-db.js, DB test/support/level-double.js) and table-drive the
  parallel topic-follow/topic-message/topic-query cases, clearing every
  dry4javascript candidate in the changed files.

Verification: indexer 113 unit / 12 property / 7 acceptance; db 387 unit /
57 property / 16 acceptance; client 441 unit / 90 property / 33 acceptance
+ build; lint clean. CRAP max 6.0; mutation scans under 100 sites/file.

By refactorer.
2026-09-18 06:02:13 -07:00
Chris Troutner 8da0f09f46 Implement topic metadata columns
Add lastSeen and followerCount to topicSummaries across the indexer, DB,
and client. The indexer now tracks the newest post seen time, updates
follower counts idempotently on follow/unfollow, and preserves post
metadata. The DB exposes lastSeen and followerCount from GET /topics and
rebuilds both from the rooms store in the backfill. The client renders the
four-column topics page with relative-time labels.

By coder.
2026-09-18 05:46:58 -07:00
Chris Troutner 92ce290873 Refactor topic recency indexing: cut CRAP, harden property coverage
- Extract roomFromEntry/applyPost from backfill collectSummaries so its
  cyclomatic complexity drops from 7 to 4 (CRAP 7.0 -> 4.0), and extract
  the shared roomRange key bounds from getTopicPostTxids/listRoomFollowers.
- Add indexer property tests over arbitrary post/follow/unfollow
  interleavings: postCount conservation, newest-height recency shape,
  replay idempotence, and inverted-key ordering.
- Add DB property tests for the topic index backfill: conservation,
  recency shape, stale-record removal, and inverted-key ordering.
- Cover the topic action error paths (invalid push counts, oversized
  topic message), the topic-query room fallbacks, and the client
  openTopic/topicFeedPath navigation.

By refactorer.
2026-09-17 08:07:24 -07:00
Chris Troutner e02bea5460 Implement topic recency ordering and pagination
- Indexer maintains topicSummaries and topicRecency for topic messages and
  follows, idempotently, moving each room's recency record to its newest post.
- DB adds the two index stores and serves GET /topics from them with
  limit/offset pagination, without iterating the rooms store. Adds
  util/room/backfill-topic-indexes.js to build the indexes idempotently.
- Client topics page loads 50 per page with Previous/Next.

By coder.
2026-09-17 07:24:45 -07:00
Chris Troutner 8310b6a1d8 Refactor multi-push helpers and add adapter property tests
Share a single UTF-8 encoding helper across hex, poll-create, and
topic-post instead of repeating new TextEncoder().encode(), drop the
redundant Uint8Array branch in the push normalizer, rename the
txid-action push builder parameter from buildPayload to buildPushes to
match what it returns, and factor the db repair unit-test setup into a
repairedFixture helper.

Add property tests for the multi-push adapter covering prefix/ordering,
field-byte conservation, buffer round trips, array expansion, single
field delegation, and idempotent attach, plus a unit test for the
unsupported-wallet guard.

By refactorer.
2026-09-16 20:56:42 -07:00
Chris Troutner a2229f7dd4 Review txid wire encoding: harden mutation coverage and DRY test doubles
Refresh the mutate4javascript and Gherkin acceptance-mutation manifests,
kill the memo-like/memo-reply boundary survivors, and extract the shared
FakeDb/makeLevel double used by the txid repair unit and property tests.

By architect.
2026-09-16 15:25:23 -07:00
Chris Troutner a76fceee73 Refactor txid wire encoding and repair library
Share the txid-and-text payload builder between hex and reply, extract the
repair store loop from repairTxidEncoding to cut CRAP from 13 to 5, correct
the poll-services property test to the little-endian wire order, and add
property tests for wire round trips and repair idempotence/conservation.

By refactorer.
2026-09-16 14:59:00 -07:00
Chris Troutner dedb7a1343 Fix txid wire encoding and add txid repair utility
Client likes, replies, poll options, and poll votes now embed the
referenced txid in little-endian wire order, matching the indexer's
byte reversal. Add a psf-memo-db repair library and CLI that rewrites
byte-reversed references in likes, postParents, pollOptions, and
pollVotes and rebuilds the postLikes and postChildren indexes, leaving
correct and unknown references untouched and staying idempotent.

By coder.
2026-09-16 14:40:13 -07:00
Chris Troutner 10deb3e9e4 Add recent-feed cap property tests
Cover the total-scan cap and bounded raw scan of
PostQuery.scanRecentPostTxidsAndCount over seeded random corpora: capped
total, newest-first pagination conservation, the raw-scan bound, the 500
default cap, and reply exclusion.

By refactorer.
2026-09-16 08:51:15 -07:00
Chris Troutner c131b91ceb Raise recent-feed total scan cap to 500
GET /posts/recent now caps its total scan at 500 eligible top-level posts
instead of 10, so corpora smaller than the cap report an exact total while
larger corpora stay bounded. Added unit coverage for the exact-total-under-cap
and default-cap cases, plus the many-top-level-posts acceptance fixture.

By coder.
2026-09-16 08:39:16 -07:00
Chris Troutner 1ff2c020ac Review thread-query-bounds: hide postChildren representation, drop dead like scan
Encapsulate the postChildren prefix scan in PostQuery.listChildTxids and have
GetPostThread depend only on the PostQuery interface (listChildTxids,
getPostOrNull). countRepliesForTxids now reuses listChildTxids. Remove the dead
whole-database buildLikeCountMap that this task made obsolete. Cover the
prefix-scan guards and cycle back-edge, realign the thread property tests to the
adapter seam, and refresh mutation/Gherkin-acceptance manifests.

By architect.
2026-09-15 13:10:46 -07:00
Chris Troutner 386fea9162 Add thread query property tests and cover GetPostThread edge cases
By refactorer.
2026-09-15 12:34:23 -07:00
Chris Troutner 3c4de9c59e Implement bounded thread query for /posts/:txid/thread
Scope the thread endpoint to per-thread reads:
- Count likes for only the thread's txids via countLikesForTxids instead of
  the whole-database buildLikeCountMap scan.
- Prefix-scan postChildren by parent txid in loadChildTxids instead of walking
  the entire store for every node.
- Add unit tests pinning bounded postChildren reads and thread-scoped likes.

By coder.
2026-09-15 12:21:54 -07:00
Chris Troutner 2bcc965efb Review feed-query-performance: remove dead full-scan methods, harden mutation coverage
Remove the full-scan methods the capped-scan optimization replaced
(topLevelPostTxids, countTopLevelPosts, countTopLevelPostsByAddr,
buildReplyCountMap) and their tests, keeping the backwards-compat wrappers.
Add hardening tests to kill 4 mutation survivors (raw scan bound, viewerAddr
fallback). Refresh mutation manifests and Gherkin acceptance-mutation stamps.

By architect.
2026-09-04 18:59:27 -07:00
Chris Troutner 65e85c9aef Implement capped-scan feed query performance for /posts/recent
- Replace full-postHeights and full-postChildren scans in ListRecentPosts with
  a single capped scan and per-page reply counting.
- Add PostQuery.scanRecentPostTxidsAndCount with TOTAL_SCAN_CAP=10.
- Update acceptance handlers and fixture for the new feed-query-performance
  spec, and add entry-count wrappers for bounded-read assertions.

By coder.
2026-09-04 17:55:35 -07:00
Chris Troutner 3992395480 Review mute-feed-filtering: fix mute dependency order and follow-key fallback, add helper coverage
Fix Adapters.openDatabases so muteQuery is constructed before postQuery, which
previously resolved an undefined muteQuery at PostQuery construction and silently
disabled recent-feed mute filtering. Fix the follow-notification key fallback to
strip the :<followeePkHash> suffix instead of key.split(':')[0] (which yielded
only the "bitcoincash" network prefix). Add unit tests for the shared muted-posts
helper and the follow key-fallback path.

By architect.
2026-09-04 13:35:02 -07:00
Chris Troutner a2e0b7691b Refactor mute filtering onto shared helper, reduce CRAP, add property coverage
Extract the duplicated _mutedAddrs lookup and _isMutedPost check into a shared
lib helper (loadMutedAddrs / isMutedPost) used by the post, topic, search, and
notifications adapters. Extract follow/like notification predicates in
NotificationsQuery to bring CRAP down to 6. Add a mute-filtering conservation,
exclusion, and pagination property test for topic queries.

By refactorer.
2026-09-04 12:34:35 -07:00
Chris Troutner 448bddc31f Implement mute feed filtering across recent, topic, search, and notifications feeds
By coder.
2026-09-04 12:23:00 -07:00
Chris Troutner 4eea327f60 Reduce CRAP and DRY in psf-memo-db
- Add tests for profile/level handleError to raise coverage
- Extract DbBackup pure helpers into testable db-backup-util module
- Add tests for DbBackup zip/unzip and the pure helpers
- Extract shared handleControllerError helper across 7 REST controllers

By refactorer.
2026-09-04 07:52:19 -07:00
Chris Troutner 5bb2906d92 Review topics-most-recent-order: hide lastHeight ordering key, harden height-0 cases
By architect.
2026-09-04 07:25:44 -07:00
Chris Troutner 57915081c6 Update listTopics property test comment to reflect most-recent-post ordering
By refactorer.
2026-09-04 06:59:50 -07:00
Chris Troutner f45f2f9e2a Order GET /topics by most recent post
By coder.
2026-09-04 06:43:28 -07:00
Chris Troutner 2704c2059d Add notifications-query property tests for ordering and pagination
Pin the NotificationsQuery aggregation invariants over broad random
inputs: newest-first ordering with seen tie-break, global ordering, and
pagination conservation. Coverage, CRAP (max 6.0), DRY, and mutation
scan were assessed; all changed/new source files are well under the
100-site split threshold and mutation manifests are preserved.

By refactorer.
2026-09-03 16:04:36 -07:00
Chris Troutner 87ca402cae Review notifications refactor: extract shared getPostOrNull, add adapter unit suite
Extract the post-lookup-with-null helper into a shared module used by both
the notifications and post query adapters, removing duplication. Add a
dedicated NotificationsQuery unit suite (26 tests) and hardening tests that
kill the sort-comparator and followee-guard mutations. Refresh mutation
manifests via the approved tool. Document 5 intrinsic mutation equivalents
and 16 soft-Gherkin equivalents in the review summary.

By architect.
2026-09-03 14:10:54 -07:00
Chris Troutner 8ee07fc35f Implement Notifications read-only feature
Add psf-memo-db notifications query, use case, route, and tests.
Add psf-memo-client NotificationsPage service, React page, nav link,
route, and tests. Wire acceptance handlers for notifications.

By coder.
2026-09-02 12:53:02 -07:00
Chris Troutner 4bf99e7db0 Review and harden following feed: kill survivors, DRY parseRequiredString
- Extract parseRequiredString into pagination.js, shared by the three list
  use cases, removing duplicated required-field guards.
- Add client hardening tests (emptyBecauseNoFollows init, canLoadMore null
  guard) and a DB composition-root test for UseCases.start.
- Refresh mutation manifests and Gherkin acceptance-mutation stamp.

By architect.
2026-09-02 12:20:37 -07:00
Chris Troutner d606ee6608 Refactor following feed: reduce CRAP/DRY, add property coverage
- Extract shared runUseCase and listPostsForAddr in the posts controller to
  remove the duplicated try/catch error wrapper (CRAP 14.7 -> 4.0) and DRY
  the addr-scoped handlers.
- Add error-path unit tests for the posts controller covering both branches
  of handleError.
- Consolidate ListFollowingFeed onto the shared ListUseCase constructor to
  remove constructor duplication across list use cases.
- Add property tests for scanFollowingFeedTxidsAndCount (ordering, reply/
  viewer/membership exclusion, pagination conservation) and for the client
  FollowingFeedPage.load state invariants.

By refactorer.
2026-08-29 10:44:28 -07:00
Chris Troutner 6d1bd148e9 Implement Following feed
Add psf-memo-db endpoint GET /posts/following/:addr that returns top-level
posts from followed profiles, excluding replies and the viewer's own posts,
newest first and paginated. Add PostQuery.scanFollowingFeedTxidsAndCount,
ListFollowingFeed use case, and REST controller/router wiring.

Add psf-memo-client Following feed page, route /posts/following, nav link,
MemoDb.getFollowingFeed, and FollowingFeedPage service. Update acceptance
handlers for the new following-feed.feature.

By coder.
2026-08-29 10:30:54 -07:00
Chris Troutner e8bf42a1f3 Review and harden search: kill mutation survivors, refresh manifests
Add hardening tests to kill mutation survivors in SearchQuery guards and
profileMatches defaults, SearchAll pagination/empty-query guards, the search
REST controller 500-error path, and the client SearchPage constructor/getPost/
getProfile. Refresh mutation manifests and the Gherkin acceptance-mutation
stamp.

By architect.
2026-08-29 08:51:42 -07:00
Chris Troutner d3e3b23bbe Refactor search: reduce CRAP, DRY, add property coverage
- Split SearchQuery.searchProfiles/searchPosts into small helpers to bring
  CRAP to 5.0 or below across the search feature.
- Extract shared normalizeQuery/sortByHeightDesc (src/lib/search.js) and
  loadReplyTxids (src/adapters/lib/load-reply-txids.js) to remove duplication
  with search-all, list-recent-profiles, and post-query.
- Add error-path unit tests for the search controller.
- Add property tests for SearchQuery and SearchAll (pagination, ordering,
  case-insensitivity, reply exclusion, substring containment).
- Refresh mutation manifests for post-query and list-recent-profiles.

By refactorer.
2026-08-29 07:35:26 -07:00
Chris Troutner 8508fdaff0 Implement Search feature (P6.4)
- Add psf-memo-db /search endpoint with SearchQuery adapter, SearchAll use case,
  and REST controller/router.
- Add psf-memo-client SearchPage service, Search component, route, and nav link.
- Wire search support into the acceptance handler fake MemoDb.
- Add unit tests for the new adapter, use case, controller, and page service.

By coder.
2026-08-29 07:18:39 -07:00
Chris Troutner ac468072df Review and harden mute-user: kill mutation survivors, refresh manifests
Add hardening tests to kill mutation survivors in MemoMute.validate,
ProfilePage constructor/_loadState/canMute, MuteQuery._nextString,
MuteState validation guards, and the mute REST controller statused-error
path. Refresh mutation manifests and Gherkin acceptance-mutation stamps.

By architect.
2026-08-28 21:08:50 -07:00
Chris Troutner d8ab5bc79c Refactor mute-user: reduce DRY, add property coverage
Extract shared _getState/_getList helpers in the client MemoDb, a shared
_loadState/_setState base in ProfilePage, and a runAction helper for the
profile follow/mute handlers. Add property tests for the MemoMute service,
the DB MuteQuery adapter, and the indexer handleMute action. Preserve
behavior; all unit, acceptance, and property suites pass across client, DB,
and indexer.

By refactorer.
2026-08-28 19:58:39 -07:00
Chris Troutner 4b03210e85 Implement mute/unmute user feature (0x6d16/0x6d17)
- Client: MemoMute service, ProfilePage mute controls, Profile UI Mute/Unmute buttons,
  Profiles mute state, MemoDb mute read APIs, unit/acceptance tests.
- DB: mutes LevelDB store, MuteQuery adapter, /mute REST routes, MuteState and ListMuted use cases, tests.
- Indexer: mute/unmute action codes, handleMute action handler, muteDb adapter, tests.
- Backlog: mark P4.1/P4.2 mute/unmute as shipped.

By coder.
2026-08-28 19:38:42 -07:00
Chris Troutner 06e95150e3 Review and harden poll actions: kill mutation survivors, reduce DRY
- Add hardening tests to kill mutation survivors across client, DB, and
  indexer (hex guards, poll byte-limit boundaries, busy-flag defaults,
  parseTxid empty-string, polls controller handleError, memo-codes
  isMemoPrefix, normalizePollCreateDatas, storePollChildRecord returns).
- Reduce duplication: extract shared reflect/isTooLong into MemoTxidAction
  (client), shared _run into the polls controller and configurable execute
  into PollReadUseCase (DB).
- Refresh mutation manifests and Gherkin acceptance-mutation stamps.

By architect.
2026-08-28 17:51:03 -07:00
Chris Troutner 739b13bfb2 Refactor poll actions: reduce CRAP/DRY, add property coverage
Extract shared txid-action and poll-page bases in the client, a shared
poll-read use-case base and a poll-txid iteration helper in the DB, and a
shared poll-child record helper in the indexer. Reduce normalizePollCreateDatas
complexity (CRAP 7.1 -> 5.0). Add property tests for poll services, handlers,
and query filtering. Preserve behavior; all unit, acceptance, and property
suites pass across client, DB, and indexer.

By refactorer.
2026-08-28 15:23:04 -07:00
Chris Troutner cf05de6630 Implement poll actions (create, option, vote) across client, indexer, and DB
- Client: add MemoPollCreate, MemoPollOption, MemoPollVote services and page controllers.
- Indexer: add create-poll, add-poll-option, and poll-vote handlers with new poll DB adapters.
- DB: add polls/pollOptions/pollVotes stores, PollQuery adapter, GET /polls/:txid endpoints, and use cases.
- Add Gherkin acceptance handlers and focused unit tests for all three components.

By coder.
2026-08-28 14:50:05 -07:00
Chris Troutner 5183359143 Review and harden topic actions: kill mutation survivors
- Add hardening tests for topic-post-page, profiles, topic-feed-page, and
  topic-query to kill mutation survivors.
- Refresh mutation manifests and Gherkin acceptance-mutation stamps.
- Add topic-actions review summary.

By architect.
2026-08-28 13:13:01 -07:00
Chris Troutner d6f56780be Refactor topic actions: reduce CRAP/DRY, add property coverage, complete acceptance
- Add unit coverage for Profiles topic follow state to bring CRAP to 4.
- DRY Profiles set/get follow state via shared nested-map helpers.
- Add TopicQuery topic-follow read-side property tests and MemoTopicPost
  byte-accounting property tests.
- Complete client and DB acceptance step handlers and mocks for the
  topic-follow and topic-post features.

By refactorer.
2026-08-28 11:13:03 -07:00