Skip to content

#419 — render: PR review comments in the report (inline threads + counts + nav + dogfood) - #439

Merged
cmbays merged 2 commits into
mainfrom
render-419-pr-comments
Jun 14, 2026
Merged

cmbays merged 2 commits into
mainfrom
render-419-pr-comments

Conversation

@cmbays

@cmbays cmbays commented Jun 14, 2026 •

Copy link
Copy Markdown
Contributor

DO NOT MERGE — builder PR; the orchestrator gates + merges.

Consolidates #419 + #420 + #421 + #422 (epic #353 — the report's inline PR review-comments surface) as one vertical slice. The comment-domain primitives were already shipped (#395 ingestion, #418 anchoring); this PR is the render layer, which was greenfield.

What shipped

Architecture

  • Domain (src/domain/pr_comment_render.rs): new pure module — CommentsView / ModelCommentBucket / RenderedThread PODs + group_comment_threads (std+serde only; re-uses anchor_comment_thread, maps the resolved path → model node, buckets per model). 11 unit tests.
  • Adapter (render.rs): additive Option<CommentsView> on ReportPayload (JSON-rendered client-side, the pr_dag / seed_cards precedent — no server-rendered template fields).
  • CLI: --pr-comments @file value-parser (deterministic golden/test seam) + a live gh api graphql fetch path that resolves owner/repo/number from --pr-url/--pr-number; gather_pr_comments is the single gating source.
  • Gate: new Experiment::PrComments; surface is --pr-diff-arm only.

Bot-review probe targets

  1. Anchor reuse (no re-anchoring). group_comment_threads calls the shipped anchor_comment_thread (domain: comment→diff-line anchoring — align PrCommentThread to the rendered diff line (+ outdated handling) #418) verbatim and only adds the manifest join (resolved path → model node-id via original_file_path). Confirm it never re-implements line-anchoring logic — the diff join stays in pr_comment_anchor, the manifest join is the new pass. The JS↔Rust join key is the model path (the model payload carries path, not the node-id), surfaced as ModelCommentBucket.model_path.
  2. Experiment-gate byte-identity contract. With pr-comments OFF / no PR context / no comments, DATA.pr_comments is omitted and the always-present static containers (.pr-comments-count, .model-comments-row) stay empty + hidden — every default golden stays byte-identical. The JS/CSS bundle change shifted ALL report goldens (all 7 regenerated; explore goldens untouched). Probe: does any OFF-state path emit comment bytes?
  3. Focusable-bubble (no native title) a11y. The per-model count is a focusable <button> + CSS bubble on :hover AND :focus (aria-label for AT, bubble aria-hidden), never a native title. The headless test asserts the bubble is hidden before focus and revealed on .focus(). Probe: is any load-bearing comment affordance reachable only by hover?

Gates (all green, raw exit 0)

cargo fmt --check · clippy --all-targets --locked -D warnings · nextest (2358 pass) · cargo test --test bdd (31 features / 242 scenarios) · headless_toggle + headless_zero_egress (real Chrome, 122 + 12 pass) · cargo doc -D warnings · cargo deny · resource-ref lint · synthetic/asset provenance · domain_clean_arch · non-mirror-guard.

Closes #419
Closes #420
Closes #421
Closes #422

🤖 Generated with Claude Code


Open in Stage

Summary by CodeRabbit

New Features

  • Added PR review comments surface to reports, displaying inline comment threads anchored to specific diff lines with per-model comment count tooltips and resolved/outdated state indicators.
  • Introduced --pr-comments CLI argument to supply PR review comment data for report rendering.

Tests

  • Added BDD test coverage for PR review comment rendering, navigation, and state management.

…nts + navigation

Consolidates #419 + #420 + #421 + #422 (epic #353): the ingested GitHub PR
review threads (#395) are anchored onto the rendered diff (the shipped #418
anchoring — re-used verbatim, never re-anchored) and grouped per model, then
rendered three ways — gated behind the new `pr-comments` experiment + the
`--pr-diff` arm.

- #419 inline diff-line comment threads: each anchored thread renders at its
  Model-SQL diff line (author · body · replies · resolved/open state,
  GitHub-style). Resolved threads collapse; outdated threads are labeled and
  surfaced in a per-model tail (never mis-anchored, never dropped).
- #420 top-of-report comment-count button: shows the report-wide total and
  navigates (selects the model via __cuteSelectNode's selectModelByName +
  scrolls to the anchored diff line). In-page JS only — zero fetch.
- #421 per-model count tooltip: the focusable-bubble pattern (.expect-tooltip
  + CSS bubble on :hover AND :focus, aria-label for AT) — never a native title.
- #422 fixture + BDD + dogfood: synthetic --pr-comments review-threads fixture
  drives the committed comments-showcase golden (visible inline threads +
  counts); 5 BDD scenarios; headless test asserting inline render, navigation,
  and tooltip-on-focus; zero-egress gate covers the example.

Domain: new pure `pr_comment_render` module (CommentsView / ModelCommentBucket
/ group_comment_threads) — std+serde only, re-uses anchor_comment_thread.
Adapter: additive Option<CommentsView> on ReportPayload (JSON-rendered, the
pr_dag precedent). CLI: --pr-comments @file value-parser (the gh-fetch live
path resolves owner/repo/number from --pr-url/--pr-number) + gather_pr_comments.

Gating: byte-identity goldens when the experiment is OFF / no PR context / no
comments (the always-present static containers stay empty). All report goldens
regenerated for the JS/CSS bundle shift; explore goldens untouched.

Closes #419
Closes #420
Closes #421
Closes #422

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@qodo-code-review

Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@coderabbitai

coderabbitai Bot commented Jun 14, 2026 •

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@cmbays, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 30 minutes and 38 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 26e74e50-a335-438c-865f-842951e6f7b3

📥 Commits

Reviewing files that changed from the base of the PR and between 8246555 and 6bd59fe.

⛔ Files ignored due to path filters (1)
  • tests/snapshots/render_integration__rendered_chrome_jaffle_shop.snap is excluded by !**/*.snap
📒 Files selected for processing (10)
  • examples/comments-showcase-report.html
  • examples/diff-showcase-report.html
  • examples/jaffle-shop-report.html
  • examples/macro-heavy-report.html
  • examples/playground-report.html
  • examples/prdiff-minidag-report.html
  • examples/seed-showcase-report.html
  • src/cli/mod.rs
  • templates/interaction.js
  • tests/headless_toggle.rs
📝 Walkthrough

Walkthrough

Implements the PR review comments epic (#353): a new pr_comment_render domain module groups GitHub PrCommentThreads into CommentsView; a new --pr-comments CLI argument ingests the payload; ReportPayload gains a pr_comments field; report.html/report.css/interaction.js templates add the inline thread injection UI; all six golden example reports are updated; BDD scenarios, a synthetic fixture, and a headless browser test validate the feature end-to-end.

Changes

PR Review Comments Feature

Layer / File(s) Summary
Domain data structures and experiment flag
src/domain/pr_comment_render.rs, src/domain/mod.rs, src/domain/experimental.rs
Defines RenderedComment, RenderedThread, ModelCommentBucket, CommentsView with serde; implements group_comment_threads (path index → anchor join → bucket → finalize); registers Experiment::PrComments with wire id "pr-comments"; re-exports all types from domain root.
CLI --pr-comments argument, parser, and gather logic
src/cli/pr_comments.rs, src/cli/args.rs, src/cli/mod.rs, src/cli/review.rs
Adds parse_pr_comments_arg (@file / inline JSON, fail-soft); new --pr-comments clap field on ReportArgs; gather_pr_comments gated on experiment + PrDiff arm; resolve_pr_comments chooses fixture vs live gh fetch; owner_repo_from_url URL normalization; review.rs sets pr_comments: None.
ReportPayload extension and render pipeline
src/adapters/render.rs
Adds pr_comments: Option<CommentsView> (serde-omit when None) to ReportPayload; extends render_report_with_externals signature; assigns payload.pr_comments = pr_comments.cloned(); updates all test struct literals and helper call sites.
HTML/CSS/JS template UI
templates/report.html, templates/report.css, templates/interaction.js
Adds hidden pr-comments-count button and model-comments row to report chassis; 60-line CSS block for count button, per-model chip/bubble, thread cards, resolved/outdated/tail states; adds all JS rendering functions (renderPrCommentsCountButton, anchorModelCommentThreads, buildCommentThread, etc.); extends renderOneDiffLine to emit data-oldline/data-newline.
Golden example reports
examples/*-report.html (6 files)
Propagates identical CSS, DOM placeholders, boot-time call, JS PR comment functions, updated renderForSelectedModel wiring, and data-oldline/data-newline diff-line attributes across all six committed example reports.
BDD feature, step definitions, and fixture
features/pr_comments.feature, tests/steps/pr_comments.rs, tests/steps/mod.rs, tests/fixtures/comments-showcase-pr-comments.json, tests/fixtures/MANIFEST.toml, tests/fixture_parse.rs, tests/common/mod.rs, .github/workflows/ci.yml, lefthook.yml
Five BDD scenarios; step definitions with run helper, payload extractor, and Then steps for total/per-model counts and outdated thread integrity; synthetic fixture with MANIFEST.toml provenance; allowlist and feature-count updates in CI and lefthook.
Headless browser tests and None propagation
tests/headless_toggle.rs
New pr_comments_render_inline_count_and_navigate test with settle_comments helper; passes None for pr_comments across 15+ existing render helpers; adjusts one fold-test substring assertion for updated attribute ordering.

Sequence Diagram(s)

sequenceDiagram
  actor User
  participant CLI as cute-dbt report
  participant gather as gather_pr_comments
  participant domain as group_comment_threads
  participant render as render_report_with_externals
  participant HTML as report HTML + interaction.js

  User->>CLI: --pr-comments `@fixture.json` --pr-diff patch (experiment=pr-comments)
  CLI->>gather: args, scope, experiments
  gather->>domain: PrCommentThread[], Manifest, NormalizedDiffIndex
  domain-->>gather: CommentsView (by_model buckets + unanchored + total)
  gather-->>CLI: Some(CommentsView)
  CLI->>render: pr_comments=Some(&CommentsView)
  render-->>HTML: ReportPayload JSON with pr_comments embedded
  HTML->>HTML: renderPrCommentsCountButton() on boot (unhide button)
  HTML->>HTML: renderModelCommentCount(m) on model select
  HTML->>HTML: renderModelSql(m) → diff lines with data-oldline/data-newline
  HTML->>HTML: anchorModelCommentThreads(m) → inject thread cards
  User->>HTML: click pr-comments-count button
  HTML->>HTML: navigateToFirstComment() → select model → scrollToThread
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related issues

  • #419 — This PR directly implements inline diff-line comment thread rendering anchored to diff lines, with author/body/resolved/outdated state, as specified.
  • #420 — This PR implements the top-of-report comment-count button with click navigation to the first anchored thread (model selection + scroll), as specified.
  • #421 — This PR implements the per-model focusable-bubble tooltip showing per-model comment counts, using the hover/focus bubble pattern, as specified.
  • #422 — This PR adds the synthetic comments-showcase-pr-comments.json fixture, BDD scenarios for all specified acceptance criteria, and updates the feature-count mirror in ci.yml and lefthook.yml, as specified.
  • Epic: GitHub PR comments in the report — inline diff-line threads + top comment-count nav + per-model counts #353 — This PR implements the complete PR review comments in report epic described in the linked issue, including generation-time gh pull, view-time zero-egress inlining, experimental gating, and byte-identity goldens when off.

Possibly related PRs

  • breezy-bays-labs/cute-dbt#38: Established the src/adapters/render.rs ReportPayload + Askama template pipeline that this PR extends with the pr_comments field and report HTML/JS DOM hooks.

Suggested labels

type:feature

🐇 A rabbit peeks through the diff, so neat,
Finding comment threads anchored — what a feat!
Resolved ones collapse, outdated ones warn,
The count button glows from dusk until morn.
Click once, scroll fast, the model appears —
Inline review comments, gone are the fears! 🎉

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly describes the main change: rendering PR review comments inline in reports with associated features like counts and navigation.
Linked Issues check ✅ Passed All linked issues (#419, #420, #421, #422) requirements are met: inline comment threads with resolved/outdated states, top-count navigation button, per-model tooltip using focusable-bubble pattern, and comprehensive BDD tests with synthetic fixture.
Out of Scope Changes check ✅ Passed All changes are directly aligned with the linked issue objectives covering inline comment rendering, navigation, tooltips, testing, and feature-count updates for the PR-comments epic.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch render-419-pr-comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented Jun 14, 2026 •

Copy link
Copy Markdown
Contributor

📄 Rendered report preview

All golden examples regenerated cleanly.

🟡 Golden examples

Committed to examples/ and byte-identity gated — the canonical reports contributors and consumers browse. Stable across PRs.

Report View Download
seed-showcase-report.html ▶ Open ↗ ⬇ Download
prdiff-minidag-report.html ▶ Open ↗ ⬇ Download
playground-report.html ▶ Open ↗ ⬇ Download
jaffle-shop-report.html ▶ Open ↗ ⬇ Download
macro-heavy-report.html ▶ Open ↗ ⬇ Download
diff-showcase-report.html ▶ Open ↗ ⬇ Download

🐶 Live dogfood preview

This PR doesn't touch dbt-project/, so there's no live dogfood preview.

🧭 Explore preview

The two-page cute-dbt explore explorer — dag.html (model lineage) + tests.html (unit-test viewer). Same golden/live split as the report.

🟡 Golden explore

The committed examples/explore/ playground golden (the full synthetic playground manifest). Byte-identity gated in Example report check. Stable across PRs.

Page View Download
explore/dag.html ▶ Open ↗ ⬇ Download
explore/tests.html ▶ Open ↗ ⬇ Download

🐶 Live explore

This PR doesn't touch dbt-project/, so there's no live explore preview.

▶ Open ↗ opens the report or explorer in your browser in one
click — published to this repo's GitHub Pages under
/pr-439/.
⬇ Download fetches the same self-contained HTML as a workflow
artifact (auth-gated; works fully offline). Either way the report
makes zero external resource requests.

The Pages preview may take ~1 min to update after this comment
posts. On PRs from forks the Open link is unavailable (read-only
token) — use Download.

Alternative: GitHub CLI
# gh CLI >= 2.63 extracts into ./report-preview-playground/.
gh run download 27512995203 -R breezy-bays-labs/cute-dbt -n report-preview-playground
open report-preview-playground/playground-report.html

Posted by report-preview.yml for 6bd59fe0e81deb120349c6601af450e0fe86f200. Affordance only — never blocks merge.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces the experimental pr-comments feature, enabling the ingestion and inline rendering of GitHub PR review comments anchored to the diff in the HTML report. The implementation includes CLI argument parsing, domain grouping logic, frontend template updates, and comprehensive headless and integration tests. The feedback suggests a minor optimization in src/cli/mod.rs to avoid an unnecessary clone of args.project_root when calling group_comment_threads.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/cli/mod.rs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🧹 Nitpick comments (1)
src/cli/mod.rs (1)

2074-2075: 💤 Low value

Avoid unnecessary clone of project_root.

group_comment_threads accepts Option<&Path>, so you can pass the reference directly without cloning the PathBuf.

🔧 Suggested fix
-    let project_root = args.project_root.clone();
-    let view = group_comment_threads(&comments.threads, current, index, project_root.as_deref());
+    let view = group_comment_threads(&comments.threads, current, index, args.project_root.as_deref());
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/cli/mod.rs` around lines 2074 - 2075, The code unnecessarily clones the
PathBuf from args.project_root before dereferencing it to pass to
group_comment_threads. Since group_comment_threads accepts Option<&Path>, remove
the clone() call entirely and pass args.project_root.as_deref() directly to
avoid the unnecessary allocation and copy of the PathBuf.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@examples/diff-showcase-report.html`:
- Around line 7427-7438: In the navigateToFirstComment function, the current
implementation unconditionally selects the first thread from bucket.threads
using bucket.threads[0], which can result in selecting an outdated or unplaced
thread. Instead of taking the first thread directly, iterate through the
bucket.threads array to find the first thread that is anchored or valid (not
outdated/unplaced), and pass that thread to the scrollToThread function. This
ensures the jump-to-first-comment button lands on the first live diff anchor as
intended for the `#420` behavior.

In `@examples/macro-heavy-report.html`:
- Around line 7200-7205: The selector that finds the anchor `.diff-line` element
in the $diff container (line 7202) matches hidden/folded-away nodes, causing
comment threads to be incorrectly placed after invisible anchors. Modify the
`.find('.diff-line[' + attr + '="' + t.line + '"]')` selector to exclude hidden
elements by adding a `:not([hidden])` pseudo-class to the selector, or
alternatively, treat folded-away lines the same way the code already handles
missing lines by routing them through the unplaced mechanism instead of
attempting to inject them after an invisible anchor.
- Around line 7204-7205: The current code uses .after($line) for each thread,
which reverses the visual order when multiple threads anchor to the same diff
line. Instead of always anchoring to the original $line, track the previously
injected thread element and insert subsequent threads after that element. After
calling $line.after(buildCommentThread(t)) for the first thread in
bucket.threads, capture the returned injected element and use it as the anchor
for the next insertion, repeating this process so each thread is inserted after
the one before it, preserving their original order.

In `@examples/playground-report.html`:
- Around line 6813-6820: The code currently assigns the first thread from
bucket.threads to the thread variable without checking if it has a live anchor,
which can cause scrollToThread() to jump to an outdated or unplaced thread
instead of the anchored diff line. Instead of directly using bucket.threads[0],
implement logic to scan through bucket.threads and select the first thread that
has a live anchor (you will need to determine what constitutes a "live anchor"
in the thread object), then fall back to bucket.threads[0] only if no thread
with a live anchor is found. Pass the selected thread (or the fallback) to the
scrollToThread() call.

In `@examples/prdiff-minidag-report.html`:
- Around line 6881-6889: The scrollToThread function directly interpolates
thread.__key into a CSS selector string on Line 6884, which will break if
thread.__key contains special characters like quotes, backslashes, or brackets.
Wrap thread.__key with CSS.escape() before inserting it into the querySelector
call to properly escape any special characters and ensure the selector remains
valid regardless of the thread key's content.

In `@examples/seed-showcase-report.html`:
- Around line 6864-6875: In the navigateToFirstComment function, replace the
unconditional selection of bucket.threads[0] with logic that iterates through
bucket.threads to find the first thread that has a live line property and is not
outdated (i.e., thread.line exists and thread.outdated is false). Pass this
valid thread to scrollToThread, or skip to the next bucket iteration if no valid
thread is found in the current bucket. This ensures the top "PR review comments"
button navigates to an anchored diff line rather than to outdated or unplaced
threads.

In `@templates/interaction.js`:
- Around line 877-880: The condition checking whether to insert the comment
thread inline after the anchor line only verifies the element exists ($line &&
$line.length) but does not verify the element is actually visible. When the
anchor line is hidden, inserting after it will effectively hide the thread from
view. Add a visibility check in the condition before calling
$line.after(buildCommentThread(t)) to ensure the line is visible; if the line is
hidden, push the thread to the unplaced array instead to allow it to surface in
the tail fallback.
- Around line 783-794: The loop iterates through pr_comments by_model buckets
and may return prematurely after selecting a non-selectable model or invalid
thread, preventing navigation to a live diff anchor. Modify the
selectModelByName function to return a boolean indicating whether the model
selection was successful (false if not selectable, true on success). Then in the
loop, check the return value of selectModelByName before proceeding to call
scrollToThread and return; if the selection failed, continue to the next
iteration to try the next bucket instead of returning immediately.

In `@templates/report.css`:
- Line 1619: The CSS keyword `currentColor` in the border property violates the
Stylelint `value-keyword-case` rule which requires keywords to be lowercase.
Change `currentColor` to `currentcolor` in the border declaration at line 1619
to normalize the keyword casing and satisfy the linting rule.

---

Nitpick comments:
In `@src/cli/mod.rs`:
- Around line 2074-2075: The code unnecessarily clones the PathBuf from
args.project_root before dereferencing it to pass to group_comment_threads.
Since group_comment_threads accepts Option<&Path>, remove the clone() call
entirely and pass args.project_root.as_deref() directly to avoid the unnecessary
allocation and copy of the PathBuf.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1999ef5d-930e-486f-b657-6656c16bca54

📥 Commits

Reviewing files that changed from the base of the PR and between 8508680 and 8246555.

⛔ Files ignored due to path filters (2)
  • tests/snapshots/golden_report__rendered_report_skeleton.snap is excluded by !**/*.snap
  • tests/snapshots/render_integration__rendered_chrome_jaffle_shop.snap is excluded by !**/*.snap
📒 Files selected for processing (28)
  • .github/workflows/ci.yml
  • examples/comments-showcase-report.html
  • examples/diff-showcase-report.html
  • examples/jaffle-shop-report.html
  • examples/macro-heavy-report.html
  • examples/playground-report.html
  • examples/prdiff-minidag-report.html
  • examples/seed-showcase-report.html
  • features/pr_comments.feature
  • lefthook.yml
  • src/adapters/render.rs
  • src/cli/args.rs
  • src/cli/mod.rs
  • src/cli/pr_comments.rs
  • src/cli/review.rs
  • src/domain/experimental.rs
  • src/domain/mod.rs
  • src/domain/pr_comment_render.rs
  • templates/interaction.js
  • templates/report.css
  • templates/report.html
  • tests/common/mod.rs
  • tests/fixture_parse.rs
  • tests/fixtures/MANIFEST.toml
  • tests/fixtures/comments-showcase-pr-comments.json
  • tests/headless_toggle.rs
  • tests/steps/mod.rs
  • tests/steps/pr_comments.rs

Comment thread examples/diff-showcase-report.html Outdated
Comment thread examples/macro-heavy-report.html Outdated
Comment thread examples/macro-heavy-report.html Outdated
Comment thread examples/playground-report.html Outdated
Comment thread examples/prdiff-minidag-report.html
Comment thread examples/seed-showcase-report.html Outdated
Comment thread templates/interaction.js
Comment thread templates/interaction.js Outdated
Comment thread templates/report.css
…4rs coverage

Addresses the PR #439 review gate failure + CodeRabbit MAJOR correctness bugs:

- crap4rs (BLOCKING): cover the two new 0%-coverage cli fns. `owner_repo_from_url`
  CRAP 30.00 → 5.00 (7 exhaustive branch tests, 100% cov); `resolve_pr_comments`
  20.00 → 5.57 (file-payload + fail-soft-no-PR tests). Gate now PASS (worst 20.0).
- Top-count button + nav must target a LIVE anchor (CR major): `navigateToFirstComment`
  picks the first thread with a live `line` + `!outdated` (was `bucket.threads[0]`,
  which lands on the outdated tail when it sorts first), scanning past
  non-selectable / anchorless buckets with a graceful fallback.
- Folded/hidden diff-line anchors → tail, not inline (CR major): the anchor
  selector adds `:not([hidden])` so a thread on a folded-away line is routed to
  the per-model "not shown" tail instead of injected after an invisible line.
- Selector injection (CR major): `scrollToThread` locates the thread by a
  non-selector `getAttribute("data-thread-key")` scan, so a model path with `"`,
  `\`, or `]` can't break the query.
- Thread order (CR minor): multiple threads on one diff line now insert after the
  previously-injected thread (per-anchor last-inserted tracking), preserving
  `bucket.threads` order instead of reversing via repeated `.after()`.
- project_root borrow (gemini): pass `args.project_root.as_deref()` (no clone).
- Declined: the `currentColor` → `currentcolor` stylelint nit — stylelint is not a
  repo gate and `currentColor` is the established codebase convention (5 existing
  uses); normalizing one line would make it inconsistent.

TDD: two new headless tests, proven RED before the fix — the count button
navigating to the LIVE line-4 thread when the bucket's first thread is outdated,
and a thread on a folded line landing in the tail (control: a visible-line thread
still inlines). All report goldens regenerated for the JS shift; chrome snapshot
re-accepted.

Refs #419 #420 #421 #422

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@cmbays
cmbays merged commit 864886f into main Jun 14, 2026
45 checks passed
@cmbays
cmbays deleted the render-419-pr-comments branch June 14, 2026 21:51
@cmbays

cmbays commented Jun 14, 2026

Copy link
Copy Markdown
Contributor Author

Merge debrief — #439 (epic #353: PR review comments · #419+#420+#421+#422)

Shipped the report's inline PR review-comments surface as one consolidated PR (founder-authorized bundle): inline diff-line threads (author/body/replies/resolved/outdated, #419), top comment-count button→navigate (#420), per-model count focusable-bubble tooltip (#421), and the synthetic fixture + BDD + comments-showcase golden (#422). Domain was shipped (#395 ingestion, #418 anchoring); this was the greenfield render layer.

Orchestrator probe (above the bots) — clean on the architecture surfaces: group_comment_threads calls the shipped anchor_comment_thread verbatim (no re-anchoring — only the manifest join is new); Experiment::PrComments is gated at the single gather_pr_comments source, report-only, OFF/no-PR/no-comments → byte-identical goldens; gen-time gh fetch keeps view-time zero-egress intact.

Gate discipline paid off — this PR did NOT merge on the builder's first "all green" claim. The orchestrator's independent CI + bot pass caught real problems the builder's local run missed:

  • crap4rs CI FAILURE — two new CLI fns (owner_repo_from_url CRAP 30 > ≤25, resolve_pr_comments CRAP 20) shipped at 0% coverage (the builder's local gate run had skipped crap4rs). Fixed in the fix pass: 7 branch tests → CRAP 30→5.00, 20→5.57, verified locally (cargo llvm-cov → crap4rs … --coverage lcov.info PASS).
  • CodeRabbit MAJOR navigation bugs (the headless tests had a coverage gap the architecture probe didn't target — bots backstop JS edge cases): top-count button landed on the outdated tail thread (outdated sorts first → bucket.threads[0] wrong) → fixed with firstLiveThread; threads on folded/hidden diff lines inserted after invisible anchors → fixed with liveDiffAnchorFor + :not([hidden]) routing to the tail; selector injection (__key/model-path raw in querySelector) → fixed with a non-selector getAttribute scan; thread-order reversal → lastInsertedByAnchor. Two new headless tests pin the live-anchor + folded-line cases, proven RED→GREEN.

Bot disposition: gemini as_deref() applied; all CodeRabbit MAJOR/MINOR applied EXCEPT the currentColor→currentcolor stylelint nit — declined (Stylelint isn't a repo gate; currentColor is the established convention, 5 existing uses; normalizing one line breaks consistency for no enforced benefit). All threads replied + resolved.

Gates (fix push, raw exit 0): fmt, clippy --all-targets --locked -D warnings, nextest (2367), bdd (31 features/242), headless toggle (123) + zero-egress (incl committed comments example), doc, deny, crap4rs PASS, all 7 byte-identity report goldens regenerated + snapshot re-accepted.

Closes #419 / #420 / #421 / #422 → epic #353 complete. Pre-design engineering lane COMPLETE.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant