mirror of
https://github.com/Permissionless-Software-Foundation/psf-memo.git
synced 2026-09-21 16:52:01 -07:00
Add the client verification record (git_sha bbbf4adcd7, result pass) and the
architect review summary for the notification-entry-display task.
By architect.
8.7 KiB
8.7 KiB
Notification Entry Display — Architect Review
By architect.
Task and commits reviewed
- Task:
notification-entry-display(refactorer handoffmerge_and_process refactorer 7dfc315840). - Merged
swarmforge-refactorer(fast-forward froma1eb548). Commits:f4d9499Add notification entry display specification (specifier)521d434Implement notification entry display (coder)7dfc315Refactor notification entry display: share acceptance assertions, add property tests (refactorer)
- Architect review commit:
bbbf4adcd7(SPA link wiring + test hardening + tool-written mutation/acceptance manifests). - Verification record
git_sha:bbbf4adcd7(client). - Records/summary commit: this commit.
The feature renders each Notifications entry with its actor's resolved Memo
display name and avatar, links both to the actor's profile, shows the full
address as small plain text, and offers a "View Post" link (opening the
referenced post's thread) for like and reply notifications only. It falls back
to the truncated address and an identicon when the actor has no name/avatar or
the profile lookup fails. Client-only. Touches psf-memo-client only.
Architectural findings
- UI/Core separation (good). The decision logic lives in a pure view-model
service (
src/services/notification-entry.js:profilePath,displayName,notificationMessage,buildNotificationEntry) and a pure controller (src/services/notifications-page.js) with injectedmemoDb/wallet. The ReactNotificationEntrycomponent is presentational plainReact.createElement, so the browser JSX build and the Node acceptance adapter (acceptance/lib/render-notification-entry.js) share one module and the whole feature is testable without a DOM. - Dependency direction (good). Component → service; service →
util; controller → view-model service.MemoDb(IO) and the wallet are injected, never imported by the controller, so core behavior runs without IO. - Information hiding (good). Profile-lookup failure is contained in
NotificationsPage._loadProfile(returns an empty profile), and_loadProfileFieldguards clients that do not exposegetName/getProfilePic, so a partial/failing DB cannot abort the page. Only the view fields the page needs are exposed; the raw profile records stay inside the controller. - Local code quality (one fix). The
NotificationEntrycomponent defined an optionalonProfileClickseam andhandleProfileClick, but the productionNotificationswrapper never passed it. Profile links therefore rendered as bare anchors and triggered a full-page navigation, which breaks on theBrowserRouter/GitHub Pages deployment (no404.htmlfallback) and is inconsistent with theLink-based profile links used everywhere else. Fixed in the wrapper (see below). - Mutation coverage gap (fixed). The new renderer component had 3 mutation
sites with 0 covered, because only the acceptance suite rendered it and
the mutation baseline is
npm test(unit). No unit test rendered the component. Added one so the renderer's sites are covered, matching the existingpost-content/like-resultcomponent-unit-test convention. - Accepted as-is (refactorer changes). The refactorer's
resolveTextnow resolves<placeholder>values inside quoted step arguments viaresolveParam, and the "thread modal opens" handler resolves its txid parameter; both are generic acceptance-harness improvements. The full acceptance suite (35 feature suites) passes with them. - Observation, not changed — profile-path constant duplication. The new
service defines
PROFILE_PATH_PREFIX/profilePath, whilesrc/services/profile-page.jsalready exportsPROFILE_PATH_PREFIXand several components inline`/profile/${encodeURIComponent(addr)}`. Consolidating into a sharedprofile-pathmodule is a cross-module refactor beyond this handoff; the new helper is a local step toward DRY anddry4javascriptreported no duplicate candidates in the changed set. Documented for a future client-consistency task.
Fixes applied
- Wired the SPA profile-link seam.
Notificationsnow callsuseNavigate()and passesonProfileClick={navigate}to everyNotificationEntry, so the avatar and display-name links navigate via the router (event.preventDefault()already runs inhandleProfileClick) instead of doing a full page reload. The acceptance adapter still renders the component without a router and keeps thehref, so acceptance assertions are unchanged. - Covered the renderer's mutation sites. Added
test/unit/notification-entry-component.test.js(8 tests,react-dom/serverstatic-render): null entry, display name/address/message, avatar<img>, identicon fallback, profile links, View Post present/absent, and empty-message fallback. This moved the component's 3 sites from uncovered to covered and killed all 3. - Killed the controller survivor. Added a fresh-state unit test to
test/unit/notifications-page.test.jsasserting the constructor defaults (empty === false,pagination === null, emptynotifications/profiles), killing thethis.empty = false -> trueconstructor mutant. - No other production changes were needed. Module structure, dependency direction, and information hiding were accepted.
Verification results
Record (pinned to the review commit bbbf4adcd7):
| File | Component |
|---|---|
docs/reviews/notification-entry-display-verification.json |
psf-memo-client |
- Language mutation (
mutate4javascript,--max-workers 8; the wrapper detected differential under-selection onnotifications-page.jsand the component and reran them with--mutate-all):src/services/notification-entry.js: 3 killed, 0 survived, 0 uncovered.src/services/notifications-page.js: 14 killed, 0 survived, 0 uncovered (first differential run 10 killed / 1 survived / 11 of 14 selected → rerun all 14; the survivor was the constructorempty=falsedefault, killed by the added fresh-state test).src/components/app-body/notifications/notification-entry.js: 3 killed, 0 survived, 0 uncovered (first run 0 killed / 0 survived / 3 uncovered → all covered and killed after adding the component unit test).- Total: 20 killed, 0 survived, 0 uncovered.
src/services/memo-db.jswas not mutated: the client HTTP adapter is a standing mutation exclusion (ESM +../configdirectory import).
- DRY (
dry4javascript, scoped to the changed production files, the acceptance adapter, and the new/changed test files): No duplicate candidates found. - Cyclomatic complexity / CRAP (
crap4javascript): max CRAP 6.0 (NotificationsPage.load, CC 6, 100% coverage);notificationMessageCC 5,NotificationEntryCC 4 at 87% (event-handler bodies are not invoked by server rendering), every other changed function CC ≤ 2 at 100%. All functions are at or below the threshold; no refactor was warranted. - Soft Gherkin acceptance mutation (
gherkin-mutator --level soft --workers 8),psf-memo-client/specs/notification-entry-display.feature: 36 total, 11 killed, 25 survived, 0 errors. All 25 survivors are single-character case mutations of example values (addr,name,avatar,my_post,reply_text,follower) used consistently on both the Given setup and the Then assertion side, so the mutated value still matches — the documented intrinsic-equivalent class (specifier gotcha #12). None is an implementation gap and none was chased. The structurally independent assertions were killed: scenario 2 (avatar/name → independentprofile_path) and the truncated-address-name scenarios 6 and 8 account for all 11 kills, so the profile-link encoding and address-truncation behavior are genuinely pinned.gherkin-mutatorwrote a manifest containing only scenario 2 (4 mutations, 4 killed) because every other scenario has an intrinsic survivor; committed as tool-written.
Suite status
verify.sh client result: pass at git_sha bbbf4adcd7:
- unit: 470 pass / 0 fail
- property: 101 pass / 0 fail
- acceptance: all 35 generated suites passed (including all 16 notification-entry-display examples)
- lint: ok
- build: ok
Handoffs
- End-of-chain
git_handofftospecifier(priority 50) to mergeswarmforge-architectintomaster, using this records/summary commit. git_handofftocoderandrefactorer(priority 00) with the review commit for follow-up review: the production change is the smalluseNavigateprofile-link wiring in the Notifications wrapper; the rest is test hardening and tool-written manifests. No further production change.