Files
psf-memo/docs/reviews/post-image-rendering-summary.md
T
Chris Troutner c2bfbc3cfb Record post-image-rendering review and verification
Commit the review summary and the client verification record (git_sha
19e57be, the review commit) alongside the review. All five client commands
pass.

By architect.
2026-09-16 07:40:15 -07:00

5.9 KiB

Review summary: post-image-rendering

Architect review of the refactorer handoff for task post-image-rendering.

Commits reviewed

  • 53d7439 (specifier): Spec post image rendering (psf-memo-client/specs/post-image-rendering.feature).
  • 3971e07 (coder): Render image URLs inline in posts — pure isImageUrl/imageAltText helpers in src/services/post-links.js, a PostImage presentational component plus failed-image state in src/components/post-feed/post-content.js, acceptance handlers/adapter, and unit tests.
  • e77e174 (refactorer): Refactor post image rendering and cover image helpers — extracted the pure addFailedImage set transition into src/services/failed-images.js, DRY'd the acceptance no-link/no-image assertions through assertFeedHasNoElement, added property tests for image detection/alt text/rendering.

Merged onto the architect worktree (swarmforge-architect) by fast-forwarding to e77e174.

Architect review commit: 19e57be (code + manifests + unit test). This summary and the verification record are committed directly on top of it, so git diff 19e57be HEAD touches only docs/. The record's git_sha is 19e57be, the commit that contains the review source changes.

Architectural findings and fixes applied

The structure is sound and matches the codebase's layered layout. UI/Core separation, the dependency rule, and information hiding all hold:

  • UI/Core separation: parsePostLinks, isImageUrl, and imageAltText are pure text/URL functions with no React or network dependency, and addFailedImage is a pure Set transition free of React and the DOM. All four are directly unit- and property-testable. PostImage/PostContent are presentational (React.createElement, no JSX, no IO), so the same module drives the browser build and the Node acceptance adapter.
  • Dependency rule: the component depends inward on the pure services (post-links, failed-images, youtube-embed); none of those services depends outward on the component, the DOM, or the acceptance layer.
  • Information hiding: the services expose only parsePostLinks/isImageUrl/imageAltText/addFailedImage; PostImage hides the anchor/img markup and the fallback behavior; the Set representation of failed images stays inside PostContent/failed-images. The acceptance seam is the initialFailedImages prop, the minimal synchronous injection needed because static server rendering cannot emit image error events.
  • Local quality: the PostImage fallback anchor repeats the plain-link anchor attributes. dry4javascript reports no candidate (below its structural threshold), so extracting a shared PostLink component would add indirection without measured duplication gain; left as-is.

One issue left by the handoff was fixed:

  1. Unkilled language mutant (edge cases). mutate4javascript left one survivor in isImageUrl at line 83 (if (typeof url !== 'string') return false; false -> true). The non-string guard was never exercised. Added isImageUrl rejects non-string input (null, undefined, 12345, {}) to test/unit/post-links.test.js, which kills it.

Tool-written artifacts committed with the review: the embedded mutate4javascript manifests in post-links.js/post-content.js (now track isImageUrl, imageAltText, and PostImage) and the gherkin-mutator acceptance-mutation stamp at the top of post-image-rendering.feature (scenarios 1 and 3 fully killed; scenario 2 withheld because of the survivor below).

Verification results

Language mutation (mutate4javascript, --max-workers 8)

  • src/services/post-links.js: 20 killed, 0 survived, 0 uncovered after the fix (1 survivor before).
  • src/components/post-feed/post-content.js: 2 killed, 0 survived, 0 uncovered.
  • src/services/failed-images.js: 0 mutation sites (no supported operators), 0 uncovered.

DRY (dry4javascript)

  • Changed files (post-links.js, failed-images.js, post-content.js): no duplicate candidates.
  • acceptance/lib/handlers.js: no new candidates involving the added handlers or assertFeedHasNoElement; the pre-existing route/controller boilerplate duplicates are untouched.

CRAP / cyclomatic complexity (crap4javascript)

All changed functions are below the 8.0 threshold: PostContent (CC 6, 97.1% covered, CRAP 6.0), parsePostLinks (CC 6, 100%, 6.0), isBareDomainBoundary (CC 4, 100%, 4.0), imageAltText (CC 3, 100%, 3.0), isImageUrl (CC 3, 100%, 3.0), addFailedImage (CC 2, 100%, 2.0), PostImage (CC 2, 100%, 2.0), pushText (CC 2, 100%, 2.0).

Soft Gherkin acceptance mutation (gherkin-mutator --level soft)

  • post-image-rendering.feature: 26 executed, 25 killed, 1 survived, 0 errors.
  • Survivor: scenario 2, example 1, read https://example.com/page now -> reaD https://example.com/page now. Scenario 2 asserts the link and the absence of an image, not the surrounding prose, so a case mutation of the setup-only word read cannot change the outcome. This is a weak example-to-assertion connection (the same class as the documented post-link-formatting trailing-prose survivors), not an implementation gap — scenario 1 asserts its surrounding text case-sensitively, which is why its mutants died. Not chased.

Suite status

swarmforge/scripts/verify.sh client --record docs/reviews/post-image-rendering-verification.json --task post-image-rendering -> pass (5/5): unit 346 passing, property 58 passing, acceptance all 27 suites passing, lint pass, build pass.

Handoffs sent

  • End-of-chain git_handoff to the specifier (priority: 50, task post-image-rendering) with the review commit so it can merge swarmforge-architect into master.
  • No coder/refactorer handoff: the review is a test addition plus tool-written manifests, with no follow-up work for them.

By architect.