Validation: Add threaded replies on property comments (#303)

validation-303-2026-07-26 Doc

validation pass

Verdict: PASS

Ticket

ldraney/landscaping-assistant#303 (board item #1900) — Add threaded replies on property comments. Single-level parent-child threading via nullable parent_id foreign key, gated behind comment_threads feature flag.
Merged PR: #316

Environment

Production: https://landscaping-assistant.app
Local: docker compose test suite (Rails 8.1, PostgreSQL 17)
Tiers executed: Tier 1 (local tests), Tier 3 (production health)

Checks

# Criterion How Verified Result Evidence
1 Comments without a parent render as top-level items Code review: <code>scope :top_level</code> filters <code>parent_id: nil</code>; <code>by_most_recent_activity</code> scope chains <code>top_level</code>. Tests: "scopes top_level to exclude replies" and "excludes replies from top-level listing" pass. PASS 38 examples, 0 failures (rspec output)
2 Each comment displays a "Reply" link to open an inline reply form Code review: <code>_comment.html.erb</code> line 23 renders Reply button with <code>data-action="click-&gt;reply-form#toggle"</code> when flag on and <code>!comment.reply?</code>. Test: "shows only top-level comments with replies nested" confirms "Reply" in response body. PASS View template + test assertion
3 Replies appear indented beneath their parent comment Code review: <code>_comment.html.erb</code> lines 28-35 render <code>ul.comment-replies</code> beneath parent; line 1 adds <code>comment-reply</code> CSS class. Test: response includes <code>replies-{parent_id}</code> container. PASS DOM structure verified via Nokogiri assertions in tests
4 Replies include author name and timestamp Code review: <code>_comment.html.erb</code> lines 4-6 render <code>crew_member.name</code> and <code>time_ago_in_words(created_at)</code> for all comments including replies. Test: "displays existing comments" confirms author name. PASS Shared partial renders both fields unconditionally
5 Threading enforces one level of nesting only Code review: <code>single_nesting_level</code> validation in PropertyComment model (lines 61-65) rejects when <code>parent.parent_id.present?</code>. Test: "prevents replies more than one level deep" passes with exact error message. PASS Model validation + test assertion
6 Turbo Stream updates enable new reply creation without full page reload Code review: <code>create.turbo_stream.erb</code> appends to <code>replies-{parent_id}</code> for replies, prepends to <code>comments-list</code> for top-level (newest-first). Tests: "responds with turbo_stream for reply creation" and "prepends new comments via turbo stream" pass. PASS Turbo stream action/target assertions in test output
7 Feature gated behind <code>comment_threads</code> flag Code review: every thread-related block in views/controller wrapped in <code>feature_enabled?(:comment_threads)</code>. Flag defined in <code>feature_flags.rake</code> as <code>enabled: false</code>. Tests: "ignores parent_id when flag off" and "appends (not prepends) when flag off" pass. PASS Flag-off fallback tests confirm graceful degradation

Pipeline Status

Pipeline #836 (merge commit aa933a3): FAILURE — clone/database/bundle-install succeeded, but lint/test/build-and-push were skipped (CI infrastructure issue, not code failure).
Pipeline #835 (PR pipeline): SUCCESS — all steps passed.
Pipeline #839 (subsequent main push for #319): SUCCESS — all steps including build-and-push. This image includes the threaded replies code since it builds from a later commit on main.

Production Health

  • curl https://landscaping-assistant.app/login → HTTP 200
  • curl https://landscaping-assistant.app/ → HTTP 302 (redirects to login, expected)
  • Keycloak login page renders correctly (screenshot captured)

Tier 1: Local Tests

bundle exec rspec spec/requests/property_comments_spec.rb: 38 examples, 0 failures in 3.81 seconds. Covers threading associations, single-nesting enforcement, controller reply creation, turbo stream targeting, comment sorting with activity bumping, cross-property parent rejection, multi-tenancy scoping, and flag-off fallback behavior.

Regression Check

  • All existing (non-threading) comment tests still pass: CRUD, photo uploads, service requests, multi-tenancy scoping, role-based access control.
  • Production app serves correctly at root and login routes.
  • Subsequent pipeline #839 passed all steps (lint, test, build-and-push), confirming no regressions introduced.

Discovered Issues

  • Pipeline #836 skipped steps: The merge commit pipeline had lint/test/build-and-push skipped despite clone and bundle-install succeeding. This appears to be a CI infrastructure issue (not a code issue). The code was deployed via the subsequent pipeline #839. This is a non-blocking observation but worth investigating if it recurs.
  • Visual verification gap: Could not log into production to visually verify threaded replies in the UI — DEMO_USERNAME/DEMO_PASSWORD environment variables are not set. The comprehensive test suite (38 tests including DOM structure assertions via Nokogiri) provides equivalent coverage. Not a blocker for PASS verdict.