7.0 KiB
Following Feed Cap — Architect Review
By architect.
Task and commits reviewed
- Task:
following-feed-cap(refactorer handoffmerge_and_process refactorer 5653ecef9d, delivered as a one-itemBATCH). - Merged
swarmforge-refactorer(fast-forward fromeaaed8e). Commits:6462977Record mute broadcast result completion in backlog and briefing (specifier; the prior task's completion record, carried in by the fast-forward and outside this task)2ce0a62Specify following feed capped scan (specifier)db21694Implement following feed capped scan (coder)5653eceRefactor following feed cap: share acceptance seeding, add cap properties (refactorer)
- Architect review commit:
b7c20560c2(dead reply-txid loader removal + tool-written mutation and acceptance manifests). - Verification record
git_sha:b7c20560c2(db). - Records/summary commit: this commit.
The feature bounds GET /posts/following/:addr: the old scan iterated the
entire global postHeights index to compute pagination.total (live total
10942) and first built a global reply-txid set by iterating all of
postParents. The new scan uses per-candidate isReply point lookups against
postParents and stops after offset + limit + 500 eligible followed posts,
reporting total = min(eligible, 500) so the first pages stay bounded while
hasMore still works. The cap counts eligible followed posts, not raw index
entries (followed posts are sparse). DB-only; the client renders
pagination.total unchanged. Touches psf-memo-db only.
Architectural review
- UI/Core separation (good). The change lives in the DB component: the
PostQueryadapter (LevelDB access) and theListFollowingFeeduse case. There is no UI/IO boundary crossed; the use case is testable with an injected adapter. - Dependency rule (good).
ListFollowingFeeddepends on the adapter through its injectedpostQuery/followQueryinterfaces; the adapter is the low-level module and does not reach back into the use case. Dependency direction is unchanged by this task. - Information hiding and encapsulation (good). The adapter exposes only
scanFollowingFeedTxidsAndCountreturning{ txids, total }; reply detection and followee membership are encapsulated inisReplyandisFolloweePost. The cap semantics are documented on the method. The acceptance runner observes store iteration through a wrapped iterator rather than reaching into adapter internals. - Local code quality (one fix). Switching from the global reply-txid set to
per-candidate lookups left
PostQuery.loadReplyTxids(and itsload-reply-txids.jsimport) with no callers; it was the only uncovered code inpost-query.js(lines 98–99 at 99.39%). Removed (see Fixes). - Duplication (documented, not changed). The capped-scan scaffold in
scanFollowingFeedTxidsAndCountnow resemblesscanRecentPostTxidsAndCount, but the two differ deliberately: the recent feed caps on raw index entries while the following feed caps on eligible followed posts, and their eligibility predicates differ (isEligibleRecentPostapplies mute filtering,isFolloweePostapplies follow membership). The recent-feed cap is pre-existing and outside this task, so no shared extraction was forced; DRY reports no candidate in the changed production file.
Fixes applied
- Removed the dead
PostQuery.loadReplyTxidsmethod and its now-unusedload-reply-txids.jsimport. Nothing called it after the capped scan moved toisReplypoint lookups (search-query.jsimports the shared lib directly). This removed the last uncovered lines in the module and its mutation-manifest entry; the mutation tool regenerated the manifest.
No other production changes were needed. Module structure, dependency direction, and information hiding were accepted.
Verification results
Record (pinned to the review commit b7c20560c2):
| File | Component |
|---|---|
docs/reviews/following-feed-cap-verification.json |
psf-memo-db |
- Language mutation (
mutate4javascript src/adapters/post-query.js --mutate-all --max-workers 8; the file removed a function, so all sites were forced to avoid differential under-selection): 40 killed, 0 survived, 0 uncovered (40 total sites, 11 reported changed by--scan). - DRY (
dry4javascript, scoped to the changed production file, the acceptance handlers, and the changed unit/property tests): no duplicate candidate involvessrc/adapters/post-query.jsor the new property tests. The 115 reported candidates are all pre-existing boilerplate:acceptance/lib/handlers.jsstep-handler bodies (therequest ... with limit ... and offset .../... store was not iteratedpatterns) andtest/unit/adapters/post-query.unit.jsrepeated test setup. Both classes pre-date the task and span unrelated suites, so they were left as-is per the standing precedent. - Cyclomatic complexity / CRAP (
crap4javascript src/adapters/post-query.js): max CRAP 6.0 at 100% coverage (scanFollowingFeedTxidsAndCountCC 6,scanRecentPostTxidsAndCountCC 6,likeTxidFromPostLike/scanPostsByAddrTxidsAndCountCC 5,isReply/listChildTxids/countLikesForTxidsCC 4);isFolloweePostCC 3, every other function CC ≤ 3. All functions are below the 8.0 threshold. - Soft Gherkin acceptance mutation (
gherkin-mutator --level soft --workers 8),psf-memo-db/specs/following-feed-performance.feature: 17 total, 14 killed, 3 survived, 0 errors. The three survivors are intrinsic equivalents caused by the small fixtures, not implementation gaps:m9scenario 1 example 2limit: 50 -> 43— only 11 posts remain afteroffset: 499, so both limits return the same 11 expected txids.m10scenario 1 example 2max_entries: 510 -> 512— the corpus (510) is below the cap, so the scan reads all 510; the assertion is "at most", a performance ceiling rather than an exact count, so 512 still holds.m15scenario 2 example 1limit: 10 -> 7— the mixed fixture has only two eligible followed posts, so both limits return both.gherkin-mutatorwrote anacceptance-mutation-manifestwith"scenarios":[](each scenario has an equivalent survivor, so none is recorded) and no# mutation-stamp; committed as tool-written.
Suite status
verify.sh db result: pass at git_sha b7c20560c2:
- unit: 405 passing
- property: 60 pass / 0 fail
- acceptance: all 19 generated suites passed (including both following-feed-performance scenarios/example sets)
- lint: ok
Handoffs
- End-of-chain
git_handofftospecifier(priority 50) to mergeswarmforge-architectintomaster, using this records/summary commit. git_handofftocoderandrefactorer(priority 00) with the review commitb7c20560c2for follow-up review: the only production change is the removal of the deadPostQuery.loadReplyTxids; the rest is tool-written mutation and acceptance manifests.