Deduplicate the notification-entry acceptance step handlers behind shared
entry-assert helpers and collapse the repeated ready-state guards in the
notifications view. Add property tests for the entry view model (profile link
round trip, name fallback, view-model derivation, message mapping) and for
notifications page profile resolution, entry derivation, and actor lookup.
CRAP stays at or below 6 and every changed source module stays below 100
mutation sites; mutation manifests are left intact for differential runs.
By refactorer.
Name each Notifications entry with the actor's Memo display name and
avatar resolved client-side, link both to the actor's profile, show the
full address as plain text, and add a View Post link for like and reply
notifications that opens the referenced post thread. Fall back to the
truncated address and an identicon when a profile is missing or its
lookup fails.
By coder.
- No production or test changes were required: buildTopicsTable passes
language mutation 2/2, dry4javascript reports no candidates, CRAP is 1.0 at
100% coverage, and soft Gherkin mutation kills all 16 mutations.
- Commit the tool-written mutate4javascript manifest and the gherkin-mutator
stamp/manifest only.
By architect.
- 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.
Require the topics page to render a react-bootstrap Table with aligned
columns, a header row, linked rows, and horizontal scroll on narrow screens.
By specifier.
- 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.
- 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.
Define four-column topics page behavior across client, indexer, and DB:
topic name, time since most recent post, post count, and follower count.
By specifier.
Architectural review of the topic recency indexes and pagination:
- Indexer topic index maintenance lives in its own topic-indexing.js,
separate from the action handlers; the backfill library takes injected
LevelDB handles so its logic stays testable.
- DB listTopics reads counts from topicSummaries and paginates the
topicRecency index without iterating the rooms store; the client reuses
PaginatedPage.
- Kill four topic-indexing mutation survivors with focused unit tests
covering the height fallbacks, plus exact-key/isNotFound and max-size
boundary tests.
- One topic-message survivor is an intrinsic equivalent: handlePost reads
the text at push index 1 and ignores push index 0.
Includes tool-written mutate4javascript and acceptance-mutation manifests.
By architect.
- 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.
Add Gherkin for efficient GET /topics ordering by most recent post, the
topicSummaries and topicRecency index stores, an idempotent backfill, and
client topics-page pagination.
By specifier.
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.
Add Gherkin pinning that reply, topic message, add-poll-option, poll
vote, and create-poll broadcast each protocol field as its own OP_RETURN
push, so the indexer and memo.cash can parse them.
By specifier.
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 client Gherkin pinning little-endian wire order for every action that
embeds a parent txid (like, reply, poll option, poll vote), and a
psf-memo-db Gherkin for a utility that repairs existing byte-reversed
references and rebuilds the affected indexes.
By specifier.
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.
Add Gherkin for inline image rendering in posts: extension-based image URL
detection (.jpg/.jpeg/.png/.gif/.webp/.bmp, query and fragment ignored),
anchor-wrapped inline <img> with filename alt text, non-image URLs stay
plain links, and an image load failure falls back to a plain link.
By specifier.
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.
Add Gherkin acceptance spec for auto-linking http(s) URLs and bare domains
in post text, opening in a new tab, while preserving YouTube embeds.
By specifier.
Every acceptance run re-parsed and regenerated all features even when only
one changed. Record feature_hash and aps_commit in the per-feature metadata
and skip parse/generate when both match, and drop generated tests for
features that no longer exist.
By specifier.
There were up to four APS copies (tmp/aps, tmp/aps-spec, tmp/aps-fresh, and a
component-local client copy) and the briefing, wrappers, and runners disagreed
on which to use. Add ensure-aps.sh (ensure/update/reclone) as the one canonical
tmp/aps checkout, point the gherkin-parser wrapper, architect startup, and all
three acceptance runners at it, and update the architect cheat-sheet.
By specifier.