- Add client property tests for buildTopicsTable: one row per topic in input
order, four-cell alignment with the header order, link percent-encoding
round trips, and post/follower count conservation over random topic lists
that also omit count/last-seen fields.
- No production change: the coder's view model already scans at CRAP 1.0
with zero dry4javascript candidates and only 2 mutation sites. The JSX
Topics component stays a thin, tool-excluded shell over the testable model.
Verification: client 447 unit / 94 property / 34 acceptance + lint + build;
indexer 113 unit / 12 property / 7 acceptance + lint; db 392 unit / 57
property / 16 acceptance + lint.
By refactorer.
Replace the topics page flex list with a react-bootstrap Table driven by a
testable topics-table view model: a labeled header row, aligned cells for
name, relative time, post count, and follower count, feed links on each row,
and a horizontal-scroll wrapper for narrow screens.
By coder.
- 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.
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.
- 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.
- 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.
Import the browser-safe buffer module in the multi-push adapter so the
CRA production bundle no longer reads the Node global Buffer, add
regression tests for the falsy-wallet and idempotent-attach paths, share
the script-encoding test helper, and de-duplicate the UTF-8 push
acceptance assertions. Refresh the language mutation manifests and the
soft acceptance-mutation manifest.
By architect.
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.
Reply, topic message, add-poll-option, poll-vote, and create-poll now
broadcast each protocol field as its own OP_RETURN script push instead of
combining them into one push. The indexer requires three pushes for reply,
topic message, and poll children, and accepts four for create-poll; the
combined form was rejected or misparsed.
hex.js builds the txid and text as separate pushes, and a new
memo-multipush adapter teaches minimal-slp-wallet's single-push sendOpReturn
to expand an array of fields into separate pushes. Acceptance step handlers
assert the individual pushes, and unit/property tests cover the new shape.
By coder.
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.
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.
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.
Add unit coverage for the LikeTipPage initial state, deps seeding, the
dust-limit boundary, and empty/null tip parsing, and assert exact
validation/broadcast error messages. Add the NewPostPage initial
result-modal state assertion. Refresh the mutate4javascript footer
manifests for the touched services and the soft Gherkin
acceptance-mutation manifest for the like-result feature.
By architect.
Deduplicate the new like-result acceptance steps behind one render helper,
and extract the bch.loping.net transaction link shared by the New Post result
modal, the post options menu, and the like/tip result into a block-explorer
service. Cover the LikeTipPage open/parse/handler failure branches and add
property tests for the explorer URL, the LikeResult markup, and the result
modal state machine.
CRAP is at or below 5 with full coverage on the changed services; every
changed/new plain source file scans at 21 or fewer mutation sites. The
existing mutation manifests are left for the mutation tool to refresh.
By refactorer.
Keep the like/tip modal open after a successful like and show a broadcast
result instead of closing: the success message, the like transaction id, and
a link to that transaction on bch.loping.net that opens in a new tab. The
like count and filled heart update immediately, and dismissing the result
closes the modal. The result state lives on the testable LikeTipPage
controller; a server-renderable LikeResult component backs the modal and the
acceptance adapter, with acceptance step handlers for the new wording.
By coder.
Move the outside-click decision into the pure post-options service so the
effect stays a thin adapter and the previously uncovered `&&` mutation is
unit testable. Add unit tests for the outside-click predicate and for the
default no-focus open menu, killing the two surviving `-1` default-focus
mutations.
Stamp the mutate4javascript manifests for both modules and the soft
acceptance-mutation manifest for post-options-menu.
By architect.
Move the keyboard shortcut mapping into the pure post-options service so the
React component is a thin adapter and the Escape/ArrowDown decisions are unit
and property testable. DRY the duplicated post-options acceptance steps behind
two helpers, and add property tests for the explorer URL, menu-item shape,
state transitions, key command, and rendered markup.
The createElement post-options component and service stay scannable by
mutate4javascript; the JSX call sites (post-feed-item, profile) are unchanged
except for rendering the shared menu.
By refactorer.
Add a shared PostOptionsMenu component, used by the feed/thread post card and
the profile post card, with a pure post-options service for the block explorer
link and the open/close/focus state. Its first item links to the post
transaction on bch.loping.net in a new tab; the menu opens on the button and
closes on the button again, an outside click, or Escape, with ArrowDown moving
focus to the first item.
By coder.
Merged the refactorer handoff (e77e174). The architecture holds: the image
helpers isImageUrl/imageAltText are pure post-links functions, failed-images
is a pure state transition, and PostImage/PostContent stay presentational
with no IO. Added a unit test for isImageUrl non-string input to kill the
last surviving language mutant; mutation and acceptance manifests are
tool-written.
By architect.
Extract the pure failed-image set transition into a testable service,
DRY the acceptance no-link/no-image assertions, and add property tests
for image URL detection, alt text, and image rendering.
By refactorer.
Recognize common image file extensions in post URLs (query string and
fragment ignored), render them as inline images inside new-tab anchors
with the filename as alt text, and fall back to a plain link when the
image fails to load. Non-image URLs keep the existing plain-link
behavior.
By coder.
Harden the post link parser after the refactorer handoff:
- add start-of-text and leading-token boundary unit tests and drop a
redundant range guard, killing the isBareDomainBoundary survivors
- replace the mutable React key counter with React.Children.toArray and
assert the embedded iframe is fullscreen, killing both PostContent
mutation survivors
- record the client verification pass and the review summary
By architect.
Render http(s) URLs and bare domains in post text as anchors that open in a
new tab. Explicit URLs keep their scheme; bare domains get an https href while
their visible text stays as written. Embeddable YouTube links keep their
embedded-player behavior.
By coder.
Replaces the Node-only Buffer global in memo-follow and memo-mute with
the existing hexToBytes helper, which returns a Uint8Array. Updates unit
and property tests to assert bytes without requiring Buffer in the
production source, and adds an acceptance handler for the new binary
payload wording so the Binary Payload Broadcast regression spec passes.
By coder.
Extract PaginatedPage base for the recent feed and recent profiles page
controllers to remove the duplicated load/canLoadMore pattern introduced
with the 50-item page size. Refresh mutation manifests via the tool. Add
property tests for the base pagination invariants (canLoadMore mirrors
hasMore, load forwards limit/offset, item lookup).
By refactorer.
Add unit tests for invalid video id and non-/watch youtube.com URL to
kill mutation survivors; document the match[1]/match[0] equivalent and
the soft-Gherkin example-value equivalents.
By architect.
Pin the profile wallet-address selector invariants over broad input
space: reactive-state precedence, wallet fallback, and null-on-absent.
CRAP (3.0), DRY, and mutation scan assessed; no split needed and no
manifests discarded.
By refactorer.
Use the reactive bchWalletState.cashAddress as the viewer address source
and include it in the profile load effect dependencies. Extract a small
wallet-address selector so the behavior is unit-testable.
By coder.
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.
- 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.
- 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.
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.
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.
- 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.
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.
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.
- 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.