From 8d12c29cd2d69b97c5fa216e0c22059de0f3f164 Mon Sep 17 00:00:00 2001 From: Nitish Singh Date: Tue, 14 Apr 2026 22:14:40 +0530 Subject: [PATCH 1/3] Benchmarking workflow setup --- README.md | 3 +- offline/README.md | 79 +- offline/analysis/benchmark_dashboard.html | 58 +- offline/analysis/benchmark_dashboard.json | 25658 ++++++---------- .../merge_neatcode_into_openai_gpt52.py | 85 + offline/analysis/report_all_tools.py | 81 + offline/analysis/report_scores_by_repo.py | 141 + .../code_review_benchmark/step5_label_prs.py | 1 - offline/golden_comments/discourse.json | 184 - .../candidates.json | 10592 +------ .../evaluations.json | 17573 +---------- .../candidates.json | 11228 +------ .../evaluations.json | 17913 +---------- offline/results/benchmark_data.json | 12554 +------- .../results/openai_gpt-5.2/candidates.json | 5695 +--- .../results/openai_gpt-5.2/evaluations.json | 18303 +---------- offline/results/pr_labels.json | 386 +- offline/uv.lock | 2 +- 18 files changed, 12230 insertions(+), 108306 deletions(-) create mode 100644 offline/analysis/merge_neatcode_into_openai_gpt52.py create mode 100644 offline/analysis/report_all_tools.py create mode 100644 offline/analysis/report_scores_by_repo.py delete mode 100644 offline/golden_comments/discourse.json diff --git a/README.md b/README.md index dfa6d4f..cb49fd8 100644 --- a/README.md +++ b/README.md @@ -31,14 +31,13 @@ Without shared evals for these tools, every company grades its own homework. You ### Offline — fixed dataset, reproducible results -**50 PRs** from 5 major open-source projects, each with human-verified golden comments — the real issues a reviewer should catch. +**40 PRs** from four major open-source projects, each with human-verified golden comments — the real issues a reviewer should catch. | Repository | Language | Domain | |---|---|---| | [Sentry](https://github.com/getsentry/sentry) | Python | Error tracking | | [Grafana](https://github.com/grafana/grafana) | Go | Observability | | [Cal.com](https://github.com/calcom/cal.com) | TypeScript | Scheduling | -| [Discourse](https://github.com/discourse/discourse) | Ruby | Forum platform | | [Keycloak](https://github.com/keycloak/keycloak) | Java | Authentication | Each PR has curated golden comments with severity labels (Low / Medium / High / Critical). An LLM judge matches each tool's review against the golden comments and computes precision and recall. diff --git a/offline/README.md b/offline/README.md index e7846c2..5121b42 100644 --- a/offline/README.md +++ b/offline/README.md @@ -1,6 +1,6 @@ # Offline Code Review Benchmark -Open replication of the code review benchmark used by companies like [Augment](https://www.augmentcode.com/blog/introducing-augment-code-review) and [Greptile](https://www.greptile.com/blog/code-review-benchmark). 50 PRs across 5 major open-source codebases with human-verified golden comments. An LLM judge evaluates each tool: does it find real issues? Does it generate noise? +Open replication of the code review benchmark used by companies like [Augment](https://www.augmentcode.com/blog/introducing-augment-code-review) and [Greptile](https://www.greptile.com/blog/code-review-benchmark). 40 PRs across four major open-source codebases with human-verified golden comments. An LLM judge evaluates each tool: does it find real issues? Does it generate noise? ## Evaluated tools @@ -22,7 +22,7 @@ Adding a new tool requires forking the benchmark PRs and collecting the tool's r ## Methodology -Each of the 50 benchmark PRs has a set of **golden comments**: real issues that a human reviewer identified, with severity labels (Low / Medium / High / Critical). These are the ground truth. +Each of the 40 benchmark PRs has a set of **golden comments**: real issues that a human reviewer identified, with severity labels (Low / Medium / High / Critical). These are the ground truth. For each tool, the pipeline: 1. **Extracts** individual issues from the tool's review comments (line-specific comments become candidates directly; general comments are sent to an LLM to extract distinct issues) @@ -246,6 +246,24 @@ uv run python analysis/benchmark_dashboard.py Open `analysis/benchmark_dashboard.html` in a browser to view results. Run this after adding new tools or re-running the judge to update the dashboard. +### Leaderboard and per-repo scores + +After you have `results/openai_gpt-5.2/evaluations.json` (and optionally merged NeatCode with `results/gpt-5.2/`): + +```bash +# Micro P/R/F1 for every tool (default path: openai_gpt-5.2) +uv run python analysis/report_all_tools.py + +# Same metrics for one upstream product (keycloak | cal | grafana | sentry) +uv run python analysis/report_scores_by_repo.py --repo keycloak --tool neatcode +``` + +To copy **neatcode** evaluations/candidates from `results/gpt-5.2/` into the merged OpenAI leaderboard file (both dirs must exist): + +```bash +uv run python analysis/merge_neatcode_into_openai_gpt52.py +``` + ### 5. Summary table Show review counts by tool and repo: @@ -256,10 +274,10 @@ uv run python -m code_review_benchmark.summary_table **Example output:** ``` -Tool cal_dot_com discourse grafana keycloak sentry Total ----------------------------------------------------------------------------------- -claude 10 10 10 10 10 50 -coderabbit 10 10 10 10 10 50 +Tool cal_dot_com grafana keycloak sentry Total +---------------------------------------------------------------------- +claude 10 10 10 10 40 +coderabbit 10 10 10 10 40 ... ``` @@ -279,6 +297,53 @@ uv run python -m code_review_benchmark.step4_export_by_tool --tool greptile --- +## GitHub Actions + +The offline benchmark workflow lives at [`.github/workflows/benchmark-offline.yml`](../.github/workflows/benchmark-offline.yml). + +### Benchmark offline (`benchmark-offline.yml`) + +**Trigger:** `workflow_dispatch` only (Actions tab → Benchmark offline → Run workflow). + +**What it runs:** From the `offline/` directory, optionally step 0 (orchestrate forks), then steps 1 → 2 → 2.5 → 3. Uploads `offline/results/` as a workflow artifact. + +**Workflow inputs** + +| Input | Meaning | +|-------|---------| +| `ref` | Git branch or tag of **this** (`neatcode-benchmarking`) repo to checkout. Use this to run a specific version of the benchmark scripts. Default: `main`. | +| `benchmark_org` | GitHub organization slug where benchmark fork repos live (e.g. a dedicated eval org). Passed to `--org` for step 0 and step 1. | +| `tool` | Tool slug matching step 0 `--name` and repo name segments (e.g. `neatcode_staging`). | +| `run_step0` | If true, runs `step0_orchestrate_forks` first (creates repos and PRs in `benchmark_org`). Long-running; requires a token that can create repositories in that org. | +| `step1_force` | If true, passes `--force` to step 1 (refetch reviews). | +| `step1_test` | If true, passes `--test` to step 1 (one repo per tool). | +| `judge_model` | Optional. If set, overrides `MARTIAN_MODEL` for the judge. If empty, the `MARTIAN_MODEL` repository secret is used. | +| `limit` | Optional. If non-empty, passed as `--limit` to steps 2 and 3 for smoke runs. | + +**Repository secrets (Settings → Secrets and variables → Actions)** + +| Secret | Required | Purpose | +|--------|----------|---------| +| `BENCHMARK_GH_TOKEN` | Yes | Personal access token (classic or fine-grained) used as `GH_TOKEN` / `GITHUB_TOKEN` for `gh` and step 0. Must be able to **list and read** PRs in `benchmark_org`, and **create repositories** there if you use `run_step0`. The default `GITHUB_TOKEN` in Actions cannot replace this for arbitrary orgs. | +| `MARTIAN_API_KEY` | Yes | API key for the OpenAI-compatible judge endpoint. | +| `MARTIAN_MODEL` | Yes (unless you always pass `judge_model`) | Default judge model id (e.g. `openai/gpt-4o-mini`). Same value as in local `.env`. | + +Optional: add `MARTIAN_BASE_URL` only if you use a non-default judge endpoint; for a custom base URL you can extend the workflow `env` or rely on local `.env` when running manually. + +**PAT permissions (typical)** + +- **Step 1 only:** read access to repositories in `benchmark_org` (metadata, pull requests, contents as needed by `gh`). +- **Step 0 (orchestrate):** additionally **create** repositories (and admin as required by your org policy) in `benchmark_org`. + +Scope the token to the **benchmark organization** when using fine-grained PATs. + +**Scripts ref vs NeatCode backend** + +- The workflow **`ref` / branch** selects which **commit of these benchmark scripts** runs (step 1–3 code, golden files, etc.). +- It does **not** select which version of **NeatCode** reviews the PRs. Review behavior is determined by the **GitHub App** installation on `benchmark_org` and the **webhook URL** configured for that app (e.g. staging or an experimental deployment). To compare backend changes, deploy the desired build and ensure the benchmark app’s webhook points at it before re-running reviews and step 1 with `step1_force` where appropriate. + +--- + ## Data format ### Golden comments (`golden_comments/*.json`) @@ -298,7 +363,7 @@ uv run python -m code_review_benchmark.step4_export_by_tool --tool greptile ] ``` -Source files: `sentry.json`, `grafana.json`, `keycloak.json`, `discourse.json`, `cal_dot_com.json` +Source files: `sentry.json`, `grafana.json`, `keycloak.json`, `cal_dot_com.json` ### benchmark_data.json diff --git a/offline/analysis/benchmark_dashboard.html b/offline/analysis/benchmark_dashboard.html index 4905b9e..480a467 100644 --- a/offline/analysis/benchmark_dashboard.html +++ b/offline/analysis/benchmark_dashboard.html @@ -191,61 +191,49 @@
Highest Precision
+
Python + Medium Risk (Precision)
Best for Concurrency (Precision)
-
Best for Complex Code (Precision)
-
Best for Bug Fixes (Recall)
-
Best for Bug Fixes (Recall)
-
Best for Bug Fixes (Recall)
+
Best for Large PRs (Precision)
Best for Performance Optimization
+
Java + Cross-File (Recall)
+
Typescript + Correctness (Recall)
Highest Recall
+
Java + Subtle Bugs (Recall)
Java + Authentication
-
Moderate Bugs + File Context (Recall)
Java + High Risk (Recall)
-
Best for Medium Ruby PRs
Typescript + Correctness
-
Best for Critical Risk
Java + Authentication
-
Bug Fixes + Cross-File
-
Best for Ui
Java + High Risk
-
File Context + Correctness
-
Medium PRs + File Context
-
Ruby + Features (Precision)
Best for Complex Code
-
Performance Optimization + Cross-File (Precision)
+
Best for Caching
+
Java + High Risk
+
Best for Typescript
+
Best for Java
Best for Medium Java PRs
-
Moderate Bugs + File Context
-
Ruby + Features
+
Medium PRs + Moderate Bugs
Best for Small Go PRs
Best for Go
Best for Small PRs
+
Best for File Context
+
Best for High Risk
Best for Medium Python PRs
Best for Python
-
Best for High Risk
-
Best for Bug Fixes
Best for Scheduling
-
Best for File Context
-
Best for Concurrency
Best for Subtle Bugs
-
Best for Caching
-
Best for Reliability
-
High Risk Auth
-
Complex & Subtle
+
Best for Concurrency
Best for Correctness
-
Best for Security
-
Security Critical
-
Best for Moderate Code
+
Complex & Subtle
+
Best for Caching
Highest F1
-
Best for Typescript
-
Best for Java
-
Best for Ruby
+
Best for Moderate Code
+
High Risk Auth
+
Best for Large PRs
Best for Authentication
-
Best for Cross-File
Best for Features
-
Best for Medium Risk
-
Best for Large PRs
+
Best for Cross-File
Best for Medium PRs
Best for Moderate Bugs
+
Best for Medium Risk
@@ -269,7 +257,6 @@ -
@@ -297,7 +284,6 @@ - @@ -413,10 +399,10 @@ ` breaks out of the attribute and injects script tags. Combined with `cook_method: raw_html` (bypassing sanitization) and `raw post.cooked` in the embed view, this allows arbitrary JavaScript execution. The URL should be HTML-escaped with `ERB::Util.html_escape` or `CGI.escapeHTML` before interpolation.", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "ERB syntax error on line 6: `<%- end if %>` should be `<%- end %>`. In Ruby, `end if` is a postfix conditional modifier that requires a condition expression, but none is provided here. This will raise a `SyntaxError` when rendering the embed view, completely breaking the embeddable comments feature for any topic with posts.", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "On line 35, `i.content` returns `nil` for RSS 2.0 feeds that use `` instead of ``, causing `NoMethodError` when `.scrub` is called on `nil`. Since the job has `sidekiq_options retry: false`, this crashes the entire feed polling job with no retry. Should fall back to `i.description` and guard against nil.", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The origin check `discourseUrl.indexOf(e.origin) === -1` on line 17 uses substring matching, which allows any origin that is a prefix/substring of `discourseUrl` to pass validation. Should use strict origin comparison: `new URL(discourseUrl).origin === e.origin`.", - "path": null, - "line": null, - "source": "extracted" - } - ], - "kodus-v2": [ - { - "text": "Syntax error in app/views/embed/best.html.erb: `<%- end if %>` uses Ruby's postfix conditional modifier which requires a boolean expression after `if`, causing SyntaxError at template compile/render time", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Command injection via Kernel#open in poll_feed.rb: with open-uri loaded, a SiteSetting.feed_polling_url value starting with `|` executes arbitrary OS commands on the server", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "NoMethodError on nil in poll_feed.rb when RSS item lacks content element: SimpleRSS returns nil for missing elements, so calling .scrub on nil crashes the entire polling loop", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "SSRF via open-redirect in topic_embed.rb: open(url).read follows HTTP redirects, allowing attacker-controlled page on embeddable host to redirect to internal resources like cloud metadata endpoints", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "XSS via wrong escaping context in embed.html.erb: using <%= request.referer %> applies HTML escaping inside a script block, but HTML entities are not interpreted by browser in JS context, and Referer header is attacker-controlled", - "path": null, - "line": null, - "source": "extracted" - } - ], - "qodo-v2-2": [ - { - "text": "app/views/embed/best.html.erb contains invalid ERB/Ruby syntax '<%- end if %>' which will raise a template parsing error when rendering the embed page, causing /embed/best to return 500", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The embed iframe uses request.referer (a full URL) as postMessage targetOrigin, but browsers require targetOrigin to be an origin (scheme+host+port), so resize messages may be dropped and iframe height will not update", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The parent-side origin check in embed.js uses substring matching instead of an origin equality check, which is fragile", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Post#cook returns raw for cook_method == raw_html, and TopicEmbed imports create posts with cook_method: raw_html using remote HTML, which bypasses the PrettyText sanitizer and enables stored XSS since views render post.cooked as HTML-safe", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Jobs::PollFeed disables Sidekiq retries and performs network fetch + RSS parsing without any error handling, so transient failures will abort polling and reduce import reliability", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "TopicRetriever can call Jobs::PollFeed.new.execute({}) synchronously, so PollFeed failures can also break the on-demand retrieval path", - "path": null, - "line": null, - "source": "extracted" - } - ], - "qodo-v22": [ - { - "text": "app/views/embed/best.html.erb closes an if block using <%- end if %>, which is invalid ERB/Ruby syntax and will raise a template compilation error when the embed page is rendered", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "app/views/layouts/embed.html.erb calls parent.postMessage(..., '<%= request.referer %>'), but request.referer is a full URL (incl. path/query) rather than an origin, so the browser will typically drop the message and iframe resizing won't work", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Posts created via TopicEmbed.import use cook_method = raw_html, and Post#cook returns raw directly for that method, bypassing PrettyText sanitization; this persists and renders remote HTML as-is, enabling stored XSS via imported content", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "TopicEmbed.import interpolates url directly into an HTML anchor () without escaping, allowing attribute/HTML injection if the URL contains quotes or special characters", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "PollFeed and TopicEmbed.import_remote call open(...) without timeouts, which can indefinitely block Sidekiq threads on slow/hung endpoints and stall job processing", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Jobs::PollFeed calls i.content.scrub without checking i.content presence, so feeds that omit content will raise NoMethodError and stop the poll job", - "path": null, - "line": null, - "source": "extracted" - } - ], - "qodo-extended": [ - { - "text": "Post#cook returns raw when cook_method is raw_html, bypassing PrettyText sanitization, enabling stored XSS for any user who views imported topic/embed output from RSS/remote content", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "EmbedController enqueues a background job when no embed exists, but the controller spec asserts that TopicRetriever is instantiated and executed synchronously, causing test suite failure", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Feed polling in Jobs::PollFeed performs network I/O and RSS parsing without exception handling and disables retries, causing transient failures to silently drop executions", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "postMessage targetOrigin uses request.referer (full URL) but embed script validates by e.origin, causing resize message to fail origin matching and leaving iframe height incorrect", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Line 6 in best.html.erb contains invalid Ruby syntax '<%- end if %>' - the if modifier requires a condition expression, causing SyntaxError when rendering", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "In topic_embed.rb line 13, the url parameter is interpolated directly into HTML without escaping, enabling stored XSS when combined with raw_html cook method", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "In TopicEmbed.import_remote, open(url) is called without validating URL scheme, allowing file://, ftp://, and other schemes enabling SSRF or local file reads", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "In embed.js, postMessage origin validation uses substring check (indexOf) instead of exact match, allowing attacker-controlled domains that are substrings of discourseUrl to bypass the check", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "X-Frame-Options: ALLOWALL is not a valid RFC 7034 value, causing inconsistent browser behavior - may fall back to DENY or ignore header entirely", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "In embed.html.erb, request.referer is interpolated directly into JavaScript string literal without escaping, causing syntax error if referer contains single quote", - "path": null, - "line": null, - "source": "extracted" - } - ], - "cubic-v2": [ - { - "text": "Sanitize `raw_html` posts before returning them - the early return turns imported embed HTML into stored, unsanitized output", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Security: `Kernel#open(url)` with user-supplied input is a command injection risk - when `open-uri` is loaded, `open` still supports the `|command` pipe syntax. Should use `URI.open(url)` or `Net::HTTP`/`OpenURI.open_uri` to restrict to HTTP(S) fetching", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "URL format validation should be added before fetching, rather than relying on the check inside `import`", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "`contents` is used without a nil check - `contents <<` will crash if nil", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "`url` is interpolated directly into HTML without escaping, risking HTML injection - should use `ERB::Util.html_escape` or `CGI.escapeHTML` on the URL before interpolating", - "path": null, - "line": null, - "source": "extracted" - } - ], - "qodo-extended-summary": [ - { - "text": "Post#cook returns raw content when cook_method is raw_html, bypassing PrettyText cooking/sanitization and enabling stored XSS in imported posts", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Jobs::PollFeed and TopicEmbed.import_remote fetch arbitrary URLs using open-uri without timeouts/allowlisting, allowing SSRF to internal resources and potentially hanging Sidekiq workers", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "request.referer is interpolated into a single-quoted JavaScript string using <%= %> which only HTML-escapes (not JS-escapes), enabling XSS via crafted referer path since single quotes are not escaped", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "postMessage origin check uses discourseUrl.indexOf(e.origin) === -1 which is substring match instead of exact origin comparison, allowing prefix-domain attacks to bypass the origin check", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "In best.html.erb line 6, <%- end if %> is invalid Ruby syntax causing SyntaxError since end if requires a condition expression", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "URL variable in topic_embed.rb line 13 is interpolated into HTML href attribute without escaping, combined with raw_html cook_method enables stored XSS", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "embed layout uses request.referer (full URL) as postMessage targetOrigin instead of valid origin string, which can raise browser SecurityError and prevent iframe resizing", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "open(url).read via open-uri follows HTTP redirects transparently, enabling SSRF via open redirects on the embeddable host since host validation only checks initial URL", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "X-Frame-Options header is set to 'ALLOWALL' which is an invalid non-standard directive, should remove header entirely to allow framing", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "TopicRetriever throttling stores raw embed_url directly in Redis key, allowing memory amplification DoS via many long distinct URLs", - "path": null, - "line": null, - "source": "extracted" - } - ], - "macroscope": [ - { - "text": "`import_remote` passes user-controlled `url` directly to `Kernel#open` on line 48 before any validation occurs, allowing shell command injection via malicious URLs like `|ls` through `open-uri`'s command injection feature", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "`i.content` is `nil` for RSS items that lack a `` element, causing `NoMethodError: undefined method 'scrub' for nil:NilClass` and crashing the job - should fall back to `i.description`", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The new `TopicEmbed.import_remote` call drops `created_at: Date.parse(t[:created_at])`, so all imported topics receive the current timestamp instead of preserving the original Disqus thread creation date", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "In `retrieved_recently?`, `$redis.setnx` and `$redis.expire` execute as two separate commands - if the process crashes between them, the key persists indefinitely without TTL, permanently blocking retrieval for that URL", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "`TopicEmbed.import_remote` calls `open(url).read` which throws unhandled network exceptions (`OpenURI::HTTPError`, `SocketError`, `Timeout::Error`, `Errno::ECONNREFUSED`) when the Disqus URL is inaccessible, crashing the entire import task mid-operation", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Adding `force: true` to `create_table` will silently DROP the existing `top_topics` table and all its data if the table already exists, destroying production data on accidental re-runs", - "path": null, - "line": null, - "source": "extracted" - } - ] - }, - "https://github.com/ai-code-review-evaluation/discourse-graphite/pull/1": { - "gemini": [ - { - "text": "The `self.downsize` method is defined twice in the file, causing the second definition to overwrite the first. This will break existing functionality that calls `downsize` with separate width and height arguments.", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Hardcoding `maxSizeKB` to `10 * 1024` (10MB) in JavaScript ignores the site settings for maximum file size, causing inconsistent client-side validation that may allow files the server will reject.", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The maximum download size for a URL is hardcoded to `10.megabytes` instead of using a configurable site setting like `SiteSetting.max_image_size_kb.kilobytes`.", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The number of downsizing attempts (5) and the downsizing percentage (80%) are hardcoded magic values that should be extracted into constants or made into configurable site settings.", - "path": null, - "line": null, - "source": "extracted" - } - ], - "claude": [ - { - "text": "Hardcoded file size limit breaks configurability - utilities.js lines 182, 246 hardcode 10MB instead of using SiteSettings.max_image_size_kb", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Tempfile.size not refreshed after downsizing - the while loop checks tempfile.size but this value is cached and won't reflect the downsized file's new size, causing the loop to always run 5 times", - "path": null, - "line": null, - "source": "extracted" - } - ], - "qodo": [ - { - "text": "SSRF/Resource exhaustion: FileHelper.download(url, 10.megabytes, ...) increases the allowable fetch size for user-provided URLs in the API path, which can amplify SSRF impact and bandwidth/CPU usage. FileHelper.download should enforce allowlists/denylists for internal IP ranges, redirects, and schemes", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "downsize method is defined twice with different signatures; in Ruby the later method definition overwrites the earlier one, which likely breaks callers expecting the (from, to, max_width, max_height, opts) variant and may cause runtime errors or incorrect downsizing behavior", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Server downloads API-provided URLs with a hard 10MB limit but then attempts to downsize to meet SiteSetting.max_image_size_kb - if max_image_size_kb is smaller than 10MB, the initial download can exceed the configured limit, and if larger, downloads will be capped unexpectedly", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The downsizing call passes \"80%\" as the dimensions argument, which may not match what downsize_instructions expects (typically WxH), so it may not actually reduce file size", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Client-side upload size checks and 413 messaging are hard-coded to 10MB, ignoring site settings (previously max_*_size_kb / max_image_size_kb), causing inconsistency where the browser blocks uploads that the server would accept or vice versa", - "path": null, - "line": null, - "source": "extracted" - } - ], - "bugbot": [ - { - "text": "Two `downsize` methods are defined with the same name\u2014one taking `max_width, max_height` arguments (line 145) and another taking a single `dimensions` argument (line 149). In Ruby, the second definition overwrites the first. The existing caller in `resize_emoji.rb` uses `OptimizedImage.downsize(path, path, 100, 100, opts)` with 4 positional arguments, which will now fail with an `ArgumentError` since only the 3-argument version exists.", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The client-side file size validation now uses a hardcoded 10MB limit for all file types instead of the type-specific `max_{type}_size_kb` site setting. While this works for images (which get auto-downsized server-side), attachments have no such downsizing. Users can select attachments larger than `max_attachment_size_kb`, the client accepts them, but the server rejects them during upload validation.", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The auto-downsize code passes `tempfile.path` to `OptimizedImage.downsize`, but `tempfile.path` is a system-generated path without the original file extension. The animated GIF detection in `optimize()` relies on matching the path against `/\\.GIF$/i`, which will never match. As a result, animated GIFs are processed with `downsize_instructions` which uses `[0]` to extract only the first frame, permanently destroying all animation frames.", - "path": null, - "line": null, - "source": "extracted" - } - ], - "copilot": [ - { - "text": "URL-download size cap is hardcoded to 10MB, ignoring site settings (max_image_size_kb / max_attachment_size_kb), which changes behavior for API URL uploads and may download larger files than admins configured, increasing bandwidth/CPU risk", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The downsize loop ignores the return value from OptimizedImage.downsize - if conversion fails, it will retry up to 5 times without making progress and still proceed to Upload.create_for, which will then reject the file as too large", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "No test coverage for the newly introduced behavior of accepting an oversized image and automatically downsizing it below SiteSetting.max_image_size_kb", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "For HTTP 413 responses, the displayed max_size_kb is hardcoded to 10MB, which risks showing a misleading limit to users since 413 is typically driven by web server/request limits", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "downsize is defined twice in Ruby - the later definition overrides the earlier one, so calls expecting the (from, to, max_width, max_height, opts) signature will pass wrong arguments into optimize, likely raising at opts[:allow_animation]", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "No tests cover downsize invocation paths - neither the legacy downsize(path, path, w, h, opts) call pattern nor the new geometry-string usage are tested", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Client-side upload size validation is hardcoded to 10MB and no longer respects per-type site settings (max_image_size_kb vs max_attachment_size_kb), allowing users to attempt uploads the server will reject and displaying incorrect limits in error messages", - "path": null, - "line": null, - "source": "extracted" - } - ], - "augment": [ - { - "text": "Hardcoding maxSizeKB to 10MB ignores the per-type max_*_size_kb site settings, so the client can accept files the server rejects or reject files when admins allow >10MB", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The hardcoded 10MB limit for images likely shouldn't apply to attachment uploads", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Using a fixed 10.megabytes download cap ignores SiteSetting.max_image_size_kb and can cause instance configuration to be bypassed or prevent larger configured limits", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The same 10MB constant is duplicated in multiple places, risking client/server limit drift", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The downsize loop only shrinks to ~33% of the original after 5 iterations (0.8^5), so a 10MB image may still exceed max_image_size_kb and proceed to Upload.create_for despite the comment promising automatic reduction", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "If OptimizedImage.downsize fails and returns false, the loop keeps retrying and the oversized file continues", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "There are two self.downsize method definitions where the latter overrides the former and changes the signature, which will raise ArgumentError for callers still passing max_width, max_height (e.g. app/jobs/regular/resize_emoji.rb)", - "path": null, - "line": null, - "source": "extracted" - } - ], - "propel": [ - { - "text": "Defining `downsize` twice means Ruby keeps only the second definition, causing existing calls that pass `width, height` to have the height argument treated as the `opts` hash, breaking thumbnail generation with incomplete dimensions", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Hard-coding uploads to 10 MB ignores the configurable `max__size_kb` limits, blocking users from uploading files allowed by server settings greater than 10 MB and allowing attempts that will be rejected when settings are less than 10 MB", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Returning a fixed '10 MB' message for every 413 response disregards the administrator's configured `max_image_size_kb`, showing misleading error messages to users", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Remote uploads are capped at 10 MB regardless of `SiteSetting.max_image_size_kb`, causing instances with higher limits to fail importing allowed files before the downsizing loop can run", - "path": null, - "line": null, - "source": "extracted" - } - ], - "baz": [ - { - "text": "downsize is now defined twice (first taking max_width,max_height, then taking dimensions), so the second definition overrides the first and the earlier width/height API disappears\u2014any caller still passing two numeric dimensions now breaks", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Upload validation now compares against a hardcoded 10 * 1024 instead of Discourse.SiteSettings['max_' + type + '_size_kb'], so uploads above the configured per-type limit (>10MB) get rejected on the client even if the site settings allow them", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The 413 handler now shows max_size_kb = 10 * 1024 instead of the configured site limit, so the error always claims 10MB even if the server rejects at a different size", - "path": null, - "line": null, - "source": "extracted" - } - ], - "coderabbit": [ - { - "text": "Declaring variables inside a switch case without a block allows other cases to erroneously access the declaration - wrap case 413 body in a block to scope maxSizeKB locally", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "tempfile.size won't reflect updated size after in-place modification by OptimizedImage.downsize because Ruby caches the file size - need to reopen or stat the file to get actual new size", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Using the same path for input and output in image processing is risky - if the operation fails mid-write, you lose the original", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The loop may never terminate correctly due to stale tempfile.size, and Line 72 will pass the wrong size to Upload.create_for", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "tempfile.size at line 72 will reflect the original file size, not the downsized size - use File.size(tempfile.path) to get the actual current size", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Duplicate method definition of downsize shadows the first version - Ruby does not support method overloading, so the second downsize definition (line 149) completely shadows the first one (line 145)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Any code calling downsize(from, to, max_width, max_height, opts) will fail because Ruby will try to match the 3-argument signature", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Hardcoded max size of 10 MB loses per-type flexibility - previous logic likely resolved max size dynamically based on type (image vs attachment)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Inline rescue nil swallows all exceptions silently, including network errors that might be worth logging", - "path": null, - "line": null, - "source": "extracted" - } - ], - "greptile": [ - { - "text": "duplicate method definition: `downsize` is defined twice (lines 145-147 and 149-151), second definition overrides the first", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "passing \"80%\" as `max_width` parameter to `downsize` which expects numeric dimensions in the original signature, but the refactored version has duplicate method definitions causing ambiguity", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "hardcoded 10MB limit doesn't match server-side `SiteSetting.max_image_size_kb`, causing frontend to accept files that may still be rejected after server-side downsizing fails", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "downsizing loop modifies file in-place but doesn't reload `tempfile.size`, so the loop condition `tempfile.size > SiteSetting.max_image_size_kb.kilobytes` never changes, causing infinite loop until attempts exhausted", - "path": null, - "line": null, - "source": "extracted" - } - ], - "kg": [], - "qodo-v2": [ - { - "text": "OptimizedImage.downsize is defined twice; the later definition overrides the earlier and changes the method arity, causing existing callers (e.g., ResizeEmoji) to raise ArgumentError at runtime", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "API URL uploads now download up to a hardcoded 10MB, ignoring SiteSettings and causing inconsistent behavior with server-side validation", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The downloader loop can exceed the requested cap by up to one chunk, wasting bandwidth/CPU and potentially downloading data that will be rejected anyway", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The new auto-downsize loop doesn't check whether downsizing succeeded and doesn't enforce that the final file is under max_image_size_kb before proceeding, which can cause repeated expensive processing and still fail uploads", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The downsize loop always uses allow_animated_thumbnails, which can strip animation for avatar uploads even when allow_animated_avatars is enabled", - "path": null, - "line": null, - "source": "extracted" - } - ], - "devin": [ - { - "text": "Duplicate `downsize` method definition in Ruby overwrites the 4-argument version - the new `downsize(from, to, dimensions, opts={})` at line 149 replaces `downsize(from, to, max_width, max_height, opts={})` at line 145, causing ArgumentError when callers like resize_emoji.rb:14 and uploads.rake:347 pass 5 arguments", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Hardcoded 10MB max upload size in validateUploadedFile ignores admin-configured `max_image_size_kb` and `max_attachment_size_kb` settings - replaced dynamic `Discourse.SiteSettings['max_' + type + '_size_kb']` with hardcoded `10 * 1024`", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Hardcoded 10MB in 413 error handler always shows wrong max size to users - replaced `Discourse.SiteSettings.max_image_size_kb` with hardcoded `10 * 1024` so error message always claims limit is 10240 KB regardless of actual server configuration", - "path": null, - "line": null, - "source": "extracted" - } - ], - "greptile-v4": [ - { - "text": "Two self.downsize methods are defined with the same name but different arities - the second definition completely overrides the first, making the 4-argument variant dead code that will never be called", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Any existing caller that passes four positional arguments (from, to, max_width, max_height) will hit the 3-argument method where max_height is silently bound to opts, causing TypeError at opts[:allow_animation] or opts[:force_aspect_ratio] lookup sites", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "tempfile.size returns stale size after in-place ImageMagick conversion because fstat() uses the file descriptor referring to the old inode after ImageMagick replaces the file via temp-then-rename pattern", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "While-loop condition never re-evaluates correctly - tempfile.size keeps returning the pre-downsize value, so the loop always runs all five iterations even when the file was already reduced below the limit after the first pass", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Upload.create_for receives wrong filesize - tempfile.size is passed as the file size argument, so the stored filesize column will record the original pre-downsize size instead of the actual downsized size", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Hardcoded 10 MB limit breaks non-image attachment validation - maxSizeKB was derived from site settings for both image and attachment types, but the replacement hard-codes 10 MB for all upload types, bypassing admin-configured max_attachment_size_kb", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Percentage dimensions string '80%' is incompatible with gifsicle for animated GIFs - gifsicle expects absolute geometry (WxH), not a percentage string, causing the gifsicle command to fail and animated GIFs to never be downsized", - "path": null, - "line": null, - "source": "extracted" - } - ], - "mra-max": [ - { - "text": "Hard-coded image size limit (10MB) in JS utilities instead of using site-wide settings, diverging from the pattern of reading max image size settings from SiteSettings", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Frontend validation uses hard-coded 10 MB limit instead of SiteSetting.max_image_size_kb, creating inconsistency with server/config", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Error handling for 413 uses hard-coded 10 MB limit, duplicating the hard-coded limit and increasing drift between frontend and backend configuration", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Multiple hard-coded 10MB limits for image handling in JS and Ruby rather than respecting SiteSetting.max_image_size_kb, reducing configurability", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Async processing for uploads via Scheduler::Defer.later may run without authenticated user context, as current_user may be unavailable or nil when the background task runs", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Remote URL download via API path can fetch arbitrary remote resources enabling SSRF-like behavior, and errors are swallowed by rescue nil leading to silent failures", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Call-site breakages in resize_emoji.rb due to signature changes to OptimizedImage.downsize - code still calls with (path, path, width, height, opts) but new signature expects single dimensions argument", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Breaking API surface changes for OptimizedImage resizing/downsize helpers with changed call signatures (dimensions as single string) may break other code paths", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Rescue nil usage around image processing (FastImage.new, optimize_image!) silently swallows errors, potentially leaving corrupted uploads or failed thumbnails unnoticed", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Gravatar URL uses insecure HTTP and is downloaded without explicit TLS validation, potentially leaking credentials", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "FileHelper.download lacks strict hostname allowlisting or SSL checks, can be exploited for SSRF or fetching restricted resources", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Auto-downsize loop for oversized images lacks dedicated tests for API upload path, iteration count verification, and failure scenarios", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "OptimizedImage dimensions string handling lacks explicit tests validating exact arguments passed to resize/downsize methods in all branches", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Hard-coded 10 MB cap in client-side validation and server error path not covered by tests exercising the 10 MB threshold or 413 error path", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "API-wide changes to image processing signatures may require updates to tests and other callers not covered in this patch", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Rescue nil around downloading external images in optimization flow can mask network failures and leave data partially processed", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Test site settings show max_image_size_kb: 3072 but hard-coded frontend limit breaks this assumption, creating drift between tests and runtime", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Tests mutating Discourse.SiteSettings.max_image_size_kb may fail or become flaky since frontend code now uses hard-coded 10 MB limit", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "No CHANGELOG.md entry documenting the new automatic downsize feature", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "PR description lacks summary of changes, rationale, and testing instructions", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "No README entry or documentation describing the new automatic image downsize behavior and hard-coded limit", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "DistributedMutex.synchronize added for thumbnail creation concurrency improvement - scope and usage require review", - "path": null, - "line": null, - "source": "extracted" - } - ], - "mra-nano": [ - { - "text": "Client-side file size validation and 413 error handling use a hard-coded 10 MB limit instead of reading from SiteSettings.max_image_size_kb, causing discrepancy between frontend and backend limits", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Uploads API URL flow downloads remote resources with a hard-coded 10.megabytes limit instead of using SiteSetting max_image_size_kb", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "resize_emoji.rb calls OptimizedImage.downsize with 4 numeric arguments (path, path, 100, 100, opts) which no longer aligns with the new interface after API refactor", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "OptimizedImage downsize and resize methods were refactored to accept a single dimensions string instead of separate width/height integers, breaking existing call sites that still pass numeric arguments", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Upload#create_for and related code have inconsistent usage patterns after OptimizedImage signature change - some code uses new dimensions string while specs and other code still use old call patterns", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "JS tests set Discourse.SiteSettings.max_image_size_kb to small values but production code uses fixed 10 MB threshold, causing tests to drift from reality", - "path": null, - "line": null, - "source": "extracted" - } - ], - "mra-ultra": [ - { - "text": "Client-side upload size checks hardcoded to 10MB, ignoring site settings - both pre-upload check and HTTP 413 error handler replace dynamic reads of Discourse.SiteSettings with hardcoded 10 * 1024 KB constant, displaying incorrect error messages", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Stale pre-downsize tempfile.size stored as upload filesize - Upload.create_for is called with tempfile.size after in-place downsize loop overwrites the file, causing incorrect filesize to be persisted and corrupting storage quota calculations", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Duplicate downsize definition silently replaces old signature - Ruby replaces 5-argument self.downsize with new 3-argument version, causing resize_emoji.rb caller to raise ArgumentError at runtime and breaking emoji resize operations", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Shell injection via unsanitized paths and dimensions in ImageMagick commands - from, to, and dimensions are interpolated directly into %W{} arrays and executed via backtick, allowing shell metacharacters to be interpreted", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "SSRF: API URL download limit increased from max_image_size_kb to hardcoded 10MB - allows API callers to trigger 10MB downloads from arbitrary URLs regardless of admin-configured limits, increasing attack surface for DoS or internal service probing", - "path": null, - "line": null, - "source": "extracted" - } - ], - "mra-b": [ - { - "text": "Hardcoded 10MB upload limit replaces configurable site settings in client-side validation, 413 error handler, and server-side remote download, breaking per-type configurability and causing client/server inconsistency", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Shell command injection vulnerability in OptimizedImage.convert_with due to unescaped file paths being interpolated into shell commands executed via backticks, allowing arbitrary command execution if attacker can influence path values", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Hardcoded upload limit breaks existing client-side tests that set SiteSettings.max_image_size_kb and assert validateUploadedFile honors it", - "path": null, - "line": null, - "source": "extracted" - } - ], - "mra-a": [ - { - "text": "Client-side file-size checks in validateUploadedFile and displayErrorForUpload are hardcoded to 10MB instead of using SiteSetting, causing inconsistent behavior when server limits differ from 10MB", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Hardcoded 10MB limit removes per-type limits that previously used 'max_' + type + '_size_kb' pattern for different file types", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "FileHelper.download in uploads_controller.rb uses hardcoded 10.megabytes instead of SiteSetting.max_image_size_kb, allowing downloads larger than administrator intended and enabling denial-of-service via resource exhaustion", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Shell invocation in optimized_image.rb uses unescaped interpolated paths/dimensions (from, to, dimensions) via backticks, creating command injection risk when values contain shell metacharacters", - "path": null, - "line": null, - "source": "extracted" - } - ], - "entelligence": [ - { - "text": "Hardcoding maxSizeKB to 10MB removes the configured site setting and makes uploads ignore admin limits, causing a functional regression when sites set different max sizes. Should restore use of Discourse.SiteSettings['max_' + type + '_size_kb']", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Using hardcoded 10MB in uploads_controller.rb ignores SiteSetting.max_image_size_kb, allowing API URL uploads to exceed the configured limit. This is a policy bypass that can lead to larger-than-allowed downloads", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Defining downsize a second time in optimized_image.rb overwrites the earlier downsize(from, to, max_width, max_height, opts) method. Callers passing width/height will now bind max_height into opts, breaking option handling and producing wrong dimensions", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Duplicate code detected: self.downsize function duplicates existing code at app/models/optimized_image.rb:145-147 with 90% similarity. Should consolidate into single method or use keyword arguments", - "path": null, - "line": null, - "source": "extracted" - } - ], - "sourcery": [ - { - "text": "Using a hard-coded 10MB download limit in FileHelper.download may diverge from the configured max_image_size_kb setting, potentially downloading and processing files that are later rejected, wasting bandwidth/CPU and violating admins' expectations", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Hard-coding the client-side max upload size to 10MB can desynchronize it from server-side limits and per-type settings, causing users to hit server-side rejections after the client has accepted the file", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The OptimizedImage.downsize method is redefined to take a dimensions string instead of width/height, which changes its public API and may break or confuse existing usages", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "In the automatic downsizing loop, there is no handling for the case where after the configured number of attempts the file is still over max_image_size_kb - it silently proceeds rather than aborting with a clear error", - "path": null, - "line": null, - "source": "extracted" - } - ], - "cubic-dev": [ - { - "text": "Hardcoding `maxSizeKB` to 10MB bypasses site-configured upload limits and breaks per-type (`image`/`attachment`) validation behavior", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "When called with percentage-based geometry (e.g. `\"80%\"`) on an animated GIF, the code routes through `downsize_instructions_animated` \u2192 `resize_instructions_animated`, which passes the percentage to `gifsicle --resize-fit`. Unlike ImageMagick, gifsicle does not support percentage geometry \u2014 it requires `WxH` format \u2014 so this will silently fail", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The second `def self.downsize(from, to, dimensions, opts={})` overwrites the first `def self.downsize(from, to, max_width, max_height, opts={})` since Ruby does not support method overloading. The existing caller in `ResizeEmoji` passes 5 arguments (`path, path, 100, 100, opts`), which will raise an `ArgumentError` at runtime because the surviving definition only accepts 3\u20134 arguments", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "`tempfile.size` will likely return the stale (original) file size after `OptimizedImage.downsize` replaces the file at the path. External tools like ImageMagick `convert` typically create a new inode (write-to-temp-then-rename), so `fstat()` on the old file descriptor still sees the old size. This makes the loop always run all 5 iterations and causes `Upload.create_for` to record the wrong file size. Use `File.size(tempfile.path)` instead", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Using allow_animated_thumbnails here will strip animation from large GIF uploads when that setting is off, because the downsize path keeps only the first frame. This setting is meant for thumbnails, not original uploads", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The 413 error path now reports a hardcoded 10MB limit instead of the configured max image size, which can show incorrect guidance to users", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The new `downsize` overload accepts a raw `dimensions` string (e.g. `\"80%\"`) with no validation against `SiteSetting.max_image_size_kb` or per-image-type limits. The controller hardcodes `\"80%\"` and retries up to 5 times, but this fixed factor may not converge for very large images or stricter per-type size settings", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "`OptimizedImage.downsize` returns `false` on failure, but the return value is not checked. If the downsize fails (e.g., ImageMagick error, corrupt image), the loop continues attempting to downsize a broken or unchanged file up to 5 times, wasting resources", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Hardcoded `10.megabytes` replaces the configurable `SiteSetting.max_image_size_kb.kilobytes` limit for all API URL downloads, not just images. This allows downloading up to 10MB from arbitrary URLs regardless of the site's configured maximum, which could waste server resources or be abused", - "path": null, - "line": null, - "source": "extracted" - } - ], - "mesa": [ - { - "text": "Changing from SiteSetting.max_image_size_kb.kilobytes to hardcoded 10.megabytes introduces a second file size limit separate from the configured site setting - should use an explicit separate setting (e.g., max_download_size_kb) rather than a magic number", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The return value of OptimizedImage.downsize() is not checked - if the downsize operation fails, the loop will continue burning retry attempts without making progress", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Missing logging to track retry attempts and failures for operational visibility in the downsize loop", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Two downsize methods defined with different signatures (one taking max_width, max_height and one taking dimensions string) - the duplicate definition will cause the second one to override the first, creating API ambiguity", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Client-side hardcoded 10MB limit in utilities.js contradicts server-side dynamic SiteSetting.max_image_size_kb, violating single source of truth principle", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Synchronous image processing in request path with 5-attempt retry loop blocks web workers in the HTTP request cycle, causing latency spikes and potential DoS amplification under concurrent large uploads with no timeout protection", - "path": null, - "line": null, - "source": "extracted" - } - ], - "codeant": [ - { - "text": "Hardcoded 10 MB limit in uploads_controller.rb instead of using SiteSetting.max_image_size_kb, causing the app to ignore admin-configured settings", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Hardcoded max size as 10 * 1024 in JS file instead of using Discourse.SiteSettings, inconsistent with server-side settings", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "In-place tempfile overwrite using OptimizedImage.downsize(tempfile.path, tempfile.path, ...) is fragile and can cause partial writes or file descriptor issues", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Using tempfile.size instead of File.size(tempfile.path) to check file size after downsizing may not reflect the actual on-disk size", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Repeated downsizing loop with 80% reduction has no check for whether the operation actually changed file size or succeeded", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "No abort mechanism if downsizing fails in the retry loop", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Possible duplicate method definitions in optimized_image.rb - both old downsize method and new downsize(from, to, dimensions, opts={}) may exist", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "optimize method signature changed from separate width/height args to single dimensions string without ensuring all call sites are updated", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "GIF detection using filename extension (from =~ /\\.GIF$/i) only checks extension, not whether GIF is actually animated, and could be spoofed", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Errors during conversion are swallowed with rescue and only logged instead of surfacing appropriate error to the user", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Units inconsistency in client-side code - maxSizeKB value is in KB but comment says 10MB, potentially confusing users", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Automatic downsizing runs imagemagick and imageoptim synchronously on web server, which could be expensive with concurrent uploads", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Missing tests for: upload larger than max size gets downsized, non-image large uploads rejected, failed downsizing returns error, client-side validation uses configured max size", - "path": null, - "line": null, - "source": "extracted" - } - ], - "kodus": [ - { - "text": "Hardcoded file size limit (10MB) in JavaScript replaces dynamic Discourse.SiteSettings lookup, causing client-side validation to diverge from server-configured limits - uploads may fail after wasting bandwidth if server limit is lower, or valid uploads may be incorrectly blocked if server limit is higher", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Null pointer dereference when URL download fails - rescue nil sets tempfile to nil, but code proceeds to call tempfile.size causing NoMethodError and 500 server error", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Method redefinition breaks existing callers - new downsize method (3 args) overwrites previous definition (4 args), causing ArgumentError for callers like Jobs::ResizeEmoji that use old signature", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Duplicate self.downsize method definitions in optimized_image.rb - Ruby silently discards first definition, leaving 4-argument form as dead code", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Hardcoded 10*1024 constants in utilities.js and 10.megabytes in uploads_controller.rb bypass admin-configured upload size restrictions for non-image attachments", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "SSRF vulnerability - user-controlled url parameter passed directly to FileHelper.download without allowlist or host validation, allowing requests to internal network resources or cloud metadata endpoints", - "path": null, - "line": null, - "source": "extracted" - } - ], - "claude-code": [ - { - "text": "Hardcoded 10MB limit on line 182 replaces dynamic max_attachment_size_kb setting, causing non-image attachments to pass client validation but fail server-side when they exceed the admin-configured limit but are under 10MB", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Error message in displayErrorForUpload on line 246 shows hardcoded 10MB instead of the actual configured limit, misleading users when server rejects files for exceeding smaller configured limits", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "PR defines downsize method twice (lines 145-151) - a 4-arg and 3-arg version - but Ruby's last definition wins, making the 4-arg version dead code", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Existing caller in app/jobs/regular/resize_emoji.rb:14 calls OptimizedImage.downsize with 5 arguments, which will raise ArgumentError at runtime since only the 3-4 arg version survives, breaking emoji resizing", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "For regular browser uploads, Rack tempfile paths lack .gif extension, so the GIF detection regex in optimize() never matches and convert with [0] strips animation from GIFs", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "For API URL uploads where .gif extension is preserved, gifsicle is triggered but --resize-fit 80% is invalid syntax (gifsicle requires WxH pixel dimensions, not percentages), causing silent failure", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The allow_animation option is effectively dead code since animated GIF downsizing fails through both upload paths", - "path": null, - "line": null, - "source": "extracted" - } - ], - "kodus-v2": [ - { - "text": "Dynamic site-setting lookup for max_image_size_kb/max_attachment_size_kb replaced with hardcoded 10 MB constant in JavaScript (utilities.js lines 182 and 246) and Ruby (uploads_controller.rb line 55), causing client-side validation to never reject files under 10 MB regardless of admin-configured limit and creating mismatch with server-side validation", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Unchecked in-place downsize loop in uploads_controller.rb lines 65-69 never checks return value of OptimizedImage.downsize, allowing ImageMagick failures to continue retrying on broken files and passing corrupt/zero-byte images to Upload.create_for", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "File.size may not reflect updated size after in-place write without re-reading the file handle in the downsize loop", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Ruby method overloading issue: second downsize definition (line 149, 3-arg form) silently replaces first definition (line 145, 4-arg form), causing callers like ResizeEmoji that use 5 positional arguments to get ArgumentError", - "path": null, - "line": null, - "source": "extracted" - } - ], - "qodo-v2-2": [ - { - "text": "OptimizedImage.downsize is defined twice and the later definition overrides the earlier one, removing the (from,to,max_width,max_height,opts) signature. Existing callers (e.g., Jobs::ResizeEmoji) still pass width/height and will raise ArgumentError at runtime, breaking background processing.", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Client-side validation now enforces a hardcoded 10MB max for uploads, ignoring SiteSetting.max_image_size_kb/max_attachment_size_kb that the server actually enforces. This causes incorrect client rejections when settings allow larger uploads and misleading client acceptance (followed by server rejection) when settings allow smaller uploads.", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "UploadsController#create_upload now caps API URL downloads at a fixed 10MB instead of respecting SiteSetting.max_image_size_kb. This breaks configured deployments that allow >10MB images (downloads fail early) and creates inconsistent limits compared to other URL-download paths that still use SiteSetting.max_image_size_kb.", - "path": null, - "line": null, - "source": "extracted" - } - ], - "qodo-v22": [ - { - "text": "OptimizedImage.downsize is defined twice, and the later 3-arg+opts definition overwrites the original width/height signature, so existing callers passing (from,to,width,height,opts) will raise ArgumentError. This breaks Jobs::ResizeEmoji at runtime and prevents emoji resizing from running.", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "For API URL uploads, the server now downloads up to a fixed 10MB regardless of configured max image/attachment sizes, wasting bandwidth/CPU for attachments that will be rejected and preventing downsizing for images >10MB because they never download. This changes behavior from being setting-driven to hardcoded and can be abused to force repeated 10MB downloads.", - "path": null, - "line": null, - "source": "extracted" - } - ], - "qodo-extended": [ - { - "text": "Client-side upload validation and 413 error messaging hardcode a 10MB maximum, ignoring per-site max_image_size_kb/max_attachment_size_kb settings, causing inconsistent behavior with server-side validation", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Server-side URL uploads in UploadsController are capped at 10MB regardless of configuration, conflicting with configurable SiteSettings", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "UploadsController#create_upload loops up to 5 downsizing attempts but never checks whether the image conversion succeeded, so failures silently continue and the upload may still exceed max_image_size_kb", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The PR adds a new def self.downsize(from, to, dimensions, opts={}) at line 149 without removing the old 4-arg version at line 145, causing Ruby to use last definition and breaking existing caller in resize_emoji.rb which passes 5 arguments", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "After OptimizedImage.downsize invokes an external convert process that overwrites the file at tempfile.path, tempfile.size uses fstat on the original file descriptor which may return stale size if convert replaces the file with a new inode, causing the loop to always run all 5 iterations and Upload.create_for to record incorrect file size metadata", - "path": null, - "line": null, - "source": "extracted" - } - ], - "cubic-v2": [ - { - "text": "Second `downsize` definition overrides the width/height version, so existing callers that pass separate max width and height will start raising `ArgumentError`", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Upload validation hardcodes 10 MB and ignores the configured image/attachment size limits from Discourse.SiteSettings", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The new `downsize` overload passes the `dimensions` string directly through to `gifsicle --resize-fit` for animated GIFs, but gifsicle only accepts pixel-based `WxH` geometry and does not support percentage strings, creating inconsistency where downsizing animated GIFs with percentage geometry will fail while static images succeed", - "path": null, - "line": null, - "source": "extracted" - } - ], - "qodo-extended-summary": [ - { - "text": "The downsizing loop ignores the boolean result of OptimizedImage.downsize, so conversion failures will still retry up to 5 times and then proceed to Upload.create_for with an oversized/invalid file instead of failing fast with a clear message", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The PR adds a new def self.downsize(from, to, dimensions, opts={}) without removing the existing 4-arg def self.downsize(from, to, max_width, max_height, opts={}). In Ruby there is no method overloading so the second definition silently replaces the first, breaking the existing caller in resize_emoji.rb which passes 5 arguments", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The dynamic Discourse.SiteSettings['max_' + type + '_size_kb'] is replaced with a hardcoded 10MB for all upload types, not just images, disabling admin-configurable client-side file size validation for attachments and creating inconsistency with server-side limits", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The while-loop evaluates tempfile.size to check if downsizing was successful, but tempfile is an open Ruby file descriptor pointing to the original deleted file because ImageMagick's convert replaces the file on disk. The loop will use stale size data and Upload.create_for will use the original large file", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "API URL uploads are now downloaded with a hardcoded 10MB cap instead of using the site's configured limits, which can unnecessarily consume bandwidth/IO and expands the amount of data an attacker can force the server to download per request before validation", - "path": null, - "line": null, - "source": "extracted" - } - ], - "macroscope": [ - { - "text": "The second `downsize` method definition on line 149 completely overwrites the first on line 145 because Ruby doesn't support method overloading. Any caller using the 4-argument form `downsize(from, to, max_width, max_height)` will have `max_width` assigned to `dimensions` and `max_height` assigned to `opts`, causing `TypeError: no implicit conversion of Symbol into Integer` when `optimize` evaluates `!!opts[:allow_animation]` (since `opts` is actually an Integer, and `Integer#[]` expects an Integer index, not a Symbol).", - "path": null, - "line": null, - "source": "extracted" - } - ] - }, - "https://github.com/ai-code-review-evaluation/discourse-graphite/pull/2": { - "gemini": [ - { - "text": "TopicUser.find_by can return nil if the user has never interacted with the topic, causing a NoMethodError when tu.notification_level is called - should use find_or_initialize_by instead", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Typo in function name 'stopNotificiationsText' should be 'stopNotificationsText' in the controller", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Typo in template 'stopNotificiationsText' should be 'stopNotificationsText' to match the controller correction", - "path": null, - "line": null, - "source": "extracted" - } - ], - "claude": [ - { - "text": "List-Unsubscribe header not updated to use per-topic unsubscribe URL - the PR adds per-topic unsubscribe functionality and passes unsubscribe_url in email template arguments, but the List-Unsubscribe header still points to the global user preferences URL instead of the new per-topic unsubscribe URL", - "path": null, - "line": null, - "source": "extracted" - } - ], - "qodo": [ - { - "text": "The new topics#unsubscribe endpoint is reachable via GET and changes server-side state (topic notification level). If CSRF protections are not enforced, a malicious site could cause a logged-in user's browser to hit the URL and alter preferences unintentionally, and email scanners/link prefetchers could also trigger it. Consider requiring a non-GET method with CSRF token or adding a confirmation page/tokenized link.", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The unsubscribe action assumes a TopicUser row exists and that current_user is present. If the topic has never been visited (no TopicUser row) or the user is not logged in, tu/current_user can be nil and tu.notification_level / current_user.id will raise. Consider using TopicUser.change (which upserts), or guard/redirect when unauthenticated, and handle nil TopicUser safely.", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Unsubscribe is implemented as a GET route that mutates state (toggles notification level). This is surprising from an HTTP semantics perspective and makes it easier to trigger unintended changes (e.g., link prefetching/crawlers). Consider making the state change a POST/PUT with CSRF protection, while still allowing a GET landing page that requires an explicit user action to confirm.", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The computed property name stopNotificiationsText is misspelled (should be stopNotificationsText). This typo increases maintenance cost and can confuse future contributors.", - "path": null, - "line": null, - "source": "extracted" - } - ], - "bugbot": [ - { - "text": "The unsubscribe action uses TopicUser.find_by which returns nil if no record exists for the user-topic combination, then calls tu.notification_level without a nil check, causing a NoMethodError when users click unsubscribe links for topics they've received notifications about but never directly viewed", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The route is defined as topicUnsubscribe (a top-level route), but the template is placed at templates/topic/unsubscribe.hbs instead of templates/topic-unsubscribe.hbs, causing Ember's resolver to not find the template and rendering the unsubscribe page blank or broken", - "path": null, - "line": null, - "source": "extracted" - } - ], - "copilot": [ - { - "text": "The unsubscribe method calls find_by which can return nil if the TopicUser record doesn't exist, causing potential errors when calling methods on nil", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The unsubscribe_url being passed is a relative path but needs to be a full URL to work properly in emails - should use Discourse.base_url to construct complete URL", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The new unsubscribe action lacks test coverage for handling missing TopicUser records, setting notification levels, redirect behavior when slug doesn't match, and authentication requirements", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Trailing comma after the last property definition is inconsistent with codebase conventions and could cause issues in older JavaScript environments", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Typo in property name 'stopNotificiationsText' should be 'stopNotificationsText' (missing 't' in notifications) in the controller", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Typo in property name 'stopNotificiationsText' should be 'stopNotificationsText' in the template file to match the corrected controller property name", - "path": null, - "line": null, - "source": "extracted" - } - ], - "augment": [ - { - "text": "topicUnsubscribe is declared as a top-level route, but the template lives at templates/topic/unsubscribe.hbs (which resolves to topic.unsubscribe), so Ember may not find/render the intended template and the unsubscribe page may render incorrectly or blank", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "TopicUser.find_by(...) can return nil if the user has no topic_users row yet, and the subsequent tu.notification_level access will raise, causing 500 errors when hitting the unsubscribe URL", - "path": null, - "line": null, - "source": "extracted" - } - ], - "propel": [ - { - "text": "TopicUser.find_by(...) returns nil when a user hits the unsubscribe link before a topic_users row exists (e.g. they were added via group/category notification but never loaded the topic), causing NoMethodError on the next line when calling tu.notification_level, resulting in a 500 error that prevents the user from opting out", - "path": null, - "line": null, - "source": "extracted" - } - ], - "baz": [ - { - "text": "TopicUser.find_by(...) in topics#unsubscribe returns nil for users without a TopicUser record, and subsequent access to tu.notification_level causes a 500 error - need to use find_or_initialize_by or TopicUser.change/upsert helper to handle missing records", - "path": null, - "line": null, - "source": "extracted" - } - ], - "coderabbit": [ - { - "text": "Missing trailing comma in multiline filter list, causing lint warning (Style/TrailingCommaInArrayLiteral)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "TopicUser.find_by can return nil in unsubscribe action, causing potential crash", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Unsubscribe action uses toggle logic which can re-subscribe users or flip state due to email link prefetching - should always set to muted", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Duplicate respond_instructions div in email template (appears at line 6 and line 19)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Header class has leading dot (.previous-discussion instead of previous-discussion) preventing CSS targeting", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "State-changing unsubscribe action is accessible via GET request, should use POST/PUT or require signed token", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Typo in property name: stopNotificiationsText should be stopNotificationsText", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Test for List-Unsubscribe header only checks presence, should assert the actual URL value to prevent regressions", - "path": null, - "line": null, - "source": "extracted" - } - ], - "greptile": [ - { - "text": "nil check missing - `find_by` can return nil if no TopicUser record exists, causing `save!` to crash", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "XSS vulnerability - triple braces `{{{...}}}` render unescaped HTML, allowing malicious topic titles to inject scripts", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "typo: 'Notificiations' should be 'Notifications'", - "path": null, - "line": null, - "source": "extracted" - } - ], - "kg": [ - { - "text": "Typo in property name - stopNotificiationsText should be stopNotificationsText in app/assets/javascripts/discourse/controllers/topic-unsubscribe.js.es6", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Typo in property name - stopNotificiationsText should be stopNotificationsText in app/assets/javascripts/discourse/templates/topic/unsubscribe.hbs", - "path": null, - "line": null, - "source": "extracted" - } - ], - "qodo-v2": [ - { - "text": "The unsubscribe endpoint mutates notification state via GET request, which is not CSRF-protected and can be triggered cross-site (e.g., via an img tag) for any logged-in user, allowing silent mute/unmute of topics without user intent", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The unsubscribe action uses TopicUser.find_by + direct mutation + save!, which can cause a 500 error if the row doesn't exist and bypasses TopicUser.change which performs upsert, sets reason/timestamps, and publishes MessageBus updates, leading to crashes and inconsistent notification state/UI", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The unsubscribe Ember route tries to hide notification reason text by setting details.notificationReasonText to null, but notificationReasonText is a computed property that always returns a translated string based on notification_level/notifications_reason_id, so the reason text will still appear", - "path": null, - "line": null, - "source": "extracted" - } - ], - "devin": [ - { - "text": "NullPointerException in unsubscribe action when TopicUser record doesn't exist - TopicUser.find_by returns nil if user has never interacted with the topic, and the next line calls tu.notification_level on the potentially-nil result, causing NoMethodError and a 500 Internal Server Error", - "path": null, - "line": null, - "source": "extracted" - } - ], - "greptile-v4": [ - { - "text": "TopicUser.find_by returns nil when no record exists for the user/topic combination, and calling .notification_level on nil will raise NoMethodError, crashing the unsubscribe flow", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The unsubscribe action immediately and silently changes the user's notification level on a GET request before presenting any UI, which can be triggered by email clients, browser prefetching, link-preview crawlers, or accidental clicks causing irreversible state change without confirmation", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Typo in computed property name: stopNotificiationsText has an extra 'i' and should be stopNotificationsText", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Potential XSS vulnerability via unescaped triple-mustache {{{stopNotificiationsText}}} which renders user-provided topic title (fancyTitle) as raw HTML without escaping", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "List-Unsubscribe header omits the per-topic URL - only the user-preferences URL is listed, but the new per-topic unsubscribe URL should also be included for RFC 2369/8058 compliance", - "path": null, - "line": null, - "source": "extracted" - } - ], - "mra-max": [ - { - "text": "REST route design uses GET for state-changing unsubscribe action, conflicting with RESTful/CSRF-safe semantics", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "State-changing GET endpoint for unsubscribe does not require CSRF token, enabling CSRF attacks via crafted links", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Topic.unsubscribe_url method relies on undefined 'url' method, will raise NoMethodError at runtime", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "XSS risk in stopNotificiationsText - interpolates user-provided fancyTitle into HTML translation without proper sanitization", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Nil dereference risk in topic-from-params.js when closestPost is null - code assumes non-null and calls methods on it", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Unescaped content in unsubscribe template using triple-mustache {{{stopNotificiationsText}}} with unsanitized fancyTitle", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "CSRF handling bypass - unsubscribe action listed in skip_before_filter :check_xhr bypasses XHR/CSRF checks", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Unsubscribe requires login (ensure_logged_in) but is used in emails, blocking legitimate anonymous unsubscription from email links", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Unsubscribe URL passed into emails relies on topic.unsubscribe_url which may render incorrect links if URL construction changes", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "XSS concern in email unsubscribe template using raw() to render translation that could contain untrusted HTML", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "New unsubscribe routes could create ambiguity with existing routes and require careful route resolution", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Widespread cross-cutting changes for unsubscribe feature across JS, Rails, mailers, templates, routes, locales, and CSS increases integration risk", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Email HTML template restructuring for unsubscribe could affect rendering in email clients", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "List-Unsubscribe header support adds coupling between mail rendering and unsubscribe URL generation", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Topic.unsubscribe_url uses string interpolation which is brittle if route structure or base URL changes", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Ensure interpolation syntax {{title}} in client locales aligns with how I18n.t is called on client side", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Verify translations across languages and ensure unsubscribe keys are used consistently in UI and emails", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Need broader test coverage for unsubscribe flow including different link formats, List-Unsubscribe header presence, and template substitution", - "path": null, - "line": null, - "source": "extracted" - } - ], - "mra-nano": [ - { - "text": "Frontend Ember router uses ':id' while Rails expects ':topic_id' in the unsubscribe route path, creating a naming boundary risk", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "XSS risk in dropdown-button.js.es6 where topic title is concatenated into HTML string without escaping", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Nil dereference in TopicsController#unsubscribe when TopicUser record doesn't exist - find_by returns nil and code immediately accesses tu.notification_level", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "CSRF risk: unsubscribe action uses GET routes to change state (mutates TopicUser notification level), violating Rails conventions", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "No tests for unauthenticated access to unsubscribe action despite being protected by ensure_logged_in", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Topic#unsubscribe_url lacks unit tests for URL construction correctness", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Email message builder needs test coverage for both presence and absence of unsubscribe_url to verify List-Unsubscribe header and body rendering", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Consistent misspelling 'stopNotificiationsText' in topic-unsubscribe.js.es6 controller and unsubscribe.hbs template", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "unsubscribe_url passed to email template should be guarded to ensure template resilience to missing values and prevent exploitation to unsubscribe another user", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Locale files add unsubscribe keys with %{unsubscribe_url} placeholder requiring all locales to supply this value with validated translations", - "path": null, - "line": null, - "source": "extracted" - } - ], - "mra-ultra": [ - { - "text": "XSS vulnerability via triple-stache unescaped rendering of stopNotificiationsText - the template uses {{{stopNotificiationsText}}} which renders HTML from fancyTitle without escaping, allowing stored XSS if topic title contains malicious HTML", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "unsubscribe action requires login via ensure_logged_in before_filter, but email unsubscribe links are clicked by unauthenticated users who will be redirected to login and not returned to the unsubscribe URL", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "unsubscribe_link i18n string now requires %{unsubscribe_url} interpolation but only send_notification_email passes this key - all other emails using add_unsubscribe_link: true will raise I18n::MissingInterpolationArgument", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "topic-unsubscribe route's model hook calls PostStream.loadTopicView without error handling - network errors, 404s, or 403s will propagate as unhandled rejections", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "unsubscribe_url from opts is passed unsanitized into email HTML via I18n interpolation - if URL contains Markdown-special characters or javascript: scheme it could inject malicious content", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "XSS vulnerability in dropdown-button component where title is concatenated directly into HTML buffer without escaping", - "path": null, - "line": null, - "source": "extracted" - } - ], - "mra-b": [ - { - "text": "XSS vulnerability: unsubscribe template renders {{{stopNotificiationsText}}} (triple-stash/unescaped) which interpolates user-controlled model.fancyTitle into HTML, allowing malicious topic titles to inject arbitrary HTML/JS", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Typo in property name 'stopNotificiations' (should be 'stopNotifications') hurts maintainability", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "unsubscribe action raises NoMethodError when TopicUser.find_by returns nil for user/topic combination, causing 500 error", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "unsubscribe action raises error when TopicUser exists but notification_level column is NULL, as comparing nil to Integer fails", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "MessageBuilder's add_unsubscribe_link uses I18n translation requiring unsubscribe_url interpolation but doesn't assert template_args includes :unsubscribe_url, causing I18n::MissingInterpolationArgument exception and email delivery failures", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "List-Unsubscribe header only points to general user preferences URL, not the per-topic unsubscribe URL included in email body, causing inconsistency for mail clients using one-click unsubscribe", - "path": null, - "line": null, - "source": "extracted" - } - ], - "mra-a": [ - { - "text": "Per-topic unsubscribe email link requires login (ensure_logged_in) instead of allowing one-click unsubscribe, and lacks secure per-user signed or single-use token, exposing endpoint to CSRF and enumeration risks", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Setting nested Ember property topic.set('details.notificationReasonText', null) may throw or silently fail if topic.get('details') is undefined/null", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Template uses triple-stash {{{stopNotificiationsText}}} to render raw HTML without explicit sanitization, creating potential XSS risk if model.fancyTitle contains unsafe HTML", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "perform_show_response now renders :show unconditionally, changing control flow and potentially causing double renders or rendering wrong template for callers like topics#unsubscribe", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "MessageBuilder may receive incomplete args if add_unsubscribe_link is set without passing unsubscribe_url, and List-Unsubscribe email header omits the new per-topic unsubscribe URL", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Conditional rendering of title in dropdown-button removes H4 element when title is falsy, potentially breaking CSS/JS layout logic or tests expecting the h4 to exist", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Ember controller property misspelled as stopNotificiationsText instead of stopNotificationsText, causing maintainability issues and potential bugs if other code expects correct spelling", - "path": null, - "line": null, - "source": "extracted" - } - ], - "entelligence": [ - { - "text": "TopicUser.find_by will return nil if the user has no existing record for this topic, causing a NoMethodError on the next line. Need to use find_or_initialize_by(user_id: current_user.id, topic_id: params[:topic_id]) or add a nil check to handle users who haven't interacted with the topic yet.", - "path": null, - "line": null, - "source": "extracted" - } - ], - "sourcery": [ - { - "text": "TopicUser.find_by may return nil when the user's first interaction with the topic is via email, causing tu.notification_level to raise. Handle the nil case explicitly (e.g., create a TopicUser with the correct level or return/redirect safely) so the unsubscribe action doesn't error for these users.", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "unsubscribe_url currently appends /unsubscribe to url, which is usually relative (e.g. /t/slug/123). This is interpolated directly into the email copy via %{unsubscribe_url}, so the email will contain a relative link that may not work in many mail clients. Build an absolute URL using Discourse.base_url or an existing helper.", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Clearing details.notificationReasonText on the shared topic model means that if the user returns to the normal topic view in the same session, the notification reason will still be null and never reappear. Either restore the original value on deactivate/willTransition, or avoid mutating the model and use a route/controller-level flag to hide the text in the template.", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The class attribute is set to .previous-discussion, so the element's class name includes the dot and won't match the .previous-discussion CSS selector. Change it to class='previous-discussion' so the selector applies correctly.", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "In app/views/email/notification.html.erb the %{respond_instructions} footer is now rendered both inside the context_posts.present? block and again after the
, which will duplicate the instructions when context_posts are present; consider only rendering it once.", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The TopicUnsubscribe client route is only defined for /t/:slug/:id/unsubscribe, but the server also exposes /t/:topic_id/unsubscribe; if users can hit the slugless URL directly in the browser, consider adding a matching Ember route or always redirecting to the slugged form to avoid inconsistent client behavior.", - "path": null, - "line": null, - "source": "extracted" - } - ], - "cubic-dev": [ - { - "text": "`TopicUser.find_by` can return `nil` if the user has no record for this topic, causing a `NoMethodError` on the next line. Add a nil guard or use `find_or_create_by` to handle the case where no `TopicUser` record exists.", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The unsubscribe route uses GET but the controller action mutates state (`tu.save!`). GET requests must be idempotent per HTTP semantics. Since these links will appear in emails, email security scanners, link prefetchers, and anti-spam crawlers commonly follow GET links automatically \u2014 this would silently unsubscribe users without their intent. The GET route should render a confirmation page, and the actual state change should happen via a POST/PUT.", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Bug: `user_id` is assigned `topic` instead of `user` in the fallback branch two lines above. When `track_visit!` is called with raw integer IDs (as it is from `TopicsController`), `user_id` receives the topic ID, so this query updates the wrong user's visit record. The fix is on the `user_id = \u2026` line: change `\u2026 : topic` to `\u2026 : user`.", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The `title` is concatenated directly into HTML without escaping, which is an XSS risk. Use `Handlebars.Utils.escapeExpression(title)` to sanitize the value before inserting it into the buffer.", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The HTML class value `.previous-discussion` includes a leading dot, which is CSS selector syntax and should not appear in an HTML `class` attribute. This prevents CSS rules targeting `.previous-discussion` from matching this element.", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "`bottom: initial` is not supported in IE. Use `bottom: auto` instead \u2014 it's equivalent (since `auto` is the initial value of `bottom`) and has universal browser support.", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Typo in property name: `stopNotificiationsText` should be `stopNotificationsText` (extra 'i' in \"Notificiations\"). The matching template also has the same misspelling, so both should be corrected together.", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The shared `unsubscribe_link` locale now requires `%{unsubscribe_url}`, which is not part of the default `template_args` in `MessageBuilder`. Unlike `user_preferences_url` (always initialized), `unsubscribe_url` is only provided by the topic-notification caller. If `add_unsubscribe_link` is ever set to true without passing `unsubscribe_url`, this will raise `I18n::MissingInterpolationArgument`. Consider either adding a default `unsubscribe_url` in `MessageBuilder#initialize` or splitting the topic-specific unsubscribe text into a separate locale key.", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Use an arrow function instead of `const self = this`. The rest of this diff already adopts ES6 features (shorthand methods, `const`); the `self` capture pattern is the one leftover that arrow functions make unnecessary.", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Inconsistent trailing punctuation: `stop_notifications` ends with a period but `change_notification_state` does not. Since both strings are in the same `unsubscribe` block and likely displayed together, the missing period is a visible inconsistency.", - "path": null, - "line": null, - "source": "extracted" - } - ], - "mesa": [ - { - "text": "find_by can return nil if no TopicUser record exists for this user/topic combination, causing NoMethodError when trying to access tu.notification_level on nil - need nil check or find_or_create pattern", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Unsubscribe action relies on current_user from active session without signed token verification, meaning if user clicks email link while logged in as different user it will modify wrong user's notification preferences", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "HTTP semantics violation: action mutates state (changing notification_level) on GET request, violating idempotency principles and causing issues with browser prefetching, email client link scanning, and proxy caching", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Naming inconsistency: typo 'stopNotificiationsText' (missing 'c') in both frontend controller and template", - "path": null, - "line": null, - "source": "extracted" - } - ], - "codeant": [ - { - "text": "Crash when TopicUser record is missing - if no TopicUser exists for that user/topic, tu will be nil and the code will raise NoMethodError. Should use TopicUser.change() or find_or_initialize_by to handle create safely", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Unsubscribe URL should be absolute for email links and List-Unsubscribe headers - if topic.url returns a path, the unsubscribe URL will be relative which is not robust for emails", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Missing permission/visibility checks - ensure guardian/permission logic used in show is applied to unsubscribe, and users who cannot see a topic can't use the unsubscribe URL to discover metadata", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Missing tests for TopicsController#unsubscribe - need tests for when TopicUser exists (notification_level toggles), when TopicUser does not exist (TopicUser is created), and redirect behavior", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Missing integration test to ensure notification emails include topic-specific unsubscribe link and List-Unsubscribe header contains absolute topic unsubscribe URL", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Typo in stopNotificiationsText - misspelled 'Notificiations' should be 'Notifications'", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Need to confirm text-only email templates also include unsubscribe link, not just HTML templates", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Email List-Unsubscribe header should contain valid angle-bracketed URL or mailto per spec, and must be absolute", - "path": null, - "line": null, - "source": "extracted" - } - ], - "kodus": [ - { - "text": "Null pointer dereference in closestPostForPostNumber - the method can return null but the code calls .get('post_number') on the result without a null check, causing TypeError", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Redundant data fetch on unsubscribe page load - backend preloads topic data but frontend route re-fetches the same data via separate AJAX call instead of using preloaded data", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Missing interpolation argument in unsubscribe_link localization - the new %{unsubscribe_url} variable is only available in topic-specific notifications but the key is also used by digest emails which lack this context, causing I18n::MissingInterpolationArgument error", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Inconsistent parameter passing - route for topic summaries without slug omits defaults: {summary: true} while the route with slug includes it, causing params[:summary] to be nil for slug-less URLs", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Route shadowing - topicUnsubscribe route at /t/:slug/:id/unsubscribe will never be reached because the topic resource with child route fromParamsNear (/:nearPost) is defined before it and greedily matches 'unsubscribe' as the nearPost parameter", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Null pointer dereference in TopicUser.find_by - returns nil when user has no existing TopicUser record but code calls tu.notification_level on nil, raising NoMethodError", - "path": null, - "line": null, - "source": "extracted" - } - ], - "claude-code": [ - { - "text": "The unsubscribe action requires ensure_logged_in but the unsubscribe URL is embedded in notification emails - when logged-out users click the link, they are silently redirected to the homepage with no error message and no unsubscribe action taken, making the per-topic email unsubscribe feature non-functional for the most common use case", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The unsubscribe_link i18n string now requires %{unsubscribe_url} interpolation but Email::MessageBuilder provides no default value for it - any plugin using build_email with add_unsubscribe_link: true without providing unsubscribe_url will raise I18n::MissingInterpolationArgument", - "path": null, - "line": null, - "source": "extracted" - } - ], - "kodus-v2": [ - { - "text": "Null pointer dereference in the unsubscribe action of topics_controller.rb: TopicUser.find_by returns nil when no record exists, but tu.notification_level is called unconditionally, causing NoMethodError for users without a TopicUser record", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Route path inconsistency between Ember client and Rails server: Ember route defines topicUnsubscribe with path /t/:slug/:id/unsubscribe using params.id, but Rails expects params[:topic_id], causing TopicView.new to receive nil", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Unsubscribe link rendering conditional causes respond_instructions placeholder to not be replaced when add_unsubscribe_link is false, leaving raw %{respond_instructions} text in email body", - "path": null, - "line": null, - "source": "extracted" - } - ], - "qodo-v2-2": [ - { - "text": "TopicsController#unsubscribe dereferences tu.notification_level immediately after TopicUser.find_by(...) without a nil check, causing 500 errors when a user has no TopicUser row for that topic", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The unsubscribe endpoint is routed as GET but mutates TopicUser.notification_level and saves, enabling CSRF attacks where cross-site requests can toggle a logged-in user's topic notification state", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The unsubscribe Ember route sets details.notificationReasonText to null, but notificationReasonText is a read-only computed property with no setter, causing the unsubscribe page transition to throw/assert and break", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The router defines a top-level topicUnsubscribe route, but the template was added as templates/topic/unsubscribe.hbs (nested route template), causing Ember to fail template resolution and render an error/blank page", - "path": null, - "line": null, - "source": "extracted" - } - ], - "qodo-v22": [ - { - "text": "TopicsController#unsubscribe calls TopicUser.find_by and then dereferences tu.notification_level without handling a missing row or NULL notification_level, which can raise and break the unsubscribe link", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The unsubscribe endpoint is exposed as GET but persists a notification_level change, which is vulnerable to CSRF and can be triggered unintentionally by email link prefetch/scanners", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The Ember router defines a top-level topicUnsubscribe route, but the added template is templates/topic/unsubscribe.hbs and the route/controller don't override templateName, so the unsubscribe UI template will not be found/rendered", - "path": null, - "line": null, - "source": "extracted" - } - ], - "qodo-extended": [ - { - "text": "TopicsController#unsubscribe calls tu.notification_level even when TopicUser.find_by returns nil, causing a 500 error for users without a topic_users row", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "TopicsController#unsubscribe can raise when notification_level is NULL because the code compares nil to an integer", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The unsubscribe action is routed via GET but mutates database state, bypassing CSRF protection since Rails skips CSRF verification for GET requests", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "External pages can silently unsubscribe a logged-in user via an embedded image tag or link due to GET-based state mutation", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Email client prefetchers will trigger the unsubscribe mutation when users receive the unsubscribe email", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The title property is concatenated directly into HTML string without escaping, enabling cross-site scripting if the title contains user-controlled content", - "path": null, - "line": null, - "source": "extracted" - } - ], - "cubic-v2": [ - { - "text": "Rendering {{title}} inside HTML translation creates an XSS vulnerability on the unsubscribe page because the title is interpolated and inserted with triple-stash without escaping", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Template is named for a nested topic.unsubscribe route, but the router/controller/view use the top-level topicUnsubscribe route, so the new page content will not be picked up unless the template is renamed to match that route", - "path": null, - "line": null, - "source": "extracted" - } - ], - "qodo-extended-summary": [ - { - "text": "TopicsController#unsubscribe dereferences tu.notification_level and tu.save! even when TopicUser.find_by returns nil or when notification_level is NULL, causing a 500 error instead of toggling state", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The unsubscribe action is protected by ensure_logged_in, so logged-out users clicking from email cannot unsubscribe, defeating the primary email-driven unsubscribe path", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The unsubscribe action modifies TopicUser.notification_level and calls save! but is routed via HTTP GET, bypassing Rails CSRF protection and allowing attackers to forge unsubscribe requests via embedded img tags or link prefetching", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "When context_posts are present, the notification email template contains %{respond_instructions} both inside the conditional block and unconditionally at the bottom, causing the respond instructions text to appear twice in the rendered email", - "path": null, - "line": null, - "source": "extracted" - } - ], - "macroscope": [ - { - "text": "TopicUser.find_by on line 105 returns nil when the user has never interacted with the topic, but line 107 immediately calls tu.notification_level without a nil check, causing NoMethodError when the record doesn't exist", + "text": "TestLogger.FromContext returns a new *TestLogger instance instead of returning the original instance (f), causing logs written through the returned logger to be recorded on an ephemeral instance that is discarded, preventing tests from verifying their logs", "path": null, "line": null, "source": "extracted" @@ -44930,4 +34554,4 @@ } ] } -} \ No newline at end of file +} diff --git a/offline/results/anthropic_claude-opus-4-5-20251101/evaluations.json b/offline/results/anthropic_claude-opus-4-5-20251101/evaluations.json index 1c3ad4f..c111649 100644 --- a/offline/results/anthropic_claude-opus-4-5-20251101/evaluations.json +++ b/offline/results/anthropic_claude-opus-4-5-20251101/evaluations.json @@ -44103,33 +44103,40 @@ "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__macroscope__PR79265__20260309/pull/1" } }, - "https://github.com/ai-code-review-evaluation/discourse-graphite/pull/9": { + "https://github.com/grafana/grafana/pull/76186": { "gemini": { "skipped": false, "true_positives": [], - "false_positives": [], + "false_positives": [ + { + "candidate": "SubscribeStream and RunStream methods are not being instrumented with a contextual logger even though they have access to PluginContext, which means logs generated during stream operations won't contain useful contextual information like pluginId" + }, + { + "candidate": "Test function name should be renamed to align with the file rename" + } + ], "false_negatives": [ { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" + "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", + "severity": "High" }, { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", + "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", "severity": "Low" } ], "errors": [], - "total_candidates": 0, + "total_candidates": 2, "total_golden": 2, "tp": 0, - "fp": 0, + "fp": 2, "fn": 2, "errors_count": 0, "precision": 0.0, "recall": 0.0, "tool": "gemini", - "repo_name": "discourse__discourse-graphite__gemini__PR9__20260128", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__gemini__PR9__20260128/pull/1" + "repo_name": "grafana__grafana__gemini__PR76186__20260128", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__gemini__PR76186__20260128/pull/1" }, "claude": { "skipped": false, @@ -44137,11 +44144,11 @@ "false_positives": [], "false_negatives": [ { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" + "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", + "severity": "High" }, { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", + "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", "severity": "Low" } ], @@ -44155,8 +44162,8 @@ "precision": 0.0, "recall": 0.0, "tool": "claude", - "repo_name": "discourse__discourse-graphite__claude__PR9__20260127", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__claude__PR9__20260127/pull/1" + "repo_name": "grafana__grafana__claude__PR76186__20260127", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__claude__PR76186__20260127/pull/1" }, "graphite": { "skipped": false, @@ -44164,11 +44171,11 @@ "false_positives": [], "false_negatives": [ { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" + "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", + "severity": "High" }, { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", + "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", "severity": "Low" } ], @@ -44182,190 +44189,169 @@ "precision": 0.0, "recall": 0.0, "tool": "graphite", - "repo_name": "discourse__discourse-graphite__graphite__PR9__20260123", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__graphite__PR9__20260123/pull/1" + "repo_name": "grafana__grafana__graphite__PR76186__20260123", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__graphite__PR76186__20260123/pull/1" }, "qodo": { "skipped": false, - "true_positives": [ - { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low", - "matched_candidate": "The ensure_loaded! method tracks loaded locales via @loaded_locales which is mutated without synchronization. Two threads may attempt to load the same locale simultaneously, or @loaded_locales may be observed in a partially updated state. Consider adding a lock around locale loading and the set update.", - "confidence": 0.98, - "reasoning": "The candidate issue directly identifies the same thread-safety concern with @loaded_locales that the golden comment references. The candidate explicitly mentions that @loaded_locales is 'mutated without synchronization' and that 'two threads may attempt to load the same locale simultaneously' or observe '@loaded_locales in a partially updated state.' This is exactly the lazy @loaded_locales thread-safety issue mentioned in the golden comment." - } - ], + "true_positives": [], "false_positives": [ { - "candidate": "I18n.fallbacks.ensure_loaded! is executed on every request in set_locale, which may introduce unnecessary per-request work and potentially repeated filesystem loads depending on caching. Consider ensuring fallbacks are loaded once per locale change or at boot." + "candidate": "FromContext in the test logger returns a completely new logger instance, which may drop any previously accumulated logger state/context and can hide bugs in production behavior. Consider returning f or f.New(...) (or otherwise preserving existing context) so tests more closely match real contextual logging behavior." + }, + { + "candidate": "FromContext relies on a type assertion to *log.ConcreteLogger. If d.l.FromContext(ctx) returns a different log.Logger implementation, this falls back to d.New() and may unexpectedly discard contextual attributes. Consider a safer wrapper strategy that does not depend on a concrete type, or ensure the underlying logger type is guaranteed here." + }, + { + "candidate": "Logging enrichment is now provided by a separate middleware, while LoggerMiddleware consumes context via logger.FromContext(ctx). This creates a strong dependency on middleware ordering and inclusion across all stacks where LoggerMiddleware is used. Validate all middleware assembly sites ensure the contextual logger middleware always runs before request logging (and that missing it doesn't silently degrade log fields)." } ], "false_negatives": [ { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", + "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", + "severity": "High" + }, + { + "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", "severity": "Low" } ], "errors": [], "total_candidates": 3, "total_golden": 2, - "tp": 1, - "fp": 1, - "fn": 1, + "tp": 0, + "fp": 3, + "fn": 2, "errors_count": 0, - "precision": 0.3333333333333333, - "recall": 0.5, + "precision": 0.0, + "recall": 0.0, "tool": "qodo", - "repo_name": "discourse__discourse-graphite__qodo__PR9__20260123", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo__PR9__20260123/pull/1" + "repo_name": "grafana__grafana__qodo__PR76186__20260123", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__qodo__PR76186__20260123/pull/1" }, "bugbot": { "skipped": false, - "true_positives": [ + "true_positives": [], + "false_positives": [ { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", - "severity": "Low", - "matched_candidate": "The FallbackLocaleList#[] method mixes string and symbol locale types in the fallback chain. The locale parameter is typically a string (from I18n.locale), while SiteSetting.default_locale.to_sym and :en are symbols. Since Ruby treats \"en\" and :en as different values, uniq won't deduplicate them, causing the same locale to be processed twice in ensure_loaded! when a user's locale matches the default", - "confidence": 0.95, - "reasoning": "Both comments identify the same underlying issue: the mixing of string and symbol locale types causing potential duplicate processing. The golden comment suggests normalizing input locale to avoid double-loading when the same locale is passed as String vs Symbol. The candidate issue specifically identifies that FallbackLocaleList#[] mixes string and symbol types, causing uniq to fail at deduplication and resulting in the same locale being processed twice in ensure_loaded!. This is the exact same problem - lack of locale type normalization leading to duplicate processing." + "candidate": "TestLogger.FromContext method returns a new TestLogger instance via NewTestLogger() instead of returning the receiver or a logger that shares state, causing logged data to be written to an orphan instance that is immediately discarded, making it impossible to verify that logging occurred during tests" } ], - "false_positives": [], "false_negatives": [ { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", + "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", + "severity": "High" + }, + { + "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", "severity": "Low" } ], "errors": [], "total_candidates": 1, "total_golden": 2, - "tp": 1, - "fp": 0, - "fn": 1, + "tp": 0, + "fp": 1, + "fn": 2, "errors_count": 0, - "precision": 1.0, - "recall": 0.5, + "precision": 0.0, + "recall": 0.0, "tool": "bugbot", - "repo_name": "discourse__discourse-graphite__bugbot__PR9__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__bugbot__PR9__20260122/pull/1" + "repo_name": "grafana__grafana__bugbot__PR76186__20260122", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__bugbot__PR76186__20260122/pull/1" }, "copilot": { "skipped": false, - "true_positives": [ - { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low", - "matched_candidate": "ensure_loaded! method reads and potentially modifies @loaded_locales without proper synchronization, bypassing LOAD_MUTEX protection used in load_locale, leading to race conditions in multi-threaded environments", - "confidence": 0.98, - "reasoning": "The candidate issue directly identifies the same problem as the golden comment. Both are about thread-safety issues with @loaded_locales. The candidate provides more detail, explaining that ensure_loaded! reads and modifies @loaded_locales without proper synchronization, bypassing the LOAD_MUTEX protection, which leads to race conditions. This is exactly the 'Thread-safety issue with lazy @loaded_locales' that the golden comment describes." - } - ], - "false_positives": [ - { - "candidate": "FallbackLocaleList#[] method calls SiteSetting.default_locale.to_sym without checking if the setting is available, which could raise an error during application initialization before site settings are loaded" - }, - { - "candidate": "ensure_loaded! method iterates over I18n.locale and its fallbacks, but I18n.locale may not be set at the time the initializer runs, causing issues if accessed before I18n.locale is properly initialized" - }, + "true_positives": [], + "false_positives": [], + "false_negatives": [ { - "candidate": "The comment 'order: after 02-freedom_patches.rb' does not follow Rails convention for controlling initializer load order since Rails loads initializers alphabetically by filename - consider using a numbered prefix like '03-i18n.rb'" + "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", + "severity": "High" }, { - "candidate": "The call to I18n.fallbacks.ensure_loaded! assumes I18n.fallbacks is always set and available, which will raise NoMethodError if I18n.fallbacks is nil" - } - ], - "false_negatives": [ - { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", + "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", "severity": "Low" } ], "errors": [], - "total_candidates": 5, + "total_candidates": 0, "total_golden": 2, - "tp": 1, - "fp": 4, - "fn": 1, + "tp": 0, + "fp": 0, + "fn": 2, "errors_count": 0, - "precision": 0.2, - "recall": 0.5, + "precision": 0.0, + "recall": 0.0, "tool": "copilot", - "repo_name": "discourse__discourse-graphite__copilot__PR9__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__copilot__PR9__20260122/pull/1" + "repo_name": "grafana__grafana__copilot__PR76186__20260122", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__copilot__PR76186__20260122/pull/1" }, "augment": { "skipped": false, "true_positives": [ { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", - "severity": "Low", - "matched_candidate": "@loaded_locales tracking is sensitive to whether callers pass locales as strings vs symbols (e.g., 'en' vs :en), which can lead to duplicate entries and repeated loads - should normalize locale to a single type before checking/adding", - "confidence": 0.98, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment: the code is sensitive to whether locales are passed as strings vs symbols (e.g., 'en' vs :en), which can cause duplicate entries and repeated loads. Both suggest normalizing the locale to a single type before checking/adding. This is a direct semantic match." + "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", + "severity": "High", + "matched_candidate": "QueryData (and the other request methods) dereference req without a nil guard, but other middlewares in this stack defensively handle req == nil; if a nil request ever propagates here this will panic", + "confidence": 0.95, + "reasoning": "The candidate issue identifies the exact same problem as the golden comment: the methods (QueryData and others) dereference req without checking for nil first, while other middlewares in the stack handle nil requests defensively. This would cause a panic if a nil request propagates to these methods. The candidate specifically mentions 'req == nil' handling and the inconsistency with other middlewares, which matches the golden comment's concern about ContextualLoggerMiddleware methods panicking on nil requests due to accessing req.PluginContext without nil checks, and that this is a regression from previous middleware behavior." } ], "false_positives": [ { - "candidate": "Calling I18n.fallbacks.ensure_loaded! in set_locale only makes fallbacks work for controller requests, but jobs/mailers that use I18n may not pass through set_locale and therefore may still not have fallback locales loaded" - }, - { - "candidate": "Changing the site default locale at runtime could change fallback results, but TranslateAccelerator caches I18n.translate by config.locale only, which might keep serving cached fallback strings from the previous default locale until I18n.reload!/cache reset occurs" + "candidate": "TestLogger.FromContext returns a fresh logger instance, so call sites like m.logger.FromContext(ctx).Info(...) won't record logs on the original TestLogger value that tests typically assert against" } ], "false_negatives": [ { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", + "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", "severity": "Low" } ], "errors": [], - "total_candidates": 3, + "total_candidates": 2, "total_golden": 2, "tp": 1, - "fp": 2, + "fp": 1, "fn": 1, "errors_count": 0, - "precision": 0.3333333333333333, + "precision": 0.5, "recall": 0.5, "tool": "augment", - "repo_name": "discourse__discourse-graphite__augment__PR9__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__augment__PR9__20260122/pull/1" + "repo_name": "grafana__grafana__augment__PR76186__20260122", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__augment__PR76186__20260122/pull/1" }, "propel": { "skipped": false, "true_positives": [], "false_positives": [ { - "candidate": "Fallback locales are only loaded from ApplicationController#set_locale, so background jobs, mailers, and rake tasks never load fallback locales (SiteSetting.default_locale or :en), causing I18n::MissingTranslation errors even when translations exist. The fix is to load fallback locales when constructing the fallback list in FallbackLocaleList#[]" - }, - { - "candidate": "Calling I18n.fallbacks.ensure_loaded! only in ApplicationController means fallback locales are never loaded for non-controller codepaths (mailers, Sidekiq jobs, rake tasks). The loading logic should be moved into the I18n layer itself (e.g., in the translate hook or inside FallbackLocaleList#[]) so every translation call preloads the fallback chain regardless of execution context" + "candidate": "/propel review" } ], "false_negatives": [ { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" + "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", + "severity": "High" }, { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", + "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", "severity": "Low" } ], "errors": [], - "total_candidates": 2, + "total_candidates": 1, "total_golden": 2, "tp": 0, - "fp": 2, + "fp": 1, "fn": 2, "errors_count": 0, "precision": 0.0, "recall": 0.0, "tool": "propel", - "repo_name": "discourse__discourse-graphite__propel__PR9__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__propel__PR9__20260122/pull/1" + "repo_name": "grafana__grafana__propel__PR76186__20260122", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__propel__PR76186__20260122/pull/1" }, "baz": { "skipped": false, @@ -44377,11 +44363,11 @@ ], "false_negatives": [ { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" + "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", + "severity": "High" }, { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", + "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", "severity": "Low" } ], @@ -44395,76 +44381,66 @@ "precision": 0.0, "recall": 0.0, "tool": "baz", - "repo_name": "discourse__discourse-graphite__baz__PR9__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__baz__PR9__20260122/pull/1" + "repo_name": "grafana__grafana__baz__PR76186__20260122", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__baz__PR76186__20260122/pull/1" }, "coderabbit": { "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "SiteSetting.default_locale.to_sym is called without nil-safety guard, which will raise NoMethodError if default_locale is nil - should use safe navigation operator (SiteSetting.default_locale&.to_sym)" - }, - { - "candidate": "Using send(:include, I18n::Backend::Pluralization) is unnecessary - should use direct include since Ruby 2.1+ makes include public on Module/Class" - }, + "true_positives": [ { - "candidate": "Using send(:include, I18n::Backend::Fallbacks) is unnecessary - should use direct include since Ruby 2.1+ makes include public on Module/Class" + "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", + "severity": "High", + "matched_candidate": "The ContextualLoggerMiddleware dereferences req.PluginContext unconditionally in QueryData, CallResource, CheckHealth, and CollectMetrics methods, which can cause a panic when req is nil", + "confidence": 0.98, + "reasoning": "The candidate issue identifies exactly the same problem as the golden comment: the ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) dereference req.PluginContext without checking if req is nil first, which causes a panic. Both comments point to the same bug - nil request handling causing panics due to unconditional access to req.PluginContext." } ], + "false_positives": [], "false_negatives": [ { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" - }, - { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", + "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", "severity": "Low" } ], "errors": [], - "total_candidates": 3, + "total_candidates": 1, "total_golden": 2, - "tp": 0, - "fp": 3, - "fn": 2, + "tp": 1, + "fp": 0, + "fn": 1, "errors_count": 0, - "precision": 0.0, - "recall": 0.0, + "precision": 1.0, + "recall": 0.5, "tool": "coderabbit", - "repo_name": "discourse__discourse-graphite__coderabbit__PR9__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__coderabbit__PR9__20260122/pull/1" + "repo_name": "grafana__grafana__coderabbit__PR76186__20260122", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__coderabbit__PR76186__20260122/pull/1" }, "greptile": { "skipped": false, "true_positives": [], - "false_positives": [ - { - "candidate": "SiteSetting.default_locale may not be available during early initialization phases if FallbackLocaleList#[] is called before the database is ready - needs nil-safety or deferred SiteSetting access" - } - ], + "false_positives": [], "false_negatives": [ { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" + "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", + "severity": "High" }, { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", + "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", "severity": "Low" } ], "errors": [], - "total_candidates": 1, + "total_candidates": 0, "total_golden": 2, "tp": 0, - "fp": 1, + "fp": 0, "fn": 2, "errors_count": 0, "precision": 0.0, "recall": 0.0, "tool": "greptile", - "repo_name": "discourse__discourse-graphite__greptile__PR9__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__greptile__PR9__20260122/pull/1" + "repo_name": "grafana__grafana__greptile__PR76186__20260122", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__greptile__PR76186__20260122/pull/1" }, "kg": { "skipped": false, @@ -44472,11 +44448,11 @@ "false_positives": [], "false_negatives": [ { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" + "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", + "severity": "High" }, { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", + "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", "severity": "Low" } ], @@ -44490,29 +44466,29 @@ "precision": 0.0, "recall": 0.0, "tool": "kg", - "repo_name": "discourse__discourse-graphite__kg__PR9__20260202", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__kg__PR9__20260202/pull/1" + "repo_name": "grafana__grafana__kg__PR76186__20260202", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__kg__PR76186__20260202/pull/1" }, "qodo-v2": { "skipped": false, "true_positives": [ { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", + "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", "severity": "Low", - "matched_candidate": "FallbackLocaleList mixes String and Symbol locales (e.g., \"en\" and :en), causing the translation lazy-loader to treat them as different locales and load the same YAML files multiple times, growing @loaded_locales unnecessarily", - "confidence": 0.95, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment - that mixing String and Symbol locales (e.g., 'en' vs :en) causes the same locale to be treated as different, leading to double-loading of YAML files. The golden comment suggests normalizing input locale to avoid this double-loading issue, and the candidate describes the same underlying bug where FallbackLocaleList mixing String/Symbol causes the lazy-loader to load the same files multiple times." + "matched_candidate": "Plugin request logs now rely on logger.FromContext(ctx) to include traceID, but the traceID contextual log provider is registered only when tracing.ProvideService is called. In tests or non-standard setups where spans exist but ProvideService wasn't invoked, plugin request logs can silently lose trace IDs.", + "confidence": 0.85, + "reasoning": "Both comments identify the same core issue: traceID is no longer being logged for plugin requests after the refactoring. The golden comment explains that the tracing import and logic to extract traceID were removed from LoggerMiddleware, and the new ContextualLoggerMiddleware doesn't add this information. The candidate comment identifies the same problem - that plugin request logs can lose trace IDs because the traceID contextual log provider may not be properly registered/available. Both are pointing to the same underlying bug: missing traceID in plugin request logs after the middleware refactoring." } ], "false_positives": [ { - "candidate": "Fallback preloading is only wired into ApplicationController#set_locale, so code paths that switch locale outside controller requests (e.g., services using I18n.with_locale) may not preload fallback locales, making fallback behavior depend on prior execution order in the process" + "candidate": "TestLogger.FromContext returns a brand-new TestLogger, dropping the original logger's accumulated state and violating the Logger contract that the returned logger should include 'this logger's context plus the given context'. This breaks tests or code paths that log via logger.FromContext(ctx) because logs go to a different instance than the one under assertion." } ], "false_negatives": [ { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" + "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", + "severity": "High" } ], "errors": [], @@ -44525,24 +44501,24 @@ "precision": 0.5, "recall": 0.5, "tool": "qodo-v2", - "repo_name": "discourse__discourse-graphite__qodo-v2__PR9__20260227", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-v2__PR9__20260227/pull/1" + "repo_name": "grafana__grafana__qodo-v2__PR76186__20260227", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__qodo-v2__PR76186__20260227/pull/1" }, "devin": { "skipped": false, "true_positives": [], "false_positives": [ { - "candidate": "LRU cache key in translate method doesn't include SiteSetting.default_locale, causing stale translations when admin changes the site's default locale because the fallback chain dynamically depends on SiteSetting.default_locale but the cache key only includes key, config.locale, and config.backend.object_id" + "candidate": "TestLogger.FromContext returns a new disconnected TestLogger instance instead of returning the receiver, causing any code path that calls logger.FromContext(ctx).Info(...) to record logs on a throwaway instance rather than the TestLogger the test holds a reference to, making tests silently pass even when logging behavior is wrong" } ], "false_negatives": [ { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" + "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", + "severity": "High" }, { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", + "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", "severity": "Low" } ], @@ -44556,93 +44532,81 @@ "precision": 0.0, "recall": 0.0, "tool": "devin", - "repo_name": "discourse__discourse-graphite__devin__PR9__20260302", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__devin__PR9__20260302/pull/1" + "repo_name": "grafana__grafana__devin__PR76186__20260302", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__devin__PR76186__20260302/pull/1" }, "mra-max": { "skipped": false, "true_positives": [ { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", - "severity": "Low", - "matched_candidate": "ensure_loaded!(locale) in translate_accelerator may cause duplicate loads because callers pass locale values that may be strings or symbols, loading the same locale twice", - "confidence": 0.95, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment: the potential for duplicate loading of locales when the same locale is passed as a String vs Symbol. Both comments point to the ensure_loaded! method in translate_accelerator and the issue of not normalizing locale input, which could cause the same locale to be loaded twice." + "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", + "severity": "High", + "matched_candidate": "ContextualLoggerMiddleware missing nil-checks when handling requests - dereferences req.PluginContext directly without validating req for nil, which can lead to panics", + "confidence": 0.98, + "reasoning": "The candidate issue identifies the exact same problem as the golden comment: ContextualLoggerMiddleware methods dereference req.PluginContext without first checking if req is nil, which can lead to panics. Both comments point to the same bug - missing nil-checks for the request parameter before accessing req.PluginContext." } ], "false_positives": [ { - "candidate": "No test files observed in repository; need to add tests for i18n fallbacks including FallbackLocaleList semantics, ApplicationController#set_locale behavior, lazy-loading of locales, translation loading via FreedomPatch translate accelerator, idempotence of locale loading, and regression tests for production env behavior" - }, - { - "candidate": "I18n.fallbacks.ensure_loaded! in set_locale triggers loading of fallback locales which can increase startup time and memory, particularly on apps with many locales, and can affect performance during request handling" - }, - { - "candidate": "preload_json loads anonymous data and current_user data on GET HTML requests which can trigger significant DB work on every request in high-traffic scenarios, introducing latency spikes and potential contention" - }, - { - "candidate": "Removing default I18n fallbacks setting from production cloud66 config changes translation fallback behavior at runtime, potentially causing missing translations unless new fallback mechanism is correctly loaded during initialization" - }, - { - "candidate": "Removing I18n fallbacks in production environment relies on new fallback mechanism and translation availability could regress if translations are not loaded in time" + "candidate": "Logger interface breaking change: New(ctx ...any) Logger signature changed from ctx ...interface{}, and FromContext(ctx context.Context) Logger method added - all implementors must adapt" }, { - "candidate": "Removing fallbacks config in profile environment may cause translation misses unless new fallback flow is loaded early" + "candidate": "Unsafe type assertions when using exemplars in metrics instrumentation - pluginRequestDurationWithLabels.(prometheus.ExemplarObserver) and pluginRequestCounterWithLabels.(prometheus.ExemplarAdder) called unconditionally without checking assertion result, risking runtime panics" }, { - "candidate": "New FallbackLocaleList initializer with per-locale fallback arrays and ensure_loaded! integration is a major architectural change with risk of compatibility issues with I18n gem, thread-safety concerns, and potential mis-loading of translations" + "candidate": "MetricsMiddleware QueryData panics on nil request - dereferences req without nil check when accessing req.Queries and req.PluginContext" }, { - "candidate": "No visible tests verifying thread-safety, fallback order, per-request preloading, or behavior differences between prod and non-prod environments" + "candidate": "Middleware ordering causes missing contextual attributes in metrics logs - ContextualLoggerMiddleware placed after MetricsMiddleware means metrics instrumentation logs execute without contextual attributes" }, { - "candidate": "Server-side i18n fallback behavior is adjusted but client-side JavaScript still sets I18n.fallbacks to false, causing potential mismatch between server-side and client-side translation resolution" + "candidate": "FromContext() assertion in grafanaInfraLogWrapper could panic if underlying logger is not a ConcreteLogger - type assertion to *log.ConcreteLogger may fail and fallback to d.New() could create wrapper with nil or default logger" }, { - "candidate": "Removing explicit 'i18n/backend/pluralization' patch file while new i18n initializer includes pluralization backend risks inconsistency or double-loads if not aligned" + "candidate": "Test function named TestInstrumentationMiddleware but constructs MetricsMiddleware via newMetricsMiddleware - naming inconsistency after rename refactor" } ], "false_negatives": [ { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", + "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", "severity": "Low" } ], "errors": [], - "total_candidates": 11, + "total_candidates": 7, "total_golden": 2, "tp": 1, - "fp": 10, + "fp": 6, "fn": 1, "errors_count": 0, - "precision": 0.09090909090909091, + "precision": 0.14285714285714285, "recall": 0.5, "tool": "mra-max", - "repo_name": "discourse__discourse-graphite__mra-max__PR9__20260306", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__mra-max__PR9__20260306/pull/1" + "repo_name": "grafana__grafana__mra-max__PR76186__20260306", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__mra-max__PR76186__20260306/pull/1" }, "greptile-v4": { "skipped": false, "true_positives": [ { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low", - "matched_candidate": "In ensure_loaded!, @loaded_locales is initialized and checked outside the mutex while load_locale mutates it inside the mutex, creating a potential TOCTOU race where two threads may simultaneously evaluate the include check as false and both call load_locale, adding unnecessary overhead", - "confidence": 0.95, - "reasoning": "The candidate issue identifies the same thread-safety problem with @loaded_locales that the golden comment references. The candidate specifically describes a TOCTOU (time-of-check-time-of-use) race condition where @loaded_locales is checked outside the mutex but modified inside it, which is exactly the 'lazy @loaded_locales' thread-safety issue the golden comment is pointing to. Both are concerned with the same underlying race condition in the lazy initialization pattern." + "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", + "severity": "High", + "matched_candidate": "ContextualLoggerMiddleware directly dereferences req.PluginContext in all four main handlers without a nil check, while it's inserted before LoggerMiddleware which has nil guards - if a nil request is passed, a panic will occur before the guard in LoggerMiddleware is reached", + "confidence": 0.98, + "reasoning": "The candidate issue identifies the exact same problem as the golden comment: ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) will panic when receiving a nil request because they directly access req.PluginContext without nil checks. The candidate also correctly notes this is problematic because it's inserted before LoggerMiddleware which has nil guards, meaning the panic occurs before those guards are reached. This is semantically the same issue - nil request handling causing panics in ContextualLoggerMiddleware." } ], "false_positives": [ { - "candidate": "The translate method only loads the current locale, not fallback locales. When the I18n backend cannot find a key in the current locale and iterates through fallback locales, those fallback locale files may not be loaded into memory, causing fallback lookups to silently fail for background jobs, rake tasks, mailers, or console usage that don't go through the controller's set_locale" + "candidate": "FromContext returns a brand-new TestLogger instance that shares no state with the receiver, causing tests that assert on testLogger.InfoLogs/WarnLogs/ErrorLogs to always see zero calls since actual log output is written to a discarded throwaway instance" }, { - "candidate": "SiteSetting.default_locale is accessed dynamically in FallbackLocaleList#[] without a guard, which can raise a NameError or return unexpected values during early application boot before SiteSetting is fully loaded" + "candidate": "Test function name TestInstrumentationMiddleware was not renamed to TestMetricsMiddleware after the type was renamed, creating inconsistency" } ], "false_negatives": [ { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", + "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", "severity": "Low" } ], @@ -44656,16972 +44620,441 @@ "precision": 0.3333333333333333, "recall": 0.5, "tool": "greptile-v4", - "repo_name": "discourse__discourse-graphite__greptile-v4__PR9__20260306", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__greptile-v4__PR9__20260306/pull/1" + "repo_name": "grafana__grafana__greptile-v4__PR76186__20260306", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__greptile-v4__PR76186__20260306/pull/1" }, "mra-nano": { "skipped": false, "true_positives": [], "false_positives": [ { - "candidate": "FallbackLocaleList subclass does not guard against nil I18n.locale, which can cause a crash in ensure_loaded!" + "candidate": "FromContext added to Logger interface with updated implementations - the fake logger and grafanaInfra wrapper now implement FromContext(ctx context.Context) Logger, and LoggerMiddleware uses this to enable contextual logging across the middleware pipeline" }, { - "candidate": "FallbackLocaleList depends on SiteSetting.default_locale at initialization time, risking incorrect fallback chains if settings are not yet loaded" + "candidate": "New ContextualLoggerMiddleware added to provide contextual logger in requests - need to confirm that calls to NewContextualLoggerMiddleware exist in pluginsintegration.go and tests, and check that instrumentation logic uses FromContext on logger" }, { - "candidate": "I18n.fallbacks.ensure_loaded! added to set_locale triggers loading of fallback chain on every request, coupling request handling to dynamic locale loading" + "candidate": "InstrumentationMiddleware and its constructor have been renamed to MetricsMiddleware/NewMetricsMiddleware, and ContextualLoggerMiddleware has been added and wired into CreateMiddlewares alongside Tracing, Metrics, and Logger middlewares - all call sites and tests need to be updated accordingly" }, { - "candidate": "If loaded locales are not present or I18n semantics change, translations may be loaded inconsistently or cause performance issues due to repeated loading of fallback locales" - }, + "candidate": "Data race risk in TestLogger: the TestLogger.Error(...) updates ErrorLogs field (Calls, Message, Ctx) without synchronization, which can cause data races and flaky tests or panics in CI when tests run with multiple goroutines logging concurrently" + } + ], + "false_negatives": [ { - "candidate": "Explicit config.i18n.fallbacks = true removed from production, profile, and cloud66 environment configs - if initializer loads too late or fails, fallbacks may silently be absent" + "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", + "severity": "High" }, { - "candidate": "load_locale(locale) calls @loaded_locales.include?(locale) while @loaded_locales can be nil on first invocation, causing NoMethodError" + "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", + "severity": "Low" + } + ], + "errors": [], + "total_candidates": 4, + "total_golden": 2, + "tp": 0, + "fp": 4, + "fn": 2, + "errors_count": 0, + "precision": 0.0, + "recall": 0.0, + "tool": "mra-nano", + "repo_name": "grafana__grafana__mra-nano__PR76186__20260306", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__mra-nano__PR76186__20260306/pull/1" + }, + "mra-ultra": { + "skipped": false, + "true_positives": [], + "false_positives": [ + { + "candidate": "Potential data race on plugin registry lookups in MetricsMiddleware - pluginRegistry is accessed via m.pluginRegistry.Plugin(ctx, pluginID) without explicit synchronization, which could race if multiple requests run concurrently and the registry uses shared maps without synchronization" }, { - "candidate": "translate method calls @loaded_locales.include?(config.locale) without initializing @loaded_locales, which can crash on first translation request" + "candidate": "TestLogger mutates internal state without synchronization (data race risk) - Info and Error methods mutate shared fields (Calls, Message, Ctx, InfoLogs, ErrorLogs) without any synchronization, creating a data race if used concurrently by multiple goroutines" }, { - "candidate": "Removal of pluralization initialization from config/initializers/pluralization.rb and moving to config/initializers/i18n.rb changes initialization order, risking pluralization being loaded before I18n is fully configured" + "candidate": "WithContextualAttributes accumulates attributes from multiple middleware layers in context, which could magnify exposure of sensitive fields if not controlled" } ], "false_negatives": [ { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" + "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", + "severity": "High" }, { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", + "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", "severity": "Low" } ], "errors": [], - "total_candidates": 8, + "total_candidates": 3, "total_golden": 2, "tp": 0, - "fp": 8, + "fp": 3, "fn": 2, "errors_count": 0, "precision": 0.0, "recall": 0.0, - "tool": "mra-nano", - "repo_name": "discourse__discourse-graphite__mra-nano__PR9__20260306", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__mra-nano__PR9__20260306/pull/1" + "tool": "mra-ultra", + "repo_name": "grafana__grafana__mra-ultra__PR76186__20260306", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__mra-ultra__PR76186__20260306/pull/1" }, - "mra-ultra": { + "mra-b": { "skipped": false, "true_positives": [ { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low", - "matched_candidate": "ensure_loaded! checks @loaded_locales outside the mutex that guards load_locale, allowing race conditions where concurrent threads trigger redundant or inconsistent locale loading", + "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", + "severity": "High", + "matched_candidate": "ContextualLoggerMiddleware and MetricsMiddleware access fields on request pointers (e.g., req.PluginContext, req.Queries, req.Body) without nil-checking req first, which can cause a nil-pointer panic", "confidence": 0.95, - "reasoning": "The candidate issue identifies the exact same thread-safety problem as the golden comment. Both point out that @loaded_locales has a thread-safety issue - the golden comment mentions 'Thread-safety issue with lazy @loaded_locales' and the candidate specifically explains the race condition where '@loaded_locales' is checked outside the mutex, allowing concurrent threads to cause problems. This is the same underlying issue." + "reasoning": "The candidate issue identifies the same underlying problem as the golden comment - that ContextualLoggerMiddleware (and also MetricsMiddleware) access fields on request pointers like req.PluginContext without first checking if req is nil, which can cause nil-pointer panics. The golden comment specifically mentions ContextualLoggerMiddleware methods panicking when nil request is received due to accessing req.PluginContext without nil checking. The candidate captures this exact issue, though it also mentions MetricsMiddleware and additional fields." } ], "false_positives": [ { - "candidate": "Removal of config.i18n.fallbacks = true from production environment configs may cause fallbacks to be absent on first requests if the custom initializer is not loaded early enough" - }, - { - "candidate": "I18n.fallbacks.ensure_loaded! is invoked on every request in set_locale, adding IO overhead on the hot path and risking runtime errors if the method is unavailable during early boot or testing" - }, - { - "candidate": "FallbackLocaleList uses SiteSetting.default_locale at runtime which may hit the database, causing failures if requests come in before the DB is fully available during boot/preload" + "candidate": "Adding FromContext(context.Context) to the exported Logger interface is a breaking API change that will cause compile errors for any unupdated implementors" }, { - "candidate": "Pluralization initializer was folded into i18n.rb - load order needs verification across all environments" + "candidate": "TestLogger.New and TestLogger.FromContext both discard context and return a fresh logger, violating the interface contract" }, { - "candidate": "Translation loader constructs Regexp using user-influenced locale value without escaping, allowing potential manipulation of which translation files are loaded via locale tampering" + "candidate": "grafanaInfraLogWrapper.FromContext may drop contextual attributes when type assertion fails - if d.l.FromContext(ctx) returns a value that does not satisfy *log.ConcreteLogger, the function returns d.New() which does not preserve contextual attributes from ctx" }, { - "candidate": "Locale value used in Regexp.new should be validated against a whitelist of allowed locales and escaped with Regexp.escape to prevent security issues" + "candidate": "LoggerMiddleware now relies on logger.FromContext(ctx) and middleware ordering must ensure context contains attributes - if contextual attributes are not injected before LoggerMiddleware runs, logs will lack plugin/request details" } ], "false_negatives": [ { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", + "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", "severity": "Low" } ], "errors": [], - "total_candidates": 7, + "total_candidates": 5, "total_golden": 2, "tp": 1, - "fp": 6, + "fp": 4, "fn": 1, "errors_count": 0, - "precision": 0.14285714285714285, + "precision": 0.2, "recall": 0.5, - "tool": "mra-ultra", - "repo_name": "discourse__discourse-graphite__mra-ultra__PR9__20260306", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__mra-ultra__PR9__20260306/pull/1" + "tool": "mra-b", + "repo_name": "grafana__grafana__mra-b__PR76186__20260307", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__mra-b__PR76186__20260307/pull/1" }, - "mra-b": { + "mra-a": { "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "Removing config.i18n.fallbacks = true from environment files makes fallback behavior entirely dependent on config/initializers/i18n.rb, which has no numeric prefix to guarantee it loads after 02-freedom_patches.rb. If the initializer loads first or fails, I18n.ensure_loaded! will be undefined and fallbacks will be silently absent in production." - }, - { - "candidate": "@loaded_locales may be nil causing NoMethodError in translate method - translate calls @loaded_locales.include?(config.locale) without ensuring @loaded_locales is initialized, and if @loaded_locales is nil (before reload! or ensure_loaded! has run), calling include? on nil will raise NoMethodError" - }, - { - "candidate": "@loaded_locales may be nil causing NoMethodError in load_locale method - load_locale's guard inside the mutex (return if @loaded_locales.include?(locale)) will raise if @loaded_locales is nil when load_locale is called directly" - }, + "true_positives": [ { - "candidate": "Calling I18n.fallbacks.ensure_loaded! on every request in set_locale before_filter incurs method dispatch overhead and on first access may perform file IO and DB access, causing potential performance regression under load" + "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", + "severity": "High", + "matched_candidate": "ContextualLoggerMiddleware methods QueryData, CallResource, CheckHealth, and CollectMetrics dereference incoming request without nil checks, which can cause panic and denial-of-service", + "confidence": 0.98, + "reasoning": "The candidate issue identifies the exact same problem as the golden comment: the ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) dereference incoming requests without nil checks, which can cause panics. Both comments point to the same bug - missing nil request validation in these middleware methods that leads to potential crashes." }, { - "candidate": "If ensure_loaded! is called before the freedom_patches initialization it may raise, and calling this on every request increases surface area for race conditions" + "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", + "severity": "Low", + "matched_candidate": "Refactor splits context enrichment between MetricsMiddleware and ContextualLoggerMiddleware, creating implicit ordering dependency that can cause LoggerMiddleware to silently omit plugin/endpoint/user/datasource/traceID fields if middleware is misordered", + "confidence": 0.85, + "reasoning": "The candidate issue mentions that traceID fields could be omitted from LoggerMiddleware due to middleware ordering dependencies in the refactoring. This directly relates to the golden comment's concern about traceID no longer being logged for plugin requests after the refactoring removed the tracing logic. Both identify the same core problem: traceID is missing from plugin request logs after the refactoring. The candidate frames it as a potential ordering issue while the golden comment states it definitively happens, but they're identifying the same underlying bug." } ], - "false_negatives": [ + "false_positives": [ { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" + "candidate": "Adding FromContext method to Logger interface is a source-level breaking change for external implementations that previously implemented the older Logger interface" }, { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", - "severity": "Low" + "candidate": "FromContext in grafanaInfraLogWrapper uses type assertion that silently falls back to fresh logger if assertion fails, potentially losing contextual attributes like trace IDs" + }, + { + "candidate": "TestLogger.FromContext ignores context and returns fresh logger, creating test fidelity gap where tests may pass while production code behaves differently regarding context-derived fields" } ], + "false_negatives": [], "errors": [], "total_candidates": 5, "total_golden": 2, - "tp": 0, - "fp": 5, - "fn": 2, + "tp": 2, + "fp": 3, + "fn": 0, "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "mra-b", - "repo_name": "discourse__discourse-graphite__mra-b__PR9__20260307", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__mra-b__PR9__20260307/pull/1" + "precision": 0.4, + "recall": 1.0, + "tool": "mra-a", + "repo_name": "grafana__grafana__mra-a__PR76186__20260307", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__mra-a__PR76186__20260307/pull/1" }, - "mra-a": { + "entelligence": { "skipped": false, "true_positives": [], "false_positives": [ { - "candidate": "Calling to_sym on SiteSetting.default_locale can raise NoMethodError if default_locale is nil during boot/initialization" + "candidate": "Adding `FromContext` to the `Logger` interface is a breaking change - all existing implementations will fail to compile. Need to update all implementations within this PR or introduce a separate interface (e.g., `ContextLogger`) to avoid breaking the `Logger` contract for downstream users." }, { - "candidate": "Unescaped interpolation of locale value into Regexp.new creates regex injection vulnerability allowing ReDoS attacks or RegexpError crashes" + "candidate": "Function `newMetricsMiddleware` duplicates existing code from `newInstrumentationMiddleware` in instrumentation_middleware.go (92% similarity) - should either delete the old function entirely or extract shared Prometheus metric registration into a shared constructor to avoid duplicated metric declarations which would panic at runtime if both are registered" }, { - "candidate": "@loaded_locales may be nil when translate() checks include?, causing NoMethodError since reload! may not have been called" + "candidate": "Function `NewMetricsMiddleware` duplicates existing code from `NewInstrumentationMiddleware` in instrumentation_middleware.go (93% similarity) - should delete NewInstrumentationMiddleware along with its backing implementation to complete the rename cleanly" }, { - "candidate": "I18n.fallbacks.ensure_loaded! called during request with user-controllable locale can trigger expensive filesystem/parsing operations causing DoS" - } - ], - "false_negatives": [ - { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" + "candidate": "Function `MetricsMiddleware.pluginTarget` duplicates existing code from `InstrumentationMiddleware.pluginTarget` in instrumentation_middleware.go (95% similarity) - should extract pluginTarget into a package-level helper function that both middlewares can call" }, { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", - "severity": "Low" + "candidate": "Function `MetricsMiddleware.instrumentPluginRequestSize` duplicates existing code from `InstrumentationMiddleware.instrumentPluginRequestSize` in instrumentation_middleware.go (95% similarity) - should consolidate on a single middleware and delete the duplicate" } ], - "errors": [], - "total_candidates": 4, - "total_golden": 2, - "tp": 0, - "fp": 4, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "mra-a", - "repo_name": "discourse__discourse-graphite__mra-a__PR9__20260307", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__mra-a__PR9__20260307/pull/1" - }, - "entelligence": { - "skipped": false, - "true_positives": [], - "false_positives": [], "false_negatives": [ { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" + "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", + "severity": "High" }, { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", + "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", "severity": "Low" } ], "errors": [], - "total_candidates": 0, + "total_candidates": 5, "total_golden": 2, "tp": 0, - "fp": 0, + "fp": 5, "fn": 2, "errors_count": 0, "precision": 0.0, "recall": 0.0, "tool": "entelligence", - "repo_name": "discourse__discourse-graphite__entelligence__PR9__20260306", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__entelligence__PR9__20260306/pull/1" + "repo_name": "grafana__grafana__entelligence__PR76186__20260306", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__entelligence__PR76186__20260306/pull/1" }, "sourcery": { "skipped": false, "true_positives": [], "false_positives": [ { - "candidate": "FallbackLocaleList#ensure_loaded! calls I18n.ensure_loaded! which doesn't exist as a class method on I18n - it should call I18n.backend.ensure_loaded!(l) instead since ensure_loaded! is defined as an instance method on the backend in translate_accelerator.rb" + "candidate": "The `*log.ConcreteLogger` type assertion in FromContext couples the wrapper to one implementation and causes it to silently drop context-derived fields whenever FromContext returns a different concrete type. The grafanaInfraLogWrapper should hold the log.Logger interface and wrap d.l.FromContext(ctx) directly without a type assertion." + }, + { + "candidate": "In contextual_logger_middleware.go, the helper instrumentContext now only enriches the logger, not metrics, so consider renaming it (and its comment) to something logger-specific (e.g. withContextualLogger) to avoid confusion with the metrics instrumentation middleware." }, { - "candidate": "In ApplicationController#set_locale, I18n.fallbacks.ensure_loaded! is called based on I18n.locale, but the translate accelerator's translate method still lazily loads using config.locale - these should be aligned to a single source of truth for the active locale to avoid confusion and potential mismatches" + "candidate": "In grafanaInfraLogWrapper.FromContext, falling back to New() when the underlying logger is not a *log.ConcreteLogger silently drops any context-derived attributes; consider either documenting this behavior or adding a lightweight log/metric so that unexpected logger types are easier to detect." } ], "false_negatives": [ { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" + "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", + "severity": "High" }, { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", + "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", "severity": "Low" } ], "errors": [], - "total_candidates": 2, + "total_candidates": 3, "total_golden": 2, "tp": 0, - "fp": 2, + "fp": 3, "fn": 2, "errors_count": 0, "precision": 0.0, "recall": 0.0, "tool": "sourcery", - "repo_name": "discourse__discourse-graphite__sourcery__PR9__20260308", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__sourcery__PR9__20260308/pull/1" + "repo_name": "grafana__grafana__sourcery__PR76186__20260308", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__sourcery__PR76186__20260308/pull/1" }, "cubic-dev": { "skipped": false, "true_positives": [ { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low", - "matched_candidate": "The lazy initialization `@loaded_locales ||= []` is not protected by `LOAD_MUTEX`, unlike every other mutation of `@loaded_locales` in this class. In a multi-threaded server this can race with `reload!`, which resets `@loaded_locales` and `@cache` in sequence. Wrapping the check-and-load inside `LOAD_MUTEX.synchronize` (similar to `load_locale`) would make this safe.", - "confidence": 0.98, - "reasoning": "The candidate issue directly addresses the same thread-safety concern as the golden comment. The golden comment mentions 'Thread-safety issue with lazy @loaded_locales' and the candidate specifically identifies that the lazy initialization '@loaded_locales ||= []' is not protected by LOAD_MUTEX, explaining the race condition with reload! and suggesting synchronization. This is exactly the same underlying issue." + "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", + "severity": "High", + "matched_candidate": "Nil `req` will cause a panic when accessing `req.PluginContext` in ContextualLoggerMiddleware.QueryData - should add nil guard like sibling LoggerMiddleware", + "confidence": 0.95, + "reasoning": "The candidate issue identifies the exact same problem as the golden comment - that nil `req` will cause a panic when accessing `req.PluginContext` in ContextualLoggerMiddleware.QueryData. The candidate specifically mentions the QueryData method and the need for a nil guard, which matches the golden comment's description of the panic occurring due to accessing req.PluginContext without checking if req is nil. The golden comment mentions multiple methods (QueryData, CallResource, CheckHealth, CollectMetrics) while the candidate only explicitly mentions QueryData, but they're identifying the same underlying issue." }, { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", + "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", "severity": "Low", - "matched_candidate": "The `locale` parameter is not normalized (e.g., via `.to_sym`), so passing `\"en\"` vs `:en` would bypass the `include?` guard and load the same locale twice. The existing `translate` method always uses `config.locale` (a symbol), but `ensure_loaded!` is a new public entry point with no such guarantee.", + "matched_candidate": "`traceID` is no longer included in the 'Plugin Request Completed' log output - the explicit tracing.TraceIDFromContext call was removed and ContextualLoggerMiddleware doesn't add traceID to contextual attributes", "confidence": 0.98, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment: the locale parameter is not normalized (e.g., to symbol), which could cause the same locale to be loaded twice when passed as String vs Symbol. The candidate specifically mentions using `.to_sym` for normalization and explains the consequence of bypassing the `include?` guard, which is the same concern about double-loading mentioned in the golden comment." + "reasoning": "The candidate issue identifies exactly the same problem as the golden comment: the traceID is no longer being logged for plugin requests because the tracing.TraceIDFromContext call was removed and the new ContextualLoggerMiddleware doesn't add traceID to the contextual attributes. Both comments point to the same missing functionality and its impact on debugging/tracing." + } + ], + "false_positives": [ + { + "candidate": "Test function is still named `TestInstrumentationMiddleware` but should be `TestMetricsMiddleware` to match the renamed middleware" + }, + { + "candidate": "Doc comment for `FromContext` appears to be copy-pasted from `New` and doesn't accurately describe the method's behavior - FromContext retrieves a logger from context, not creates one with key/value pairs" + }, + { + "candidate": "`FromContext` in TestLogger returns a new disconnected `TestLogger`, so logs written through the returned logger are silently lost - should return the receiver instead" } ], - "false_positives": [], "false_negatives": [], "errors": [], - "total_candidates": 2, + "total_candidates": 5, "total_golden": 2, "tp": 2, - "fp": 0, + "fp": 3, "fn": 0, "errors_count": 0, - "precision": 1.0, + "precision": 0.4, "recall": 1.0, "tool": "cubic-dev", - "repo_name": "discourse__discourse-graphite__cubic-dev__PR9__20260306", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__cubic-dev__PR9__20260306/pull/1" + "repo_name": "grafana__grafana__cubic-dev__PR76186__20260306", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__cubic-dev__PR76186__20260306/pull/1" }, "mesa": { "skipped": false, - "true_positives": [ - { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low", - "matched_candidate": "The `@loaded_locales` instance variable is not thread-safe. In multi-threaded servers (Puma in production), concurrent requests could race when checking and modifying this array, potentially causing duplicate locale loads or partial visibility of loaded locales. Consider using a `Concurrent::Set` or wrapping access in a `Mutex` to ensure thread safety.", - "confidence": 0.98, - "reasoning": "The candidate issue directly identifies the same thread-safety concern with @loaded_locales that the golden comment mentions. The candidate provides more detail about the specific problem (concurrent requests racing when checking/modifying the array in multi-threaded servers like Puma) and suggests solutions (Concurrent::Set or Mutex), but it's fundamentally pointing to the exact same issue - that @loaded_locales has a thread-safety problem due to lazy initialization." - } - ], + "true_positives": [], "false_positives": [ { - "candidate": "The `ensure_loaded!` method iterates through the fallback chain calling `I18n.ensure_loaded!`, but there's no explicit check that this method exists on the I18n module. This appears to be calling back into the monkey-patched method in `translate_accelerator.rb`. Consider making this dependency more explicit or adding a guard to prevent failures if the patch isn't loaded." - }, - { - "candidate": "The custom `FallbackLocaleList` replaces Rails' default fallback behavior entirely. This increases maintenance burden and could break across i18n gem upgrades. Consider adding comprehensive tests covering: regional locale variants (e.g., pt-BR -> pt), plugin-provided locales, and edge cases with missing/invalid locales to ensure this custom implementation remains robust." + "candidate": "Type assertion to concrete `*log.ConcreteLogger` creates tight coupling to infrastructure internals. If the underlying logger implementation changes, this will silently fall back to `d.New()`, dropping all contextual attributes without error visibility" }, { - "candidate": "The fallback chain accesses `SiteSetting.default_locale` on every lookup, which may not be available during early initialization (before database connection or settings are loaded). If `set_locale` or translation occurs before SiteSetting is ready, this will raise an error. Consider adding a safe default or ensuring SiteSetting initialization order is documented." + "candidate": "`FromContext` returns a new `TestLogger` instance instead of the receiver (`f`), breaking test observability - tests using `FromContext` won't be able to verify logged calls/messages through the original TestLogger's captured state" }, { - "candidate": "This assumes all translation usage occurs after `set_locale` runs. If any middleware, error handlers, or early controller concerns use I18n.t before this point, they won't have fallbacks loaded. Consider either: (1) calling `I18n.fallbacks.ensure_loaded!` in a before-boot initializer as a safety net, or (2) documenting that this must run before any translation usage." + "candidate": "Middleware ordering may be incorrect: `LoggerMiddleware` executes before `ContextualLoggerMiddleware`, so logs won't contain expected contextual attributes (plugin ID, endpoint, datasource)" } ], "false_negatives": [ { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", + "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", + "severity": "High" + }, + { + "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", "severity": "Low" } ], "errors": [], - "total_candidates": 5, + "total_candidates": 3, "total_golden": 2, - "tp": 1, - "fp": 4, - "fn": 1, + "tp": 0, + "fp": 3, + "fn": 2, "errors_count": 0, - "precision": 0.2, - "recall": 0.5, + "precision": 0.0, + "recall": 0.0, "tool": "mesa", - "repo_name": "discourse__discourse-graphite__mesa__PR9__20260307", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__mesa__PR9__20260307/pull/1" + "repo_name": "grafana__grafana__mesa__PR76186__20260306", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__mesa__PR76186__20260306/pull/1" }, "codeant": { "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "SiteSetting.default_locale is accessed inside FallbackLocaleList#[] but SiteSetting may not be available at initializer load time, causing potential errors or incorrect values" - }, - { - "candidate": "The comment '# order: after 02-freedom_patches.rb' doesn't enforce initializer ordering - need to name the file with a number prefix or document in deployment notes" - }, + "true_positives": [ { - "candidate": "I18n.ensure_loaded! is called in the initializer but may not be defined on I18n - the ensure_loaded! method is added to translate_accelerator which may not be the same as I18n.ensure_loaded!" - }, - { - "candidate": "The ensure_loaded! method in translate_accelerator.rb may not be accessible via I18n.ensure_loaded! if it's defined on a separate TranslateAccelerator instance rather than patched onto I18n.backend" - }, - { - "candidate": "FallbackLocaleList#ensure_loaded! could add per-request overhead if load operations are expensive - consider eager preloading commonly-used locales at boot" - }, - { - "candidate": "Hardcoded :en as final fallback instead of using I18n.default_locale - should ensure SiteSetting.default_locale and I18n.default_locale are consistent" - }, - { - "candidate": "Missing tests to verify fallback order is [user locale, site locale, en]" - }, - { - "candidate": "Missing tests to verify fallback locales are preloaded and translations are available for server-side translation" - }, - { - "candidate": "Missing tests to verify behavior when SiteSetting.default_locale is nil or not set" - }, - { - "candidate": "Need to confirm no other places expect the config.i18n.fallbacks = true environment variable flag after removal" - } - ], - "false_negatives": [ - { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" - }, - { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 10, - "total_golden": 2, - "tp": 0, - "fp": 10, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "codeant", - "repo_name": "discourse__discourse-graphite__codeant__PR9__20260309", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__codeant__PR9__20260309/pull/1" - }, - "kodus": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "I18n fallback mechanism is incomplete - requires manual calls to I18n.fallbacks.ensure_loaded! after setting the locale, which is missing in critical contexts like background jobs, rake tasks, and test setup, leading to potential translation errors" - }, - { - "candidate": "The I18n.fallbacks.ensure_loaded! call needs to be added to app/jobs/base.rb after I18n.locale is set to prevent bugs in background job processing" - }, - { - "candidate": "The I18n initializer should be modified to automatically call ensure_loaded! whenever the locale is changed by patching the I18n.locale= setter method" - } - ], - "false_negatives": [ - { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" - }, - { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 3, - "total_golden": 2, - "tp": 0, - "fp": 3, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "kodus", - "repo_name": "discourse__discourse-graphite__kodus__PR9__20260309", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__kodus__PR9__20260309/pull/1" - }, - "claude-code": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low", - "matched_candidate": "The translate_accelerator.rb freedom patch has its own locale loading logic, and the new ensure_loaded! method adds another entry point that should be verified for race conditions or double-loading issues", - "confidence": 0.65, - "reasoning": "The golden comment identifies a thread-safety issue specifically with the lazy @loaded_locales variable. The candidate issue mentions 'race conditions' in the context of the new ensure_loaded! method and locale loading logic, which is related to thread-safety concerns. However, the candidate is more broadly about verifying race conditions between different loading mechanisms rather than specifically identifying the thread-safety issue with the @loaded_locales lazy initialization. The candidate touches on related concerns but doesn't precisely identify the same specific issue about @loaded_locales thread-safety." - } - ], - "false_positives": [ - { - "candidate": "FallbackLocaleList class inherits from Hash but only implements [] and ensure_loaded!, meaning other Hash methods could return unexpected results if called by the i18n framework internally" - }, - { - "candidate": "ensure_loaded! call happens on every request, which has performance implications" - }, - { - "candidate": "Use of send(:include) to monkey-patch backend classes is fragile across gem upgrades" - }, - { - "candidate": "No test files are included in the diff for the new i18n fallback system" - } - ], - "false_negatives": [ - { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 5, - "total_golden": 2, - "tp": 1, - "fp": 4, - "fn": 1, - "errors_count": 0, - "precision": 0.2, - "recall": 0.5, - "tool": "claude-code", - "repo_name": "discourse__discourse-graphite__claude-code__PR9__20260310", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__claude-code__PR9__20260310/pull/1" - }, - "vercel": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" - }, - { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 2, - "tp": 0, - "fp": 0, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "vercel", - "repo_name": "discourse__discourse-graphite__vercel__PR9__20260310", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__vercel__PR9__20260310/pull/1" - }, - "bito": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" - }, - { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 2, - "tp": 0, - "fp": 0, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "bito", - "repo_name": "discourse__discourse-graphite__bito__PR9__20260310", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__bito__PR9__20260310/pull/1" - }, - "sentry": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" - }, - { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 2, - "tp": 0, - "fp": 0, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "sentry", - "repo_name": "discourse__discourse-graphite__sentry__PR9__20260310", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__sentry__PR9__20260310/pull/1" - }, - "linearb": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" - }, - { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 2, - "tp": 0, - "fp": 0, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "linearb", - "repo_name": "discourse__discourse-graphite__linearb__PR9__20260309", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__linearb__PR9__20260309/pull/1" - }, - "kodus-v2": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" - }, - { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 2, - "tp": 0, - "fp": 0, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "kodus-v2", - "repo_name": "discourse__discourse-graphite__kodus-v2__PR9__20260312", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__kodus-v2__PR9__20260312/pull/1" - }, - "qodo-v2-2": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "Background jobs set I18n.locale but do not call I18n.fallbacks.ensure_loaded!, causing fallback locales (like :en) to not be loaded in Sidekiq/job contexts, resulting in missing translations even when fallback translations exist" - } - ], - "false_negatives": [ - { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" - }, - { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 1, - "total_golden": 2, - "tp": 0, - "fp": 1, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "qodo-v2-2", - "repo_name": "discourse__discourse-graphite__qodo-v2-2__PR9__20260312", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-v2-2__PR9__20260312/pull/1" - }, - "qodo-v22": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", - "severity": "Low", - "matched_candidate": "String/symbol locale mismatch - FallbackLocaleList mixes the current locale value (potentially a String) with symbolized locales, so the same locale can be loaded/checked multiple times under different types (e.g., 'en' vs :en), reducing cache effectiveness and causing redundant locale-load work", - "confidence": 0.95, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment - the potential for double-loading/redundant work when the same locale is passed as a String vs Symbol (e.g., 'en' vs :en). Both comments point out the need to normalize locale input to avoid this mismatch issue. The candidate specifically mentions FallbackLocaleList and the cache effectiveness problem, which aligns with the golden comment's concern about 'double-loading if the same locale is passed as a String vs Symbol'." - } - ], - "false_positives": [ - { - "candidate": "Jobs lack fallback preload - Only controller requests call I18n.fallbacks.ensure_loaded!, so background jobs/services that translate under user locales can still miss fallback translations because only the active locale is lazily loaded, yielding missing-translation strings in notifications/emails executed via Jobs::Base/PostAlerter" - }, - { - "candidate": "Unescaped locale regex - Locale loading interpolates the locale string into Regexp.new without escaping, and user.locale can be set from raw request params; a crafted locale can raise RegexpError (500s) or broaden matches to load extra locale files" - } - ], - "false_negatives": [ - { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 3, - "total_golden": 2, - "tp": 1, - "fp": 2, - "fn": 1, - "errors_count": 0, - "precision": 0.3333333333333333, - "recall": 0.5, - "tool": "qodo-v22", - "repo_name": "discourse__discourse-graphite__qodo-v22__PR9__20260312", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-v22__PR9__20260312/pull/1" - }, - "qodo-extended": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "Background jobs set I18n.locale but never call I18n.fallbacks.ensure_loaded!, so fallback locales (e.g., :en) are not pre-loaded in job context. Since the PR removes config.i18n.fallbacks = true (the previous automatic mechanism), background jobs that call I18n.t may get missing-translation strings when the primary locale file lacks a key that exists in a fallback locale." - } - ], - "false_negatives": [ - { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" - }, - { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 1, - "total_golden": 2, - "tp": 0, - "fp": 1, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "qodo-extended", - "repo_name": "discourse__discourse-graphite__qodo-extended__PR9__20260312", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-extended__PR9__20260312/pull/1" - }, - "cubic-v2": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low", - "matched_candidate": "`@loaded_locales ||= []` is not protected by `LOAD_MUTEX`, introducing a race condition. Two threads could simultaneously see `nil`, each create a new array, and one write is lost. Consider removing the lazy init and relying on `load_locale` (which already guards access with `LOAD_MUTEX`) directly, or wrapping this in the mutex.", - "confidence": 0.98, - "reasoning": "The candidate issue directly identifies the same thread-safety problem with @loaded_locales that the golden comment references. The candidate specifically points out that `@loaded_locales ||= []` is not protected by the mutex, creating a race condition - this is exactly the 'lazy @loaded_locales' thread-safety issue mentioned in the golden comment. The candidate provides more detail about the specific race condition scenario and potential solutions, but it's addressing the identical underlying issue." - }, - { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", - "severity": "Low", - "matched_candidate": "`locale` is not normalized to a Symbol (or String) before the `include?` check. In Ruby `:en != \"en\"`, so passing a String when the locale was previously loaded as a Symbol (from `I18n.config.locale`) will bypass the guard and trigger a redundant load. Consider adding `locale = locale.to_sym` at the top of the method.", - "confidence": 0.95, - "reasoning": "Both comments identify the same issue: the locale parameter is not normalized (to Symbol or String) before checking, which can cause problems when the same locale is passed as different types (String vs Symbol). Both mention that this can lead to redundant/double loading because :en != \"en\" in Ruby. The candidate specifically suggests adding `locale = locale.to_sym` as a fix, which aligns with the golden comment's suggestion to normalize the input locale." - } - ], - "false_positives": [ - { - "candidate": "Preserve standard locale-ancestor fallbacks here; otherwise regional locales skip matching base-language translations." - } - ], - "false_negatives": [], - "errors": [], - "total_candidates": 3, - "total_golden": 2, - "tp": 2, - "fp": 1, - "fn": 0, - "errors_count": 0, - "precision": 0.6666666666666666, - "recall": 1.0, - "tool": "cubic-v2", - "repo_name": "discourse__discourse-graphite__cubic-v2__PR9__20260313", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__cubic-v2__PR9__20260313/pull/1" - }, - "qodo-extended-summary": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low", - "matched_candidate": "The ensure_loaded! method reads and writes @loaded_locales outside LOAD_MUTEX, creating a race condition where two concurrent requests can both pass the include? check and both call load_locale", - "confidence": 0.98, - "reasoning": "The candidate issue identifies exactly the same thread-safety problem as the golden comment. Both point out that @loaded_locales has a race condition - the golden comment mentions 'Thread-safety issue with lazy @loaded_locales' and the candidate specifically explains the race condition where the include? check and load_locale call happen outside the LOAD_MUTEX, allowing concurrent requests to both pass the check and both call load_locale. This is the same underlying thread-safety issue." - }, - { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", - "severity": "Low", - "matched_candidate": "FallbackLocaleList#[] receives I18n.locale as a string but mixes it with symbols in the returned array, causing type mismatches when checking @loaded_locales.include?(locale) since config.locale is a symbol", - "confidence": 0.85, - "reasoning": "Both comments identify the same underlying issue: there's a type mismatch between String and Symbol representations of locales that can cause problems. The golden comment suggests normalizing input locale to avoid double-loading when the same locale is passed as String vs Symbol. The candidate comment identifies that FallbackLocaleList#[] mixes strings with symbols, causing type mismatches when checking @loaded_locales.include?(locale). Both are pointing to the same fundamental problem of inconsistent locale types (String vs Symbol) causing issues in the locale loading/checking logic." - } - ], - "false_positives": [ - { - "candidate": "Background jobs in app/jobs/base.rb set I18n.locale but never call I18n.fallbacks.ensure_loaded!, so fallback locale translation files are never loaded, causing translations that rely on the fallback chain to fail" - }, - { - "candidate": "Translation LRU cache key does not include SiteSetting.default_locale which is part of the fallback chain, causing stale cached translations when site admin changes default_locale" - } - ], - "false_negatives": [], - "errors": [], - "total_candidates": 4, - "total_golden": 2, - "tp": 2, - "fp": 2, - "fn": 0, - "errors_count": 0, - "precision": 0.5, - "recall": 1.0, - "tool": "qodo-extended-summary", - "repo_name": "discourse__discourse-graphite__qodo-extended-summary__PR9__20260313", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-extended-summary__PR9__20260313/pull/1" - }, - "macroscope": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" - }, - { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 2, - "tp": 0, - "fp": 0, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "macroscope", - "repo_name": "discourse__discourse-graphite__macroscope__PR9__20260309", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__macroscope__PR9__20260309/pull/1" - } - }, - "https://github.com/grafana/grafana/pull/76186": { - "gemini": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "SubscribeStream and RunStream methods are not being instrumented with a contextual logger even though they have access to PluginContext, which means logs generated during stream operations won't contain useful contextual information like pluginId" - }, - { - "candidate": "Test function name should be renamed to align with the file rename" - } - ], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 2, - "total_golden": 2, - "tp": 0, - "fp": 2, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "gemini", - "repo_name": "grafana__grafana__gemini__PR76186__20260128", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__gemini__PR76186__20260128/pull/1" - }, - "claude": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 2, - "tp": 0, - "fp": 0, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "claude", - "repo_name": "grafana__grafana__claude__PR76186__20260127", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__claude__PR76186__20260127/pull/1" - }, - "graphite": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 2, - "tp": 0, - "fp": 0, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "graphite", - "repo_name": "grafana__grafana__graphite__PR76186__20260123", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__graphite__PR76186__20260123/pull/1" - }, - "qodo": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "FromContext in the test logger returns a completely new logger instance, which may drop any previously accumulated logger state/context and can hide bugs in production behavior. Consider returning f or f.New(...) (or otherwise preserving existing context) so tests more closely match real contextual logging behavior." - }, - { - "candidate": "FromContext relies on a type assertion to *log.ConcreteLogger. If d.l.FromContext(ctx) returns a different log.Logger implementation, this falls back to d.New() and may unexpectedly discard contextual attributes. Consider a safer wrapper strategy that does not depend on a concrete type, or ensure the underlying logger type is guaranteed here." - }, - { - "candidate": "Logging enrichment is now provided by a separate middleware, while LoggerMiddleware consumes context via logger.FromContext(ctx). This creates a strong dependency on middleware ordering and inclusion across all stacks where LoggerMiddleware is used. Validate all middleware assembly sites ensure the contextual logger middleware always runs before request logging (and that missing it doesn't silently degrade log fields)." - } - ], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 3, - "total_golden": 2, - "tp": 0, - "fp": 3, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "qodo", - "repo_name": "grafana__grafana__qodo__PR76186__20260123", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__qodo__PR76186__20260123/pull/1" - }, - "bugbot": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "TestLogger.FromContext method returns a new TestLogger instance via NewTestLogger() instead of returning the receiver or a logger that shares state, causing logged data to be written to an orphan instance that is immediately discarded, making it impossible to verify that logging occurred during tests" - } - ], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 1, - "total_golden": 2, - "tp": 0, - "fp": 1, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "bugbot", - "repo_name": "grafana__grafana__bugbot__PR76186__20260122", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__bugbot__PR76186__20260122/pull/1" - }, - "copilot": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 2, - "tp": 0, - "fp": 0, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "copilot", - "repo_name": "grafana__grafana__copilot__PR76186__20260122", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__copilot__PR76186__20260122/pull/1" - }, - "augment": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High", - "matched_candidate": "QueryData (and the other request methods) dereference req without a nil guard, but other middlewares in this stack defensively handle req == nil; if a nil request ever propagates here this will panic", - "confidence": 0.95, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment: the methods (QueryData and others) dereference req without checking for nil first, while other middlewares in the stack handle nil requests defensively. This would cause a panic if a nil request propagates to these methods. The candidate specifically mentions 'req == nil' handling and the inconsistency with other middlewares, which matches the golden comment's concern about ContextualLoggerMiddleware methods panicking on nil requests due to accessing req.PluginContext without nil checks, and that this is a regression from previous middleware behavior." - } - ], - "false_positives": [ - { - "candidate": "TestLogger.FromContext returns a fresh logger instance, so call sites like m.logger.FromContext(ctx).Info(...) won't record logs on the original TestLogger value that tests typically assert against" - } - ], - "false_negatives": [ - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 2, - "total_golden": 2, - "tp": 1, - "fp": 1, - "fn": 1, - "errors_count": 0, - "precision": 0.5, - "recall": 0.5, - "tool": "augment", - "repo_name": "grafana__grafana__augment__PR76186__20260122", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__augment__PR76186__20260122/pull/1" - }, - "propel": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "/propel review" - } - ], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 1, - "total_golden": 2, - "tp": 0, - "fp": 1, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "propel", - "repo_name": "grafana__grafana__propel__PR76186__20260122", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__propel__PR76186__20260122/pull/1" - }, - "baz": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "Baz review" - } - ], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 1, - "total_golden": 2, - "tp": 0, - "fp": 1, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "baz", - "repo_name": "grafana__grafana__baz__PR76186__20260122", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__baz__PR76186__20260122/pull/1" - }, - "coderabbit": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High", - "matched_candidate": "The ContextualLoggerMiddleware dereferences req.PluginContext unconditionally in QueryData, CallResource, CheckHealth, and CollectMetrics methods, which can cause a panic when req is nil", - "confidence": 0.98, - "reasoning": "The candidate issue identifies exactly the same problem as the golden comment: the ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) dereference req.PluginContext without checking if req is nil first, which causes a panic. Both comments point to the same bug - nil request handling causing panics due to unconditional access to req.PluginContext." - } - ], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 1, - "total_golden": 2, - "tp": 1, - "fp": 0, - "fn": 1, - "errors_count": 0, - "precision": 1.0, - "recall": 0.5, - "tool": "coderabbit", - "repo_name": "grafana__grafana__coderabbit__PR76186__20260122", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__coderabbit__PR76186__20260122/pull/1" - }, - "greptile": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 2, - "tp": 0, - "fp": 0, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "greptile", - "repo_name": "grafana__grafana__greptile__PR76186__20260122", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__greptile__PR76186__20260122/pull/1" - }, - "kg": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 2, - "tp": 0, - "fp": 0, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "kg", - "repo_name": "grafana__grafana__kg__PR76186__20260202", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__kg__PR76186__20260202/pull/1" - }, - "qodo-v2": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low", - "matched_candidate": "Plugin request logs now rely on logger.FromContext(ctx) to include traceID, but the traceID contextual log provider is registered only when tracing.ProvideService is called. In tests or non-standard setups where spans exist but ProvideService wasn't invoked, plugin request logs can silently lose trace IDs.", - "confidence": 0.85, - "reasoning": "Both comments identify the same core issue: traceID is no longer being logged for plugin requests after the refactoring. The golden comment explains that the tracing import and logic to extract traceID were removed from LoggerMiddleware, and the new ContextualLoggerMiddleware doesn't add this information. The candidate comment identifies the same problem - that plugin request logs can lose trace IDs because the traceID contextual log provider may not be properly registered/available. Both are pointing to the same underlying bug: missing traceID in plugin request logs after the middleware refactoring." - } - ], - "false_positives": [ - { - "candidate": "TestLogger.FromContext returns a brand-new TestLogger, dropping the original logger's accumulated state and violating the Logger contract that the returned logger should include 'this logger's context plus the given context'. This breaks tests or code paths that log via logger.FromContext(ctx) because logs go to a different instance than the one under assertion." - } - ], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 2, - "total_golden": 2, - "tp": 1, - "fp": 1, - "fn": 1, - "errors_count": 0, - "precision": 0.5, - "recall": 0.5, - "tool": "qodo-v2", - "repo_name": "grafana__grafana__qodo-v2__PR76186__20260227", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__qodo-v2__PR76186__20260227/pull/1" - }, - "devin": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "TestLogger.FromContext returns a new disconnected TestLogger instance instead of returning the receiver, causing any code path that calls logger.FromContext(ctx).Info(...) to record logs on a throwaway instance rather than the TestLogger the test holds a reference to, making tests silently pass even when logging behavior is wrong" - } - ], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 1, - "total_golden": 2, - "tp": 0, - "fp": 1, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "devin", - "repo_name": "grafana__grafana__devin__PR76186__20260302", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__devin__PR76186__20260302/pull/1" - }, - "mra-max": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High", - "matched_candidate": "ContextualLoggerMiddleware missing nil-checks when handling requests - dereferences req.PluginContext directly without validating req for nil, which can lead to panics", - "confidence": 0.98, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment: ContextualLoggerMiddleware methods dereference req.PluginContext without first checking if req is nil, which can lead to panics. Both comments point to the same bug - missing nil-checks for the request parameter before accessing req.PluginContext." - } - ], - "false_positives": [ - { - "candidate": "Logger interface breaking change: New(ctx ...any) Logger signature changed from ctx ...interface{}, and FromContext(ctx context.Context) Logger method added - all implementors must adapt" - }, - { - "candidate": "Unsafe type assertions when using exemplars in metrics instrumentation - pluginRequestDurationWithLabels.(prometheus.ExemplarObserver) and pluginRequestCounterWithLabels.(prometheus.ExemplarAdder) called unconditionally without checking assertion result, risking runtime panics" - }, - { - "candidate": "MetricsMiddleware QueryData panics on nil request - dereferences req without nil check when accessing req.Queries and req.PluginContext" - }, - { - "candidate": "Middleware ordering causes missing contextual attributes in metrics logs - ContextualLoggerMiddleware placed after MetricsMiddleware means metrics instrumentation logs execute without contextual attributes" - }, - { - "candidate": "FromContext() assertion in grafanaInfraLogWrapper could panic if underlying logger is not a ConcreteLogger - type assertion to *log.ConcreteLogger may fail and fallback to d.New() could create wrapper with nil or default logger" - }, - { - "candidate": "Test function named TestInstrumentationMiddleware but constructs MetricsMiddleware via newMetricsMiddleware - naming inconsistency after rename refactor" - } - ], - "false_negatives": [ - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 7, - "total_golden": 2, - "tp": 1, - "fp": 6, - "fn": 1, - "errors_count": 0, - "precision": 0.14285714285714285, - "recall": 0.5, - "tool": "mra-max", - "repo_name": "grafana__grafana__mra-max__PR76186__20260306", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__mra-max__PR76186__20260306/pull/1" - }, - "greptile-v4": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High", - "matched_candidate": "ContextualLoggerMiddleware directly dereferences req.PluginContext in all four main handlers without a nil check, while it's inserted before LoggerMiddleware which has nil guards - if a nil request is passed, a panic will occur before the guard in LoggerMiddleware is reached", - "confidence": 0.98, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment: ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) will panic when receiving a nil request because they directly access req.PluginContext without nil checks. The candidate also correctly notes this is problematic because it's inserted before LoggerMiddleware which has nil guards, meaning the panic occurs before those guards are reached. This is semantically the same issue - nil request handling causing panics in ContextualLoggerMiddleware." - } - ], - "false_positives": [ - { - "candidate": "FromContext returns a brand-new TestLogger instance that shares no state with the receiver, causing tests that assert on testLogger.InfoLogs/WarnLogs/ErrorLogs to always see zero calls since actual log output is written to a discarded throwaway instance" - }, - { - "candidate": "Test function name TestInstrumentationMiddleware was not renamed to TestMetricsMiddleware after the type was renamed, creating inconsistency" - } - ], - "false_negatives": [ - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 3, - "total_golden": 2, - "tp": 1, - "fp": 2, - "fn": 1, - "errors_count": 0, - "precision": 0.3333333333333333, - "recall": 0.5, - "tool": "greptile-v4", - "repo_name": "grafana__grafana__greptile-v4__PR76186__20260306", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__greptile-v4__PR76186__20260306/pull/1" - }, - "mra-nano": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "FromContext added to Logger interface with updated implementations - the fake logger and grafanaInfra wrapper now implement FromContext(ctx context.Context) Logger, and LoggerMiddleware uses this to enable contextual logging across the middleware pipeline" - }, - { - "candidate": "New ContextualLoggerMiddleware added to provide contextual logger in requests - need to confirm that calls to NewContextualLoggerMiddleware exist in pluginsintegration.go and tests, and check that instrumentation logic uses FromContext on logger" - }, - { - "candidate": "InstrumentationMiddleware and its constructor have been renamed to MetricsMiddleware/NewMetricsMiddleware, and ContextualLoggerMiddleware has been added and wired into CreateMiddlewares alongside Tracing, Metrics, and Logger middlewares - all call sites and tests need to be updated accordingly" - }, - { - "candidate": "Data race risk in TestLogger: the TestLogger.Error(...) updates ErrorLogs field (Calls, Message, Ctx) without synchronization, which can cause data races and flaky tests or panics in CI when tests run with multiple goroutines logging concurrently" - } - ], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 4, - "total_golden": 2, - "tp": 0, - "fp": 4, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "mra-nano", - "repo_name": "grafana__grafana__mra-nano__PR76186__20260306", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__mra-nano__PR76186__20260306/pull/1" - }, - "mra-ultra": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "Potential data race on plugin registry lookups in MetricsMiddleware - pluginRegistry is accessed via m.pluginRegistry.Plugin(ctx, pluginID) without explicit synchronization, which could race if multiple requests run concurrently and the registry uses shared maps without synchronization" - }, - { - "candidate": "TestLogger mutates internal state without synchronization (data race risk) - Info and Error methods mutate shared fields (Calls, Message, Ctx, InfoLogs, ErrorLogs) without any synchronization, creating a data race if used concurrently by multiple goroutines" - }, - { - "candidate": "WithContextualAttributes accumulates attributes from multiple middleware layers in context, which could magnify exposure of sensitive fields if not controlled" - } - ], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 3, - "total_golden": 2, - "tp": 0, - "fp": 3, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "mra-ultra", - "repo_name": "grafana__grafana__mra-ultra__PR76186__20260306", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__mra-ultra__PR76186__20260306/pull/1" - }, - "mra-b": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High", - "matched_candidate": "ContextualLoggerMiddleware and MetricsMiddleware access fields on request pointers (e.g., req.PluginContext, req.Queries, req.Body) without nil-checking req first, which can cause a nil-pointer panic", - "confidence": 0.95, - "reasoning": "The candidate issue identifies the same underlying problem as the golden comment - that ContextualLoggerMiddleware (and also MetricsMiddleware) access fields on request pointers like req.PluginContext without first checking if req is nil, which can cause nil-pointer panics. The golden comment specifically mentions ContextualLoggerMiddleware methods panicking when nil request is received due to accessing req.PluginContext without nil checking. The candidate captures this exact issue, though it also mentions MetricsMiddleware and additional fields." - } - ], - "false_positives": [ - { - "candidate": "Adding FromContext(context.Context) to the exported Logger interface is a breaking API change that will cause compile errors for any unupdated implementors" - }, - { - "candidate": "TestLogger.New and TestLogger.FromContext both discard context and return a fresh logger, violating the interface contract" - }, - { - "candidate": "grafanaInfraLogWrapper.FromContext may drop contextual attributes when type assertion fails - if d.l.FromContext(ctx) returns a value that does not satisfy *log.ConcreteLogger, the function returns d.New() which does not preserve contextual attributes from ctx" - }, - { - "candidate": "LoggerMiddleware now relies on logger.FromContext(ctx) and middleware ordering must ensure context contains attributes - if contextual attributes are not injected before LoggerMiddleware runs, logs will lack plugin/request details" - } - ], - "false_negatives": [ - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 5, - "total_golden": 2, - "tp": 1, - "fp": 4, - "fn": 1, - "errors_count": 0, - "precision": 0.2, - "recall": 0.5, - "tool": "mra-b", - "repo_name": "grafana__grafana__mra-b__PR76186__20260307", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__mra-b__PR76186__20260307/pull/1" - }, - "mra-a": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High", - "matched_candidate": "ContextualLoggerMiddleware methods QueryData, CallResource, CheckHealth, and CollectMetrics dereference incoming request without nil checks, which can cause panic and denial-of-service", - "confidence": 0.98, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment: the ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) dereference incoming requests without nil checks, which can cause panics. Both comments point to the same bug - missing nil request validation in these middleware methods that leads to potential crashes." - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low", - "matched_candidate": "Refactor splits context enrichment between MetricsMiddleware and ContextualLoggerMiddleware, creating implicit ordering dependency that can cause LoggerMiddleware to silently omit plugin/endpoint/user/datasource/traceID fields if middleware is misordered", - "confidence": 0.85, - "reasoning": "The candidate issue mentions that traceID fields could be omitted from LoggerMiddleware due to middleware ordering dependencies in the refactoring. This directly relates to the golden comment's concern about traceID no longer being logged for plugin requests after the refactoring removed the tracing logic. Both identify the same core problem: traceID is missing from plugin request logs after the refactoring. The candidate frames it as a potential ordering issue while the golden comment states it definitively happens, but they're identifying the same underlying bug." - } - ], - "false_positives": [ - { - "candidate": "Adding FromContext method to Logger interface is a source-level breaking change for external implementations that previously implemented the older Logger interface" - }, - { - "candidate": "FromContext in grafanaInfraLogWrapper uses type assertion that silently falls back to fresh logger if assertion fails, potentially losing contextual attributes like trace IDs" - }, - { - "candidate": "TestLogger.FromContext ignores context and returns fresh logger, creating test fidelity gap where tests may pass while production code behaves differently regarding context-derived fields" - } - ], - "false_negatives": [], - "errors": [], - "total_candidates": 5, - "total_golden": 2, - "tp": 2, - "fp": 3, - "fn": 0, - "errors_count": 0, - "precision": 0.4, - "recall": 1.0, - "tool": "mra-a", - "repo_name": "grafana__grafana__mra-a__PR76186__20260307", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__mra-a__PR76186__20260307/pull/1" - }, - "entelligence": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "Adding `FromContext` to the `Logger` interface is a breaking change - all existing implementations will fail to compile. Need to update all implementations within this PR or introduce a separate interface (e.g., `ContextLogger`) to avoid breaking the `Logger` contract for downstream users." - }, - { - "candidate": "Function `newMetricsMiddleware` duplicates existing code from `newInstrumentationMiddleware` in instrumentation_middleware.go (92% similarity) - should either delete the old function entirely or extract shared Prometheus metric registration into a shared constructor to avoid duplicated metric declarations which would panic at runtime if both are registered" - }, - { - "candidate": "Function `NewMetricsMiddleware` duplicates existing code from `NewInstrumentationMiddleware` in instrumentation_middleware.go (93% similarity) - should delete NewInstrumentationMiddleware along with its backing implementation to complete the rename cleanly" - }, - { - "candidate": "Function `MetricsMiddleware.pluginTarget` duplicates existing code from `InstrumentationMiddleware.pluginTarget` in instrumentation_middleware.go (95% similarity) - should extract pluginTarget into a package-level helper function that both middlewares can call" - }, - { - "candidate": "Function `MetricsMiddleware.instrumentPluginRequestSize` duplicates existing code from `InstrumentationMiddleware.instrumentPluginRequestSize` in instrumentation_middleware.go (95% similarity) - should consolidate on a single middleware and delete the duplicate" - } - ], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 5, - "total_golden": 2, - "tp": 0, - "fp": 5, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "entelligence", - "repo_name": "grafana__grafana__entelligence__PR76186__20260306", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__entelligence__PR76186__20260306/pull/1" - }, - "sourcery": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "The `*log.ConcreteLogger` type assertion in FromContext couples the wrapper to one implementation and causes it to silently drop context-derived fields whenever FromContext returns a different concrete type. The grafanaInfraLogWrapper should hold the log.Logger interface and wrap d.l.FromContext(ctx) directly without a type assertion." - }, - { - "candidate": "In contextual_logger_middleware.go, the helper instrumentContext now only enriches the logger, not metrics, so consider renaming it (and its comment) to something logger-specific (e.g. withContextualLogger) to avoid confusion with the metrics instrumentation middleware." - }, - { - "candidate": "In grafanaInfraLogWrapper.FromContext, falling back to New() when the underlying logger is not a *log.ConcreteLogger silently drops any context-derived attributes; consider either documenting this behavior or adding a lightweight log/metric so that unexpected logger types are easier to detect." - } - ], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 3, - "total_golden": 2, - "tp": 0, - "fp": 3, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "sourcery", - "repo_name": "grafana__grafana__sourcery__PR76186__20260308", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__sourcery__PR76186__20260308/pull/1" - }, - "cubic-dev": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High", - "matched_candidate": "Nil `req` will cause a panic when accessing `req.PluginContext` in ContextualLoggerMiddleware.QueryData - should add nil guard like sibling LoggerMiddleware", - "confidence": 0.95, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment - that nil `req` will cause a panic when accessing `req.PluginContext` in ContextualLoggerMiddleware.QueryData. The candidate specifically mentions the QueryData method and the need for a nil guard, which matches the golden comment's description of the panic occurring due to accessing req.PluginContext without checking if req is nil. The golden comment mentions multiple methods (QueryData, CallResource, CheckHealth, CollectMetrics) while the candidate only explicitly mentions QueryData, but they're identifying the same underlying issue." - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low", - "matched_candidate": "`traceID` is no longer included in the 'Plugin Request Completed' log output - the explicit tracing.TraceIDFromContext call was removed and ContextualLoggerMiddleware doesn't add traceID to contextual attributes", - "confidence": 0.98, - "reasoning": "The candidate issue identifies exactly the same problem as the golden comment: the traceID is no longer being logged for plugin requests because the tracing.TraceIDFromContext call was removed and the new ContextualLoggerMiddleware doesn't add traceID to the contextual attributes. Both comments point to the same missing functionality and its impact on debugging/tracing." - } - ], - "false_positives": [ - { - "candidate": "Test function is still named `TestInstrumentationMiddleware` but should be `TestMetricsMiddleware` to match the renamed middleware" - }, - { - "candidate": "Doc comment for `FromContext` appears to be copy-pasted from `New` and doesn't accurately describe the method's behavior - FromContext retrieves a logger from context, not creates one with key/value pairs" - }, - { - "candidate": "`FromContext` in TestLogger returns a new disconnected `TestLogger`, so logs written through the returned logger are silently lost - should return the receiver instead" - } - ], - "false_negatives": [], - "errors": [], - "total_candidates": 5, - "total_golden": 2, - "tp": 2, - "fp": 3, - "fn": 0, - "errors_count": 0, - "precision": 0.4, - "recall": 1.0, - "tool": "cubic-dev", - "repo_name": "grafana__grafana__cubic-dev__PR76186__20260306", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__cubic-dev__PR76186__20260306/pull/1" - }, - "mesa": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "Type assertion to concrete `*log.ConcreteLogger` creates tight coupling to infrastructure internals. If the underlying logger implementation changes, this will silently fall back to `d.New()`, dropping all contextual attributes without error visibility" - }, - { - "candidate": "`FromContext` returns a new `TestLogger` instance instead of the receiver (`f`), breaking test observability - tests using `FromContext` won't be able to verify logged calls/messages through the original TestLogger's captured state" - }, - { - "candidate": "Middleware ordering may be incorrect: `LoggerMiddleware` executes before `ContextualLoggerMiddleware`, so logs won't contain expected contextual attributes (plugin ID, endpoint, datasource)" - } - ], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 3, - "total_golden": 2, - "tp": 0, - "fp": 3, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "mesa", - "repo_name": "grafana__grafana__mesa__PR76186__20260306", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__mesa__PR76186__20260306/pull/1" - }, - "codeant": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low", - "matched_candidate": "TraceID was removed from LoggerMiddleware logParams - if TraceID is still desirable on plugin logs, need to add it back or include in ContextualLoggerMiddleware", - "confidence": 0.95, - "reasoning": "The candidate issue directly identifies the same problem as the golden comment: the traceID was removed from LoggerMiddleware logParams during refactoring, and it suggests either adding it back or including it in ContextualLoggerMiddleware. This matches the golden comment's concern about traceID no longer being logged for plugin requests and the impact on debugging/tracing capabilities." - } - ], - "false_positives": [ - { - "candidate": "Order of middlewares in CreateMiddlewares needs validation - ensure ContextualLoggerMiddleware runs before LoggerMiddleware so LoggerMiddleware can call logger.FromContext(ctx) and get contextual attributes" - }, - { - "candidate": "MetricsMiddleware placement should be validated to ensure instrumentation metrics still measure the desired durations correctly" - }, - { - "candidate": "Logger.FromContext implementation in grafanaInfraLogWrapper relies on type assertion to *log.ConcreteLogger which could fail if underlying infra log package changes - consider using interface method instead of concrete type assertion" - }, - { - "candidate": "Missing unit tests for new ContextualLoggerMiddleware - should verify context attributes (pluginId, endpoint, dsName/dsUID, uname) are attached to logger" - }, - { - "candidate": "Missing tests for ContextualLoggerMiddleware edge cases where DataSourceInstanceSettings is nil or User is nil to prevent panics" - }, - { - "candidate": "Logger name in CreateMiddlewares still uses 'plugin.instrumentation' but middleware was renamed to metrics - consider updating logger name" - }, - { - "candidate": "Need to verify all packages implementing Logger interface now satisfy the new interface with FromContext method - check for missed implementations beyond grafanaInfraLogWrapper and TestLogger" - } - ], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 8, - "total_golden": 2, - "tp": 1, - "fp": 7, - "fn": 1, - "errors_count": 0, - "precision": 0.125, - "recall": 0.5, - "tool": "codeant", - "repo_name": "grafana__grafana__codeant__PR76186__20260309", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__codeant__PR76186__20260309/pull/1" - }, - "kodus": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "Stream-related methods (SubscribeStream, RunStream) in ContextualLoggerMiddleware are not instrumented with contextual logging unlike other methods, causing missing critical contextual information (pluginId, dsUID, etc.) in logs for streaming operations" - }, - { - "candidate": "TestLogger.FromContext method returns a new TestLogger instance via NewTestLogger() instead of returning the receiver (f), causing logs to be recorded on an un-referenced instance and breaking test assertions" - }, - { - "candidate": "Refactoring of logRequest removes pluginCtx parameter which eliminates critical contextual information (pluginId, dsName, dsUID, uname) from plugin request logs since calling functions don't enrich the context before calling logRequest" - } - ], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 3, - "total_golden": 2, - "tp": 0, - "fp": 3, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "kodus", - "repo_name": "grafana__grafana__kodus__PR76186__20260309", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__kodus__PR76186__20260309/pull/1" - }, - "claude-code": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "TestLogger.FromContext returns a new disconnected TestLogger via NewTestLogger() instead of returning the receiver (f), causing any test using TestLogger to assert on LoggerMiddleware logging behavior to see zero calls since logs go to a throwaway instance - fix by changing 'return NewTestLogger()' to 'return f' on line 47" - }, - { - "candidate": "Test function on line 21 is still named TestInstrumentationMiddleware but the struct was renamed to MetricsMiddleware - should be TestMetricsMiddleware for consistency with the rest of the rename" - } - ], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 2, - "total_golden": 2, - "tp": 0, - "fp": 2, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "claude-code", - "repo_name": "grafana__grafana__claude-code__PR76186__20260310", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__claude-code__PR76186__20260310/pull/1" - }, - "vercel": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 2, - "tp": 0, - "fp": 0, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "vercel", - "repo_name": "grafana__grafana__vercel__PR76186__20260310", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__vercel__PR76186__20260310/pull/1" - }, - "bito": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 2, - "tp": 0, - "fp": 0, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "bito", - "repo_name": "grafana__grafana__bito__PR76186__20260310", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__bito__PR76186__20260310/pull/1" - }, - "sentry": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 2, - "tp": 0, - "fp": 0, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "sentry", - "repo_name": "grafana__grafana__sentry__PR76186__20260310", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__sentry__PR76186__20260310/pull/1" - }, - "linearb": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "/gs review" - } - ], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 1, - "total_golden": 2, - "tp": 0, - "fp": 1, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "linearb", - "repo_name": "grafana__grafana__linearb__PR76186__20260309", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__linearb__PR76186__20260309/pull/1" - }, - "kodus-v2": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low", - "matched_candidate": "TraceID dropped from 'Plugin Request Completed' log entries - the instrumentContext function in contextual_logger_middleware.go only injects endpoint, pluginId, dsName, dsUID, and uname but never extracts and includes traceID via tracing.TraceIDFromContext, breaking distributed trace correlation", - "confidence": 0.98, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment: the traceID is no longer being logged for plugin requests. The candidate specifically mentions that the instrumentContext function in contextual_logger_middleware.go doesn't extract and include traceID via tracing.TraceIDFromContext, which matches the golden comment's description that the ContextualLoggerMiddleware does not add traceID information, resulting in missing traceID in plugin request logs and impacting debugging/tracing capabilities." - } - ], - "false_positives": [ - { - "candidate": "FromContext returns a new, disconnected TestLogger instead of the receiver, causing any production code that calls logger.FromContext(ctx) to write to a discarded instance - the original TestLogger used for test assertions never sees those logs" - } - ], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 2, - "total_golden": 2, - "tp": 1, - "fp": 1, - "fn": 1, - "errors_count": 0, - "precision": 0.5, - "recall": 0.5, - "tool": "kodus-v2", - "repo_name": "grafana__grafana__kodus-v2__PR76186__20260312", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__kodus-v2__PR76186__20260312/pull/1" - }, - "qodo-v2-2": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 2, - "tp": 0, - "fp": 0, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "qodo-v2-2", - "repo_name": "grafana__grafana__qodo-v2-2__PR76186__20260312", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__qodo-v2-2__PR76186__20260312/pull/1" - }, - "qodo-v22": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low", - "matched_candidate": "LoggerMiddleware no longer appends traceID directly and relies on logger.FromContext(ctx), so traceID is only logged when the tracing package has registered a contextual log provider. tracing.InitializeTracerForTest does not register that provider, so plugin request logs in tests lose trace correlation even when spans exist in the context", - "confidence": 0.92, - "reasoning": "Both comments identify the same core issue: the LoggerMiddleware no longer logs traceID for plugin requests after refactoring. The golden comment explains that tracing import and logic to extract/add traceID were removed, and the new ContextualLoggerMiddleware doesn't add this information. The candidate comment identifies the same problem - LoggerMiddleware no longer appends traceID directly and now relies on logger.FromContext(ctx), which means traceID is only logged when a contextual log provider is registered. Both point to the same underlying bug: missing traceID in plugin request logs affecting debugging and request tracing." - } - ], - "false_positives": [ - { - "candidate": "TestLogger.FromContext returns a new TestLogger instance, so any code that logs via logger.FromContext(ctx) writes into a fresh logger and the original test logger never sees the call, breaking log assertions and silently hiding logging regressions" - } - ], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 2, - "total_golden": 2, - "tp": 1, - "fp": 1, - "fn": 1, - "errors_count": 0, - "precision": 0.5, - "recall": 0.5, - "tool": "qodo-v22", - "repo_name": "grafana__grafana__qodo-v22__PR76186__20260312", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__qodo-v22__PR76186__20260312/pull/1" - }, - "qodo-extended": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "TestLogger.FromContext returns a brand-new disconnected TestLogger via NewTestLogger() instead of the receiver, causing any test injecting a TestLogger to silently lose all log output to an ephemeral instance, making log assertions always see zero recorded logs" - } - ], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 1, - "total_golden": 2, - "tp": 0, - "fp": 1, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "qodo-extended", - "repo_name": "grafana__grafana__qodo-extended__PR76186__20260312", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__qodo-extended__PR76186__20260312/pull/1" - }, - "cubic-v2": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High", - "matched_candidate": "Accessing `req.PluginContext` without a nil guard will panic if `req` is nil - the middleware should guard against nil requests like LoggerMiddleware does in all four methods", - "confidence": 0.98, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment: the ContextualLoggerMiddleware methods will panic when accessing req.PluginContext if req is nil, and suggests adding nil guards like LoggerMiddleware does. This matches the golden comment's description of the panic occurring due to accessing req.PluginContext via instrumentContext without nil checking, and that this is a regression from previous middleware that handled nil requests gracefully." - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low", - "matched_candidate": "The contextual log attributes omit `traceID` - since this middleware now owns the contextual logging setup, it should include `traceID` from `tracing.TraceIDFromContext` so plugin request logs still carry trace correlation", - "confidence": 0.98, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment: the traceID is no longer being logged for plugin requests because the new ContextualLoggerMiddleware doesn't include traceID from tracing.TraceIDFromContext. Both comments point out that this information was previously available but is now missing, affecting trace correlation/debugging capabilities." - } - ], - "false_positives": [ - { - "candidate": "Returning a fresh `TestLogger` in `FromContext` drops all log writes made through `FromContext`, so tests can no longer observe logs emitted by code paths that use contextual logging - should return `f` instead" - } - ], - "false_negatives": [], - "errors": [], - "total_candidates": 3, - "total_golden": 2, - "tp": 2, - "fp": 1, - "fn": 0, - "errors_count": 0, - "precision": 0.6666666666666666, - "recall": 1.0, - "tool": "cubic-v2", - "repo_name": "grafana__grafana__cubic-v2__PR76186__20260313", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__cubic-v2__PR76186__20260313/pull/1" - }, - "qodo-extended-summary": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low", - "matched_candidate": "The traceID field was removed from the 'Plugin Request Completed' log line. The old logRequest explicitly extracted the trace ID via tracing.TraceIDFromContext and appended it to logParams, but this was removed in the refactor and the new ContextualLoggerMiddleware.instrumentContext does not add it either, breaking trace-to-log correlation", - "confidence": 0.98, - "reasoning": "The candidate issue identifies exactly the same problem as the golden comment: the traceID field was removed from plugin request logs during refactoring. Both comments point out that (1) the old LoggerMiddleware/logRequest extracted traceID via tracing.TraceIDFromContext, (2) this was removed during refactoring, (3) the new ContextualLoggerMiddleware does not add this information, and (4) this breaks trace-to-log correlation/debugging capabilities. The wording is slightly different but describes the identical underlying issue." - } - ], - "false_positives": [ - { - "candidate": "TestLogger.FromContext returns a brand-new TestLogger instance (NewTestLogger()), discarding all accumulated state. Since LoggerMiddleware.logRequest now calls m.logger.FromContext(ctx).Info(...), the log entry goes to this throwaway instance rather than the original TestLogger that tests hold a reference to, making it impossible to assert on InfoLogs.Calls, InfoLogs.Message, or InfoLogs.Ctx" - } - ], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 2, - "total_golden": 2, - "tp": 1, - "fp": 1, - "fn": 1, - "errors_count": 0, - "precision": 0.5, - "recall": 0.5, - "tool": "qodo-extended-summary", - "repo_name": "grafana__grafana__qodo-extended-summary__PR76186__20260313", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__qodo-extended-summary__PR76186__20260313/pull/1" - }, - "macroscope": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "TestLogger.FromContext returns a new *TestLogger instance instead of returning the original instance (f), causing logs written through the returned logger to be recorded on an ephemeral instance that is discarded, preventing tests from verifying their logs" - } - ], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 1, - "total_golden": 2, - "tp": 0, - "fp": 1, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "macroscope", - "repo_name": "grafana__grafana__macroscope__PR76186__20260309", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__macroscope__PR76186__20260309/pull/1" - } - }, - "https://github.com/ai-code-review-evaluation/discourse-graphite/pull/10": { - "gemini": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical", - "matched_candidate": "Using `where(id: params[:id]).first` instead of `find(params[:id])` in update action returns nil if record not found, causing NoMethodError in save_host(nil)", - "confidence": 0.85, - "reasoning": "The golden comment mentions 'NoMethodError before_validation in EmbeddableHost'. The candidate issue identifies that using `where(id: params[:id]).first` instead of `find(params[:id])` returns nil if record not found, causing NoMethodError in save_host(nil). This is the same underlying issue - when a record is not found, nil is passed to save_host, and calling methods like before_validation on nil causes NoMethodError. Both identify the same bug related to NoMethodError when the record doesn't exist." - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium", - "matched_candidate": "Using `where(id: params[:id]).first` instead of `find(params[:id])` in update action returns nil if record not found, causing NoMethodError in save_host(nil)", - "confidence": 0.95, - "reasoning": "The candidate issue identifies the same underlying problem as the golden comment - using `where(id: params[:id]).first` instead of `find` returns nil when the record doesn't exist, which causes a NoMethodError when trying to call methods on nil. The candidate specifically mentions the update action and save_host(nil), while the golden comment mentions both update and destroy methods. However, they both identify the same core issue: lack of validation for record existence leading to NoMethodError on nil objects." - } - ], - "false_positives": [ - { - "candidate": "Using `where(id: params[:id]).first` instead of `find(params[:id])` in destroy action returns nil if record not found, causing NoMethodError on host.destroy" - }, - { - "candidate": "Directly assigning attributes from params without strong parameters creates mass assignment vulnerability" - }, - { - "candidate": "Accessing `[0]['id']` on execute result is unsafe - if query returns no rows, `[0]` will be nil causing NoMethodError when calling `['id']`" - }, - { - "candidate": "SQL query uses string interpolation (`'#{h}'`) to insert values, which is vulnerable to SQL injection - should use parameter binding or quoting" - }, - { - "candidate": "Manually setting category on host model after saving is a workaround - server should respond with embedded category object for Ember Data to handle relationship automatically" - }, - { - "candidate": "saveChanges action appears to be dead code - calls update({}) on embedding model but server-side update action is a no-op with no UI element triggering it" - }, - { - "candidate": "Using inline style attributes for layout mixes presentation with structure - should use CSS classes for styling table column widths" - }, - { - "candidate": "Regex for host validation includes `(\\/.*)?` for optional path but before_validation hook strips paths, making this regex part redundant and confusing" - }, - { - "candidate": "Methods `id`, `host`, and `category_id` are redundant because `attributes :id, :host, :category_id` already defines them" - }, - { - "candidate": "The `id` method is redundant because `attributes :id` already defines it" - } - ], - "false_negatives": [ - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 11, - "total_golden": 4, - "tp": 2, - "fp": 10, - "fn": 2, - "errors_count": 0, - "precision": 0.18181818181818182, - "recall": 0.5, - "tool": "gemini", - "repo_name": "discourse__discourse-graphite__gemini__PR10__20260128", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__gemini__PR10__20260128/pull/1" - }, - "claude": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "Test fabricators file swap - category_fabricator.rb was overwritten with embeddable_host content, destroying all category fabricators (Fabricator(:category), Fabricator(:diff_category), etc.)" - }, - { - "candidate": "Uses deprecated where().first pattern instead of find_by() (codebase-wide refactoring established find_by as the standard)" - }, - { - "candidate": "Missing strong parameters - directly accesses params[:embeddable_host] without using require/permit" - } - ], - "false_negatives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical" - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium" - }, - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 3, - "total_golden": 4, - "tp": 0, - "fp": 3, - "fn": 4, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "claude", - "repo_name": "discourse__discourse-graphite__claude__PR10__20260127", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__claude__PR10__20260127/pull/1" - }, - "qodo": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical", - "matched_candidate": "before_validation callback calls sub! on self.host without guarding against nil, which can raise exceptions when host is unset", - "confidence": 0.95, - "reasoning": "The golden comment mentions 'NoMethodError before_validation in EmbeddableHost', indicating an issue where a method is called on nil in the before_validation callback. The candidate issue specifically identifies that 'before_validation callback calls sub! on self.host without guarding against nil, which can raise exceptions when host is unset'. This is exactly the same issue - calling sub! on a nil host value in the before_validation callback would raise a NoMethodError." - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium", - "matched_candidate": "update and destroy actions fetch records with where(...).first but don't handle the not-found case, leading to potential NoMethodError when record is missing", - "confidence": 0.98, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment: the update and destroy actions use where(...).first to fetch records but don't handle the case when the record doesn't exist (returns nil), which would lead to a NoMethodError when trying to call methods on nil. Both comments point to the same bug in the same methods with the same consequence." - } - ], - "false_positives": [ - { - "candidate": "SQL injection vulnerability in db/migrate/20150818190757_create_embeddable_hosts.rb where host value is interpolated directly into INSERT statement without quoting/parameterization" - }, - { - "candidate": "app/controllers/admin/embeddable_hosts_controller.rb assigns params[:embeddable_host][:host] directly to host.host without explicit sanitization and assumes nested params exist, which can cause unexpected behavior or crashes" - }, - { - "candidate": "save_host method doesn't check if params[:embeddable_host] is present before accessing it" - } - ], - "false_negatives": [ - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 5, - "total_golden": 4, - "tp": 2, - "fp": 3, - "fn": 2, - "errors_count": 0, - "precision": 0.4, - "recall": 0.5, - "tool": "qodo", - "repo_name": "discourse__discourse-graphite__qodo__PR10__20260125", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo__PR10__20260125/pull/1" - }, - "graphite": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical" - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium" - }, - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 4, - "tp": 0, - "fp": 0, - "fn": 4, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "graphite", - "repo_name": "discourse__discourse-graphite__graphite__PR10__20260123", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__graphite__PR10__20260123/pull/1" - }, - "bugbot": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical", - "matched_candidate": "The before_validation callback in embeddable_host.rb calls self.host.sub! without checking if host is nil, raising NoMethodError instead of a validation error", - "confidence": 0.98, - "reasoning": "The candidate issue identifies exactly the same problem as the golden comment - a NoMethodError in the before_validation callback of EmbeddableHost (embeddable_host.rb) due to calling a method on host without checking if it's nil. Both comments point to the same bug: the before_validation callback will raise NoMethodError when host is nil instead of properly handling the validation." - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium", - "matched_candidate": "The update and destroy actions in embeddable_hosts_controller.rb don't verify the record exists before operating on it, causing NoMethodError when an invalid ID is provided", - "confidence": 0.99, - "reasoning": "The candidate issue identifies exactly the same problem as the golden comment: the update and destroy actions in embeddable_hosts_controller.rb don't validate that the record exists before operating on it, which will cause a NoMethodError when an invalid ID is provided. Both comments point to the same bug - missing nil check on the EmbeddableHost record lookup." - }, - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium", - "matched_candidate": "The record_for_host method uses case-insensitive comparison on the database column but not on the host parameter, causing legitimate embeds with uppercase letters in URLs to be rejected", - "confidence": 0.98, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment: the record_for_host method uses case-insensitive comparison on the database column (lower(host)) but doesn't normalize the host parameter's case, which could cause matching failures for mixed-case hosts. Both comments point to the same bug where the parameter isn't lowercased before comparison." - } - ], - "false_positives": [ - { - "candidate": "The contents of category_fabricator.rb and embeddable_host_fabricator.rb are swapped - the category fabricator file contains the embeddable_host fabricator definition, while the embeddable_host fabricator file contains all the category fabricators" - }, - { - "candidate": "The migration accesses execute(...)[0]['id'] without checking if the query returned any rows, crashing when no embed_category setting exists" - }, - { - "candidate": "The delete action in embeddable-host.js.es6 calls destroyRecord() without a .catch() handler, silently swallowing errors without user feedback" - }, - { - "candidate": "The plural *_ids hydration in store.js.es6 unconditionally deletes the original key regardless of whether hydration succeeded, potentially losing original IDs if lookups fail" - } - ], - "false_negatives": [ - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 7, - "total_golden": 4, - "tp": 3, - "fp": 4, - "fn": 1, - "errors_count": 0, - "precision": 0.42857142857142855, - "recall": 0.75, - "tool": "bugbot", - "repo_name": "discourse__discourse-graphite__bugbot__PR10__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__bugbot__PR10__20260122/pull/1" - }, - "copilot": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium", - "matched_candidate": "Both `update` and `destroy` methods look up the embeddable host with `EmbeddableHost.where(id: params[:id]).first` and then immediately dereference it; if no record exists for the given id, this will raise a `NoMethodError` instead of returning a 404-style error. Should use `EmbeddableHost.find(params[:id])` or explicitly handle the `nil` case", - "confidence": 1.0, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment: both the update and destroy methods in Admin::EmbeddableHostsController use EmbeddableHost.where(id: params[:id]).first without validating the existence of the record, which will cause a NoMethodError if the record doesn't exist (returns nil). The candidate even suggests the same solution approach (using find or handling nil explicitly)." - } - ], - "false_positives": [ - { - "candidate": "The file now only defines the `:embeddable_host` fabricator while `:category`-related fabricators have been moved into `spec/fabricators/embeddable_host_fabricator.rb`, inverting the usual convention. The `:category` fabricators should be in `category_fabricator.rb` and `:embeddable_host` fabricator in `embeddable_host_fabricator.rb`" - }, - { - "candidate": "The description string 'with an emeddable host' contains a typo - `emeddable` should be `embeddable`" - }, - { - "candidate": "In the test, `store.find('fruit', 2)` requests fruit with id 2 (banana with `color_ids: [3]`), but expectations assert the `colors` association has ids 1 and 2. The pretender route always returns `fruits[0]` (id 1 with `color_ids: [1,2]`), so the test passes only because the route ignores the requested id. The fixture, pretender route, or requested id should be adjusted to align" - }, - { - "candidate": "The migration assumes `SELECT c.id ... WHERE s.name = 'embed_category'` and `SELECT value FROM site_settings WHERE name = 'uncategorized_category_id'` always return at least one row, but if either setting is missing or `embed_category` doesn't match an existing category name, `execute(...)[0]` will return `nil` and calling `['id']`/`['value']` on it will raise, breaking the migration. Should check `cmd_tuples` or presence of the first row before indexing" - } - ], - "false_negatives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical" - }, - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 5, - "total_golden": 4, - "tp": 1, - "fp": 4, - "fn": 3, - "errors_count": 0, - "precision": 0.2, - "recall": 0.25, - "tool": "copilot", - "repo_name": "discourse__discourse-graphite__copilot__PR10__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__copilot__PR10__20260122/pull/1" - }, - "propel": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical", - "matched_candidate": "If the admin leaves the Host field blank, before_validation will attempt to call sub! on nil, raising NoMethodError and returning a 500 instead of a validation error", - "confidence": 0.98, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment - that before_validation will raise a NoMethodError when the host field is nil/blank because sub! is called on nil. Both comments point to the same bug in the same location (before_validation in EmbeddableHost)." - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium", - "matched_candidate": "Both the update and destroy actions assume the requested embeddable host exists, and if a stale or invalid id hits either endpoint, it will raise NoMethodError and return a 500 instead of a clean 404/422", - "confidence": 0.98, - "reasoning": "The candidate issue identifies exactly the same problem as the golden comment: both the update and destroy actions in Admin::EmbeddableHostsController don't validate that the EmbeddableHost record exists before calling methods on it. Both point out that if the record doesn't exist (nil is returned), a NoMethodError will be raised. The candidate even adds the detail about returning a 500 instead of a proper 404/422, which is the consequence of the same underlying issue." - }, - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium", - "matched_candidate": "EmbeddableHost.record_for_host compares lower(host) with the host extracted from the URI, but the right-hand side isn't downcased, so embeds with mixed-case referrers are rejected even though DNS is case-insensitive", - "confidence": 0.98, - "reasoning": "Both comments identify the same issue: the record_for_host method uses lower(host) in the SQL comparison but doesn't normalize/downcase the input parameter (the host extracted from the URI/referer), causing mixed-case hosts to fail matching despite the intended case-insensitive comparison." - } - ], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 3, - "total_golden": 4, - "tp": 3, - "fp": 0, - "fn": 1, - "errors_count": 0, - "precision": 1.0, - "recall": 0.75, - "tool": "propel", - "repo_name": "discourse__discourse-graphite__propel__PR10__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__propel__PR10__20260122/pull/1" - }, - "augment": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical", - "matched_candidate": "If host is nil, sub! will raise before validations run, turning a bad request into a 500 instead of surfacing a validation error", - "confidence": 0.95, - "reasoning": "The golden comment mentions 'NoMethodError before_validation in EmbeddableHost', indicating an error that occurs before validation runs. The candidate issue describes the same problem: 'If host is nil, sub! will raise before validations run' - this is exactly the NoMethodError that would occur when calling sub! on nil. Both identify the same underlying issue: a method being called on a potentially nil value before validations can catch the invalid state." - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium", - "matched_candidate": "If params[:id] doesn't match a record, host will be nil and save_host(host) / host.destroy will raise a 500 error instead of returning 404", - "confidence": 0.98, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment: when params[:id] doesn't match a record, EmbeddableHost.where(id: params[:id]).first returns nil, and calling methods like save_host or destroy on nil will cause an error. The candidate correctly identifies this will result in a 500 error (which would be caused by NoMethodError) instead of a proper 404 response. This is the same underlying issue about missing nil validation for the host record." - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High", - "matched_candidate": "Migration inserts h via string interpolation which can break on quotes and bypasses the model's host normalization (scheme/path stripping), potentially leaving records that will never match record_for_host", - "confidence": 0.95, - "reasoning": "The candidate issue identifies the same core problem as the golden comment: the migration inserts data via raw SQL that bypasses the EmbeddableHost model's normalization (scheme/path stripping), which could result in records that won't match the new host lookup mechanism. Both comments point to the same underlying issue - migrated hosts may not be normalized to the same format as newly created records, causing existing embedding configurations to fail." - } - ], - "false_positives": [ - { - "candidate": "record_for_host compares against uri.host (no port), but the model allows saving host values with :port, so a stored example.com:3000 would never match and will be treated as disallowed" - }, - { - "candidate": "Server-side save_host defaults blank category_id to uncategorized, but the client sets host.category from categoryId rather than the saved record's returned category_id, causing UI to show no category even though the server persisted one" - } - ], - "false_negatives": [ - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - } - ], - "errors": [], - "total_candidates": 5, - "total_golden": 4, - "tp": 3, - "fp": 2, - "fn": 1, - "errors_count": 0, - "precision": 0.6, - "recall": 0.75, - "tool": "augment", - "repo_name": "discourse__discourse-graphite__augment__PR10__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__augment__PR10__20260122/pull/1" - }, - "baz": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical", - "matched_candidate": "before_validation runs self.host.sub! without checking if host is present, causing NoMethodError when admin POST/PUT with embeddable_host params lacks host or has nil host - needs guard with 'return if host.blank?' before calling sub!", - "confidence": 0.98, - "reasoning": "The candidate issue identifies exactly the same problem as the golden comment - a NoMethodError in the before_validation callback of EmbeddableHost when host is nil/blank. The candidate correctly identifies that calling sub! on a nil host will cause NoMethodError and suggests the same fix (adding a guard clause to check if host is blank before calling sub!)." - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High", - "matched_candidate": "Migration inserts raw site_settings.embeddable_hosts values directly into embeddable_hosts.host without stripping scheme/path, but EmbeddableHost.record_for_host compares URI(host).host against stored hosts, so migrated values containing http:// or paths will never match and previously allowed hosts become disallowed after migration - values need to be normalized before inserting", - "confidence": 0.98, - "reasoning": "Both comments identify the exact same issue: the migration inserts raw embeddable_hosts values from site_settings without normalizing them (stripping scheme like http:// or path segments), but the EmbeddableHost.record_for_host method compares against bare hosts. This means migrated data with schemes/paths won't match during lookup, breaking existing embedding configurations. The candidate and golden comment describe the same underlying bug with the same root cause and consequence." - } - ], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium" - }, - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - } - ], - "errors": [], - "total_candidates": 2, - "total_golden": 4, - "tp": 2, - "fp": 0, - "fn": 2, - "errors_count": 0, - "precision": 1.0, - "recall": 0.5, - "tool": "baz", - "repo_name": "discourse__discourse-graphite__baz__PR10__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__baz__PR10__20260122/pull/1" - }, - "coderabbit": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical", - "matched_candidate": "self.host.sub! in before_validation raises on nil host instead of returning validation error - needs nil guard", - "confidence": 0.95, - "reasoning": "The candidate issue identifies the same problem as the golden comment. Both are pointing out that there's a NoMethodError/error that occurs in the before_validation callback in EmbeddableHost when the host is nil. The candidate specifically mentions that 'self.host.sub!' raises on nil host and needs a nil guard, which is exactly the NoMethodError issue the golden comment is referring to." - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium", - "matched_candidate": "EmbeddableHost.where(id:).first returns nil causing NoMethodError in update/destroy actions when invalid id is passed - need find_by with explicit guard", - "confidence": 0.99, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment: EmbeddableHost.where(id:).first can return nil, and calling methods like save_host or destroy on nil will cause a NoMethodError in the update/destroy actions. Both comments point to the same bug - missing validation/guard for the existence of the EmbeddableHost record." - } - ], - "false_positives": [ - { - "candidate": "Icon-only d-button components (save, cancel, edit, delete) lack accessible labels for screen readers - need title attributes with i18n keys that don't currently exist in locale files" - }, - { - "candidate": "obj[k].map in store.js.es6 will throw if API returns null or non-array for *_ids fields - needs Array.isArray guard before calling map" - }, - { - "candidate": "Host validation regex caps TLDs at 5 characters, rejecting valid domains like .museum or .community - should allow up to 63 characters" - }, - { - "candidate": "Migration execute(...)[0] will raise IndexError if embed_category or uncategorized_category_id queries return no rows - needs presence checks" - }, - { - "candidate": "Migration backfill interpolates host strings directly into SQL without escaping and doesn't filter blank lines - needs connection.quote and blank check" - }, - { - "candidate": "Fabricator(:private_category) after_build uses update! which persists even for Fabricate.build, and transients[:group].id raises if group not passed - should set attribute directly and guard group usage" - }, - { - "candidate": "Extra blank lines in spec files violate RuboCop layout rules and may fail CI" - }, - { - "candidate": "Typo in spec context description: 'emeddable' should be 'embeddable'" - }, - { - "candidate": "Test requests fruit id 2 but mock always returns fruit id 1 - test passes accidentally due to matching color_ids" - } - ], - "false_negatives": [ - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 11, - "total_golden": 4, - "tp": 2, - "fp": 9, - "fn": 2, - "errors_count": 0, - "precision": 0.18181818181818182, - "recall": 0.5, - "tool": "coderabbit", - "repo_name": "discourse__discourse-graphite__coderabbit__PR10__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__coderabbit__PR10__20260122/pull/1" - }, - "greptile": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical", - "matched_candidate": "`self.host` can be nil in EmbeddableHost model, causing NoMethodError", - "confidence": 0.95, - "reasoning": "The candidate issue identifies that `self.host` can be nil in the EmbeddableHost model, causing a NoMethodError. The golden comment mentions 'NoMethodError before_validation in EmbeddableHost'. Both are referring to the same underlying issue - a NoMethodError occurring in the EmbeddableHost model related to the before_validation callback where host can be nil. The candidate correctly identifies the root cause (nil host) and the consequence (NoMethodError) in the same model context." - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium", - "matched_candidate": "Missing nil check in update action - crashes if host not found", - "confidence": 0.9, - "reasoning": "The candidate issue identifies the same underlying problem as the golden comment - that the update action lacks a nil check and will crash if the host is not found. The golden comment mentions both update and destroy methods, while the candidate only mentions update, but they are pointing to the same core issue: missing nil validation when retrieving EmbeddableHost by ID, which will cause a crash (NoMethodError) when trying to call methods on nil." - } - ], - "false_positives": [ - { - "candidate": "SQL injection vulnerability - `h` from `embeddable_hosts` site setting is directly interpolated into SQL without escaping" - }, - { - "candidate": "Migration crashes if no matching category found - query returns empty result set" - } - ], - "false_negatives": [ - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 5, - "total_golden": 4, - "tp": 2, - "fp": 2, - "fn": 2, - "errors_count": 0, - "precision": 0.4, - "recall": 0.5, - "tool": "greptile", - "repo_name": "discourse__discourse-graphite__greptile__PR10__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__greptile__PR10__20260122/pull/1" - }, - "kg": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "Incorrect fabricator definition in category_fabricator.rb - this file should define category fabricators, not embeddable_host. The content has been swapped with embeddable_host_fabricator.rb, breaking existing tests that rely on Fabricate(:category)." - }, - { - "candidate": "Incorrect fabricator definition in embeddable_host_fabricator.rb - this file should define embeddable_host fabricator, not category. The content has been swapped with category_fabricator.rb." - } - ], - "false_negatives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical" - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium" - }, - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 2, - "total_golden": 4, - "tp": 0, - "fp": 2, - "fn": 4, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "kg", - "repo_name": "discourse__discourse-graphite__kg__PR10__20260202", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__kg__PR10__20260202/pull/1" - }, - "qodo-v2": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical", - "matched_candidate": "EmbeddableHost before_validation callback runs sub! on self.host without guarding nil, raising exception before validation errors are added", - "confidence": 0.98, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment - a NoMethodError occurring in the before_validation callback of EmbeddableHost because the code calls sub! on self.host without checking if host is nil first. Both comments point to the same bug: the before_validation callback will raise an exception when host is nil instead of allowing validation to handle the nil case gracefully." - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium", - "matched_candidate": "Admin::EmbeddableHostsController update/destroy methods can operate on nil when record is missing, causing NoMethodError", - "confidence": 1.0, - "reasoning": "The candidate issue directly matches the golden comment. Both identify the same problem: the update and destroy methods in Admin::EmbeddableHostsController don't validate that the EmbeddableHost record exists before operating on it, which will cause a NoMethodError when the record is nil (not found)." - } - ], - "false_positives": [ - { - "candidate": "Migration uses create_table(..., force: true) which risks data loss if migration is re-run or partially applied" - }, - { - "candidate": "Migration interpolates unescaped host values into SQL INSERTs without quoting/escaping, enabling SQL injection via stored site_setting values and breaking on valid data containing quotes" - }, - { - "candidate": "Admin::EmbeddableHostsController save_host assumes params[:embeddable_host] exists without validation, causing crash on malformed params" - }, - { - "candidate": "After host.save, admin component force-sets host.category from local categoryId which can be null/blank, clearing the hydrated category in UI even though server defaulted it to uncategorized" - }, - { - "candidate": "Client store hydration treats any *_ids field as embedded objects, blindly calling .map and deleting original ids, which can throw or silently drop ids if embedded collection isn't present or value isn't an array" - } - ], - "false_negatives": [ - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 7, - "total_golden": 4, - "tp": 2, - "fp": 5, - "fn": 2, - "errors_count": 0, - "precision": 0.2857142857142857, - "recall": 0.5, - "tool": "qodo-v2", - "repo_name": "discourse__discourse-graphite__qodo-v2__PR10__20260227", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-v2__PR10__20260227/pull/1" - }, - "devin": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "Migration crashes with NoMethodError when embed_category setting is absent or has no matching category - the code unconditionally accesses [0]['id'] on the query result which will be nil if no embed_category setting exists, causing the migration to crash before reaching the fallback on line 14" - }, - { - "candidate": "SQL injection vulnerability in migration via unescaped host value interpolation - host values from site_settings table are interpolated directly into SQL string without escaping, which can break the migration or allow SQL injection if any host value contains a single quote" - } - ], - "false_negatives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical" - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium" - }, - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 2, - "total_golden": 4, - "tp": 0, - "fp": 2, - "fn": 4, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "devin", - "repo_name": "discourse__discourse-graphite__devin__PR10__20260302", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__devin__PR10__20260302/pull/1" - }, - "mra-max": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "REST adapter pathFor() method returns '/admin/customize/embedding' but Rails routes expose embedding under '/customize/embedding' (no /admin), causing routing mismatch and 404 errors" - }, - { - "candidate": "Large breadth of changes across JS layers increases merge conflicts risk and regressions in the admin UI" - }, - { - "candidate": "New embeddable-host.js.es6 Ember component has large change surface with risk of UI regressions, event handling issues, and coupling to backend API" - }, - { - "candidate": "Admin::EmbeddableHostsController needs proper permissions and strong params enforcement for security" - }, - { - "candidate": "Admin::EmbeddingController requires strict authentication and authorization enforcement to prevent unauthorized access" - }, - { - "candidate": "EmbeddableHost.host_allowed? relies on EmbeddableHost data being seeded - if migrations have not run or data is absent, embedding could be blocked" - }, - { - "candidate": "Data integrity risk if EmbeddableHost host normalization behavior diverges from expectations or if category association is misconfigured" - }, - { - "candidate": "expandable_first_post? method semantic change removes embeddable_hosts presence check, potentially causing incorrect UI/behavior when no embeddable hosts are configured" - }, - { - "candidate": "TopicEmbed.import ties to EmbeddableHost model with risk of misbehavior if host resolution fails or EmbeddableHost data is incomplete" - }, - { - "candidate": "Routes map customize/embedding to embedding#show targeting top-level EmbeddingController instead of Admin::EmbeddingController namespace, causing 404s or misrouting" - }, - { - "candidate": "SQL injection risk in migration: host values interpolated directly into SQL string without parameterization when seeding embeddable_hosts table" - }, - { - "candidate": "topic_retriever invalid_host? dependency on database-backed allowlist increases risk if database is not in sync with runtime config" - }, - { - "candidate": "Missing integration tests for Admin::EmbeddableHostsController create/update/destroy actions" - }, - { - "candidate": "Missing tests for Admin::EmbeddingController show/update/saveChanges actions" - }, - { - "candidate": "Admin REST adapter type.replace('_','-') only affects first underscore, potentially failing to match ADMIN_MODELS for types with multiple underscores" - }, - { - "candidate": "Store.js hydration of _ids/_id fields uses replace that only affects first underscore, causing incorrect hydration for keys with multiple underscores" - }, - { - "candidate": "Removal of embed_category label from server.en.yml could lead to translation drift or missing keys" - }, - { - "candidate": "Removal of embeddable_hosts/embed_category from site_settings.yml introduces migration boundary and potential confusion if migration does not run in all environments" - }, - { - "candidate": "Limited end-to-end or UI interaction coverage for editing/saving host relationships in tests" - }, - { - "candidate": "Tests previously exercising SiteSetting.allows_embeddable_host? have been removed - need to confirm test environment runs migrations" - }, - { - "candidate": "Missing test coverage for edge cases when host.category_id is nil or when EmbeddableHost has multiple hosts with different URLs" - }, - { - "candidate": "Missing CHANGELOG entry for embedding feature and admin endpoints" - }, - { - "candidate": "PR description lacks scope, rationale, impact, testing plan, and rollback considerations" - }, - { - "candidate": "Empty embedding hosts table may render with header but no rows since empty array is truthy in Ember Handlebars" - }, - { - "candidate": "Test fabricators need to align with new EmbeddableHost model and associations to avoid flaky tests" - } - ], - "false_negatives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical" - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium" - }, - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 25, - "total_golden": 4, - "tp": 0, - "fp": 25, - "fn": 4, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "mra-max", - "repo_name": "discourse__discourse-graphite__mra-max__PR10__20260306", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__mra-max__PR10__20260306/pull/1" - }, - "greptile-v4": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "The contents of spec/fabricators/category_fabricator.rb and spec/fabricators/embeddable_host_fabricator.rb appear to have been accidentally swapped - category_fabricator.rb now defines Fabricator(:embeddable_host) instead of category fabricators" - }, - { - "candidate": "embeddable_host_fabricator.rb contains all the original category fabricators (:category, :diff_category, :happy_category, :private_category) instead of the embeddable host fabricator" - }, - { - "candidate": "File naming is incorrect - Fabricator(:embeddable_host) definition should live in embeddable_host_fabricator.rb, and category fabricators should remain in category_fabricator.rb" - } - ], - "false_negatives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical" - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium" - }, - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 3, - "total_golden": 4, - "tp": 0, - "fp": 3, - "fn": 4, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "greptile-v4", - "repo_name": "discourse__discourse-graphite__greptile-v4__PR10__20260306", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__greptile-v4__PR10__20260306/pull/1" - }, - "mra-nano": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "DB migration creates embeddable_hosts table and deletes site_settings keys - a one-way structural change that could cause new embedding endpoints to lack data and fail if migration fails or is skipped" - }, - { - "candidate": "High-churn embedding feature spans frontend adapter, backend controller, and routes across wide surface area, increasing risk of merge conflicts, permission gaps, and regressions" - }, - { - "candidate": "Admin embedding management update action is a no-op - the PUT /admin/customize/embedding endpoint does not apply any changes despite UI triggering update when saving" - }, - { - "candidate": "Embed controller ensure_embeddable uses rescue after end of method with unusual Ruby syntax that may be interpreted differently by Ruby versions or lead to syntax/runtime errors" - }, - { - "candidate": "EmbeddableHost model tightly couples embedding import and host validation - if no matching EmbeddableHost record exists for a URL, imported posts may receive nil category or fail silently" - }, - { - "candidate": "Topic expandable_first_post? gating removed embeddable_hosts check, potentially enabling embeds more broadly even when no hosts are configured" - }, - { - "candidate": "Embedding UI component and template changes introduced in parallel with REST adapter and serializer changes increases risk of binding mismatches and UI regressions" - }, - { - "candidate": "Rest adapter admin model lookup uses brittle type normalization (underscore to dash) that could fail if Ember Data uses different type naming, breaking admin embedding operations" - }, - { - "candidate": "Hydration of embedded associations changed to handle *_ids with pluralized key, changing in-memory representation that may break callers expecting original key shape" - }, - { - "candidate": "Site settings config removal of embeddable_hosts and embed_category risks new deployments having neither site_settings defaults nor database records if migration is skipped or fails" - }, - { - "candidate": "X-Frame-Options set to ALLOWALL on embedding endpoint disables clickjacking protection and could allow embedding the admin/embedding UI in third-party sites via iframe" - }, - { - "candidate": "Topic retrieval flow now relies on EmbeddableHost.host_allowed? instead of SiteSetting-based checks - need to verify all call sites and tests cover new validation path" - }, - { - "candidate": "Admin embedding and embeddable-hosts controller REST actions lack test coverage beyond subclass assertions, leaving authorization failures, param edge cases, and serialization behavior unverified" - }, - { - "candidate": "Federated embedding tests adapted to use EmbeddableHost but may have gaps in coverage for invalid referer, missing referer, and multiple hosts scenarios" - }, - { - "candidate": "Fabricator duplication in tests - category fabricator logic duplicated across spec/fabricators/category_fabricator.rb and spec/fabricators/embeddable_host_fabricator.rb causing potential conflicts" - }, - { - "candidate": "EmbeddableHost model validations and belongs_to :category association edge cases not fully covered in tests, including invalid host formats and nil category behavior" - }, - { - "candidate": "TopicEmbed.import test confirms embeddable_host's category is used for new post via eh.try(:category_id) pattern" - } - ], - "false_negatives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical" - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium" - }, - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 17, - "total_golden": 4, - "tp": 0, - "fp": 17, - "fn": 4, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "mra-nano", - "repo_name": "discourse__discourse-graphite__mra-nano__PR10__20260306", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__mra-nano__PR10__20260306/pull/1" - }, - "mra-ultra": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical", - "matched_candidate": "EmbeddableHost.before_validation calls self.host.sub! without nil guard, raising NoMethodError when host is nil", - "confidence": 0.98, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment - a NoMethodError in EmbeddableHost's before_validation method. The candidate provides more detail by explaining that it's caused by calling self.host.sub! without a nil guard when host is nil, but it's pointing to the same underlying bug." - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium", - "matched_candidate": "Admin::EmbeddableHostsController#update does not guard against nil when fetching host by id, causing NoMethodError when host.save is called on nil", - "confidence": 0.9, - "reasoning": "The candidate issue identifies the same underlying problem as the golden comment - that the update method in Admin::EmbeddableHostsController doesn't validate the existence of the EmbeddableHost record, and calling methods on a nil object will cause a NoMethodError. The candidate specifically mentions the update method and the nil guard issue, which is part of the golden comment's concern. While the golden comment also mentions the destroy method, the candidate correctly identifies the core issue for the update method." - } - ], - "false_positives": [ - { - "candidate": "Admin embedding REST adapter's pathFor() always returns /admin/customize/embedding, causing REST calls intended for /admin/embeddable_hosts to be silently routed to the wrong endpoint" - }, - { - "candidate": "EmbeddableHost component uses decorator syntax (@on, @observes, @computed) which requires specific Babel/Ember build configuration for stage-2 decorators support, risking build failures or runtime errors" - }, - { - "candidate": "DB-backed embeddable host allowlist blocks all embedding for non-admins when EmbeddableHost table is empty, with no fallback to old site_setting-based list" - }, - { - "candidate": "Migration creates embeddable_hosts table and migrates data from site settings with risk of data loss, mis-mapped categories, and rollback complexity" - }, - { - "candidate": "Host allowlist check in topic_retriever.rb depends on EmbeddableHost.host_allowed? with no fallback if embeddable_hosts table is empty" - }, - { - "candidate": "Admin UI saveChanges calls embedding.update({}) but Admin::EmbeddingController#update only re-serializes without persisting changes, making the save action a no-op" - }, - { - "candidate": "EmbeddingSerializer uses embed: :ids and EmbeddableHostSerializer only exposes category_id, causing N+1 requests when UI fetches category data separately" - }, - { - "candidate": "TopicEmbed.import uses eh.try(:category_id) which may be nil when no EmbeddableHost matches the URL, potentially causing post creation failure or unexpected default category" - }, - { - "candidate": "Routes expose both customize/embedding and resources :embeddable_hosts creating overlapping contract surfaces that diverge from Ember REST adapter's path resolution" - }, - { - "candidate": "Removal of old SiteSetting.allows_embeddable_host? tests creates coverage gaps for edge cases like nil/malformed hosts" - } - ], - "false_negatives": [ - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 12, - "total_golden": 4, - "tp": 2, - "fp": 10, - "fn": 2, - "errors_count": 0, - "precision": 0.16666666666666666, - "recall": 0.5, - "tool": "mra-ultra", - "repo_name": "discourse__discourse-graphite__mra-ultra__PR10__20260306", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__mra-ultra__PR10__20260306/pull/1" - }, - "mra-b": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical", - "matched_candidate": "before_validation calls String#sub! without guarding for nil - if self.host is nil, calling sub! will raise NoMethodError causing 500 response", - "confidence": 0.98, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment - that before_validation will raise a NoMethodError when self.host is nil because sub! is called without a nil guard. Both comments point to the same bug in the same location (before_validation in EmbeddableHost)." - } - ], - "false_positives": [ - { - "candidate": "SQL injection via unsafe string interpolation in migration INSERT statements - host strings are interpolated directly into raw SQL without escaping or parameterization" - }, - { - "candidate": "_hydrateEmbedded changed to support *_ids arrays - central change to client store hydration that changes payload contract and could silently break data wiring across the app" - }, - { - "candidate": "Payload root key name mismatch risk between Ember type and Rails controller params - if adapter's computed typeField doesn't match Rails controller's expected root key (embeddable_host), controller will receive nil and raise NoMethodError" - }, - { - "candidate": "Admin model detection type normalization may misclassify types - code only replaces first underscore with dash, which may be incorrect for types with multiple underscores or other naming variations" - }, - { - "candidate": "Whitelist check moved from SiteSetting to EmbeddableHost API - removes SiteSetting.allows_embeddable_host? method which may break plugins or external code relying on it" - } - ], - "false_negatives": [ - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium" - }, - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 6, - "total_golden": 4, - "tp": 1, - "fp": 5, - "fn": 3, - "errors_count": 0, - "precision": 0.16666666666666666, - "recall": 0.25, - "tool": "mra-b", - "repo_name": "discourse__discourse-graphite__mra-b__PR10__20260307", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__mra-b__PR10__20260307/pull/1" - }, - "mra-a": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical", - "matched_candidate": "before_validation in EmbeddableHost model uses sub! on host without nil check, which will raise NoMethodError if host is nil instead of returning a clean validation error", - "confidence": 0.98, - "reasoning": "The candidate issue identifies exactly the same problem as the golden comment - a NoMethodError in the before_validation callback of EmbeddableHost due to calling sub! on host when host could be nil. Both point to the same bug: missing nil check before calling sub! on the host attribute." - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium", - "matched_candidate": "Admin controller update and destroy actions fetch host record but do not check for nil before calling methods, causing NoMethodError and 500 errors when invalid id is supplied", - "confidence": 0.98, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment: the Admin controller's update and destroy actions fetch a host record but don't check for nil before calling methods on it, which would cause a NoMethodError when an invalid/non-existent ID is supplied. Both comments point to the same bug in Admin::EmbeddableHostsController where EmbeddableHost.where(id: params[:id]).first could return nil and subsequent method calls would fail." - } - ], - "false_positives": [ - { - "candidate": "Change to embedded _id(s) hydration in store.js modifies core store hydration behavior, changing how embedded arrays (_ids) are turned into object references, always setting an array (possibly empty) at the pluralized property and deleting the raw _ids key, which has broad impact on any API using __rest_serializer format" - }, - { - "candidate": "Admin embedding saveChanges calls update but server-side Admin::EmbeddingController#update is a no-op that ignores incoming parameters and does not persist any changes, making Save Changes appear to succeed but changes are not kept across reloads" - }, - { - "candidate": "SQL injection risk in migration when inserting hosts - host string is inserted directly into SQL statement using string interpolation without escaping or parameterization" - }, - { - "candidate": "ADMIN_MODELS basePath logic change applies underscore-to-dash normalization generically, which could accidentally classify other models as admin models if naming overlaps, potentially routing AJAX calls to wrong controllers" - }, - { - "candidate": "Removing fallback to SiteSetting.embed_category can lead to nil category when creating embedded topics if no matching EmbeddableHost exists, potentially causing post/topic creation to fail" - }, - { - "candidate": "EmbeddableHostSerializer returns only category_id but Ember UI expects host.category to be an associated Category model object, causing host.category to be nil and breaking UI rendering" - } - ], - "false_negatives": [ - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 8, - "total_golden": 4, - "tp": 2, - "fp": 6, - "fn": 2, - "errors_count": 0, - "precision": 0.25, - "recall": 0.5, - "tool": "mra-a", - "repo_name": "discourse__discourse-graphite__mra-a__PR10__20260307", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__mra-a__PR10__20260307/pull/1" - }, - "entelligence": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical" - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium" - }, - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 4, - "tp": 0, - "fp": 0, - "fn": 4, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "entelligence", - "repo_name": "discourse__discourse-graphite__entelligence__PR10__20260306", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__entelligence__PR10__20260306/pull/1" - }, - "sourcery": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical", - "matched_candidate": "before_validation callback in EmbeddableHost calls sub! on self.host without handling nil, which will raise if a record is instantiated without a host", - "confidence": 0.98, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment - a NoMethodError in the before_validation callback of EmbeddableHost. The candidate provides more detail by explaining that sub! is called on self.host without handling nil, which would raise an error. This is the same underlying issue about a potential NoMethodError in the before_validation callback." - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium", - "matched_candidate": "update and destroy methods in embeddable_hosts_controller call methods on host without handling the case where no EmbeddableHost is found for the given id (host is nil), which will raise an exception", - "confidence": 0.98, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment: both point out that the update and destroy methods in the embeddable_hosts_controller do not handle the case where EmbeddableHost.where(id: params[:id]).first returns nil, and calling methods on the nil object will raise an exception (NoMethodError). The wording is slightly different but the underlying issue is identical." - }, - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium", - "matched_candidate": "record_for_host compares lower(host) in SQL against raw URI#host without downcasing, so URLs with uppercase characters may fail the lookup", - "confidence": 0.98, - "reasoning": "Both comments identify the same issue: the record_for_host method uses lower(host) in SQL but doesn't normalize/downcase the input parameter, causing case-sensitive matching problems when the referer/URL contains uppercase characters. The golden comment mentions 'mixed-case referer hosts' while the candidate mentions 'URLs with uppercase characters', but they're describing the exact same bug." - } - ], - "false_positives": [ - { - "candidate": "Migration SELECT calls index [0] without checking for rows, which will raise if those settings are missing during deployment" - }, - { - "candidate": "Migration INSERT statement interpolates host value directly into SQL without parameterization or escaping, risking issues with malformed values like apostrophes" - }, - { - "candidate": "host.save(props) in embeddable-host.js.es6 passes props as argument but Ember Data ignores positional arguments to save, so category_id may not be persisted" - }, - { - "candidate": "Missing tests for TopicEmbed.import when no matching embeddable host exists to exercise the eh.try(:category_id) fallback" - }, - { - "candidate": "Missing tests for record_for_host edge cases including invalid URL input, nil/empty string inputs, and case-insensitive matching" - }, - { - "candidate": "Missing controller specs for Admin::EmbeddingController show/update responses and embeddable hosts ordering" - }, - { - "candidate": "Plural embedded hydration in store.js.es6 calls obj[k].map without checking that obj[k] is an array, so malformed payloads will throw" - }, - { - "candidate": "Plural embedded hydration assigns hydrated array directly without filtering out null/undefined entries from failed _lookupSubType calls" - }, - { - "candidate": "ADMIN_MODELS.indexOf(type.replace('_', '-')) only replaces the first underscore, which may behave unexpectedly for admin models with multiple underscores" - } - ], - "false_negatives": [ - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 12, - "total_golden": 4, - "tp": 3, - "fp": 9, - "fn": 1, - "errors_count": 0, - "precision": 0.25, - "recall": 0.75, - "tool": "sourcery", - "repo_name": "discourse__discourse-graphite__sourcery__PR10__20260308", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__sourcery__PR10__20260308/pull/1" - }, - "cubic-dev": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical" - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium" - }, - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 4, - "tp": 0, - "fp": 0, - "fn": 4, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "cubic-dev", - "repo_name": "discourse__discourse-graphite__cubic-dev__PR10__20260306", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__cubic-dev__PR10__20260306/pull/1" - }, - "mesa": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical", - "matched_candidate": "Potential crash if `self.host` is nil - the `sub!` method will raise NoMethodError on nil, need to add nil check: `self.host.sub!(/^https?:\\/\\//, '') if self.host.present?`", - "confidence": 0.95, - "reasoning": "The candidate issue identifies a potential NoMethodError when calling `sub!` on `self.host` if it's nil. The golden comment mentions 'NoMethodError before_validation in EmbeddableHost'. Both are pointing to the same underlying issue - a NoMethodError that can occur in the EmbeddableHost model's before_validation callback when host is nil. The candidate correctly identifies the specific line and method that would cause the error." - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium", - "matched_candidate": "Missing nil check before calling `destroy` in embeddable_hosts_controller - if no host with given ID exists, `host` will be nil and raise NoMethodError", - "confidence": 0.9, - "reasoning": "The candidate issue identifies the same underlying problem as the golden comment - that the destroy method in Admin::EmbeddableHostsController doesn't validate the existence of the EmbeddableHost record, and if the record doesn't exist (nil), calling destroy on it will raise a NoMethodError. The golden comment mentions both update and destroy methods, while the candidate only mentions destroy, but they're identifying the same core issue for the destroy method." - } - ], - "false_positives": [ - { - "candidate": "Security risk with case-insensitive host matching - without a unique index on `LOWER(host)`, duplicate hosts with different casing can be created (e.g., 'Example.com' and 'example.com'), leading to ambiguous authorization checks" - }, - { - "candidate": "SQL Injection vulnerability - variable `h` (host from site settings) is directly interpolated into SQL without escaping, allowing arbitrary SQL execution if site_settings contains malicious SQL" - }, - { - "candidate": "Category lookup query will fail if no matching category is found - returns empty result set causing `[0]['id']` to raise an error, needs error handling for when no category is found" - }, - { - "candidate": "Missing nil check in update action - if embeddable host doesn't exist, `host` will be nil and calling `save_host(nil)` will cause errors" - }, - { - "candidate": "Breaking behavioral change when `eh` is nil - `eh.try(:category_id)` returns nil and topic will be created without a category, unlike previous implementation which always used `SiteSetting.embed_category`" - } - ], - "false_negatives": [ - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 7, - "total_golden": 4, - "tp": 2, - "fp": 5, - "fn": 2, - "errors_count": 0, - "precision": 0.2857142857142857, - "recall": 0.5, - "tool": "mesa", - "repo_name": "discourse__discourse-graphite__mesa__PR10__20260307", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__mesa__PR10__20260307/pull/1" - }, - "codeant": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical", - "matched_candidate": "before_validation modifies self.host in place but will throw if host is nil - needs host.present? check before sub! calls", - "confidence": 0.95, - "reasoning": "The golden comment mentions 'NoMethodError before_validation in EmbeddableHost', indicating there's an error when calling methods on a nil host. The candidate issue identifies the same problem - that before_validation calls sub! on host which will throw an error if host is nil, and suggests adding a host.present? check. Both are pointing to the same underlying issue: the before_validation callback will raise a NoMethodError when trying to call sub! on a nil host value." - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium", - "matched_candidate": "Admin::EmbeddableHostsController#update and #destroy don't guard against missing records - if id not found, host will be nil and calling methods will raise NoMethodError", - "confidence": 1.0, - "reasoning": "The candidate issue identifies exactly the same problem as the golden comment: both point out that the update and destroy methods in Admin::EmbeddableHostsController don't validate whether the EmbeddableHost record exists, and if the record is not found (nil), calling methods on it will raise a NoMethodError. The wording is slightly different but describes the identical underlying issue." - } - ], - "false_positives": [ - { - "candidate": "Migration SQL insertion is vulnerable to SQL injection - raw SQL interpolates host strings directly without escaping, which can break or allow injection if host contains single quotes or malicious input" - }, - { - "candidate": "Migration assumes site_settings rows exist and returns value arrays, which is brittle and may fail on different DB adapters" - }, - { - "candidate": "EmbeddableHost model has belongs_to :category but no validates_presence_of :category_id despite DB having NOT NULL constraint" - }, - { - "candidate": "save_host(host) assumes params[:embeddable_host] exists without strong params guard" - }, - { - "candidate": "Controllers use deprecated before_filter instead of before_action (for newer Rails versions)" - }, - { - "candidate": "EmbeddableHost host validation regex is too strict - rejects valid hostnames with TLDs longer than 5 chars, IDNs, IPv6 addresses" - }, - { - "candidate": "TopicEmbed.import: if EmbeddableHost.record_for_host returns nil, category_id will be nil but should potentially fall back to SiteSetting.uncategorized_category_id" - }, - { - "candidate": "EmbeddableHost.record_for_host returns false if URL invalid instead of nil, which is inconsistent" - }, - { - "candidate": "Admin::EmbeddingController#update doesn't actually persist any changes - just renders @embedding" - }, - { - "candidate": "Admin::EmbeddableHostsController#create/update missing strong params (permit host, category_id) to avoid mass-assignment issues" - }, - { - "candidate": "Fabricator files appear to have exchanged contents - category_fabricator.rb was replaced by embeddable_host fabricator content, which could confuse maintainers and cause loading order issues" - } - ], - "false_negatives": [ - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 13, - "total_golden": 4, - "tp": 2, - "fp": 11, - "fn": 2, - "errors_count": 0, - "precision": 0.15384615384615385, - "recall": 0.5, - "tool": "codeant", - "repo_name": "discourse__discourse-graphite__codeant__PR10__20260309", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__codeant__PR10__20260309/pull/1" - }, - "kodus": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical", - "matched_candidate": "NoMethodError in before_validation callback if EmbeddableHost is saved with nil host - the callback calls self.host.sub! without a presence check", - "confidence": 0.98, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment - a NoMethodError occurring in the before_validation callback of EmbeddableHost. The candidate provides additional detail about the cause (calling self.host.sub! without a presence check when host is nil), but it's pointing to the same underlying bug." - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium", - "matched_candidate": "Null pointer dereference in destroy action - if a request is made with an id that does not exist, calling .destroy on nil raises NoMethodError", - "confidence": 0.95, - "reasoning": "The candidate issue identifies the same underlying problem as the golden comment - that the destroy action doesn't validate the existence of the EmbeddableHost record, and calling .destroy on nil will raise a NoMethodError. The golden comment mentions both update and destroy methods have this issue, while the candidate only mentions destroy, but they're identifying the same core bug for the destroy action." - }, - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium", - "matched_candidate": "Case-sensitivity mismatch in record_for_host causes valid hosts to be rejected - the query uses lower(host) but the parameter passed is not lowercased", - "confidence": 0.98, - "reasoning": "The candidate issue identifies exactly the same problem as the golden comment: the record_for_host function uses lower(host) in the SQL query for case-insensitive comparison, but the parameter being passed is not lowercased/normalized, which could cause valid hosts with mixed-case to fail matching. Both comments point to the same case-sensitivity mismatch bug." - } - ], - "false_positives": [ - { - "candidate": "Inconsistent state on cancel - the cancel action fails to fully revert the component's state by not resetting the categoryId property after rollbackBuffer(), leaving categoryId with the new unsaved value" - }, - { - "candidate": "Null pointer dereference - the embedding property is initialized to null and calling saveChanges, addHost, or deleteHost before embedding is populated will cause a TypeError" - }, - { - "candidate": "Unused code - the saveChanges action in admin-embedding.js.es6 and the corresponding update action in embedding_controller.rb are implemented but never used by the UI" - }, - { - "candidate": "Incorrect route nesting due to an absolute path - the path '/embedding' starts with a forward slash, creating the route at /embedding instead of the intended /customize/embedding" - }, - { - "candidate": "Race condition from enabled buttons during save - the edit and delete buttons are not disabled when host.isSaving is true, allowing actions while a save operation is in-flight" - }, - { - "candidate": "Incomplete string replacement causes incorrect API path generation - type.replace('_', '-') only replaces the first underscore, causing ADMIN_MODELS lookup to fail for types with multiple underscores" - }, - { - "candidate": "Null pointer dereference in data hydration - calling .map on a property value without verifying it is an array will cause TypeError if API provides null for a key ending in _ids" - }, - { - "candidate": "Mass assignment vulnerability in the create action - the action calls save_host which directly assigns attributes from raw params hash, bypassing Strong Parameters" - }, - { - "candidate": "Unbounded database query loads all records into memory - EmbeddableHost.all fetches the entire table on every request without pagination" - }, - { - "candidate": "Unhandled TypeError when request.referer is nil - the Referer header is optional and passing nil to EmbeddableHost.host_allowed? will raise TypeError" - }, - { - "candidate": "Inconsistent return type from record_for_host causes NoMethodError - the method returns false for invalid URIs but TopicEmbed calls .try on the result which is not defined for FalseClass" - }, - { - "candidate": "Duplicate YAML key - the key 'embedding' is added under admin_js.admin scope but an identical key already exists at line 2552" - }, - { - "candidate": "Unhandled nil access causes migration failure - accessing [0] on database query result without verifying rows exist will raise NoMethodError if embed_category or uncategorized_category_id settings are missing" - }, - { - "candidate": "SQL injection vulnerability in data migration - the INSERT statement uses raw string interpolation for hostname which could contain single quotes breaking SQL syntax" - }, - { - "candidate": "Incorrect file content swap - the fabricator for embeddable_host has been placed in category_fabricator.rb, overwriting original category fabricators" - }, - { - "candidate": "Null pointer dereference in after_build callback - the :private_category fabricator will crash if invoked without a transient :group parameter" - }, - { - "candidate": "The /fruits/:id endpoint handler is hardcoded to return the first fruit, ignoring the id parameter from the URL" - }, - { - "candidate": "Brittle test relies on implicit findAll result ordering - test makes assertions on records at specific indices without guaranteed order" - }, - { - "candidate": "Raw params access without whitelisting in save_host exposes the application to mass assignment vulnerabilities" - }, - { - "candidate": "Dead update action discards all submitted changes - the method body is identical to show, re-rendering current state without reading params or persisting anything" - } - ], - "false_negatives": [ - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 24, - "total_golden": 4, - "tp": 3, - "fp": 20, - "fn": 1, - "errors_count": 0, - "precision": 0.125, - "recall": 0.75, - "tool": "kodus", - "repo_name": "discourse__discourse-graphite__kodus__PR10__20260309", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__kodus__PR10__20260309/pull/1" - }, - "claude-code": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "Migration at db/migrate/20150818190757_create_embeddable_hosts.rb uses string interpolation in raw SQL: VALUES ('#{h}', ...) which is unparameterized and a SQL injection risk" - }, - { - "candidate": "Fabricator files appear swapped: category_fabricator.rb contains the embeddable_host fabricator and embeddable_host_fabricator.rb contains category fabricators" - }, - { - "candidate": "Migration uses force: true which will drop the table if it already exists, potentially causing data loss on re-run" - }, - { - "candidate": "embedding_controller.rb update action is a no-op - it re-renders without saving anything" - }, - { - "candidate": "rest.js.es6 type.replace('_', '-') only replaces the first underscore, not all occurrences, which could be fragile for other types" - }, - { - "candidate": "Tests for the new controller are minimal - only checks inheritance with no integration tests for CRUD actions" - } - ], - "false_negatives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical" - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium" - }, - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 6, - "total_golden": 4, - "tp": 0, - "fp": 6, - "fn": 4, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "claude-code", - "repo_name": "discourse__discourse-graphite__claude-code__PR10__20260310", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__claude-code__PR10__20260310/pull/1" - }, - "vercel": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical" - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium" - }, - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 4, - "tp": 0, - "fp": 0, - "fn": 4, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "vercel", - "repo_name": "discourse__discourse-graphite__vercel__PR10__20260310", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__vercel__PR10__20260310/pull/1" - }, - "sentry": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical" - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium" - }, - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 4, - "tp": 0, - "fp": 0, - "fn": 4, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "sentry", - "repo_name": "discourse__discourse-graphite__sentry__PR10__20260310", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__sentry__PR10__20260310/pull/1" - }, - "bito": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical" - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium" - }, - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 4, - "tp": 0, - "fp": 0, - "fn": 4, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "bito", - "repo_name": "discourse__discourse-graphite__bito__PR10__20260310", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__bito__PR10__20260310/pull/1" - }, - "linearb": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "/gs review" - } - ], - "false_negatives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical" - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium" - }, - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 1, - "total_golden": 4, - "tp": 0, - "fp": 1, - "fn": 4, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "linearb", - "repo_name": "discourse__discourse-graphite__linearb__PR10__20260309", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__linearb__PR10__20260309/pull/1" - }, - "kodus-v2": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical", - "matched_candidate": "Missing null guard in destroy and update actions - EmbeddableHost.where(id: params[:id]).first can return nil, causing NoMethodError on nil.save or nil.destroy", - "confidence": 0.85, - "reasoning": "The golden comment mentions 'NoMethodError before_validation in EmbeddableHost'. The candidate issue identifies that 'EmbeddableHost.where(id: params[:id]).first can return nil, causing NoMethodError on nil.save or nil.destroy'. Both are identifying the same underlying issue - that the code can raise a NoMethodError when operating on a nil EmbeddableHost object. The candidate specifically mentions the destroy and update actions where this nil check is missing, which would cause NoMethodError when calling methods like save (which triggers before_validation) or destroy on nil. This is the same core issue." - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium", - "matched_candidate": "Missing null guard in destroy and update actions - EmbeddableHost.where(id: params[:id]).first can return nil, causing NoMethodError on nil.save or nil.destroy", - "confidence": 0.98, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment: both point out that the update and destroy methods in Admin::EmbeddableHostsController don't validate the existence of the EmbeddableHost record, and that EmbeddableHost.where(id: params[:id]).first can return nil, which would cause a NoMethodError when trying to call methods on nil. The candidate mentions 'nil.save or nil.destroy' while the golden mentions 'save_host or destroy', but they're referring to the same underlying issue." - }, - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium", - "matched_candidate": "Case-sensitivity bypass in record_for_host - SQL query applies lower() to DB column but Ruby-side host parameter is not downcased, breaking host_allowed? for URLs with uppercase characters and bypassing embed access control", - "confidence": 0.98, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment: the record_for_host method uses lower() on the database column in the SQL query, but the Ruby-side host parameter is not downcased/normalized. Both comments point out that this causes mixed-case/uppercase host characters to fail matching despite the intended case-insensitive comparison. The candidate even provides additional context about the security implications (bypassing embed access control), but it's fundamentally the same bug." - } - ], - "false_positives": [ - { - "candidate": "Unfiltered undefined entries in hydrated array - _lookupSubType returns undefined when sub-type ID not found, and .map() always returns truthy array so fallback never activates, resulting in arrays with undefined values corrupting downstream consumers" - }, - { - "candidate": "Mass assignment vulnerability in save_host - attributes extracted directly from raw params[:embeddable_host] instead of using Rails strong parameters (require/permit), bypassing protection against malicious parameter injection" - }, - { - "candidate": "Ember component's delete action lacks error handling on destroyRecord promise, so server errors would be silently swallowed" - }, - { - "candidate": "update action in embedding_controller is a no-op - it renders current state without processing request parameters or persisting changes, silently discarding all client-submitted data" - }, - { - "candidate": "NoMethodError/IndexError crashes migration when no embed_category site setting exists - execute() returns empty PG::Result, so [0] yields nil and ['id'] is called on it" - }, - { - "candidate": "SQL injection via direct string interpolation of host value into INSERT statement - host value containing single quote breaks query or allows arbitrary SQL execution" - }, - { - "candidate": "Swapped file contents between category_fabricator.rb and embeddable_host_fabricator.rb - category_fabricator.rb contains embeddable_host fabricator definition while embeddable_host_fabricator.rb contains category fabricator definitions" - }, - { - "candidate": "Duplicate fabricator definition - embeddable_host_fabricator.rb defines category fabricators instead of embeddable_host fabricator, causing Fabrication::DuplicateFabricatorError at test suite load time" - } - ], - "false_negatives": [ - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 10, - "total_golden": 4, - "tp": 3, - "fp": 8, - "fn": 1, - "errors_count": 0, - "precision": 0.3, - "recall": 0.75, - "tool": "kodus-v2", - "repo_name": "discourse__discourse-graphite__kodus-v2__PR10__20260312", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__kodus-v2__PR10__20260312/pull/1" - }, - "qodo-v2-2": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical", - "matched_candidate": "EmbeddableHost runs sub! on self.host in a before_validation callback without guarding nil, so requests missing embeddable_host[host] raise NoMethodError and return 500s instead of validation errors", - "confidence": 0.98, - "reasoning": "The candidate issue identifies exactly the same problem as the golden comment - a NoMethodError occurring in EmbeddableHost's before_validation callback. The candidate provides more detail, explaining that the issue is with calling sub! on self.host when host is nil, which causes NoMethodError instead of proper validation errors. This is the same underlying bug." - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium", - "matched_candidate": "Admin::EmbeddableHostsController#update and #destroy use .where(...).first and then call methods on the result without handling nil, so invalid IDs or race-deleted records cause 500s instead of 404s", - "confidence": 0.98, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment: the update and destroy methods in Admin::EmbeddableHostsController use .where(...).first without handling the nil case, which will cause errors (candidate says 500s, golden says NoMethodError) when the record doesn't exist. Both point to the same missing nil check on the EmbeddableHost lookup." - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High", - "matched_candidate": "CreateEmbeddableHosts migration inserts raw embeddable_hosts strings directly into the new table without escaping or normalization, so previously-allowed values like http://example.com/path can be migrated into records that will never match EmbeddableHost.record_for_host host lookups", - "confidence": 0.95, - "reasoning": "Both comments identify the same issue: the migration inserts raw embeddable_hosts strings directly into the new table without normalization, which means values containing http://, https://, or path segments won't match the new EmbeddableHost.record_for_host lookup that compares only bare hosts. The candidate specifically mentions 'http://example.com/path' as an example of values that won't match, which aligns with the golden comment's concern about 'http:// or /https:// or path segments'. Both are concerned about migrated data not working with the new host lookup system." - } - ], - "false_positives": [ - { - "candidate": "TopicEmbed.import sets category from EmbeddableHost.record_for_host(url) and passes nil when no matching host record exists, and because it uses skip_validations: true, topics can be saved without a category_id" - }, - { - "candidate": "CreateEmbeddableHosts migration can crash by indexing empty SQL results with [0], aborting deploys when expected site_settings rows are missing" - } - ], - "false_negatives": [ - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - } - ], - "errors": [], - "total_candidates": 5, - "total_golden": 4, - "tp": 3, - "fp": 2, - "fn": 1, - "errors_count": 0, - "precision": 0.6, - "recall": 0.75, - "tool": "qodo-v2-2", - "repo_name": "discourse__discourse-graphite__qodo-v2-2__PR10__20260312", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-v2-2__PR10__20260312/pull/1" - }, - "qodo-v22": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical", - "matched_candidate": "EmbeddableHost runs sub! on self.host in before_validation without guarding nil, so a missing host raises NoMethodError instead of producing a validation error", - "confidence": 0.99, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment - that EmbeddableHost's before_validation callback calls sub! on self.host without checking for nil first, which would cause a NoMethodError instead of a proper validation error. Both comments point to the same bug in the same location with the same root cause." - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium", - "matched_candidate": "Admin::EmbeddableHostsController update/destroy does not handle a missing record (where(...).first can return nil) and then calls methods on nil, returning 500 instead of 404", - "confidence": 0.99, - "reasoning": "The candidate issue identifies exactly the same problem as the golden comment: the update and destroy methods in Admin::EmbeddableHostsController don't handle the case where EmbeddableHost.where(id: params[:id]).first returns nil, which would cause methods to be called on nil. The candidate even adds the detail about returning 500 instead of 404, which is the consequence of the NoMethodError mentioned in the golden comment." - } - ], - "false_positives": [ - { - "candidate": "Migration assumes embed_category row exists and indexes [0] on an empty SQL result when the legacy embed_category setting isn't set, causing the migration to raise and abort deploy/boot before the fallback to uncategorized can be reached" - }, - { - "candidate": "Host strings are interpolated directly into INSERT SQL statement without escaping, so a host containing quotes can break the migration or be interpreted as additional SQL" - }, - { - "candidate": "After saving an embeddable host, the UI sets host.category using the pre-save categoryId rather than the server-persisted category_id, causing client/server state mismatch when server defaults a blank category to uncategorized" - }, - { - "candidate": "The embeddable-host component does not catch errors from destroyRecord(), so a failed delete results in no user-visible error and an unhandled promise rejection" - } - ], - "false_negatives": [ - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 6, - "total_golden": 4, - "tp": 2, - "fp": 4, - "fn": 2, - "errors_count": 0, - "precision": 0.3333333333333333, - "recall": 0.5, - "tool": "qodo-v22", - "repo_name": "discourse__discourse-graphite__qodo-v22__PR10__20260312", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-v22__PR10__20260312/pull/1" - }, - "qodo-extended": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical", - "matched_candidate": "EmbeddableHost runs sub! on self.host in a before_validation callback without guarding for nil, so creating/updating a record with a missing host raises NoMethodError instead of returning a validation error", - "confidence": 0.98, - "reasoning": "The candidate issue identifies exactly the same problem as the golden comment - a NoMethodError occurring in the before_validation callback of EmbeddableHost. The candidate provides more detail, explaining that the issue is caused by calling sub! on self.host when host is nil, but it's pointing to the same underlying bug." - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium", - "matched_candidate": "Admin::EmbeddableHostsController update/destroy do not handle missing records and will call methods on nil (save_host(nil) or nil.destroy), causing 500 errors instead of a 404/JSON error response", - "confidence": 0.98, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment: the update and destroy methods in Admin::EmbeddableHostsController don't handle the case when EmbeddableHost.where(id: params[:id]).first returns nil, which will cause errors when calling methods (save_host or destroy) on the nil object. Both comments point to the same missing record validation bug and the same consequence (NoMethodError/500 errors)." - } - ], - "false_positives": [ - { - "candidate": "The migration copies the old embeddable_hosts setting into the new table by interpolating host strings directly into SQL, so quotes or unexpected characters in the stored setting can break the migration or inject SQL" - }, - { - "candidate": "The migration unconditionally accesses [0]['id'] on the embed_category query result. On fresh installs where this setting doesn't exist, the query returns zero rows, [0] returns nil, and nil['id'] raises NoMethodError, blocking all migrations" - }, - { - "candidate": "In _hydrateEmbedded, when a property matching the _ids pattern has a null/undefined value instead of an array, obj[k].map() throws a TypeError. The singular _id path has a null guard but the plural _ids path does not" - }, - { - "candidate": "type.replace('_', '-') in JavaScript's String.prototype.replace with a string argument only replaces the first occurrence. Any admin model type name with multiple underscores would fail to match its hyphenated entry in ADMIN_MODELS, causing incorrect non-admin path resolution" - } - ], - "false_negatives": [ - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 6, - "total_golden": 4, - "tp": 2, - "fp": 4, - "fn": 2, - "errors_count": 0, - "precision": 0.3333333333333333, - "recall": 0.5, - "tool": "qodo-extended", - "repo_name": "discourse__discourse-graphite__qodo-extended__PR10__20260312", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-extended__PR10__20260312/pull/1" - }, - "cubic-v2": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical", - "matched_candidate": "`self.host.sub!` will raise `NoMethodError` if `host` is `nil`. The `before_validation` callback runs before the format validation, so a nil host bypasses the validates_format_of check and crashes instead of producing a validation error. Guard with a nil check or use the safe navigation operator.", - "confidence": 0.98, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment - a NoMethodError that can occur in the before_validation callback of EmbeddableHost when host is nil. The candidate provides more detail about why this happens (self.host.sub! will raise NoMethodError if host is nil) and suggests solutions, but it's pointing to the same underlying bug." - }, - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium", - "matched_candidate": "The query lowercases the DB column with `lower(host)` but does not downcase the input parameter. Call `.downcase` on `host` so the comparison is consistent.", - "confidence": 0.99, - "reasoning": "Both comments identify the exact same issue: the SQL query uses lower(host) to lowercase the database column, but the input parameter is not lowercased/downcased, causing case-insensitive comparison to fail for mixed-case inputs. The candidate even suggests the same fix (.downcase on the host parameter)." - } - ], - "false_positives": [ - { - "candidate": "Fetch the singleton embedding record by id instead of calling `store.find` without an id, which resolves to a collection." - }, - { - "candidate": "Guard the `embed_category` lookup before indexing it. As written, missing data raises before the uncategorized fallback can run." - } - ], - "false_negatives": [ - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 4, - "total_golden": 4, - "tp": 2, - "fp": 2, - "fn": 2, - "errors_count": 0, - "precision": 0.5, - "recall": 0.5, - "tool": "cubic-v2", - "repo_name": "discourse__discourse-graphite__cubic-v2__PR10__20260313", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__cubic-v2__PR10__20260313/pull/1" - }, - "qodo-extended-summary": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical", - "matched_candidate": "The before_validation callback in EmbeddableHost calls sub! directly on self.host, but if an API request omits the host parameter, self.host is nil, and nil.sub! raises a NoMethodError resulting in a 500 Server Error instead of a 422 Unprocessable Entity", - "confidence": 1.0, - "reasoning": "The candidate issue exactly matches the golden comment. Both identify the same bug: a NoMethodError occurs in the before_validation callback of EmbeddableHost when calling sub! on self.host when host is nil. The candidate provides additional context about the consequences (500 vs 422 error) but identifies the exact same underlying issue." - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium", - "matched_candidate": "In Admin::EmbeddableHostsController, both update and destroy call .first which returns nil for non-existent IDs, then immediately invoke methods on the result without a nil guard, causing NoMethodError (500 error) instead of a proper 404 response", - "confidence": 0.98, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment: both point out that the update and destroy methods in Admin::EmbeddableHostsController use .first which can return nil for non-existent IDs, and then calling methods on nil will cause a NoMethodError. The candidate even adds the detail about returning a 500 error instead of a proper 404 response, which is a consequence of the same underlying issue." - } - ], - "false_positives": [ - { - "candidate": "The :embeddable_host fabricator is defined in spec/fabricators/category_fabricator.rb but depends on the :category fabricator which was moved to a different file; because spec_helper requires fabricator files in filename order, the suite can fail to boot with an undefined fabricator error" - }, - { - "candidate": "The database migration assumes that embed_category exists in the site_settings table, but instances that never modified it will return an empty result, causing execute(...)[0] to be nil, and nil['id'] will raise a NoMethodError, crashing the migration" - }, - { - "candidate": "The migration interpolates legacy host strings directly into an INSERT SQL statement without quoting/escaping; a host containing a single quote can break the migration and the pattern is vulnerable to SQL injection" - }, - { - "candidate": "In _hydrateEmbedded, the plural _ids branch unconditionally deletes the original ID array and replaces it with hydrated results even when _lookupSubType returns undefined for some or all IDs, unlike the singular _id branch which guards deletion on successful hydration" - }, - { - "candidate": "type.replace('_', '-') in basePath uses JavaScript's String.replace with a string argument, which only replaces the first underscore occurrence; any admin model type with multiple underscores would fail to match its hyphenated entry in ADMIN_MODELS" - } - ], - "false_negatives": [ - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 7, - "total_golden": 4, - "tp": 2, - "fp": 5, - "fn": 2, - "errors_count": 0, - "precision": 0.2857142857142857, - "recall": 0.5, - "tool": "qodo-extended-summary", - "repo_name": "discourse__discourse-graphite__qodo-extended-summary__PR10__20260313", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-extended-summary__PR10__20260313/pull/1" - }, - "macroscope": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical", - "matched_candidate": "In `update`, `EmbeddableHost.where(id: params[:id]).first` returns `nil` when the ID doesn't exist, and this `nil` is passed to `save_host`. Line 23 then calls `host.host = ...` on the `nil` value, causing a `NoMethodError`. Consider returning a 404 response when the host is not found.", - "confidence": 0.85, - "reasoning": "The golden comment mentions 'NoMethodError before_validation in EmbeddableHost'. The candidate issue identifies a NoMethodError that occurs when `host` is nil and code tries to call methods on it (like `host.host = ...`). Both are identifying the same underlying problem - a NoMethodError occurring in EmbeddableHost due to nil handling. The candidate provides more specific details about where and why the nil occurs (in the update method when ID doesn't exist), but it's pointing to the same fundamental issue of a NoMethodError in EmbeddableHost." - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium", - "matched_candidate": "In `update`, `EmbeddableHost.where(id: params[:id]).first` returns `nil` when the ID doesn't exist, and this `nil` is passed to `save_host`. Line 23 then calls `host.host = ...` on the `nil` value, causing a `NoMethodError`. Consider returning a 404 response when the host is not found.", - "confidence": 0.9, - "reasoning": "The candidate issue identifies the same underlying problem as the golden comment - that EmbeddableHost.where(id: params[:id]).first can return nil, and calling methods on this nil object will cause a NoMethodError. The candidate specifically mentions the update method and save_host, which matches the golden comment. While the candidate doesn't explicitly mention the destroy method, it correctly identifies the core issue of missing nil validation for the retrieved record." - }, - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium", - "matched_candidate": "In `record_for_host`, the SQL query compares `lower(host)` (the DB column) against `host` (the parameter) without downcasing the parameter. When `uri.host` contains uppercase letters, the lookup fails.", - "confidence": 0.99, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment: the record_for_host method compares lower(host) in SQL against the host parameter, but the parameter itself is not lowercased/normalized. Both comments point out that this causes mixed-case/uppercase host values to fail matching despite the query intending to be case-insensitive." - } - ], - "false_positives": [ - { - "candidate": "NoMethodError when `uncategorized_category_id` setting doesn't exist: If the site_setting is missing, `execute(...)[0]` returns `nil`, and `['value']` on `nil` raises an error." - }, - { - "candidate": "`transients[:group].id` will raise `NoMethodError: undefined method 'id' for nil:NilClass` when `:private_category` is fabricated without explicitly passing a `:group` transient. The transient is declared on line 18 with no default value, so `transients[:group]` will be `nil` if not provided." - }, - { - "candidate": "The `update` action ignores all parameters and returns the same static response as `show`, so callers receive a 200 success even though no data is modified." - }, - { - "candidate": "The migration interpolates `h` directly into the SQL string at line 25 without escaping, so a host value containing a single quote causes a SQL syntax error and fails the migration." - } - ], - "false_negatives": [ - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 7, - "total_golden": 4, - "tp": 3, - "fp": 4, - "fn": 1, - "errors_count": 0, - "precision": 0.42857142857142855, - "recall": 0.75, - "tool": "macroscope", - "repo_name": "discourse__discourse-graphite__macroscope__PR10__20260309", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__macroscope__PR10__20260309/pull/1" - } - }, - "https://github.com/ai-code-review-evaluation/discourse-graphite/pull/7": { - "gemini": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low", - "matched_candidate": "Light theme lightness value changed from 30% to 70% results in poor contrast and readability on light background", - "confidence": 0.95, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment - the lightness value change from 30% to 70% for the light theme. Both comments point out this dramatic change in the .topic-meta-data h5 a selector, with the candidate additionally noting the consequence (poor contrast/readability) while the golden comment describes it as a 'dramatic inversion'. They are identifying the same underlying issue." - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low", - "matched_candidate": "Light theme lightness for .group-member-info .name changed from 30% to 50%, which may be an unintentional visual change", - "confidence": 0.9, - "reasoning": "The candidate issue identifies the same problem as the golden comment - the change from 30% to 50% lightness for the light theme in the .name selector. The golden comment mentions this applies to desktop/user.css and also notes a similar change in the mobile variant. The candidate specifically mentions '.group-member-info .name' which is the same selector being discussed. Both are flagging this as a potentially unintentional or inconsistent visual change." - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low", - "matched_candidate": "Inconsistency between desktop and mobile for .custom-message-length color - desktop uses lightness 70% for light theme while mobile uses 30%, causing low contrast on desktop", - "confidence": 0.85, - "reasoning": "The candidate issue mentions a discrepancy between desktop (70%) and mobile (30%) lightness values for .custom-message-length color in light theme. The golden comment states that the replacement uses 30% instead of the original 70% for light theme, making text darker than intended. Both are identifying the same underlying problem - the lightness value being 30% when it should be 70% for the light theme. The candidate frames it as a desktop/mobile inconsistency while the golden frames it as a change from original, but they're pointing to the same bug." - } - ], - "false_positives": [ - { - "candidate": "Dark theme color with lightness 30% results in poor contrast on dark background - should use 70% for dark theme" - }, - { - "candidate": "Mobile h3 color lightness changed from 20% to 50% for light theme, inconsistent with desktop version which keeps 20% and provides better contrast" - } - ], - "false_negatives": [], - "errors": [], - "total_candidates": 5, - "total_golden": 3, - "tp": 3, - "fp": 2, - "fn": 0, - "errors_count": 0, - "precision": 0.6, - "recall": 1.0, - "tool": "gemini", - "repo_name": "discourse__discourse-graphite__gemini__PR7__20260128", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__gemini__PR7__20260128/pull/1" - }, - "claude": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low", - "matched_candidate": "Incorrect lightness value inversion in desktop/topic-post.scss line 291 - changed from 30% to 70%/30% when it should be 30%/70% to maintain original lightness for light theme", - "confidence": 0.95, - "reasoning": "The candidate issue identifies the same problem as the golden comment - the lightness value change from 30% to 70% in the light theme for .topic-meta-data h5 a. The candidate correctly notes this is in desktop/topic-post.scss line 291 and that the values were inverted. The candidate suggests it should be 30%/70% instead of 70%/30%, which aligns with the golden comment's observation that the original 30% lightness was changed to 70% for the light theme, representing a dramatic inversion." - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low", - "matched_candidate": "Incorrect lightness values for .group-member-info .name in desktop/user.scss line 522 - changed from 30% to 50%/50% when it should be 30%/70% following the X%/(100-X%) pattern", - "confidence": 0.9, - "reasoning": "Both comments identify the same issue: the lightness value change from 30% to 50% for .name in the desktop user stylesheet. The golden comment mentions this is in desktop/user.css and notes it should be reviewed because it deviates from the pattern of preserving the original $primary value. The candidate issue identifies the same problem in desktop/user.scss line 522, noting the change from 30% to 50% is incorrect. They're pointing to the same underlying concern about this specific value change." - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low", - "matched_candidate": "Incorrect lightness value inversion in desktop/topic-post.scss line 291 - changed from 30% to 70%/30% when it should be 30%/70% to maintain original lightness for light theme", - "confidence": 0.9, - "reasoning": "The candidate issue identifies the same problem - incorrect lightness values in the topic-post.scss file. The candidate mentions the values are inverted (30%/70% vs 70%/30%), which is the same issue as the golden comment pointing out that the light theme uses 30% instead of the original 70%, making text darker than intended. Both are identifying the same lightness value swap problem in the same file." - } - ], - "false_positives": [ - { - "candidate": "Missed transformation in topic-list.scss line 115 - scale-color($primary, $lightness: 50%) was not wrapped with dark-light-choose() like all other instances in the PR" - }, - { - "candidate": "Incorrect lightness values in mobile/modal.scss line 102 - changed from 70% to 30%/70% when it should be 70%/30% to match desktop version and established pattern" - }, - { - "candidate": "Incorrect lightness values in mobile/topic-post.scss line 182 - changed from 20% to 50%/50% when it should be 20%/80% to match desktop version and maintain proper contrast" - }, - { - "candidate": "Property type changed during transformation in desktop/topic-post.scss line ~892 - original code used border: 1px solid scale-color(...) but was changed to background-color: dark-light-choose(...)" - } - ], - "false_negatives": [], - "errors": [], - "total_candidates": 7, - "total_golden": 3, - "tp": 3, - "fp": 4, - "fn": 0, - "errors_count": 0, - "precision": 0.42857142857142855, - "recall": 1.0, - "tool": "claude", - "repo_name": "discourse__discourse-graphite__claude__PR7__20260127", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__claude__PR7__20260127/pull/1" - }, - "graphite": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low" - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low" - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 3, - "tp": 0, - "fp": 0, - "fn": 3, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "graphite", - "repo_name": "discourse__discourse-graphite__graphite__PR7__20260123", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__graphite__PR7__20260123/pull/1" - }, - "qodo": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "The new dark-light-choose(...) usages mix different $lightness values between light and dark themes (e.g., 40% vs 60%, 80% vs 20%) - need to validate actual contrast/readability in both themes and ensure these values align with the intended design system, especially for borders and helper text" - }, - { - "candidate": "Many elements now share the same dark-light-choose(scale-color(...)) pattern - should extract a shared variable/mixin/token for 'muted text' / 'subtle text' to avoid divergence over time and make future theme tweaks easier" - }, - { - "candidate": "Disabled button hover styling now changes text color via dark-light-choose(...) which may produce an unexpected 'enabled-looking' interaction state or reduce legibility on disabled buttons, especially in dark theme where hover color may increase contrast" - } - ], - "false_negatives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low" - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low" - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 3, - "total_golden": 3, - "tp": 0, - "fp": 3, - "fn": 3, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "qodo", - "repo_name": "discourse__discourse-graphite__qodo__PR7__20260123", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo__PR7__20260123/pull/1" - }, - "bugbot": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low", - "matched_candidate": "The `.topic-meta-data h5 a` color was originally `scale-color($primary, $lightness: 30%)` but the light theme value in `dark-light-choose` is now `70%`, unintentionally changing the light theme appearance from a darker to a much lighter color", - "confidence": 1.0, - "reasoning": "The candidate issue identifies exactly the same problem as the golden comment: in `.topic-meta-data h5 a`, the lightness value changed from 30% to 70% for the light theme in the `dark-light-choose` function, which dramatically changes the appearance from darker to lighter. Both comments point to the same code location, the same values, and the same concern about the unintentional change." - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low", - "matched_candidate": "The `.group-member-info .name` color was originally `scale-color($primary, $lightness: 30%)` but the light theme value in `dark-light-choose` is now `50%`, unintentionally changing the light theme appearance in both desktop and mobile stylesheets", - "confidence": 0.95, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment: the change from 30% to 50% lightness for $primary in the light theme for .group-member-info .name, and notes it affects both desktop and mobile stylesheets. This matches the golden comment's concern about the $primary value changing from 30% to 50% for light theme in desktop/user.css and the similar change in mobile variant." - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low", - "matched_candidate": "The `.custom-message-length` color was originally `scale-color($primary, $lightness: 70%)` but the light theme value in `dark-light-choose` is now `30%`, unintentionally changing the light theme appearance from a lighter to a much darker color", - "confidence": 0.99, - "reasoning": "The candidate issue identifies exactly the same problem as the golden comment: the lightness value changed from 70% to 30% for the light theme in the `.custom-message-length` color, making the text significantly darker than intended. Both comments point to the same bug in topic-post.css where the dark-light-choose function uses 30% instead of the original 70% for the light theme." - } - ], - "false_positives": [ - { - "candidate": "The `.topic-map h3` color was originally `scale-color($primary, $lightness: 20%)` but the light theme value in `dark-light-choose` is now `50%`, unintentionally changing the light theme appearance from a darker heading to a significantly lighter one" - } - ], - "false_negatives": [], - "errors": [], - "total_candidates": 4, - "total_golden": 3, - "tp": 3, - "fp": 1, - "fn": 0, - "errors_count": 0, - "precision": 0.75, - "recall": 1.0, - "tool": "bugbot", - "repo_name": "discourse__discourse-graphite__bugbot__PR7__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__bugbot__PR7__20260122/pull/1" - }, - "copilot": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low", - "matched_candidate": "Original code used scale-color($primary, $lightness: 30%) but change converts it to use 70% lightness for the light theme, which is inconsistent with the dark theme value of 30% - should use dark-light-choose(scale-color($primary, $lightness: 30%), scale-color($secondary, $lightness: 70%))", - "confidence": 0.95, - "reasoning": "The candidate identifies the same issue - that the lightness value changed from 30% to 70% for the light theme in the .topic-meta-data h5 a selector. Both comments point out this dramatic change in lightness values. The candidate goes further to suggest the values should be swapped (30% for light, 70% for dark), which is a reasonable interpretation of the inconsistency, but the core issue identified is the same: the lightness changed from 30% to 70% for the light theme." - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low", - "matched_candidate": "Original code used scale-color($primary, $lightness: 30%) but change converts it to use 50% lightness for the light theme, which changes visual appearance and may not be intentional - should use dark-light-choose(scale-color($primary, $lightness: 30%), scale-color($secondary, $lightness: 70%))", - "confidence": 0.95, - "reasoning": "Both comments identify the same issue: the change from 30% to 50% lightness for the light theme in the desktop/user.css file. The golden comment notes this specific change and suggests reviewing it, while the candidate issue also identifies this exact change (30% to 50% for light theme) and suggests it may not be intentional. Both are pointing to the same underlying concern about the lightness value change." - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low", - "matched_candidate": "Original code used scale-color($primary, $lightness: 70%) but change converts it to use 30% lightness for the light theme, which changes visual appearance and may not be intentional - should use dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%))", - "confidence": 0.98, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment - that the original code used 70% lightness but the replacement incorrectly uses 30% lightness for the light theme. The candidate even provides the same specific values (70% vs 30%) and notes this changes the visual appearance. The candidate also suggests the correct fix using dark-light-choose with 70% for light theme." - } - ], - "false_positives": [ - { - "candidate": "Original code used scale-color($primary, $lightness: 20%) but change converts it to use 50% lightness for the light theme, which changes visual appearance and may not be intentional - should use dark-light-choose(scale-color($primary, $lightness: 20%), scale-color($secondary, $lightness: 80%))" - } - ], - "false_negatives": [], - "errors": [], - "total_candidates": 4, - "total_golden": 3, - "tp": 3, - "fp": 1, - "fn": 0, - "errors_count": 0, - "precision": 0.75, - "recall": 1.0, - "tool": "copilot", - "repo_name": "discourse__discourse-graphite__copilot__PR7__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__copilot__PR7__20260122/pull/1" - }, - "augment": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low", - "matched_candidate": "dark-light-choose returns its first argument for light themes, so the change alters light-theme color from scale-color($primary, $lightness: 30%) to scale-color($primary, $lightness: 70%), which is a big possibly unintended contrast shift", - "confidence": 0.98, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment: the lightness value for light themes changed from 30% to 70% in the .topic-meta-data h5 a selector. Both comments point out that this is a significant/dramatic change (golden says 'dramatic inversion', candidate says 'big possibly unintended contrast shift'). The candidate correctly explains the dark-light-choose function behavior and identifies the same underlying issue." - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low", - "matched_candidate": "dark-light-choose changes .group-member-info .name in light mode from scale-color($primary, $lightness: 30%) to scale-color($primary, $lightness: 50%), which is inconsistent with the keep light theme, adjust dark theme pattern elsewhere", - "confidence": 0.95, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment: the change from 30% to 50% lightness for .group-member-info .name in light mode, and notes this is inconsistent with the pattern used elsewhere (keeping light theme values and adjusting dark theme). This matches the golden comment's concern about changing $primary from 30% to 50% for light theme when other changes preserve the original $primary value." - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low", - "matched_candidate": "dark-light-choose returns its first argument for light themes, so the change alters light-theme color from scale-color($primary, $lightness: 30%) to scale-color($primary, $lightness: 70%), which is a big possibly unintended contrast shift", - "confidence": 0.85, - "reasoning": "The candidate identifies the same issue - a contrast/lightness shift in the light theme between 30% and 70%. However, the candidate has the direction reversed (says it changes FROM 30% TO 70%), while the golden comment says it changes FROM 70% TO 30%. Both identify the same underlying problem (unintended lightness change in light theme), but the candidate has the values swapped. This is the same core issue being identified, just with confusion about which direction the change goes." - } - ], - "false_positives": [ - { - "candidate": "dark-light-choose appears to swap the light-theme value from scale-color($primary, $lightness: 70%) to scale-color($primary, $lightness: 30%) and vice-versa for dark in mobile/modal.scss" - }, - { - "candidate": "dark-light-choose changes the light-theme h3 color from scale-color($primary, $lightness: 20%) to scale-color($primary, $lightness: 50%) which may be unintentional" - } - ], - "false_negatives": [], - "errors": [], - "total_candidates": 4, - "total_golden": 3, - "tp": 3, - "fp": 2, - "fn": 0, - "errors_count": 0, - "precision": 0.75, - "recall": 1.0, - "tool": "augment", - "repo_name": "discourse__discourse-graphite__augment__PR7__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__augment__PR7__20260122/pull/1" - }, - "propel": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low", - "matched_candidate": "When wrapping the color in `dark-light-choose`, the light-theme value was changed from `scale-color($primary, $lightness: 70%)` to `scale-color($primary, $lightness: 30%)`, making the custom message length hint dramatically darker than before and inconsistent with the desktop modal which still uses 70%", - "confidence": 0.6, - "reasoning": "Both comments identify the same issue: a lightness value change in the dark-light-choose function. However, they describe the change in opposite directions. The golden comment says lightness changed FROM 30% TO 70% for light theme, while the candidate says it changed FROM 70% TO 30%. They're identifying the same code location and the same type of problem (dramatic lightness change), but they describe the direction of change oppositely. This could be the same underlying issue being described from different perspectives (before vs after), or it could be about different selectors. The golden mentions '.topic-meta-data h5 a' while the candidate mentions 'custom message length hint'. Despite the directional confusion, they're both flagging a lightness value discrepancy in dark-light-choose that creates inconsistency." - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low", - "matched_candidate": "When wrapping the color in `dark-light-choose`, the light-theme value was changed from `scale-color($primary, $lightness: 70%)` to `scale-color($primary, $lightness: 30%)`, making the custom message length hint dramatically darker than before and inconsistent with the desktop modal which still uses 70%", - "confidence": 1.0, - "reasoning": "The candidate issue identifies exactly the same problem as the golden comment: the lightness value was changed from 70% to 30% for the light theme when wrapping in dark-light-choose, making the text significantly darker than intended. The candidate even provides additional context about the inconsistency with the desktop modal." - } - ], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 1, - "total_golden": 3, - "tp": 2, - "fp": 0, - "fn": 1, - "errors_count": 0, - "precision": 2.0, - "recall": 0.6666666666666666, - "tool": "propel", - "repo_name": "discourse__discourse-graphite__propel__PR7__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__propel__PR7__20260122/pull/1" - }, - "baz": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low", - "matched_candidate": "Light-theme topic-meta link now uses scale-color($primary, $lightness: 70%) instead of 30%, causing links to become almost white and lose contrast - need to preserve 30% lightness for the light branch when wrapping in dark-light-choose", - "confidence": 0.98, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment: in .topic-meta-data h5 a, the lightness value for the light theme changed from 30% to 70% when wrapping in dark-light-choose, which is incorrect and causes contrast issues. Both comments point to the same bug - the dramatic inversion of lightness from 30% to 70% for the light theme." - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low", - "matched_candidate": "Light-theme topic-meta link now uses scale-color($primary, $lightness: 70%) instead of 30%, causing links to become almost white and lose contrast - need to preserve 30% lightness for the light branch when wrapping in dark-light-choose", - "confidence": 0.85, - "reasoning": "Both comments identify the same issue: a change in the lightness percentage for the light theme. The golden comment mentions $primary changing from 30% to 50%, while the candidate mentions scale-color($primary, $lightness: 70%) instead of 30%. Both are pointing out that the light theme's color value was incorrectly changed and needs to preserve the original 30% lightness value. They're addressing the same underlying contrast/color issue in the light theme styling." - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low", - "matched_candidate": ".custom-message-length now uses scale-color($primary, $lightness: 30%) instead of the previous 70% for the light branch, making helper text much darker than before - need to keep 70% lightness for light theme while adding dark branch", - "confidence": 0.98, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment: in the light theme, the lightness value was changed from 70% to 30% for the .custom-message-length class, making the text significantly darker than intended. Both comments point out that the light theme should keep 70% lightness." - } - ], - "false_positives": [ - { - "candidate": "Light-theme h3 now uses scale-color($primary, $lightness: 50%) instead of the previous 20%, brightening header text and regressing contrast - need to keep original 20% lightness for light branch while supplying dark branch" - } - ], - "false_negatives": [], - "errors": [], - "total_candidates": 3, - "total_golden": 3, - "tp": 3, - "fp": 1, - "fn": 0, - "errors_count": 0, - "precision": 1.0, - "recall": 1.0, - "tool": "baz", - "repo_name": "discourse__discourse-graphite__baz__PR7__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__baz__PR7__20260122/pull/1" - }, - "coderabbit": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "Inconsistent theme handling: #file-uploading not updated - Line 33 still uses scale-color($secondary, $lightness: 50%) while the sibling #draft-status was updated to use dark-light-choose, which may cause visibility issues for #file-uploading text in dark themes" - }, - { - "candidate": ".wmd-input text color at line 175 uses darken($primary, 40%) which was not updated - in some theme configurations, darkening an already dark primary color could cause readability issues and should potentially use dark-light-choose for consistent theme support" - } - ], - "false_negatives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low" - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low" - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 2, - "total_golden": 3, - "tp": 0, - "fp": 2, - "fn": 3, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "coderabbit", - "repo_name": "discourse__discourse-graphite__coderabbit__PR7__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__coderabbit__PR7__20260122/pull/1" - }, - "greptile": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low", - "matched_candidate": "In desktop/user.scss line 522, original was scale-color($primary, $lightness: 30%), should be inverted to 70% for dark themes, not changed to 50%", - "confidence": 0.85, - "reasoning": "The golden comment points out that desktop/user.css changes $primary from 30% to 50% for the light theme, questioning whether this is intentional. The candidate issue mentions desktop/user.scss line 522 where the original was 30% and is being changed to 50%, suggesting it should be 70% instead. Both are identifying the same underlying concern: the 30% to 50% change in the desktop user stylesheet, though they have different opinions on what the correct value should be. The candidate is pointing to the same problematic change." - } - ], - "false_positives": [ - { - "candidate": "In desktop/topic-post.scss line 291, the lightness values are flipped - original was scale-color($primary, $lightness: 30%), so for dark themes it should use $secondary with 70% (inverted), not 30%" - }, - { - "candidate": "In mobile/user.scss line 497, original was scale-color($primary, $lightness: 30%), should be inverted to 70% for dark themes, not changed to 50%" - }, - { - "candidate": "In mobile/user.scss line 502, original was scale-color($primary, $lightness: 30%), should be inverted to 70% for dark themes, not changed to 50%" - }, - { - "candidate": "Inconsistency between desktop and mobile versions: desktop/modal.scss uses (70%, 30%) while mobile/modal.scss uses (30%, 70%) for the same .custom-message-length class" - } - ], - "false_negatives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low" - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 6, - "total_golden": 3, - "tp": 1, - "fp": 4, - "fn": 2, - "errors_count": 0, - "precision": 0.16666666666666666, - "recall": 0.3333333333333333, - "tool": "greptile", - "repo_name": "discourse__discourse-graphite__greptile__PR7__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__greptile__PR7__20260122/pull/1" - }, - "kg": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low" - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low" - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 3, - "tp": 0, - "fp": 0, - "fn": 3, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "kg", - "repo_name": "discourse__discourse-graphite__kg__PR7__20260202", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__kg__PR7__20260202/pull/1" - }, - "qodo-v2": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low", - "matched_candidate": "In desktop/topic-post.scss the light-theme value for .topic-meta-data h5 a was accidentally changed from a darker 30% shade to a much lighter 70% shade, which is inconsistent with the base topic-post author link styling and reduces contrast/readability in light theme", - "confidence": 0.98, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment: in .topic-meta-data h5 a, the lightness value for the light theme was changed from 30% to 70%, which is described as a dramatic/accidental change that affects the styling. Both comments point to the same code location, the same specific value change (30% to 70% lightness), and recognize this as problematic." - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low", - "matched_candidate": "In desktop/user.scss, .group-member-info .name uses a 50%/50% pair which is less prominent than the base user name styling (30%/70%), and the same issue exists in mobile/user.scss", - "confidence": 0.85, - "reasoning": "The candidate issue identifies the same underlying concern as the golden comment - both point out that in desktop/user.scss (and mobile/user.scss), the .name styling uses 50%/50% values which differs from the base user name styling that uses 30%/70%. The golden comment notes that $primary changed from 30% to 50% for light theme and suggests reviewing this inconsistency. The candidate identifies this same discrepancy, noting the 50%/50% pair is 'less prominent than the base user name styling (30%/70%)'. Both also mention the same issue exists in the mobile variant." - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low", - "matched_candidate": "In desktop/topic-post.scss the light-theme value for .topic-meta-data h5 a was accidentally changed from a darker 30% shade to a much lighter 70% shade, which is inconsistent with the base topic-post author link styling and reduces contrast/readability in light theme", - "confidence": 0.6, - "reasoning": "The candidate issue identifies the same file and the same problem with the lightness values being swapped between 30% and 70% for the light theme. However, the candidate describes the change as going from 30% to 70% (making it lighter), while the golden comment describes it as going from 70% to 30% (making it darker). These are opposite directions of the same issue - they're both identifying that the lightness values are wrong/swapped, but they describe the change in opposite directions. This could be the same underlying issue (incorrect lightness value) but described from different perspectives or there may be confusion about which was original vs replacement." - } - ], - "false_positives": [ - { - "candidate": "In mobile/modal.scss, .custom-message-length uses inverted light/dark lightness values (30%/70%) compared to desktop modal behavior (70%/30%), which is likely an accidental swap during conversion to dark-light-choose" - }, - { - "candidate": "Mobile topic-map h3 uses a 50%/50% pair while desktop uses a stronger 20%/80% pair for the same element, which is inconsistent and reduces emphasis relative to desktop" - } - ], - "false_negatives": [], - "errors": [], - "total_candidates": 4, - "total_golden": 3, - "tp": 3, - "fp": 2, - "fn": 0, - "errors_count": 0, - "precision": 0.75, - "recall": 1.0, - "tool": "qodo-v2", - "repo_name": "discourse__discourse-graphite__qodo-v2__PR7__20260227", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-v2__PR7__20260227/pull/1" - }, - "devin": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low", - "matched_candidate": "Light-theme color for .embedded-posts .topic-meta-data h5 a changed from 30% to 70% lightness - values appear swapped, making embedded post author names hard to read on light themes", - "confidence": 0.98, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment: the lightness value for .topic-meta-data h5 a changed from 30% to 70% for the light theme, which is a dramatic/significant change that appears to be incorrect (swapped values). Both comments point to the same CSS selector, the same lightness value change (30% to 70%), and both identify this as problematic." - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low", - "matched_candidate": "Light-theme color for .group-member-info .name in desktop/user.scss changed from 30% to 50% lightness - making group member names lighter and less readable on light themes", - "confidence": 0.9, - "reasoning": "The candidate issue identifies the same core problem as the golden comment - the change from 30% to 50% lightness for the light theme in the desktop user.scss file. The golden comment mentions this applies to $primary changing from 30% to 50% for light theme and mentions a similar .name change in mobile variant. The candidate specifically calls out the .group-member-info .name selector with the same 30% to 50% lightness change and notes the readability concern. This is the same underlying issue." - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low", - "matched_candidate": "Light-theme color for mobile .custom-message-length inverted from 70% to 30% lightness - inconsistent with desktop version, causing incorrect contrast on both light and dark themes on mobile", - "confidence": 0.95, - "reasoning": "The candidate issue identifies the same problem - the lightness value was changed from 70% to 30% for the light theme. The candidate specifically mentions 'Light-theme color... inverted from 70% to 30% lightness' which matches the golden comment's concern about '$lightness: 70%' being replaced with '$lightness: 30%' making text darker than intended. Both are pointing to the same bug in topic-post.css." - } - ], - "false_positives": [ - { - "candidate": "Light-theme color for mobile .topic-map h3 changed from 20% to 50% lightness - inconsistent with desktop equivalent, making headings lighter and less prominent on light themes" - } - ], - "false_negatives": [], - "errors": [], - "total_candidates": 5, - "total_golden": 3, - "tp": 3, - "fp": 1, - "fn": 0, - "errors_count": 0, - "precision": 0.6, - "recall": 1.0, - "tool": "devin", - "repo_name": "discourse__discourse-graphite__devin__PR7__20260302", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__devin__PR7__20260302/pull/1" - }, - "mra-max": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "Global color function migration from scale-color to dark-light-choose creates high churn risk of visual regressions and merge conflicts, plus potential compile issues if variables are missing in some themes" - }, - { - "candidate": "Widespread changes to high-PR files (admin_base.scss, headers, topic lists, topic posts) amplify risk that a single theming tweak or bug will ripple through large portion of UI" - }, - { - "candidate": "In admin_base.scss, h3 color for .setting.overridden uses scale-color($highlight, $lightness: -50%) bypassing dark-light-choose pattern, creating potential contrast issues in dark themes" - }, - { - "candidate": "admin_base.scss has multiple scale-color usages not wrapped by dark-light-choose (lines 418, 443, 496), breaking dark-mode support consistency" - }, - { - "candidate": "Typo/syntax error in topic-list SCSS patch shows truncated 'color: dark-ligh' which would cause compile-time SCSS error" - }, - { - "candidate": "In _topic-list.scss line 115, color uses scale-color($primary, $lightness: 50%) without dark-light-choose wrapper, inconsistent with surrounding usage" - }, - { - "candidate": "Header color rule at line 301 uses dark-light-choose with primary as light-theme color, contradicting goal of dark-theme colors relying on $secondary" - }, - { - "candidate": "Header color rule at line 332 reuses primary in dark-light-choose call, risking dark-theme deriving from primary rather than secondary" - }, - { - "candidate": "Centralized dark-light-choose function creates tight coupling between theming palette and all UI components - changes to primary/secondary colors or brightness logic will cascade across thousands of style rules" - }, - { - "candidate": "Large-scale color refactor in desktop topic-list styling may lead to inconsistent theming across breakpoints if secondary color not defined identically in all themes" - }, - { - "candidate": "Mobile topic list styling updates add churn across mobile styles with potential for regressions in mobile rendering" - }, - { - "candidate": "No explicit color-contrast tests or visual regression tests across light and dark themes despite hundreds of components relying on new color logic" - }, - { - "candidate": "Broad patch scope touching desktop, mobile, and admin styles increases surface area for regressions" - }, - { - "candidate": "dark-light-choose function relies on theme_variables imports - if import graph changes or theme_variables unavailable in some build contexts, builds could fail" - }, - { - "candidate": "PR description is minimal ('Test 7') and does not summarize changes, rationale, affected files, or testing performed" - }, - { - "candidate": "No CHANGELOG.md present despite project referencing standard changelog practices - change affecting theming/tokens should have changelog entry" - }, - { - "candidate": "Partial adoption with some color usages remaining as direct tokens (e.g., color: $primary) creates mixed color strategies within same theme" - }, - { - "candidate": "Static uses of $header_primary in topic-post.scss not part of dark-theme toggle via dark-light-choose, making dark-theme behavior potentially inconsistent" - }, - { - "candidate": "No dark-theme-specific tests found in test folder covering dark-theme behavior or fallback logic" - }, - { - "candidate": "No visual regression or theming tests that would guard the CSS theming changes introduced by this PR" - } - ], - "false_negatives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low" - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low" - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 20, - "total_golden": 3, - "tp": 0, - "fp": 20, - "fn": 3, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "mra-max", - "repo_name": "discourse__discourse-graphite__mra-max__PR7__20260306", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__mra-max__PR7__20260306/pull/1" - }, - "greptile-v4": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low", - "matched_candidate": "Light-theme lightness accidentally changed from 30% to 70% in desktop/topic-post.scss line 291 for .topic-meta-data h5 a - the dark-light-choose first argument should use 30% to preserve the original value", - "confidence": 1.0, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment: in .topic-meta-data h5 a, the lightness value for the light theme was changed from 30% to 70% when implementing dark-light-choose, which is incorrect. Both comments point to the same bug in the same location (desktop/topic-post.scss, .topic-meta-data h5 a) and identify that the first argument should use 30% to preserve the original value." - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low", - "matched_candidate": "Light-theme lightness changed from 30% to 50% for .name in mobile/user.scss line 497 - same issue as desktop/user.scss, making name text lighter in light mode than intended", - "confidence": 0.95, - "reasoning": "The golden comment points out that the light theme's $primary was changed from 30% to 50% in desktop/user.css, and mentions this also applies to a similar .name change in the mobile variant. The candidate issue identifies the same problem - the light-theme lightness being changed from 30% to 50% for .name in mobile/user.scss, explicitly noting it's the 'same issue as desktop/user.scss'. This is the exact same underlying concern about the lightness value change making text lighter than intended in light mode." - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low", - "matched_candidate": "Light-theme lightness accidentally changed from 30% to 70% in desktop/topic-post.scss line 291 for .topic-meta-data h5 a - the dark-light-choose first argument should use 30% to preserve the original value", - "confidence": 0.75, - "reasoning": "Both comments identify the same issue about lightness values being swapped in topic-post.css/scss for the light theme. However, they describe the swap in opposite directions - the golden says it went from 70% to 30% (making it darker), while the candidate says it went from 30% to 70%. They're identifying the same location and same type of bug (lightness value mismatch), but they disagree on which direction the error occurred. This is the same underlying issue being flagged, just with confusion about which value is correct." - } - ], - "false_positives": [ - { - "candidate": "Light/dark arguments appear swapped for .custom-message-length in mobile/modal.scss - the light-theme result uses 30% instead of the original 70%, making it much darker than intended and inconsistent with desktop/modal.scss" - }, - { - "candidate": "Light-theme lightness changed from 20% to 50% for .media-body h3 in mobile/topic-post.scss line 182 - inconsistent with desktop/topic-post.scss which correctly uses 20%" - } - ], - "false_negatives": [], - "errors": [], - "total_candidates": 5, - "total_golden": 3, - "tp": 3, - "fp": 2, - "fn": 0, - "errors_count": 0, - "precision": 0.6, - "recall": 1.0, - "tool": "greptile-v4", - "repo_name": "discourse__discourse-graphite__greptile-v4__PR7__20260306", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__greptile-v4__PR7__20260306/pull/1" - }, - "mra-nano": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low" - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low" - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 3, - "tp": 0, - "fp": 0, - "fn": 3, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "mra-nano", - "repo_name": "discourse__discourse-graphite__mra-nano__PR7__20260306", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__mra-nano__PR7__20260306/pull/1" - }, - "mra-ultra": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "dark-light-choose function relies on global color vars ($primary/$secondary) for brightness decisions, which could produce unexpected results if files override these variables in nested scopes" - }, - { - "candidate": "Inconsistent lightness distributions across color replacements (80%/20%, 75%/25%, 50%/50%, 60%/40%) may lead to uneven contrast and readability between elements depending on theme" - }, - { - "candidate": "Remaining scale-color usage in topic-list (.category .badge-notification color) not migrated to dark-light-choose pattern used elsewhere" - }, - { - "candidate": "No unit tests or visual regression tests validating dark-light-choose color outputs for light vs dark themes" - }, - { - "candidate": "Outdated inline doc comment in discourse.scss claims default table cell color uses scale-color($primary, $lightness: 50%) but actual code uses dark-light-choose with different lightness values" - } - ], - "false_negatives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low" - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low" - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 5, - "total_golden": 3, - "tp": 0, - "fp": 5, - "fn": 3, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "mra-ultra", - "repo_name": "discourse__discourse-graphite__mra-ultra__PR7__20260306", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__mra-ultra__PR7__20260306/pull/1" - }, - "mra-b": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "dark-light-choose() function definition cannot be located in the repository to verify correctness of argument ordering" - }, - { - "candidate": "dark-light-choose() condition appears inverted - returns $light-theme-result when brightness($primary) < brightness($secondary), which may produce incorrect color selection for dark themes" - }, - { - "candidate": "Large set of callers updated to use dark-light-choose() need review to ensure correct argument ordering matches the function signature (light-then-dark vs dark-then-light)" - }, - { - "candidate": "Large-scope mechanical CSS replacement across many frequently-edited stylesheet files creates merge-conflict and regression risk" - }, - { - "candidate": "Heatmap/coldmap classes now use dark-light-choose() which may unintentionally change numeric highlight semantics by swapping color source to $secondary based on brightness heuristic" - }, - { - "candidate": "In mobile/modal.scss, .custom-message-length color change uses 30% lightness for primary instead of original 70%, which differs from other similar changes and may be a mistake" - } - ], - "false_negatives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low" - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low" - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 6, - "total_golden": 3, - "tp": 0, - "fp": 6, - "fn": 3, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "mra-b", - "repo_name": "discourse__discourse-graphite__mra-b__PR7__20260307", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__mra-b__PR7__20260307/pull/1" - }, - "mra-a": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low", - "matched_candidate": "Automated refactor swapped lightness values inconsistently across desktop/mobile - multiple selectors have primary-branch lightness values changed (e.g. 30%\u219270% or 70%\u219230%) and desktop vs mobile counterparts for the same element use inverted lightness pairs", - "confidence": 0.85, - "reasoning": "The candidate issue identifies the same underlying problem - that lightness values were swapped inconsistently during the automated refactor, specifically mentioning the 30%\u219270% change pattern. While the candidate describes this more broadly across multiple selectors rather than focusing on the specific .topic-meta-data h5 a selector mentioned in the golden comment, it captures the same core issue: the dramatic inversion of lightness values (30% to 70%) that occurred during the refactoring." - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low", - "matched_candidate": "Automated refactor swapped lightness values inconsistently across desktop/mobile - multiple selectors have primary-branch lightness values changed (e.g. 30%\u219270% or 70%\u219230%) and desktop vs mobile counterparts for the same element use inverted lightness pairs", - "confidence": 0.85, - "reasoning": "The candidate issue identifies the same underlying problem - inconsistent lightness value changes across desktop/mobile variants during the refactor. The golden comment specifically mentions the 30% to 50% change in desktop/user.css for light theme and notes a similar issue in mobile variant. The candidate describes 'lightness values changed inconsistently' and 'desktop vs mobile counterparts for the same element use inverted lightness pairs', which captures the same concern about inconsistent handling of the primary/lightness values between desktop and mobile versions." - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low", - "matched_candidate": "Automated refactor swapped lightness values inconsistently across desktop/mobile - multiple selectors have primary-branch lightness values changed (e.g. 30%\u219270% or 70%\u219230%) and desktop vs mobile counterparts for the same element use inverted lightness pairs", - "confidence": 0.9, - "reasoning": "The candidate issue identifies the same underlying problem - that lightness values were swapped inconsistently during refactoring, specifically mentioning '70%\u219230%' changes. The golden comment specifically calls out that $lightness: 70% was changed to $lightness: 30% for light theme in topic-post.css. The candidate captures this exact issue as part of a broader pattern of inconsistent lightness value swaps." - } - ], - "false_positives": [ - { - "candidate": "dark-light-choose function has inverted logic - returns $light-theme-result when brightness($primary) < brightness($secondary), which is the dark-theme condition, causing wrong colors in dark themes" - }, - { - "candidate": "Existing call sites pass reversed args to dark-light-choose (secondary, primary) to compensate for current semantics, but PR's bulk replacement uses (primary, secondary) order without preserving that compensation, creating inconsistent behavior across stylesheets" - }, - { - "candidate": "search-highlight uses asymmetric 25%/75% lightness values in dark-light-choose call - if dark-light-choose has inverted logic, highlights will display wrong colors, and this edge case needs specific test coverage" - } - ], - "false_negatives": [], - "errors": [], - "total_candidates": 4, - "total_golden": 3, - "tp": 3, - "fp": 3, - "fn": 0, - "errors_count": 0, - "precision": 0.75, - "recall": 1.0, - "tool": "mra-a", - "repo_name": "discourse__discourse-graphite__mra-a__PR7__20260307", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__mra-a__PR7__20260307/pull/1" - }, - "entelligence": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low" - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low" - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 3, - "tp": 0, - "fp": 0, - "fn": 3, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "entelligence", - "repo_name": "discourse__discourse-graphite__entelligence__PR7__20260306", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__entelligence__PR7__20260306/pull/1" - }, - "sourcery": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low", - "matched_candidate": "Mobile custom-message-length color lightness is inverted compared to desktop and the previous implementation - the change switches to 30% for $primary and 70% for $secondary, inverting the contrast hierarchy. Should align mobile rule with desktop one to keep contrast and visual weight consistent.", - "confidence": 0.85, - "reasoning": "The candidate issue identifies the same underlying problem - that the lightness values are inverted/swapped incorrectly. The golden comment specifically mentions that $lightness: 70% was changed to $lightness: 30% for light theme, making text darker than intended. The candidate describes this as 'lightness is inverted' with '30% for $primary and 70% for $secondary, inverting the contrast hierarchy.' Both are pointing to the same bug where the lightness percentages were incorrectly swapped/inverted." - } - ], - "false_positives": [ - { - "candidate": "Consider introducing a small Sass helper/mixin for the dark-light-choose(scale-color($primary, ...), scale-color($secondary, ...)) pattern to reduce repetition and make future theme tuning easier" - } - ], - "false_negatives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low" - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 3, - "total_golden": 3, - "tp": 1, - "fp": 1, - "fn": 2, - "errors_count": 0, - "precision": 0.3333333333333333, - "recall": 0.3333333333333333, - "tool": "sourcery", - "repo_name": "discourse__discourse-graphite__sourcery__PR7__20260308", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__sourcery__PR7__20260308/pull/1" - }, - "cubic-dev": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low", - "matched_candidate": "Light-theme lightness changed from 30% to 70% in embedded-post author link (topic-post.scss:291), which appears to be an accidental swap - arguments should be swapped: light \u2192 30%, dark \u2192 70%", - "confidence": 0.95, - "reasoning": "The candidate issue identifies the same problem as the golden comment - the lightness value change from 30% to 70% for the light theme in the topic-meta-data/embedded-post author link styling. Both comments point out that this appears to be an error where the lightness values were swapped or inverted incorrectly. The candidate even suggests the arguments should be swapped, which aligns with the golden comment's observation that this is a 'dramatic inversion'." - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low", - "matched_candidate": "Light-theme lightness for .group-member-info .name in desktop/user.scss:59 was changed from 30% to 50% - should preserve original 30% value for light theme", - "confidence": 0.95, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment - that the light-theme lightness for .group-member-info .name in desktop/user.scss was changed from 30% to 50%, and suggests preserving the original 30% value. The golden comment also mentions this applies to a similar .name change in the mobile variant, which the candidate doesn't mention, but the core issue about the desktop light theme change is correctly identified." - } - ], - "false_positives": [ - { - "candidate": "Light-theme lightness for .group-member-info .name in mobile/user.scss:115 was changed from 30% to 50%, making it visually identical to .title - should preserve original 30% value" - }, - { - "candidate": "Light-theme lightness for .topic-map h3 in mobile/topic-post.scss:49 was changed from 20% to 50%, creating desktop/mobile inconsistency - should use 20% for light theme and 80% for dark theme" - }, - { - "candidate": ".fa-thumb-tack.unpinned has the exact same color as .fa-thumb-tack in desktop/topic-list.scss:40, making it redundant - should either assign distinct color or remove the rule" - } - ], - "false_negatives": [ - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 5, - "total_golden": 3, - "tp": 2, - "fp": 3, - "fn": 1, - "errors_count": 0, - "precision": 0.4, - "recall": 0.6666666666666666, - "tool": "cubic-dev", - "repo_name": "discourse__discourse-graphite__cubic-dev__PR7__20260306", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__cubic-dev__PR7__20260306/pull/1" - }, - "mesa": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low", - "matched_candidate": "Lightness percentages (70% for light, 30% for dark) in desktop/topic-post.scss line 291 are inverted compared to original value of 30%, causing significant color shift with much lighter text in light mode - may be accidental parameter swap", - "confidence": 0.98, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment: the lightness percentage changed from 30% to 70% for light mode in the topic-post.scss file, causing a significant color shift. Both comments point out that this appears to be an accidental inversion of the lightness values, with the candidate specifically mentioning it may be an 'accidental parameter swap'. The candidate even references the specific line number (291) and correctly identifies the 70%/30% values." - } - ], - "false_positives": [ - { - "candidate": "Mobile lightness value (50%) for h3 element is inconsistent with desktop version (70%) in topic-post.scss, causing visual inconsistency between mobile and desktop views" - }, - { - "candidate": "Mobile modal.scss line 102 inverts intended lightness values - uses 30% for light theme instead of original 70%, making text darker instead of lighter, inconsistent with desktop/modal.scss line 94 which correctly keeps 70% for light theme" - } - ], - "false_negatives": [ - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low" - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 3, - "total_golden": 3, - "tp": 1, - "fp": 2, - "fn": 2, - "errors_count": 0, - "precision": 0.3333333333333333, - "recall": 0.3333333333333333, - "tool": "mesa", - "repo_name": "discourse__discourse-graphite__mesa__PR7__20260307", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__mesa__PR7__20260307/pull/1" - }, - "codeant": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "In mobile/modal.scss, .custom-message-length: the new expression uses primary 30% \u2192 secondary 70%, but previously it was 70% for primary - this may be accidentally inverted compared to the pattern elsewhere (primary 70% \u2192 secondary 30%)" - }, - { - "candidate": "Repeated expression dark-light-choose(scale-color($primary, $lightness: X%), scale-color($secondary, $lightness: Y%)) should be DRYed up with a helper function or mixin to reduce duplication and typos" - }, - { - "candidate": "Color changes between themes may alter contrast ratios - need to validate readability for small text against backgrounds (WCAG guidance) in both light and dark themes" - }, - { - "candidate": "Components with !important or background overlays should be tested visually to ensure nothing becomes unreadable" - }, - { - "candidate": "Need to run repo-wide grep for scale-color($primary to verify all intended instances are updated" - }, - { - "candidate": "The dark-light-choose function implementation should be well-documented to explain how it decides 'dark' vs 'light'" - }, - { - "candidate": "Need to confirm dark-light-choose and dark-light-diff functions are present and behave as expected across themes" - }, - { - "candidate": "Should run full Sass compile to ensure no errors from the changes" - } - ], - "false_negatives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low" - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low" - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 8, - "total_golden": 3, - "tp": 0, - "fp": 8, - "fn": 3, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "codeant", - "repo_name": "discourse__discourse-graphite__codeant__PR7__20260309", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__codeant__PR7__20260309/pull/1" - }, - "kodus": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low", - "matched_candidate": "Incorrect color contrast in topic-post.scss line 291: improper dark-light-choose and scale-color usage causing contrast issues", - "confidence": 0.7, - "reasoning": "The candidate issue mentions 'Incorrect color contrast in topic-post.scss line 291' with 'improper dark-light-choose and scale-color usage causing contrast issues'. The golden comment is specifically about .topic-meta-data h5 a where the lightness changed from 30% to 70% for the light theme, which is a dramatic inversion. The candidate appears to be pointing to the same general area (topic-post.scss, dark-light-choose and scale-color usage, contrast issues), but the line number 291 and the specific details don't perfectly match. However, the core issue - improper color contrast due to dark-light-choose and scale-color changes - seems to be the same underlying problem. The candidate is identifying contrast issues with the same CSS constructs mentioned in the golden comment." - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low", - "matched_candidate": "dark-light-choose function arguments may have inverted theme logic - first argument should be for dark theme, second for light theme", - "confidence": 0.85, - "reasoning": "The candidate issue mentions that the dark-light-choose function arguments may have inverted theme logic, which relates to the same underlying problem. The golden comment specifically identifies that the light theme uses $lightness: 30% instead of the original 70%, making text darker than intended. The candidate is pointing to the same issue - incorrect values being applied to the wrong theme due to argument order/logic inversion. Both are identifying that the light theme is getting the wrong lightness value." - } - ], - "false_positives": [ - { - "candidate": "Incorrect color contrast in share_link.scss line 62: dark-light-choose function uses scale-color with $primary at 50% lightness for both themes, but the parent container has $secondary background, causing unreadable text in light theme" - }, - { - "candidate": "Incorrect color contrast in compose.scss lines 35-37: improper dark-light-choose and scale-color usage causing contrast issues" - }, - { - "candidate": "Incorrect color contrast in modal.scss line 102: improper dark-light-choose and scale-color usage causing contrast issues" - }, - { - "candidate": "Inappropriate lightness values in scale-color function not providing sufficient contrast ratios between text and background colors" - } - ], - "false_negatives": [ - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 6, - "total_golden": 3, - "tp": 2, - "fp": 4, - "fn": 1, - "errors_count": 0, - "precision": 0.3333333333333333, - "recall": 0.6666666666666666, - "tool": "kodus", - "repo_name": "discourse__discourse-graphite__kodus__PR7__20260309", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__kodus__PR7__20260309/pull/1" - }, - "claude-code": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low", - "matched_candidate": "desktop/topic-post.scss: .topic-meta-data h5 a light-theme value changed from $primary 30% to 70% lightness, causing visual regression on light themes", - "confidence": 0.98, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment: in .topic-meta-data h5 a, the light theme lightness value changed from 30% to 70% for $primary color, which is a dramatic change that could cause visual regression. Both comments point to the same file, same selector, and same specific issue with the lightness percentage change." - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low", - "matched_candidate": "desktop/user.scss and mobile/user.scss: .group-member-info .name changed from $primary 30% to 50% lightness, altering light-theme appearance", - "confidence": 0.95, - "reasoning": "The candidate issue identifies the same problem as the golden comment - the change from 30% to 50% lightness for $primary in the .name selector, affecting both desktop/user.scss and mobile/user.scss files. The golden comment specifically mentions this change alters the light theme appearance and suggests reviewing it, which aligns with the candidate's observation about 'altering light-theme appearance'. Both identify the same underlying concern about this specific lightness value change." - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low", - "matched_candidate": "mobile/modal.scss: .custom-message-length light-theme value changed from $primary 70% to 30% lightness, inconsistent with desktop equivalent which correctly keeps 70%", - "confidence": 0.85, - "reasoning": "The candidate issue identifies the same underlying problem - the lightness value was incorrectly changed from 70% to 30% for the light theme. While the golden comment mentions topic-post.css and the candidate mentions mobile/modal.scss, they both describe the same type of bug: the light theme lightness value being changed from 70% to 30%, making text darker than intended. The candidate also notes the inconsistency with the desktop equivalent which keeps 70%, which aligns with the golden comment's concern about the replacement being different from the original." - } - ], - "false_positives": [ - { - "candidate": "mobile/topic-post.scss: h3 light-theme value changed from $primary 20% to 50% lightness, while desktop keeps 20%" - } - ], - "false_negatives": [], - "errors": [], - "total_candidates": 4, - "total_golden": 3, - "tp": 3, - "fp": 1, - "fn": 0, - "errors_count": 0, - "precision": 0.75, - "recall": 1.0, - "tool": "claude-code", - "repo_name": "discourse__discourse-graphite__claude-code__PR7__20260310", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__claude-code__PR7__20260310/pull/1" - }, - "vercel": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low" - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low" - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 3, - "tp": 0, - "fp": 0, - "fn": 3, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "vercel", - "repo_name": "discourse__discourse-graphite__vercel__PR7__20260310", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__vercel__PR7__20260310/pull/1" - }, - "bito": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low" - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low" - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 3, - "tp": 0, - "fp": 0, - "fn": 3, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "bito", - "repo_name": "discourse__discourse-graphite__bito__PR7__20260310", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__bito__PR7__20260310/pull/1" - }, - "sentry": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low" - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low" - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 3, - "tp": 0, - "fp": 0, - "fn": 3, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "sentry", - "repo_name": "discourse__discourse-graphite__sentry__PR7__20260310", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__sentry__PR7__20260310/pull/1" - }, - "linearb": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "/gs review" - } - ], - "false_negatives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low" - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low" - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 1, - "total_golden": 3, - "tp": 0, - "fp": 1, - "fn": 3, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "linearb", - "repo_name": "discourse__discourse-graphite__linearb__PR7__20260309", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__linearb__PR7__20260309/pull/1" - }, - "kodus-v2": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low", - "matched_candidate": "Light-mode regression: `.embedded-posts .topic-body .row` link color changed from `$lightness: 30%` (dark, prominent) to `$lightness: 70%` (faded) on line 291. The original code had `scale-color($primary, $lightness: 30%)` but the conversion swapped to `$lightness: 70%` for primary, causing a severe contrast reduction that makes links nearly invisible in light mode.", - "confidence": 0.85, - "reasoning": "Both comments identify the same issue: a lightness value swap in topic-post.css for the light theme. The golden comment says the replacement uses 30% instead of 70%, while the candidate says it uses 70% instead of 30%. They're describing the same bug from opposite perspectives - the values were swapped. Both identify this causes incorrect text/link darkness in light mode. The candidate provides more specific details (line 291, selector name) but it's the same underlying issue about lightness values being incorrectly swapped during the conversion." - } - ], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low" - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 1, - "total_golden": 3, - "tp": 1, - "fp": 0, - "fn": 2, - "errors_count": 0, - "precision": 1.0, - "recall": 0.3333333333333333, - "tool": "kodus-v2", - "repo_name": "discourse__discourse-graphite__kodus-v2__PR7__20260312", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__kodus-v2__PR7__20260312/pull/1" - }, - "qodo-v2-2": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low", - "matched_candidate": "In desktop/topic-post.scss, .embedded-posts .topic-meta-data h5 a uses dark-light-choose with inverted 30%/70% mapping compared to the established pattern used elsewhere for link-like text, making embedded-post metadata links inconsistent with common/base link styling", - "confidence": 0.9, - "reasoning": "The candidate issue identifies the same problem in the same location (.topic-meta-data h5 a in desktop/topic-post.scss). Both comments point out the inverted 30%/70% lightness values in the dark-light-choose function. The golden comment specifically notes the lightness changed from 30% to 70% for light theme (a dramatic inversion), while the candidate describes it as 'inverted 30%/70% mapping compared to the established pattern'. They are identifying the same underlying issue - the lightness values appear to be swapped/inverted." - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low", - "matched_candidate": "In desktop/topic-post.scss, .embedded-posts .topic-meta-data h5 a uses dark-light-choose with inverted 30%/70% mapping compared to the established pattern used elsewhere for link-like text, making embedded-post metadata links inconsistent with common/base link styling", - "confidence": 0.9, - "reasoning": "The candidate issue identifies the same problem - the inverted 30%/70% lightness values in the dark-light-choose function for embedded-posts styling. The golden comment specifically notes that $lightness: 30% is used for light theme instead of the original 70%, making text darker. The candidate describes this as 'inverted 30%/70% mapping' which is the same underlying issue, just worded differently." - } - ], - "false_positives": [ - { - "candidate": "In mobile/modal.scss, .custom-message-length uses dark-light-choose with swapped arguments, applying wrong values in both light and dark themes because dark-light-choose returns its first argument in light themes, creating inconsistency with desktop/modal.scss" - }, - { - "candidate": "In mobile/topic-post.scss, .topic-map h3 was changed to the same 50%/50% dark-light-choose mapping as .topic-map h4, eliminating visual distinction between heading levels and diverging from desktop which retains distinct h3 (20%/80%) vs h4 (50%/50%)" - } - ], - "false_negatives": [ - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 3, - "total_golden": 3, - "tp": 2, - "fp": 2, - "fn": 1, - "errors_count": 0, - "precision": 0.6666666666666666, - "recall": 0.6666666666666666, - "tool": "qodo-v2-2", - "repo_name": "discourse__discourse-graphite__qodo-v2-2__PR7__20260312", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-v2-2__PR7__20260312/pull/1" - }, - "qodo-v22": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "mobile/modal.scss and desktop/modal.scss define .custom-message-length with opposite dark-light-choose(...) lightness mappings, producing inconsistent hint text contrast between mobile and desktop in the same theme" - }, - { - "candidate": "Mobile topic-map h3 was changed to the same color mapping as h4 (50%/50%), reducing visual hierarchy and diverging from desktop's topic-map styling which uses 20%/80% for h3" - } - ], - "false_negatives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low" - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low" - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 2, - "total_golden": 3, - "tp": 0, - "fp": 2, - "fn": 3, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "qodo-v22", - "repo_name": "discourse__discourse-graphite__qodo-v22__PR7__20260312", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-v22__PR7__20260312/pull/1" - }, - "qodo-extended": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low", - "matched_candidate": "In desktop/topic-post.scss, the .topic-meta-data h5 a light-theme color was changed from 30% lightness to 70% lightness, which is likely a typo and reduces link contrast in light theme since dark-light-choose returns its first argument for light themes", - "confidence": 0.98, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment: in .topic-meta-data h5 a, the lightness value for the light theme changed from 30% to 70% in the dark-light-choose function. Both comments point out this dramatic change in lightness percentage and identify it as problematic (the candidate calls it 'likely a typo' and notes it 'reduces link contrast')." - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low", - "matched_candidate": "In desktop/topic-post.scss, the .topic-meta-data h5 a light-theme color was changed from 30% lightness to 70% lightness, which is likely a typo and reduces link contrast in light theme since dark-light-choose returns its first argument for light themes", - "confidence": 0.85, - "reasoning": "Both comments identify the same issue about lightness values being swapped/incorrect in the topic-post CSS for light theme. The golden comment says the replacement uses 30% instead of 70% (making text darker), while the candidate says it was changed from 30% to 70% (making it lighter/reducing contrast). They're identifying the same mismatch between original and replacement lightness values, though they describe the direction of change differently. The core issue is the same: the lightness percentages are wrong for the light theme in the topic-meta-data styling." - } - ], - "false_positives": [ - { - "candidate": "In mobile/modal.scss, .custom-message-length was changed to use 30% lightness for the light theme instead of the previous 70% lightness, altering the light-theme UI rather than only adding a dark-theme alternative, inconsistent with desktop which uses 70% for light theme" - }, - { - "candidate": "In mobile/topic-post.scss, .topic-map h3 now uses 50%/50% lightness for light/dark themes, which diverges from the desktop topic-map heading styling (20%/80%) and changes the heading's emphasis" - } - ], - "false_negatives": [ - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 3, - "total_golden": 3, - "tp": 2, - "fp": 2, - "fn": 1, - "errors_count": 0, - "precision": 0.6666666666666666, - "recall": 0.6666666666666666, - "tool": "qodo-extended", - "repo_name": "discourse__discourse-graphite__qodo-extended__PR7__20260312", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-extended__PR7__20260312/pull/1" - }, - "cubic-v2": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low", - "matched_candidate": "In desktop/topic-post.scss line 291: The dark-light-choose conversion swaps the light and dark lightness values, regressing the existing light-theme color instead of preserving it", - "confidence": 0.95, - "reasoning": "The candidate issue identifies the same problem as the golden comment - both point out that in the dark-light-choose conversion, the lightness values were swapped/inverted, causing the light theme color to be regressed rather than preserved. The golden comment specifically notes the lightness changed from 30% to 70% for light theme, and the candidate describes this as 'swaps the light and dark lightness values, regressing the existing light-theme color'. This is the same underlying issue." - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low", - "matched_candidate": "In desktop/topic-post.scss line 291: The dark-light-choose conversion swaps the light and dark lightness values, regressing the existing light-theme color instead of preserving it", - "confidence": 0.9, - "reasoning": "The candidate issue identifies the same problem - that the dark-light-choose conversion has swapped/incorrect lightness values, specifically mentioning it 'regresses the existing light-theme color instead of preserving it'. This is semantically the same as the golden comment which states the light theme uses 30% instead of the original 70%, making text darker than intended. Both point to the same bug: incorrect lightness value for the light theme." - } - ], - "false_positives": [ - { - "candidate": "In mobile/topic-post.scss line 49: The light-theme $lightness for .topic-map h3 was changed from 20% to 50%, altering light-theme appearance - should preserve original 20% for light variant and use complementary 80% for dark variant to match desktop stylesheet" - } - ], - "false_negatives": [ - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 3, - "total_golden": 3, - "tp": 2, - "fp": 1, - "fn": 1, - "errors_count": 0, - "precision": 0.6666666666666666, - "recall": 0.6666666666666666, - "tool": "cubic-v2", - "repo_name": "discourse__discourse-graphite__cubic-v2__PR7__20260313", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__cubic-v2__PR7__20260313/pull/1" - }, - "qodo-extended-summary": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low", - "matched_candidate": "In desktop/topic-post.scss line 291, light-theme lightness was changed from 30% to 70% in dark-light-choose first argument, making .embedded-posts link text significantly lighter in light themes", - "confidence": 0.9, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment - the lightness value change from 30% to 70% in the dark-light-choose first argument (light theme). While the golden comment refers to '.topic-meta-data h5 a' and the candidate mentions '.embedded-posts link text', both are pointing to the same code change in desktop/topic-post.scss where the light theme lightness was dramatically changed from 30% to 70%, making the text significantly lighter." - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low", - "matched_candidate": "In desktop/user.scss and mobile/user.scss, .group-member-info .name uses 50%/50% color instead of 30%/70% used for user names elsewhere, making member names visually indistinguishable from titles", - "confidence": 0.85, - "reasoning": "The golden comment points out that the desktop/user.css changes $primary from 30% to 50% for the light theme, which is inconsistent with other changes that preserve the original $primary value. The candidate issue identifies the same problem - that .group-member-info .name uses 50%/50% instead of 30%/70%, making it inconsistent with user names elsewhere. Both are flagging the same inconsistency in the color percentage change from 30% to 50% in the .name styling." - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low", - "matched_candidate": "In mobile/modal.scss line 102, .custom-message-length has light and dark theme arguments swapped (light gets 30%, dark gets 70% instead of 70%/30%), making text much darker in light themes", - "confidence": 0.85, - "reasoning": "The candidate issue identifies the same underlying problem - the lightness values being swapped between light and dark themes, resulting in text being much darker in light themes (30% instead of 70%). While the candidate mentions mobile/modal.scss instead of topic-post.css, the core issue is identical: light theme incorrectly using 30% lightness instead of 70%, making text significantly darker than intended." - } - ], - "false_positives": [ - { - "candidate": "Mobile topic-map h3 uses 50%/50% lightness instead of the 20%/80% scheme used on desktop, removing heading emphasis and reducing contrast" - } - ], - "false_negatives": [], - "errors": [], - "total_candidates": 4, - "total_golden": 3, - "tp": 3, - "fp": 1, - "fn": 0, - "errors_count": 0, - "precision": 0.75, - "recall": 1.0, - "tool": "qodo-extended-summary", - "repo_name": "discourse__discourse-graphite__qodo-extended-summary__PR7__20260313", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-extended-summary__PR7__20260313/pull/1" - }, - "macroscope": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low", - "matched_candidate": "The `dark-light-choose` call for `.topic-meta-data h5 a` incorrectly uses `lightness: 70%` for the light theme instead of the original `30%`, making links in embedded posts significantly lighter and less visible in light themes. The values should be swapped: 30% for light themes and 70% for dark themes.", - "confidence": 0.98, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment: in `.topic-meta-data h5 a`, the lightness value for the light theme was changed from 30% to 70%, which is incorrect. Both comments point out this dramatic change in lightness values and identify it as a bug that makes the links less visible in light themes." - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low", - "matched_candidate": "The `dark-light-choose` call for `.topic-meta-data h5 a` incorrectly uses `lightness: 70%` for the light theme instead of the original `30%`, making links in embedded posts significantly lighter and less visible in light themes. The values should be swapped: 30% for light themes and 70% for dark themes.", - "confidence": 0.75, - "reasoning": "Both comments identify the same issue with the lightness values in the dark-light-choose call for topic-post.css. However, they describe the problem in opposite directions. The golden comment says the replacement uses 30% instead of 70% for light theme (making text darker). The candidate says it uses 70% instead of 30% for light theme (making text lighter). These are contradictory descriptions of the same code location. Without seeing the actual code, I cannot determine which description is accurate, but they are pointing to the same underlying issue - incorrect lightness values being swapped between light and dark themes in the same CSS selector." - } - ], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 1, - "total_golden": 3, - "tp": 2, - "fp": 0, - "fn": 1, - "errors_count": 0, - "precision": 2.0, - "recall": 0.6666666666666666, - "tool": "macroscope", - "repo_name": "discourse__discourse-graphite__macroscope__PR7__20260309", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__macroscope__PR7__20260309/pull/1" - } - }, - "https://github.com/ai-code-review-evaluation/discourse-graphite/pull/8": { - "gemini": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "HTTP method mismatch in .remove_member - test uses PUT but remove_member action expects DELETE", - "severity": "Medium", - "matched_candidate": "Test uses PUT HTTP verb to test remove_member action but the route is defined with DELETE - should use xhr :delete", - "confidence": 1.0, - "reasoning": "Both comments identify the exact same issue: the test is using PUT HTTP method but the remove_member action expects/is routed with DELETE. The golden comment says 'test uses PUT but remove_member action expects DELETE' and the candidate says 'Test uses PUT HTTP verb to test remove_member action but the route is defined with DELETE'. These are semantically identical." - } - ], - "false_positives": [ - { - "candidate": "The route provides the group's ID in params[:id], but the code attempts to read it from params[:group_id], causing the action to fail" - }, - { - "candidate": "The totalPages calculation is incorrect when the number of users is an exact multiple of the page limit - should use Math.ceil instead" - }, - { - "candidate": "The addMembers action should clear the usernames input after adding members for better user experience" - }, - { - "candidate": "Use of !important in CSS should be avoided as it makes CSS harder to maintain - consider increasing selector specificity instead" - }, - { - "candidate": "The add_members action has an N+1 query problem - iterates through usernames with separate database queries instead of fetching all users in a single query" - } - ], - "false_negatives": [ - { - "golden_comment": " The findMembers() call is now asynchronous and unhandled. The controller may not have member data immediately available, creating a race condition.", - "severity": "High" - }, - { - "golden_comment": "In the next action, capping the next offset at user_count can produce an empty page (e.g., total equal to limit results in offset == total, showing 2/2 with no members). This can cause confusing UX on the last page.", - "severity": "Medium" - } - ], - "errors": [], - "total_candidates": 6, - "total_golden": 3, - "tp": 1, - "fp": 5, - "fn": 2, - "errors_count": 0, - "precision": 0.16666666666666666, - "recall": 0.3333333333333333, - "tool": "gemini", - "repo_name": "discourse__discourse-graphite__gemini__PR8__20260128", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__gemini__PR8__20260128/pull/1" - }, - "claude": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In the next action, capping the next offset at user_count can produce an empty page (e.g., total equal to limit results in offset == total, showing 2/2 with no members). This can cause confusing UX on the last page.", - "severity": "Medium", - "matched_candidate": "Pagination calculation error returns extra empty page (bug in totalPages formula)", - "confidence": 0.75, - "reasoning": "The golden comment describes an issue where capping the next offset at user_count can produce an empty page, causing confusing UX on the last page. The candidate issue mentions a 'pagination calculation error returns extra empty page' and references a 'bug in totalPages formula'. Both issues are about pagination logic producing an empty/extra page, which is essentially the same underlying problem - the pagination calculation leads to showing an empty page at the end. While the candidate focuses on 'totalPages formula' and the golden focuses on 'next offset capping', they both identify the same symptom: an empty page being shown due to pagination calculation issues." - }, - { - "golden_comment": "HTTP method mismatch in .remove_member - test uses PUT but remove_member action expects DELETE", - "severity": "Medium", - "matched_candidate": "Test uses wrong HTTP method - uses PUT but route expects DELETE for remove_member endpoint", - "confidence": 1.0, - "reasoning": "The candidate issue identifies exactly the same problem as the golden comment: the test uses PUT HTTP method but the remove_member action/endpoint expects DELETE. Both are pointing to the same HTTP method mismatch bug in the .remove_member test." - } - ], - "false_positives": [ - { - "candidate": "Function returns undefined instead of promise when name is empty (inconsistent return type breaks promise chaining)" - }, - { - "candidate": "Regression of truncation fix from 5 days earlier - commit 35edfb5b9 on Dec 31, 2014 explicitly prevented truncation of non-automatic groups, this PR removes that protection by applying pagination limits to all groups" - }, - { - "candidate": "TODO comment about fixing group truncation bug was removed without clearly addressing the root cause mentioned in the comment" - }, - { - "candidate": "New TODO comment added in admin-group.js.es6 indicating input field should be cleared after adding members but functionality not implemented" - } - ], - "false_negatives": [ - { - "golden_comment": " The findMembers() call is now asynchronous and unhandled. The controller may not have member data immediately available, creating a race condition.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 6, - "total_golden": 3, - "tp": 2, - "fp": 4, - "fn": 1, - "errors_count": 0, - "precision": 0.3333333333333333, - "recall": 0.6666666666666666, - "tool": "claude", - "repo_name": "discourse__discourse-graphite__claude__PR8__20260127", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__claude__PR8__20260127/pull/1" - }, - "graphite": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": " The findMembers() call is now asynchronous and unhandled. The controller may not have member data immediately available, creating a race condition.", - "severity": "High" - }, - { - "golden_comment": "In the next action, capping the next offset at user_count can produce an empty page (e.g., total equal to limit results in offset == total, showing 2/2 with no members). This can cause confusing UX on the last page.", - "severity": "Medium" - }, - { - "golden_comment": "HTTP method mismatch in .remove_member - test uses PUT but remove_member action expects DELETE", - "severity": "Medium" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 3, - "tp": 0, - "fp": 0, - "fn": 3, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "graphite", - "repo_name": "discourse__discourse-graphite__graphite__PR8__20260123", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__graphite__PR8__20260123/pull/1" - }, - "qodo": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In the next action, capping the next offset at user_count can produce an empty page (e.g., total equal to limit results in offset == total, showing 2/2 with no members). This can cause confusing UX on the last page.", - "severity": "Medium", - "matched_candidate": "Pagination bug: next caps offset to user_count, which can produce an empty page (valid last offset should usually be user_count - limit)", - "confidence": 0.98, - "reasoning": "The candidate issue identifies the exact same pagination bug as the golden comment - that capping the next offset at user_count can produce an empty page. Both comments point out the same underlying problem with the pagination logic where the offset calculation can result in showing an empty last page. The candidate even provides the same technical insight about what the valid last offset should be." - } - ], - "false_positives": [ - { - "candidate": "Potential DoS via unbounded pagination: the limit param in GroupsController#members is client-controlled and not capped, which could allow very large queries/responses" - }, - { - "candidate": "Pagination math off-by-one: totalPages uses Math.floor(user_count / limit) + 1, which yields an extra page when user_count is an exact multiple of limit" - }, - { - "candidate": "members accepts limit directly from params and applies it to the query without a server-side max, allowing clients to request very large limits increasing DB load and response size" - }, - { - "candidate": "add_members splits a comma-separated string but does not trim whitespace, dedupe, or handle empty tokens, which can cause unnecessary lookups and surprising behavior" - }, - { - "candidate": "remove_member deletes by user_id directly instead of using the same membership API as group.add/remove, potentially causing callbacks/validation consistency issues" - } - ], - "false_negatives": [ - { - "golden_comment": " The findMembers() call is now asynchronous and unhandled. The controller may not have member data immediately available, creating a race condition.", - "severity": "High" - }, - { - "golden_comment": "HTTP method mismatch in .remove_member - test uses PUT but remove_member action expects DELETE", - "severity": "Medium" - } - ], - "errors": [], - "total_candidates": 6, - "total_golden": 3, - "tp": 1, - "fp": 5, - "fn": 2, - "errors_count": 0, - "precision": 0.16666666666666666, - "recall": 0.3333333333333333, - "tool": "qodo", - "repo_name": "discourse__discourse-graphite__qodo__PR8__20260123", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo__PR8__20260123/pull/1" - }, - "bugbot": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In the next action, capping the next offset at user_count can produce an empty page (e.g., total equal to limit results in offset == total, showing 2/2 with no members). This can cause confusing UX on the last page.", - "severity": "Medium", - "matched_candidate": "Pagination total pages calculation uses Math.floor(user_count / limit) + 1 which calculates one extra page when user_count is a multiple of limit - should use Math.ceil(user_count / limit) instead. This causes the next button to remain enabled on the actual last page, allowing navigation to an empty page.", - "confidence": 0.9, - "reasoning": "Both comments identify the same underlying issue: the pagination logic allows navigation to an empty page when the user count is a multiple of the limit. The golden comment describes this as 'capping the next offset at user_count can produce an empty page' while the candidate describes it as 'total pages calculation uses Math.floor + 1 which calculates one extra page when user_count is a multiple of limit'. Both point to the same bug causing confusing UX by showing an empty last page." - } - ], - "false_positives": [ - { - "candidate": "The remove_member method calls group.users.delete(user_id) with an integer ID, but the delete method on a has_many :through association expects a User record object, not an integer. This will fail to actually remove the user from the group." - } - ], - "false_negatives": [ - { - "golden_comment": " The findMembers() call is now asynchronous and unhandled. The controller may not have member data immediately available, creating a race condition.", - "severity": "High" - }, - { - "golden_comment": "HTTP method mismatch in .remove_member - test uses PUT but remove_member action expects DELETE", - "severity": "Medium" - } - ], - "errors": [], - "total_candidates": 2, - "total_golden": 3, - "tp": 1, - "fp": 1, - "fn": 2, - "errors_count": 0, - "precision": 0.5, - "recall": 0.3333333333333333, - "tool": "bugbot", - "repo_name": "discourse__discourse-graphite__bugbot__PR8__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__bugbot__PR8__20260122/pull/1" - }, - "copilot": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In the next action, capping the next offset at user_count can produce an empty page (e.g., total equal to limit results in offset == total, showing 2/2 with no members). This can cause confusing UX on the last page.", - "severity": "Medium", - "matched_candidate": "next sets offset to min(offset + limit, user_count), which can move the offset past the last valid page start and produce an empty result set. Clamp to the last page start (e.g., max(user_count - limit, 0)) to avoid navigating to empty pages.", - "confidence": 0.95, - "reasoning": "Both comments identify the same issue: the 'next' action's offset calculation using min(offset + limit, user_count) can result in navigating to an empty page. The golden comment explains this causes confusing UX on the last page, while the candidate comment describes the same problem and even suggests a fix (clamping to max(user_count - limit, 0)). They're pointing to the exact same bug in the pagination logic." - } - ], - "false_positives": [ - { - "candidate": "totalPages is computed with Math.floor(user_count / limit) + 1, which over-counts when user_count is an exact multiple of limit (e.g., 50 users @ limit 50 reports 2 pages). Use Math.ceil(user_count / limit) for correct pagination." - }, - { - "candidate": "create forces group.visible to false whenever the param is omitted (nil == \"true\"), overriding the model default (visible is true by default). Only assign visible when the param is present to avoid new groups becoming invisible." - }, - { - "candidate": "create ignores alias_level even though the client sends it, so alias level is not persisted." - }, - { - "candidate": "update unconditionally sets group.visible based on a string comparison; if visible is omitted from the request it will be reset to false. Only update visible when the param key is present to avoid unintended visibility changes from partial updates." - }, - { - "candidate": "add_members uses group.add(user) (which persists the join row immediately) and then calls group.save to decide success. If group.save fails, the endpoint can return an error even though memberships were already added. Persist membership changes atomically instead." - }, - { - "candidate": "remove_member uses group.users.delete(user_id) on a has_many :through, which deletes join rows without running GroupUser destroy callbacks, so the user_count counter_cache can become incorrect. Use group.group_users.where(user_id: user_id).destroy_all instead." - }, - { - "candidate": "findMembers returns undefined when name is empty (return ;), but callers sometimes treat it like a promise. Return a resolved promise consistently." - }, - { - "candidate": "Database query operation happens inside a loop and could be hoisted to a single call outside the loop for better performance." - } - ], - "false_negatives": [ - { - "golden_comment": " The findMembers() call is now asynchronous and unhandled. The controller may not have member data immediately available, creating a race condition.", - "severity": "High" - }, - { - "golden_comment": "HTTP method mismatch in .remove_member - test uses PUT but remove_member action expects DELETE", - "severity": "Medium" - } - ], - "errors": [], - "total_candidates": 10, - "total_golden": 3, - "tp": 1, - "fp": 8, - "fn": 2, - "errors_count": 0, - "precision": 0.1, - "recall": 0.3333333333333333, - "tool": "copilot", - "repo_name": "discourse__discourse-graphite__copilot__PR8__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__copilot__PR8__20260122/pull/1" - }, - "augment": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In the next action, capping the next offset at user_count can produce an empty page (e.g., total equal to limit results in offset == total, showing 2/2 with no members). This can cause confusing UX on the last page.", - "severity": "Medium", - "matched_candidate": "totalPages uses Math.floor(user_count / limit) + 1, which over-counts when user_count is an exact multiple of limit (e.g. 50/50 -> 2 pages), allowing next to set offset to user_count, yielding an empty last page and incorrect currentPage/totalPages display", - "confidence": 0.95, - "reasoning": "Both comments identify the same underlying issue: when the user count is an exact multiple of the limit (e.g., 50 users with limit 50), the pagination logic allows navigating to an empty last page. The golden comment focuses on the 'next action' capping offset at user_count producing an empty page, while the candidate comment explains the root cause (totalPages calculation using Math.floor + 1 over-counts) and the same consequence (offset equals user_count, yielding an empty last page). They're describing the same bug from slightly different angles." - }, - { - "golden_comment": "HTTP method mismatch in .remove_member - test uses PUT but remove_member action expects DELETE", - "severity": "Medium", - "matched_candidate": "Spec uses xhr :put for remove_member but the route is defined as DELETE /admin/groups/:group_id/members - using PUT can let the spec pass while the real route/verb would behave differently", - "confidence": 0.98, - "reasoning": "Both comments identify the exact same issue: the test uses PUT method for remove_member but the actual route/action expects DELETE. The golden comment says 'test uses PUT but remove_member action expects DELETE' and the candidate says 'Spec uses xhr :put for remove_member but the route is defined as DELETE'. This is the same HTTP method mismatch problem." - } - ], - "false_positives": [ - { - "candidate": "The automatic variable may not be in scope for the item view template - if it resolves against the member context it may be undefined, causing the remove control to show even for automatic groups, which would allow clicking remove and likely surface a 422 without UI handling" - }, - { - "candidate": "offset = params[:offset].to_i can produce negative offsets and limit can be <= 0, which may raise or produce invalid queries in ActiveRecord - need to clamp/sanitize limit and offset before calling .limit(...).offset(...)" - } - ], - "false_negatives": [ - { - "golden_comment": " The findMembers() call is now asynchronous and unhandled. The controller may not have member data immediately available, creating a race condition.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 4, - "total_golden": 3, - "tp": 2, - "fp": 2, - "fn": 1, - "errors_count": 0, - "precision": 0.5, - "recall": 0.6666666666666666, - "tool": "augment", - "repo_name": "discourse__discourse-graphite__augment__PR8__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__augment__PR8__20260122/pull/1" - }, - "propel": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In the next action, capping the next offset at user_count can produce an empty page (e.g., total equal to limit results in offset == total, showing 2/2 with no members). This can cause confusing UX on the last page.", - "severity": "Medium", - "matched_candidate": "When user_count is an exact multiple of the page size, Math.floor(user_count / limit) + 1 reports one extra page, keeping the next button enabled even when there is no data - should use Math.ceil instead", - "confidence": 0.85, - "reasoning": "Both comments identify pagination issues related to edge cases when the total count aligns with page boundaries. The golden comment describes how capping next offset at user_count can produce an empty page when total equals limit. The candidate comment describes how Math.floor(user_count / limit) + 1 reports one extra page, keeping the next button enabled when there's no data. Both are pointing to the same underlying problem: pagination logic that incorrectly handles boundary cases, leading to confusing UX where users can navigate to pages with no content. They're describing the same bug from slightly different angles." - } - ], - "false_positives": [ - { - "candidate": "The add/remove member routes are defined outside of a `member` block, so the generated paths are `/admin/groups/members` and do not include a group id, causing 404 errors when the Ember client calls `/admin/groups/${id}/members.json`" - }, - { - "candidate": "In add_members, the code raises ActionController::ParameterMissing because the Ember client only supplies the group id in the path and no `group_id` parameter in the request body - should read from params[:id] falling back to :group_id" - }, - { - "candidate": "In remove_member, same issue as add_members - the client never sends a `group_id` parameter, only the id embedded in the URL, causing ActionController::ParameterMissing and 400 errors" - }, - { - "candidate": "The user selector sends comma-separated usernames with spaces after commas, but the server looks up usernames verbatim, causing User.find_by_username to return nil and silently skip members - should strip whitespace and skip blank entries" - } - ], - "false_negatives": [ - { - "golden_comment": " The findMembers() call is now asynchronous and unhandled. The controller may not have member data immediately available, creating a race condition.", - "severity": "High" - }, - { - "golden_comment": "HTTP method mismatch in .remove_member - test uses PUT but remove_member action expects DELETE", - "severity": "Medium" - } - ], - "errors": [], - "total_candidates": 5, - "total_golden": 3, - "tp": 1, - "fp": 4, - "fn": 2, - "errors_count": 0, - "precision": 0.2, - "recall": 0.3333333333333333, - "tool": "propel", - "repo_name": "discourse__discourse-graphite__propel__PR8__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__propel__PR8__20260122/pull/1" - }, - "baz": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In the next action, capping the next offset at user_count can produce an empty page (e.g., total equal to limit results in offset == total, showing 2/2 with no members). This can cause confusing UX on the last page.", - "severity": "Medium", - "matched_candidate": "next clamps offset with Math.min(offset + limit, user_count) so when you hit the last page offset becomes user_count and the backend returns zero members and a blank page", - "confidence": 0.98, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment. Both point out that using Math.min(offset + limit, user_count) to cap the next offset can result in offset becoming equal to user_count, which causes the backend to return zero members and display a blank/empty page. This is the same UX issue described in the golden comment." - } - ], - "false_positives": [ - { - "candidate": "alias_level from the flat payload is never assigned before group.save, so creating a group with a non-default alias_level from the UI silently drops that value" - }, - { - "candidate": "members now always runs with limit=50/offset even for non-automatic groups but the public group/members template has no pagination controls, so the UI never loads past the first page" - } - ], - "false_negatives": [ - { - "golden_comment": " The findMembers() call is now asynchronous and unhandled. The controller may not have member data immediately available, creating a race condition.", - "severity": "High" - }, - { - "golden_comment": "HTTP method mismatch in .remove_member - test uses PUT but remove_member action expects DELETE", - "severity": "Medium" - } - ], - "errors": [], - "total_candidates": 4, - "total_golden": 3, - "tp": 1, - "fp": 2, - "fn": 2, - "errors_count": 0, - "precision": 0.25, - "recall": 0.3333333333333333, - "tool": "baz", - "repo_name": "discourse__discourse-graphite__baz__PR8__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__baz__PR8__20260122/pull/1" - }, - "coderabbit": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In the next action, capping the next offset at user_count can produce an empty page (e.g., total equal to limit results in offset == total, showing 2/2 with no members). This can cause confusing UX on the last page.", - "severity": "Medium", - "matched_candidate": "next() function allows navigating to an empty page by clamping offset to user_count instead of the last valid offset", - "confidence": 0.95, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment - that the next() function allows navigating to an empty page because it clamps/caps the offset to user_count instead of the last valid offset. Both comments point out that this capping behavior can result in showing an empty page, which is the same underlying bug/UX concern." - }, - { - "golden_comment": "HTTP method mismatch in .remove_member - test uses PUT but remove_member action expects DELETE", - "severity": "Medium", - "matched_candidate": "Test for automatic-group remove_member uses PUT instead of DELETE, which won't validate verb correctness", - "confidence": 1.0, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment - that the test for remove_member uses PUT instead of DELETE. Both comments point out the HTTP method mismatch where the test uses PUT but the remove_member action expects DELETE." - } - ], - "false_positives": [ - { - "candidate": "totalPages calculation has off-by-one error when user_count is a multiple of limit - Math.floor(user_count / limit) + 1 yields an extra page for exact multiples, should use Math.ceil instead" - }, - { - "candidate": "Usernames are not normalized before lookup in add_members - whitespace or blank entries will silently skip valid users" - }, - { - "candidate": "Pagination params (limit and offset) are not clamped - negative or very large values can trigger heavy queries or odd behavior" - }, - { - "candidate": "Guard condition in group/members.hbs checks model but loop iterates over members - if model is truthy but members is undefined/empty, table header renders with no rows" - }, - { - "candidate": "Stray closing tag with no matching opening tag in group/members.hbs template" - }, - { - "candidate": "Route parameter mismatch - routes provide :id but controller expects :group_id, causing 'param is missing' error" - }, - { - "candidate": "Hardcoded colors (#333 and #aaa) in SCSS instead of SCSS variables may break theme consistency" - }, - { - "candidate": "usernames input is not cleared after add operation, making it easy to accidentally re-add the same usernames" - } - ], - "false_negatives": [ - { - "golden_comment": " The findMembers() call is now asynchronous and unhandled. The controller may not have member data immediately available, creating a race condition.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 10, - "total_golden": 3, - "tp": 2, - "fp": 8, - "fn": 1, - "errors_count": 0, - "precision": 0.2, - "recall": 0.6666666666666666, - "tool": "coderabbit", - "repo_name": "discourse__discourse-graphite__coderabbit__PR8__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__coderabbit__PR8__20260122/pull/1" - }, - "greptile": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "HTTP method mismatch in .remove_member - test uses PUT but remove_member action expects DELETE", - "severity": "Medium", - "matched_candidate": "HTTP method should be DELETE, not PUT, to match the route and controller action", - "confidence": 0.95, - "reasoning": "The candidate issue identifies the same problem as the golden comment - that the HTTP method should be DELETE instead of PUT. The golden comment specifically mentions this is in the context of .remove_member test, and the candidate correctly identifies the HTTP method mismatch issue." - } - ], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": " The findMembers() call is now asynchronous and unhandled. The controller may not have member data immediately available, creating a race condition.", - "severity": "High" - }, - { - "golden_comment": "In the next action, capping the next offset at user_count can produce an empty page (e.g., total equal to limit results in offset == total, showing 2/2 with no members). This can cause confusing UX on the last page.", - "severity": "Medium" - } - ], - "errors": [], - "total_candidates": 1, - "total_golden": 3, - "tp": 1, - "fp": 0, - "fn": 2, - "errors_count": 0, - "precision": 1.0, - "recall": 0.3333333333333333, - "tool": "greptile", - "repo_name": "discourse__discourse-graphite__greptile__PR8__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__greptile__PR8__20260122/pull/1" - }, - "kg": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": " The findMembers() call is now asynchronous and unhandled. The controller may not have member data immediately available, creating a race condition.", - "severity": "High" - }, - { - "golden_comment": "In the next action, capping the next offset at user_count can produce an empty page (e.g., total equal to limit results in offset == total, showing 2/2 with no members). This can cause confusing UX on the last page.", - "severity": "Medium" - }, - { - "golden_comment": "HTTP method mismatch in .remove_member - test uses PUT but remove_member action expects DELETE", - "severity": "Medium" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 3, - "tp": 0, - "fp": 0, - "fn": 3, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "kg", - "repo_name": "discourse__discourse-graphite__kg__PR8__20260202", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__kg__PR8__20260202/pull/1" - }, - "qodo-v2": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In the next action, capping the next offset at user_count can produce an empty page (e.g., total equal to limit results in offset == total, showing 2/2 with no members). This can cause confusing UX on the last page.", - "severity": "Medium", - "matched_candidate": "Admin group pagination computes totalPages with Math.floor(user_count/limit)+1, which adds an extra page when user_count is an exact multiple of limit, allowing navigation to an offset equal to user_count and yielding an empty members list", - "confidence": 0.9, - "reasoning": "Both comments identify the same underlying issue: the pagination logic can result in navigating to an offset equal to user_count, which produces an empty page/members list. The golden comment focuses on the 'next action' capping offset at user_count, while the candidate focuses on the totalPages calculation using Math.floor()+1. Both are describing the same bug - when user_count is exactly divisible by limit (or equals limit), the pagination allows navigating to an empty final page, causing confusing UX." - } - ], - "false_positives": [ - { - "candidate": "The remove_member endpoint removes memberships via group.users.delete(user_id) but does not trigger the existing primary_group_id cleanup used by bulk username updates, leaving User.primary_group_id pointing at a group the user is no longer a member of and bypassing GroupUser counter-cache/callback semantics" - } - ], - "false_negatives": [ - { - "golden_comment": " The findMembers() call is now asynchronous and unhandled. The controller may not have member data immediately available, creating a race condition.", - "severity": "High" - }, - { - "golden_comment": "HTTP method mismatch in .remove_member - test uses PUT but remove_member action expects DELETE", - "severity": "Medium" - } - ], - "errors": [], - "total_candidates": 2, - "total_golden": 3, - "tp": 1, - "fp": 1, - "fn": 2, - "errors_count": 0, - "precision": 0.5, - "recall": 0.3333333333333333, - "tool": "qodo-v2", - "repo_name": "discourse__discourse-graphite__qodo-v2__PR8__20260227", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-v2__PR8__20260227/pull/1" - }, - "devin": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In the next action, capping the next offset at user_count can produce an empty page (e.g., total equal to limit results in offset == total, showing 2/2 with no members). This can cause confusing UX on the last page.", - "severity": "Medium", - "matched_candidate": "totalPages calculation is off-by-one when user_count is evenly divisible by limit - using Math.floor(user_count / limit) + 1 instead of Math.ceil(user_count / limit) causes an extra phantom page to be calculated, keeping the 'next' button enabled on the real last page and allowing navigation to an empty final page", - "confidence": 0.85, - "reasoning": "Both comments identify the same underlying issue: the pagination logic can result in navigating to an empty final page. The golden comment describes this as capping next offset at user_count producing an empty page when offset equals total. The candidate describes it as a totalPages calculation being off-by-one, causing an extra phantom page and allowing navigation to an empty final page. Both are pointing to the same UX problem - users can navigate to an empty last page due to pagination calculation issues." - } - ], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": " The findMembers() call is now asynchronous and unhandled. The controller may not have member data immediately available, creating a race condition.", - "severity": "High" - }, - { - "golden_comment": "HTTP method mismatch in .remove_member - test uses PUT but remove_member action expects DELETE", - "severity": "Medium" - } - ], - "errors": [], - "total_candidates": 1, - "total_golden": 3, - "tp": 1, - "fp": 0, - "fn": 2, - "errors_count": 0, - "precision": 1.0, - "recall": 0.3333333333333333, - "tool": "devin", - "repo_name": "discourse__discourse-graphite__devin__PR8__20260302", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__devin__PR8__20260302/pull/1" - }, - "mra-max": { - "skipped": false, - "true_positives": [ - { - "golden_comment": " The findMembers() call is now asynchronous and unhandled. The controller may not have member data immediately available, creating a race condition.", - "severity": "High", - "matched_candidate": "In admin_group_route.js, afterModel has been removed and setupController now calls model.findMembers() without awaiting its promise, causing potential race condition", - "confidence": 0.98, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment: the findMembers() call is now asynchronous/unhandled (not awaited), which creates a race condition where member data may not be immediately available. Both comments point to the same bug in the same location (admin_group_route.js/setupController)." - } - ], - "false_positives": [ - { - "candidate": "Tests updated to cover new group membership endpoints and behavior including admin controller specs for index, update, refresh_automatic_groups, add_members, and remove_member flows" - }, - { - "candidate": "Admin-group.js.es6 controller includes client-side pagination helpers and separate usernames input, creating risk of desynchronization between UI state and server data" - }, - { - "candidate": "Group admin UI templates were heavily rewritten, introducing breaking change for UI layer or embedded components that depend on old markup/structure" - }, - { - "candidate": "Group.findMembers returns undefined if group's name is blank and uses /members.json with limit/offset, changing API contract and affecting callers expecting a Promise" - }, - { - "candidate": "Admin-group-selector template removal will result in runtime error or missing UI if Ember app still references this template" - }, - { - "candidate": "Admin groups controller overhauled with changed create/update endpoints and added add_members/remove_member, breaking backward compatibility" - }, - { - "candidate": "GroupsController#posts now paginates group members and returns meta object with total/limit/offset, altering client expectations for response shape" - }, - { - "candidate": "Routes.rb exposes add/remove member actions on /members and adds refresh_automatic_groups on collection, shifting client expectations away from RESTful nested routes" - }, - { - "candidate": "Test suite references :posts action but updated code introduces members endpoint with JSON payload for pagination, causing potential test mismatch" - }, - { - "candidate": "group_member.hbs template gates remove button with '#unless automatic' but automatic property belongs to parent group not member, potentially showing/hiding button incorrectly" - }, - { - "candidate": "Tests reorganized to reflect new endpoints but need to ensure all new edge cases are covered" - }, - { - "candidate": "New CSS scaffolding for Groups UI could affect layout across admin screens" - }, - { - "candidate": "Placeholder for group member selector changed from 'add users' to 'enter username', may cause UI test failures due to string mismatch" - }, - { - "candidate": "New i18n keys for delete_member_confirm and related UI texts introduced, clients relying on older keys may need updates" - } - ], - "false_negatives": [ - { - "golden_comment": "In the next action, capping the next offset at user_count can produce an empty page (e.g., total equal to limit results in offset == total, showing 2/2 with no members). This can cause confusing UX on the last page.", - "severity": "Medium" - }, - { - "golden_comment": "HTTP method mismatch in .remove_member - test uses PUT but remove_member action expects DELETE", - "severity": "Medium" - } - ], - "errors": [], - "total_candidates": 15, - "total_golden": 3, - "tp": 1, - "fp": 14, - "fn": 2, - "errors_count": 0, - "precision": 0.06666666666666667, - "recall": 0.3333333333333333, - "tool": "mra-max", - "repo_name": "discourse__discourse-graphite__mra-max__PR8__20260306", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__mra-max__PR8__20260306/pull/1" - }, - "greptile-v4": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In the next action, capping the next offset at user_count can produce an empty page (e.g., total equal to limit results in offset == total, showing 2/2 with no members). This can cause confusing UX on the last page.", - "severity": "Medium", - "matched_candidate": "Off-by-one error in totalPages calculation: Math.floor(user_count / limit) + 1 overestimates page count when user_count is divisible by limit, allowing navigation to empty results page", - "confidence": 0.95, - "reasoning": "Both comments identify the same underlying issue: when the total count is evenly divisible by the limit, the pagination logic allows navigation to an empty results page. The golden comment describes this as 'capping the next offset at user_count can produce an empty page' while the candidate describes it as 'off-by-one error in totalPages calculation' that 'overestimates page count when user_count is divisible by limit, allowing navigation to empty results page'. Both are pointing to the same pagination bug that results in confusing UX with an empty last page." - }, - { - "golden_comment": "HTTP method mismatch in .remove_member - test uses PUT but remove_member action expects DELETE", - "severity": "Medium", - "matched_candidate": "Spec uses wrong HTTP method for remove_member: test calls xhr :put but the route is DELETE, misrepresenting how the endpoint is actually reached", - "confidence": 1.0, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment: the test uses PUT (xhr :put) but the remove_member action expects/is routed as DELETE. Both comments point to the HTTP method mismatch in the remove_member test." - } - ], - "false_positives": [ - { - "candidate": "Route parameter name mismatch: add_members and remove_member use params.require(:group_id), but Rails member routes expose the parent resource's primary key as params[:id], causing ActionController::ParameterMissing at runtime" - }, - { - "candidate": "alias_level is silently dropped during group creation: the create action only sets name and visible, ignoring the alias_level parameter that the frontend sends" - } - ], - "false_negatives": [ - { - "golden_comment": " The findMembers() call is now asynchronous and unhandled. The controller may not have member data immediately available, creating a race condition.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 4, - "total_golden": 3, - "tp": 2, - "fp": 2, - "fn": 1, - "errors_count": 0, - "precision": 0.5, - "recall": 0.6666666666666666, - "tool": "greptile-v4", - "repo_name": "discourse__discourse-graphite__greptile-v4__PR8__20260306", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__greptile-v4__PR8__20260306/pull/1" - }, - "mra-nano": { - "skipped": false, - "true_positives": [ - { - "golden_comment": " The findMembers() call is now asynchronous and unhandled. The controller may not have member data immediately available, creating a race condition.", - "severity": "High", - "matched_candidate": "Admin route's afterModel hook was removed and setupController now calls model.findMembers() with async member loading - risk of race conditions or incomplete view state if findMembers fails or resolves after render", - "confidence": 0.95, - "reasoning": "The candidate issue identifies the same core problem as the golden comment: the findMembers() call is now asynchronous and there's a risk of race conditions. The candidate specifically mentions 'async member loading - risk of race conditions' and 'incomplete view state if findMembers fails or resolves after render', which directly corresponds to the golden comment's concern about the controller not having member data immediately available and creating a race condition." - } - ], - "false_positives": [ - { - "candidate": "Frontend admin-group.js.es6 controller and group.hbs template were substantially rewritten with new membership representation via 'usernames', pagination helpers, and conditional rendering based on 'automatic' flag - risk of mismatch between controller logic and template rendering" - }, - { - "candidate": "Admin groups controller gained create, update, add_members, remove_member, and refresh_automatic_groups actions with new routes - mismatch between new routes and controller actions risks 404/422 errors and regressions" - }, - { - "candidate": "GroupsController#members now returns JSON structure with members and pagination metadata instead of plain serialized array - gaps between implementation and test expectations could mask integration regressions" - }, - { - "candidate": "Client.en translation changes include updating selector_placeholder and adding group management labels - need to ensure UI messages align with the new UI" - } - ], - "false_negatives": [ - { - "golden_comment": "In the next action, capping the next offset at user_count can produce an empty page (e.g., total equal to limit results in offset == total, showing 2/2 with no members). This can cause confusing UX on the last page.", - "severity": "Medium" - }, - { - "golden_comment": "HTTP method mismatch in .remove_member - test uses PUT but remove_member action expects DELETE", - "severity": "Medium" - } - ], - "errors": [], - "total_candidates": 5, - "total_golden": 3, - "tp": 1, - "fp": 4, - "fn": 2, - "errors_count": 0, - "precision": 0.2, - "recall": 0.3333333333333333, - "tool": "mra-nano", - "repo_name": "discourse__discourse-graphite__mra-nano__PR8__20260306", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__mra-nano__PR8__20260306/pull/1" - }, - "mra-ultra": { - "skipped": false, - "true_positives": [ - { - "golden_comment": " The findMembers() call is now asynchronous and unhandled. The controller may not have member data immediately available, creating a race condition.", - "severity": "High", - "matched_candidate": "Removed afterModel hook and setupController calling model.findMembers() without awaiting leaves controller's 'members' property unset when template renders, breaking group.hbs template rendering", - "confidence": 0.95, - "reasoning": "The candidate issue identifies the same underlying problem as the golden comment. Both are pointing out that findMembers() is called without being awaited/handled, which means the controller won't have member data available when needed. The golden comment describes this as a 'race condition' while the candidate describes it as 'controller's members property unset when template renders' - these are the same issue expressed differently. Both identify the asynchronous nature of findMembers() being unhandled as the core problem." - }, - { - "golden_comment": "HTTP method mismatch in .remove_member - test uses PUT but remove_member action expects DELETE", - "severity": "Medium", - "matched_candidate": "Admin groups controller spec calls remove_member via HTTP PUT while Rails route defines it as DELETE and client AJAX issues DELETE, meaning spec is not testing correct HTTP verb", - "confidence": 0.98, - "reasoning": "The candidate issue identifies exactly the same problem as the golden comment: the test uses HTTP PUT method for remove_member, but the action expects DELETE. The candidate provides additional context about Rails routes and client AJAX, but the core issue is identical - HTTP method mismatch between PUT in the test and DELETE expected by remove_member." - } - ], - "false_positives": [ - { - "candidate": "Pagination totalPages computation uses floor(user_count / limit) + 1 which overcounts when user_count is an exact multiple of limit; should use Math.ceil(user_count / limit)" - }, - { - "candidate": "findMembers returns undefined instead of a promise when group's name is empty, causing silent failures or broken async flows for callers expecting a promise" - }, - { - "candidate": "Admin GroupsController heavily rewritten with altered behavior and endpoints (create, update, destroy, refresh_automatic_groups, add_members, remove_member) representing major API shift affecting authorization, data integrity, and client compatibility" - }, - { - "candidate": "Public Groups controller posts action returns paginated payload with meta section instead of plain list, breaking API change for clients relying on old response shape" - }, - { - "candidate": "Admin route removes prior GET 'users' route for listing group members and replaces with new member endpoints, potentially breaking admin UI or client code relying on GET /admin/groups/:id/users" - } - ], - "false_negatives": [ - { - "golden_comment": "In the next action, capping the next offset at user_count can produce an empty page (e.g., total equal to limit results in offset == total, showing 2/2 with no members). This can cause confusing UX on the last page.", - "severity": "Medium" - } - ], - "errors": [], - "total_candidates": 7, - "total_golden": 3, - "tp": 2, - "fp": 5, - "fn": 1, - "errors_count": 0, - "precision": 0.2857142857142857, - "recall": 0.6666666666666666, - "tool": "mra-ultra", - "repo_name": "discourse__discourse-graphite__mra-ultra__PR8__20260306", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__mra-ultra__PR8__20260306/pull/1" - }, - "mra-b": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "HTTP method mismatch in .remove_member - test uses PUT but remove_member action expects DELETE", - "severity": "Medium", - "matched_candidate": "Admin group membership modification API breaking change - membership changes moved from PUT /admin/groups/:id to new endpoints (PUT /admin/groups/:group_id/members for adds, DELETE /admin/groups/:group_id/members for removes) with different parameter shapes", - "confidence": 0.85, - "reasoning": "The candidate issue discusses the API change from PUT to DELETE for remove operations, mentioning 'DELETE /admin/groups/:group_id/members for removes'. This aligns with the golden comment's concern about HTTP method mismatch where 'test uses PUT but remove_member action expects DELETE'. Both are identifying the same underlying issue - that remove_member operations should use DELETE method, not PUT." - } - ], - "false_positives": [ - { - "candidate": "totalPages calculation is off-by-one when user_count is an exact multiple of limit - using Math.floor(user_count/limit)+1 instead of Math.ceil(user_count/limit) results in one extra page being displayed" - }, - { - "candidate": "findMembers returns undefined instead of a Promise when group name is empty, breaking callers that chain .then() on the result" - }, - { - "candidate": "Public groups#members endpoint response shape changed from array to object with members and meta keys, breaking API consumers expecting an array" - }, - { - "candidate": "HTTP status for attempting to modify automatic groups changed from 403 Forbidden to 422 Unprocessable Entity, affecting clients that branch on HTTP status codes" - } - ], - "false_negatives": [ - { - "golden_comment": " The findMembers() call is now asynchronous and unhandled. The controller may not have member data immediately available, creating a race condition.", - "severity": "High" - }, - { - "golden_comment": "In the next action, capping the next offset at user_count can produce an empty page (e.g., total equal to limit results in offset == total, showing 2/2 with no members). This can cause confusing UX on the last page.", - "severity": "Medium" - } - ], - "errors": [], - "total_candidates": 5, - "total_golden": 3, - "tp": 1, - "fp": 4, - "fn": 2, - "errors_count": 0, - "precision": 0.2, - "recall": 0.3333333333333333, - "tool": "mra-b", - "repo_name": "discourse__discourse-graphite__mra-b__PR8__20260307", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__mra-b__PR8__20260307/pull/1" - }, - "mra-a": { - "skipped": false, - "true_positives": [ - { - "golden_comment": " The findMembers() call is now asynchronous and unhandled. The controller may not have member data immediately available, creating a race condition.", - "severity": "High", - "matched_candidate": "Template iteration changed from model to members but members may not exist in template scope since code sets model and calls model.findMembers() without explicitly setting controller.members, causing empty list rendering", - "confidence": 0.85, - "reasoning": "Both comments identify the same underlying issue: the findMembers() call creates a problem where member data may not be available when needed. The golden comment frames it as an async/race condition issue, while the candidate frames it as a scope/binding issue where members won't exist in the template. Both are pointing to the same root cause - the asynchronous findMembers() call means the controller/template won't have member data immediately available, resulting in missing or empty data rendering." - } - ], - "false_positives": [ - { - "candidate": "totalPages off-by-one when user_count is divisible by limit - Math.floor(user_count/limit)+1 yields incorrect page count (e.g., 2 instead of 1 for 50/50), should use Math.ceil(user_count/limit)" - }, - { - "candidate": "findMembers returns undefined instead of a Promise for empty group name, causing TypeErrors at runtime since callers expect a thenable (regression from prior Ember.RSVP.resolve([]) return)" - }, - { - "candidate": "Group.create no longer accepts initial usernames - createWithUsernames behavior was removed, breaking atomic group creation with initial members in a single request" - }, - { - "candidate": "Admin API create endpoint parameter shape changed from nested params (group[name]) to top-level params (params[:name]), breaking external callers and third-party integrations expecting the previous format" - } - ], - "false_negatives": [ - { - "golden_comment": "In the next action, capping the next offset at user_count can produce an empty page (e.g., total equal to limit results in offset == total, showing 2/2 with no members). This can cause confusing UX on the last page.", - "severity": "Medium" - }, - { - "golden_comment": "HTTP method mismatch in .remove_member - test uses PUT but remove_member action expects DELETE", - "severity": "Medium" - } - ], - "errors": [], - "total_candidates": 5, - "total_golden": 3, - "tp": 1, - "fp": 4, - "fn": 2, - "errors_count": 0, - "precision": 0.2, - "recall": 0.3333333333333333, - "tool": "mra-a", - "repo_name": "discourse__discourse-graphite__mra-a__PR8__20260307", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__mra-a__PR8__20260307/pull/1" - }, - "entelligence": { - "skipped": false, - "true_positives": [ - { - "golden_comment": " The findMembers() call is now asynchronous and unhandled. The controller may not have member data immediately available, creating a race condition.", - "severity": "High", - "matched_candidate": "Removing afterModel and the members assignment means the controller no longer receives the loaded members. model.findMembers() returns a promise but its result is now ignored, so any template/controller expecting members will see undefined.", - "confidence": 0.95, - "reasoning": "The candidate issue identifies the same core problem as the golden comment. Both point out that findMembers() is now asynchronous/returns a promise, and the result is not being handled properly. The golden comment describes this as a 'race condition' where 'controller may not have member data immediately available', while the candidate describes it as 'result is now ignored, so any template/controller expecting members will see undefined'. Both are describing the same underlying issue: the async findMembers() call is unhandled, leading to missing member data in the controller." - }, - { - "golden_comment": "In the next action, capping the next offset at user_count can produce an empty page (e.g., total equal to limit results in offset == total, showing 2/2 with no members). This can cause confusing UX on the last page.", - "severity": "Medium", - "matched_candidate": "Using Math.floor(...)+1 overcounts when user_count is a multiple of limit (e.g., 10/5 => 3 pages), making showingLast false on the real last page and allowing navigation to an empty page. Should use Math.ceil instead.", - "confidence": 0.85, - "reasoning": "Both comments identify the same underlying issue: the pagination logic can lead to navigation to an empty page. The golden comment focuses on how capping next offset at user_count can produce an empty page. The candidate comment identifies that using Math.floor()+1 instead of Math.ceil() causes incorrect page counting when user_count is a multiple of limit, which allows navigation to an empty page. Both are pointing to the same bug - the pagination logic allows users to navigate to an empty final page, causing confusing UX." - } - ], - "false_positives": [ - { - "candidate": "Dropping saveWithUsernames/createWithUsernames means the usernames entered in the UI are no longer sent during save/create, so members won't be added when saving a group." - }, - { - "candidate": "Returning undefined when the group name is empty breaks the previous contract of always returning a promise; callers that chain .then() will now crash. Should return a resolved promise (e.g., empty array) to preserve the async API." - }, - { - "candidate": "Both create and update now read attributes from the top-level params, but the previous update_put expected params[:group]. If clients still send { group: { ... } }, these actions will silently ignore input (e.g., name/visible won't update)." - }, - { - "candidate": "This change paginates all groups with a default limit of 50, whereas previously non-automatic groups returned the full member list. That's a breaking behavioral regression for clients that expect complete results without paging." - } - ], - "false_negatives": [ - { - "golden_comment": "HTTP method mismatch in .remove_member - test uses PUT but remove_member action expects DELETE", - "severity": "Medium" - } - ], - "errors": [], - "total_candidates": 6, - "total_golden": 3, - "tp": 2, - "fp": 4, - "fn": 1, - "errors_count": 0, - "precision": 0.3333333333333333, - "recall": 0.6666666666666666, - "tool": "entelligence", - "repo_name": "discourse__discourse-graphite__entelligence__PR8__20260306", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__entelligence__PR8__20260306/pull/1" - }, - "sourcery": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "Member routes are using `group_id` param, but routes will supply `:id`, which will raise on `params.require`. Using `params.require(:group_id)` in `add_members` will raise `ActionController::ParameterMissing` for valid requests. Use `params.require(:id)` instead to match the routes." - }, - { - "candidate": "`remove_member` has the same `group_id` param issue as `add_members`. `remove_member` should load the group from `params[:id]` rather than `params[:group_id]`; otherwise DELETE `/admin/groups/:id/members` requests will raise a missing parameter error." - }, - { - "candidate": "Pagination `totalPages` calculation is off by one when `user_count` is an exact multiple of `limit`. For example, `user_count = 100` and `limit = 50` yields `Math.floor(100 / 50) + 1 = 3`, but there are only 2 valid pages. Use `Math.ceil(this.get(\"user_count\") / this.get(\"limit\"))` instead." - }, - { - "candidate": "In the new `group-member` view/template, the template references `member` explicitly (`{{avatar member ...}}`), but when used as `itemView=\"group-member\"` inside `{{each member in members}}` the item view's context is the member itself; should reference `this` (or `view.content`) instead of `member` to avoid an undefined binding." - }, - { - "candidate": "The `Group#findMembers` method now returns `undefined` when `name` is empty instead of an immediately-resolved promise; if any callers still assume a promise (e.g. previously chained `.then`), this can break their flow. Consider always returning a promise for API consistency." - } - ], - "false_negatives": [ - { - "golden_comment": " The findMembers() call is now asynchronous and unhandled. The controller may not have member data immediately available, creating a race condition.", - "severity": "High" - }, - { - "golden_comment": "In the next action, capping the next offset at user_count can produce an empty page (e.g., total equal to limit results in offset == total, showing 2/2 with no members). This can cause confusing UX on the last page.", - "severity": "Medium" - }, - { - "golden_comment": "HTTP method mismatch in .remove_member - test uses PUT but remove_member action expects DELETE", - "severity": "Medium" - } - ], - "errors": [], - "total_candidates": 5, - "total_golden": 3, - "tp": 0, - "fp": 5, - "fn": 3, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "sourcery", - "repo_name": "discourse__discourse-graphite__sourcery__PR8__20260308", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__sourcery__PR8__20260308/pull/1" - }, - "cubic-dev": { - "skipped": false, - "true_positives": [ - { - "golden_comment": " The findMembers() call is now asynchronous and unhandled. The controller may not have member data immediately available, creating a race condition.", - "severity": "High", - "matched_candidate": "The `findMembers()` promise is discarded (fire-and-forget), introducing a race condition. The old `afterModel` hook returned the promise so Ember waited for members to load before rendering. Now the template renders immediately with no members, and if the user navigates away before the AJAX completes, the `.then` callback will still set properties on the (now stale) model.", - "confidence": 0.98, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment: the findMembers() call is now asynchronous and unhandled (described as 'fire-and-forget' with 'promise discarded'), creating a race condition where member data may not be immediately available. The candidate provides additional detail about the consequences (template renders without members, stale model issues), but the core issue identified is identical." - }, - { - "golden_comment": "In the next action, capping the next offset at user_count can produce an empty page (e.g., total equal to limit results in offset == total, showing 2/2 with no members). This can cause confusing UX on the last page.", - "severity": "Medium", - "matched_candidate": "The `findMembers()` method caps `offset` at `user_count` (`Math.min(this.get(\"user_count\"), ...)`), which means if the offset equals the total user count, the request will return zero results \u2014 an empty last page. The cap should account for the limit.", - "confidence": 0.98, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment: the capping of offset at user_count in findMembers() can result in an empty last page when offset equals total user count. Both comments point out that this creates a confusing UX situation where the last page shows no members. The candidate even mentions the same solution direction (accounting for the limit)." - }, - { - "golden_comment": "HTTP method mismatch in .remove_member - test uses PUT but remove_member action expects DELETE", - "severity": "Medium", - "matched_candidate": "HTTP method mismatch: this test uses `:put` but the success-case test for the same `remove_member` action uses `:delete`. This test likely hits the wrong route and doesn't actually verify the automatic-group protection on the `remove_member` endpoint.", - "confidence": 0.98, - "reasoning": "The candidate issue identifies the exact same problem as the golden comment - that the test uses PUT method but the remove_member action expects DELETE. The candidate provides additional context about the implications (hitting wrong route, not verifying automatic-group protection), but the core issue identified is identical." - } - ], - "false_positives": [ - { - "candidate": "Wrong interpolation syntax for a client-side locale file. This file uses `{{variable}}` (Handlebars-style) for interpolation, but `%{variable}` (Ruby i18n style) is used here. The variables will render literally in the UI instead of being substituted." - }, - { - "candidate": "`totalPages` has an off-by-one bug; it should use ceiling division instead of `floor(...) + 1`." - }, - { - "candidate": "`group.users.delete(user_id)` passes a raw integer and bypasses `Group#remove`, which runs destroy callbacks that reset `primary_group_id`. Use the model's `remove` method with a User object, consistent with how `add_members` uses `group.add(user)`." - }, - { - "candidate": "Returning `undefined` instead of a resolved promise breaks the method's promise-based contract. The previous code returned `Ember.RSVP.resolve([])`, ensuring callers could safely chain `.then()`. Any future (or current) caller that chains on `findMembers()` will get a TypeError when the name is empty." - }, - { - "candidate": "The remove control uses `
` without `href`, making it inaccessible via keyboard (not focusable). Use a `\")\n \n deactivate Component\n```\n\n\n\n---\n\n## \ud83d\udd17 Cross-Repository Impact Analysis\n\nEnable automatic detection of breaking changes across your dependent repositories. **[\u2192 Set up now](https://app.entelligence.ai/settings?tab=code-management)**\n\n
\nLearn more about Cross-Repository Analysis\n\n### What It Does\n\n- Automatically identifies repositories that depend on this code\n- Analyzes potential breaking changes across your entire codebase \n- Provides risk assessment before merging to prevent cross-repo issues\n\n### How to Enable\n\n1. Visit **[Settings \u2192 Code Management](https://app.entelligence.ai/settings?tab=code-management)**\n2. Configure repository dependencies \n3. Future PRs will automatically include cross-repo impact analysis!\n\n### Benefits\n\n- \ud83d\udee1\ufe0f Prevent breaking changes across repositories\n- \ud83d\udd0d Catch integration issues before they reach production\n- \ud83d\udcca Better visibility into your multi-repo architecture\n\n
\n\n---\n", - "created_at": "2026-03-07T00:08:16Z" - } - ] - }, - { - "tool": "sourcery", - "repo_name": "discourse__discourse-graphite__sourcery__PR2__20260308", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__sourcery__PR2__20260308/pull/1", - "review_comments": [ - { - "path": "app/controllers/topics_controller.rb", - "line": 105, - "body": "**issue:** Handle missing TopicUser record in unsubscribe action to avoid nil access\n\n`TopicUser.find_by` may return `nil` when the user\u2019s first interaction with the topic is via email, causing `tu.notification_level` to raise. Please handle the `nil` case explicitly (e.g., create a `TopicUser` with the correct level or return/redirect safely) so the unsubscribe action doesn\u2019t error for these users.", - "created_at": "2026-03-09T16:51:42Z" - }, - { - "path": "app/models/topic.rb", - "line": 720, - "body": "**issue (bug_risk):** Use an absolute URL for unsubscribe links used in emails\n\n`unsubscribe_url` currently appends `/unsubscribe` to `url`, which is usually relative (e.g. `/t/slug/123`). This is interpolated directly into the email copy via `%{unsubscribe_url}`, so the email will contain a relative link that may not work in many mail clients. Please build an absolute URL here (e.g. using `Discourse.base_url` or an existing helper) so the unsubscribe link is fully qualified.", - "created_at": "2026-03-09T16:51:42Z" - }, - { - "path": "app/assets/javascripts/discourse/routes/topic-unsubscribe.js.es6", - "line": 14, - "body": "**suggestion (bug_risk):** Avoid permanently mutating the topic model when hiding `notificationReasonText`\n\nClearing `details.notificationReasonText` on the shared topic model means that if the user returns to the normal topic view in the same session, the notification reason will still be `null` and never reappear. Instead, either restore the original value on `deactivate`/`willTransition`, or avoid mutating the model and use a route/controller-level flag to hide the text in the template.\n\nSuggested implementation:\n\n```\n },\n\n setupController(controller, topic) {\n this._super(controller, topic);\n controller.set(\"hideNotificationReasonText\", true);\n\n```\n\nTo fully implement the behavior:\n\n1. Update the relevant template(s) that currently rely on `topic.details.notificationReasonText` being cleared. Instead, conditionally hide the notification reason text when `hideNotificationReasonText` is true, e.g.:\n - Wrap the notification reason text block in `{{#unless this.hideNotificationReasonText}} ... {{/unless}}` (for Glimmer) or `{{#unless hideNotificationReasonText}} ... {{/unless}}` (for classic).\n2. Remove any template logic that depends on `details.notificationReasonText` being `null` to infer \"hidden\" state; use the flag instead.", - "created_at": "2026-03-09T16:51:42Z" - }, - { - "path": "app/views/email/notification.html.erb", - "line": 10, - "body": "**issue (bug_risk):** Fix CSS class name that incorrectly includes a dot in the `class` attribute\n\nThe `class` attribute is set to `.previous-discussion`, so the element\u2019s class name includes the dot and won\u2019t match the `.previous-discussion` CSS selector. Change it to `class='previous-discussion'` so the selector applies correctly.", - "created_at": "2026-03-09T16:51:43Z" - }, - { - "path": null, - "line": null, - "body": "Hey - I've found 4 issues, and left some high level feedback:\n\n- In `app/views/email/notification.html.erb` the `%{respond_instructions}` footer is now rendered both inside the `context_posts.present?` block and again after the `
`, which will duplicate the instructions when `context_posts` are present; consider only rendering it once.\n- In `TopicsController#unsubscribe`, `TopicUser.find_by` can return `nil`, so accessing `tu.notification_level` without a guard will raise for users without an existing `topic_users` row; you may want to handle the `nil` case or use `TopicUser.change` to centralize this logic.\n- The `TopicUnsubscribe` client route is only defined for `/t/:slug/:id/unsubscribe`, but the server also exposes `/t/:topic_id/unsubscribe`; if users can hit the slugless URL directly in the browser, consider adding a matching Ember route or always redirecting to the slugged form to avoid inconsistent client behavior.\n\n
\nPrompt for AI Agents\n\n~~~markdown\nPlease address the comments from this code review:\n\n## Overall Comments\n- In `app/views/email/notification.html.erb` the `%{respond_instructions}` footer is now rendered both inside the `context_posts.present?` block and again after the `
`, which will duplicate the instructions when `context_posts` are present; consider only rendering it once.\n- In `TopicsController#unsubscribe`, `TopicUser.find_by` can return `nil`, so accessing `tu.notification_level` without a guard will raise for users without an existing `topic_users` row; you may want to handle the `nil` case or use `TopicUser.change` to centralize this logic.\n- The `TopicUnsubscribe` client route is only defined for `/t/:slug/:id/unsubscribe`, but the server also exposes `/t/:topic_id/unsubscribe`; if users can hit the slugless URL directly in the browser, consider adding a matching Ember route or always redirecting to the slugged form to avoid inconsistent client behavior.\n\n## Individual Comments\n\n### Comment 1\n\n\n raise ex\n end\n\n+ def unsubscribe\n+ @topic_view = TopicView.new(params[:topic_id], current_user)\n+\n+ if slugs_do_not_match || (!request.format.json? && params[:slug].blank?)\n+ return redirect_to @topic_view.topic.unsubscribe_url, status: 301\n+ end\n+\n+ tu = TopicUser.find_by(user_id: current_user.id, topic_id: params[:topic_id])\n+\n+ if tu.notification_level > TopicUser.notification_levels[:regular]\n\n\n**issue:** Handle missing TopicUser record in unsubscribe action to avoid nil access\n\n`TopicUser.find_by` may return `nil` when the user\u2019s first interaction with the topic is via email, causing `tu.notification_level` to raise. Please handle the `nil` case explicitly (e.g., create a `TopicUser` with the correct level or return/redirect safely) so the unsubscribe action doesn\u2019t error for these users.\n\n\n### Comment 2\n\n\n url\n end\n\n+ def unsubscribe_url\n+ \"#{url}/unsubscribe\"\n+ end\n+\n\n\n**issue (bug_risk):** Use an absolute URL for unsubscribe links used in emails\n\n`unsubscribe_url` currently appends `/unsubscribe` to `url`, which is usually relative (e.g. `/t/slug/123`). This is interpolated directly into the email copy via `%{unsubscribe_url}`, so the email will contain a relative link that may not work in many mail clients. Please build an absolute URL here (e.g. using `Discourse.base_url` or an existing helper) so the unsubscribe link is fully qualified.\n\n\n### Comment 3\n\n\n+ });\n+ },\n+\n+ afterModel(topic) {\n+ // hide the notification reason text\n+ topic.set(\"details.notificationReasonText\", null);\n+ },\n+\n\n\n**suggestion (bug_risk):** Avoid permanently mutating the topic model when hiding `notificationReasonText`\n\nClearing `details.notificationReasonText` on the shared topic model means that if the user returns to the normal topic view in the same session, the notification reason will still be `null` and never reappear. Instead, either restore the original value on `deactivate`/`willTransition`, or avoid mutating the model and use a route/controller-level flag to hide the text in the template.\n\nSuggested implementation:\n\n```\n },\n\n setupController(controller, topic) {\n this._super(controller, topic);\n controller.set(\"hideNotificationReasonText\", true);\n\n```\n\nTo fully implement the behavior:\n\n1. Update the relevant template(s) that currently rely on `topic.details.notificationReasonText` being cleared. Instead, conditionally hide the notification reason text when `hideNotificationReasonText` is true, e.g.:\n - Wrap the notification reason text block in `{{#unless this.hideNotificationReasonText}} ... {{/unless}}` (for Glimmer) or `{{#unless hideNotificationReasonText}} ... {{/unless}}` (for classic).\n2. Remove any template logic that depends on `details.notificationReasonText` being `null` to infer \"hidden\" state; use the flag instead.\n\n\n### Comment 4\n\n\n- %{respond_instructions}\n- \n-
\n-

<%= t \"user_notifications.previous_discussion\" %>

\n+ <% if context_posts.present? %>\n+ \n
\n\n**issue (bug_risk):** Fix CSS class name that incorrectly includes a dot in the `class` attribute\n\nThe `class` attribute is set to `.previous-discussion`, so the element\u2019s class name includes the dot and won\u2019t match the `.previous-discussion` CSS selector. Change it to `class='previous-discussion'` so the selector applies correctly.\n\n~~~\n\n
\n\n***\n\n
\nSourcery is free for open source - if you like our reviews please consider sharing them \u2728\n\n- [X](https://twitter.com/intent/tweet?text=I%20just%20got%20an%20instant%20code%20review%20from%20%40SourceryAI%2C%20and%20it%20was%20brilliant%21%20It%27s%20free%20for%20open%20source%20and%20has%20a%20free%20trial%20for%20private%20code.%20Check%20it%20out%20https%3A//sourcery.ai)\n- [Mastodon](https://mastodon.social/share?text=I%20just%20got%20an%20instant%20code%20review%20from%20%40SourceryAI%2C%20and%20it%20was%20brilliant%21%20It%27s%20free%20for%20open%20source%20and%20has%20a%20free%20trial%20for%20private%20code.%20Check%20it%20out%20https%3A//sourcery.ai)\n- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https://sourcery.ai)\n- [Facebook](https://www.facebook.com/sharer/sharer.php?u=https://sourcery.ai)\n\n
\n\n\nHelp me be more useful! Please click \ud83d\udc4d or \ud83d\udc4e on each comment and I'll use the feedback to improve your reviews.\n", - "created_at": "2026-03-09T16:51:42Z" - }, - { - "path": null, - "line": null, - "body": "@sourcery-ai review", - "created_at": "2026-03-09T16:47:41Z" - }, - { - "path": null, - "line": null, - "body": "\n\n## Reviewer's Guide\n\nImplements a per-topic email unsubscribe flow by wiring a new /t/:id/unsubscribe endpoint from emails through Rails, routes, and Ember, rendering a minimal topic page with notification controls, while also performing small refactors and formatting cleanups in existing Ruby, JS, and template code.\n\n#### Sequence diagram for per-topic email unsubscribe flow\n\n```mermaid\nsequenceDiagram\n actor User as user\n participant EmailClient as email_client\n participant Browser as browser\n participant RailsRouter as rails_router\n participant TopicsController as topics_controller\n participant TopicView as topic_view\n participant TopicUser as topic_user\n participant EmberRouter as ember_router\n participant TopicUnsubscribeRoute as topic_unsubscribe_route\n participant TopicUnsubscribeController as topic_unsubscribe_controller\n participant PostStream as post_stream\n participant Topic as topic\n\n User->>EmailClient: open_notification_email\n EmailClient->>User: display_unsubscribe_link\n User->>Browser: click_unsubscribe_link(%{unsubscribe_url})\n\n Browser->>RailsRouter: GET /t/:slug/:topic_id/unsubscribe\n RailsRouter->>TopicsController: unsubscribe(topic_id, slug)\n\n TopicsController->>TopicView: new(topic_id, current_user)\n TopicsController->>TopicsController: slugs_do_not_match?\n alt slugs_do_not_match_or_missing_slug\n TopicsController-->>Browser: 301 redirect_to topic.unsubscribe_url\n else slugs_valid\n TopicsController->>TopicUser: find_by(user_id, topic_id)\n alt notification_level > regular\n TopicsController->>TopicUser: set notification_level = regular\n else notification_level <= regular\n TopicsController->>TopicUser: set notification_level = muted\n end\n TopicsController->>TopicUser: save!\n TopicsController->>TopicsController: perform_show_response\n TopicsController-->>Browser: render_html(show)\n end\n\n Browser->>EmberRouter: load_route(topicUnsubscribe)\n EmberRouter->>TopicUnsubscribeRoute: model(params)\n TopicUnsubscribeRoute->>PostStream: loadTopicView(topic_id)\n PostStream-->>TopicUnsubscribeRoute: topic_json\n TopicUnsubscribeRoute->>Topic: updateFromJson(json)\n TopicUnsubscribeRoute-->>EmberRouter: topic_model\n EmberRouter->>TopicUnsubscribeRoute: afterModel(topic)\n TopicUnsubscribeRoute->>Topic: set(details.notificationReasonText = null)\n EmberRouter->>TopicUnsubscribeController: setupController(model)\n TopicUnsubscribeController-->>Browser: render_unsubscribe_template\n Browser-->>User: show_stop_notifications_message_and_topic_notifications_button\n```\n\n#### Class diagram for unsubscribe-related backend and frontend types\n\n```mermaid\nclassDiagram\n class TopicsController {\n +show()\n +unsubscribe()\n +perform_show_response()\n }\n\n class Topic {\n +id: integer\n +slug: string\n +url(): string\n +unsubscribe_url(): string\n }\n\n class TopicUser {\n +user_id: integer\n +topic_id: integer\n +notification_level: integer\n +change(user_id, topic_id, attrs)\n +track_visit!(topic, user)\n +update_last_read(user, topic_id, post_number, msecs, opts)\n }\n\n class UserNotifications {\n +send_notification_email(opts)\n }\n\n class MessageBuilder {\n -to\n -opts\n -template_args\n +initialize(to, opts)\n +html_part()\n }\n\n class TopicUnsubscribeRoute {\n +model(params)\n +afterModel(topic)\n }\n\n class TopicUnsubscribeController {\n +stopNotificiationsText(): string\n }\n\n class TopicUnsubscribeView {\n +classNames: array\n }\n\n class ClientTopicModel {\n +id: integer\n +fancyTitle: string\n +details\n +updateFromJson(json)\n }\n\n class PostStream {\n +loadTopicView(topic_id)\n +refresh(params)\n +closestPostForPostNumber(post_number)\n +progressIndexOfPost(post)\n }\n\n class RouterConfig {\n +route(topicUnsubscribe)\n }\n\n UserNotifications --> MessageBuilder : uses\n MessageBuilder --> Topic : uses\n MessageBuilder --> TopicUser : uses_notification_levels\n\n TopicsController --> Topic : uses\n TopicsController --> TopicUser : modifies_notification_level\n\n TopicUnsubscribeRoute --> ClientTopicModel : updates\n TopicUnsubscribeRoute --> PostStream : calls\n TopicUnsubscribeController --> ClientTopicModel : reads_title\n TopicUnsubscribeView --> TopicUnsubscribeController : renders\n RouterConfig --> TopicUnsubscribeRoute : maps_route\n```\n\n### File-Level Changes\n\n| Change | Details | Files |\n| ------ | ------- | ----- |\n| Add server-side endpoint and routing to handle per-topic unsubscribe actions and adjust topic rendering for that path. |
  • Introduce TopicsController#unsubscribe to load TopicView, normalize slug, downgrade or mute notification_level for the current user, and reuse perform_show_response for rendering
  • Allow :unsubscribe through the guardian callbacks and skip_xhr filter, and ensure HTML show responses explicitly render :show
  • Expose Topic#unsubscribe_url helper returning the topic URL with /unsubscribe appended
  • Add new routes for /t/:slug/:topic_id/unsubscribe and /t/:topic_id/unsubscribe, mirroring the existing topic routes and constraints, and tidy summary route constraints
| `app/controllers/topics_controller.rb`
`app/models/topic.rb`
`config/routes.rb` |\n| Wire per-topic unsubscribe links into notification emails and email templates, including localized copy and tests. |
  • Extend Email::MessageBuilder template_args to accept unsubscribe_url and use cleaner Ruby style in initializer
  • Ensure html_part replaces %{respond_instructions} and %{unsubscribe_link} placeholders only when add_unsubscribe_link is enabled, and slightly tidy gsub spacing
  • Update UserNotifications#send_notification_email to pass post.topic.unsubscribe_url and simplify from_alias assignment
  • Expand the unsubscribe_link I18n string to include both profile-level and per-topic unsubscribe instructions with %{unsubscribe_url}
  • Adjust message_builder_spec to provide an unsubscribe_url when building a message with add_unsubscribe_link
| `lib/email/message_builder.rb`
`app/mailers/user_notifications.rb`
`config/locales/server.en.yml`
`spec/components/email/message_builder_spec.rb` |\n| Add a client-side unsubscribe flow that shows a minimal topic page with confirmation text and notification controls, including styles and routing. |
  • Register a new Ember route topicUnsubscribe mapped to /t/:slug/:id/unsubscribe in app-route-map
  • Implement topic-unsubscribe route that loads the TopicView via PostStream.loadTopicView, updates a lightweight topic record, hides notificationReasonText, and ensures the footer is shown
  • Add a topic-unsubscribe controller that exposes stopNotificiationsText computed from the topic title using new I18n keys
  • Create an unsubscribe template that confirms notifications will stop and shows the topic-notifications-button to change notification state
  • Add a view with .topic-unsubscribe class and styles to make the notification dropdown usable (inline-block, no float on toggle, reset dropdown-menu bottom)
  • Provide client-side I18n strings for the unsubscribe confirmation and hint text
| `app/assets/javascripts/discourse/routes/app-route-map.js.es6`
`app/assets/javascripts/discourse/routes/topic-unsubscribe.js.es6`
`app/assets/javascripts/discourse/controllers/topic-unsubscribe.js.es6`
`app/assets/javascripts/discourse/templates/topic/unsubscribe.hbs`
`app/assets/javascripts/discourse/views/topic-unsubscribe.js.es6`
`app/assets/stylesheets/common/base/topic.scss`
`config/locales/client.en.yml` |\n| Update the email notification HTML template to show unsubscribe content appropriately and clean up formatting. |
  • Indent and modernize the notification.html.erb template (new hash syntax, spacing, structure)
  • Render context posts and their footer only when present, then always render a footer with respond_instructions and unsubscribe_link at the bottom of the email
  • Minor whitespace and indentation fixes in the schema.org EmailMessage markup
| `app/views/email/notification.html.erb` |\n| Apply small style and idiomatic code cleanups in various Ruby and Ember files. |
  • Modernize topic-from-params route JS with ES6 syntax (const, property shorthand) and minor formatting fixes around promises and scheduling
  • Tidy TopicUser model by simplifying create_lookup, using keyword-style hashes, and condensing MessageBus.publish calls and where/update_all argument style
  • Guard dropdown-button title rendering so no empty

    is emitted when title is falsy

  • Fix minor formatting and indentation issues in tracking scope and other methods
| `app/assets/javascripts/discourse/routes/topic-from-params.js.es6`
`app/models/topic_user.rb`
`app/assets/javascripts/discourse/components/dropdown-button.js.es6` |\n\n---\n\n
\nTips and commands\n\n#### Interacting with Sourcery\n\n- **Trigger a new review:** Comment `@sourcery-ai review` on the pull request.\n- **Continue discussions:** Reply directly to Sourcery's review comments.\n- **Generate a GitHub issue from a review comment:** Ask Sourcery to create an\n issue from a review comment by replying to it. You can also reply to a\n review comment with `@sourcery-ai issue` to create an issue from it.\n- **Generate a pull request title:** Write `@sourcery-ai` anywhere in the pull\n request title to generate a title at any time. You can also comment\n `@sourcery-ai title` on the pull request to (re-)generate the title at any time.\n- **Generate a pull request summary:** Write `@sourcery-ai summary` anywhere in\n the pull request body to generate a PR summary at any time exactly where you\n want it. You can also comment `@sourcery-ai summary` on the pull request to\n (re-)generate the summary at any time.\n- **Generate reviewer's guide:** Comment `@sourcery-ai guide` on the pull\n request to (re-)generate the reviewer's guide at any time.\n- **Resolve all Sourcery comments:** Comment `@sourcery-ai resolve` on the\n pull request to resolve all Sourcery comments. Useful if you've already\n addressed all the comments and don't want to see them anymore.\n- **Dismiss all Sourcery reviews:** Comment `@sourcery-ai dismiss` on the pull\n request to dismiss all existing Sourcery reviews. Especially useful if you\n want to start fresh with a new review - don't forget to comment\n `@sourcery-ai review` to trigger a new review!\n\n#### Customizing Your Experience\n\nAccess your [dashboard](https://app.sourcery.ai) to:\n- Enable or disable review features such as the Sourcery-generated pull request\n summary, the reviewer's guide, and others.\n- Change the review language.\n- Add, remove or edit custom review instructions.\n- Adjust other review settings.\n\n#### Getting Help\n\n- [Contact our support team](mailto:support@sourcery.ai) for questions or feedback.\n- Visit our [documentation](https://docs.sourcery.ai) for detailed guides and information.\n- Keep in touch with the Sourcery team by following us on [X/Twitter](https://x.com/SourceryAI), [LinkedIn](https://www.linkedin.com/company/sourcery-ai/) or [GitHub](https://github.com/sourcery-ai).\n\n
\n\n", - "created_at": "2026-03-09T16:51:41Z" - } - ] - }, - { - "tool": "cubic-dev", - "repo_name": "discourse__discourse-graphite__cubic-dev__PR2__20260306", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__cubic-dev__PR2__20260306/pull/1", - "review_comments": [ - { - "path": "app/controllers/topics_controller.rb", - "line": 105, - "body": "\nP1: `TopicUser.find_by` can return `nil` if the user has no record for this topic, causing a `NoMethodError` on the next line. Add a nil guard or use `find_or_create_by` to handle the case where no `TopicUser` record exists.\n\n
\nPrompt for AI agents\n\n```text\nCheck if this issue is valid \u2014 if so, understand the root cause and fix it. At app/controllers/topics_controller.rb, line 105:\n\n`TopicUser.find_by` can return `nil` if the user has no record for this topic, causing a `NoMethodError` on the next line. Add a nil guard or use `find_or_create_by` to handle the case where no `TopicUser` record exists.\n\n\n@@ -94,6 +95,26 @@ def show\n+ return redirect_to @topic_view.topic.unsubscribe_url, status: 301\n+ end\n+\n+ tu = TopicUser.find_by(user_id: current_user.id, topic_id: params[:topic_id])\n+\n+ if tu.notification_level > TopicUser.notification_levels[:regular]\n\n```\n\n
\n\n```suggestion\n tu = TopicUser.find_by(user_id: current_user.id, topic_id: params[:topic_id])\n\n if tu.nil?\n TopicUser.change(current_user.id, params[:topic_id], notification_level: TopicUser.notification_levels[:muted])\n return perform_show_response\n end\n```\n\n\n \n \n \n \"Fix\n \n", - "created_at": "2026-03-08T04:13:06Z" - }, - { - "path": "config/routes.rb", - "line": 440, - "body": "\nP1: The unsubscribe route uses GET but the controller action mutates state (`tu.save!`). GET requests must be idempotent per HTTP semantics. Since these links will appear in emails, email security scanners, link prefetchers, and anti-spam crawlers commonly follow GET links automatically \u2014 this would silently unsubscribe users without their intent. The GET route should render a confirmation page, and the actual state change should happen via a POST/PUT.\n\n
\nPrompt for AI agents\n\n```text\nCheck if this issue is valid \u2014 if so, understand the root cause and fix it. At config/routes.rb, line 440:\n\nThe unsubscribe route uses GET but the controller action mutates state (`tu.save!`). GET requests must be idempotent per HTTP semantics. Since these links will appear in emails, email security scanners, link prefetchers, and anti-spam crawlers commonly follow GET links automatically \u2014 this would silently unsubscribe users without their intent. The GET route should render a confirmation page, and the actual state change should happen via a POST/PUT.\n\n\n@@ -434,10 +434,12 @@\n- get \"t/:topic_id/summary\" => \"topics#show\", constraints: {topic_id: /\\d+/, post_number: /\\d+/}\n+ get \"t/:slug/:topic_id/moderator-liked\" => \"topics#moderator_liked\", constraints: {topic_id: /\\d+/}\n+ get \"t/:slug/:topic_id/summary\" => \"topics#show\", defaults: {summary: true}, constraints: {topic_id: /\\d+/}\n+ get \"t/:slug/:topic_id/unsubscribe\" => \"topics#unsubscribe\", constraints: {topic_id: /\\d+/}\n+ get \"t/:topic_id/unsubscribe\" => \"topics#unsubscribe\", constraints: {topic_id: /\\d+/}\n+ get \"t/:topic_id/summary\" => \"topics#show\", constraints: {topic_id: /\\d+/}\n\n```\n\n
\n\n\n \n \n \n \"Fix\n \n", - "created_at": "2026-03-08T04:13:06Z" - }, - { - "path": "app/models/topic_user.rb", - "line": 125, - "body": "\nP1: Bug: `user_id` is assigned `topic` instead of `user` in the fallback branch two lines above. When `track_visit!` is called with raw integer IDs (as it is from `TopicsController`), `user_id` receives the topic ID, so this query updates the wrong user's visit record. The fix is on the `user_id = \u2026` line: change `\u2026 : topic` to `\u2026 : user`.\n\n
\nPrompt for AI agents\n\n```text\nCheck if this issue is valid \u2014 if so, understand the root cause and fix it. At app/models/topic_user.rb, line 125:\n\nBug: `user_id` is assigned `topic` instead of `user` in the fallback branch two lines above. When `track_visit!` is called with raw integer IDs (as it is from `TopicsController`), `user_id` receives the topic ID, so this query updates the wrong user's visit record. The fix is on the `user_id = \u2026` line: change `\u2026 : topic` to `\u2026 : user`.\n\n\n@@ -127,7 +122,7 @@ def track_visit!(topic,user)\n \n now = DateTime.now\n- rows = TopicUser.where({topic_id: topic_id, user_id: user_id}).update_all({last_visited_at: now})\n+ rows = TopicUser.where(topic_id: topic_id, user_id: user_id).update_all(last_visited_at: now)\n if rows == 0\n TopicUser.create(topic_id: topic_id, user_id: user_id, last_visited_at: now, first_visited_at: now)\n\n```\n\n
\n\n\n \n \n \n \"Fix\n \n", - "created_at": "2026-03-08T04:13:06Z" - }, - { - "path": "app/assets/javascripts/discourse/components/dropdown-button.js.es6", - "line": 29, - "body": "\nP1: The `title` is concatenated directly into HTML without escaping, which is an XSS risk. Use `Handlebars.Utils.escapeExpression(title)` to sanitize the value before inserting it into the buffer.\n\n
\nPrompt for AI agents\n\n```text\nCheck if this issue is valid \u2014 if so, understand the root cause and fix it. At app/assets/javascripts/discourse/components/dropdown-button.js.es6, line 29:\n\nThe `title` is concatenated directly into HTML without escaping, which is an XSS risk. Use `Handlebars.Utils.escapeExpression(title)` to sanitize the value before inserting it into the buffer.\n\n\n@@ -24,8 +24,11 @@ export default Ember.Component.extend(StringBuffer, {\n renderString(buffer) {\n+ const title = this.get('title');\n+ if (title) {\n+ buffer.push(\"

\" + title + \"

\");\n+ }\n \n
\n```\n\n
\n\n```suggestion\n buffer.push(\"

\" + Handlebars.Utils.escapeExpression(title) + \"

\");\n```\n\n\n \n \n \n \"Fix\n \n", - "created_at": "2026-03-08T04:13:06Z" - }, - { - "path": "app/views/email/notification.html.erb", - "line": 10, - "body": "\nP2: The HTML class value `.previous-discussion` includes a leading dot, which is CSS selector syntax and should not appear in an HTML `class` attribute. This prevents CSS rules targeting `.previous-discussion` from matching this element.\n\n
\nPrompt for AI agents\n\n```text\nCheck if this issue is valid \u2014 if so, understand the root cause and fix it. At app/views/email/notification.html.erb, line 10:\n\nThe HTML class value `.previous-discussion` includes a leading dot, which is CSS selector syntax and should not appear in an HTML `class` attribute. This prevents CSS rules targeting `.previous-discussion` from matching this element.\n\n\n@@ -1,31 +1,29 @@\n \n- <% context_posts.each do |p| %>\n- <%= render :partial => 'email/post', :locals => {:post => p} %>\n+

<%= t \"user_notifications.previous_discussion\" %>

\n+\n+ <% context_posts.each do |p| %>\n
\n```\n\n
\n\n```suggestion\n

<%= t \"user_notifications.previous_discussion\" %>

\n```\n\n\n \n \n \n \"Fix\n \n", - "created_at": "2026-03-08T04:13:06Z" - }, - { - "path": "app/assets/stylesheets/common/base/topic.scss", - "line": 74, - "body": "\nP2: `bottom: initial` is not supported in IE. Use `bottom: auto` instead \u2014 it's equivalent (since `auto` is the initial value of `bottom`) and has universal browser support.\n\n
\nPrompt for AI agents\n\n```text\nCheck if this issue is valid \u2014 if so, understand the root cause and fix it. At app/assets/stylesheets/common/base/topic.scss, line 74:\n\n`bottom: initial` is not supported in IE. Use `bottom: auto` instead \u2014 it's equivalent (since `auto` is the initial value of `bottom`) and has universal browser support.\n\n\n@@ -63,3 +63,15 @@\n+ float: none;\n+ }\n+ .dropdown-menu {\n+ bottom: initial;\n+ }\n+ }\n\n```\n\n
\n\n```suggestion\n bottom: auto;\n```\n\n\n \n \n \n \"Fix\n \n", - "created_at": "2026-03-08T04:13:06Z" - }, - { - "path": "app/assets/javascripts/discourse/controllers/topic-unsubscribe.js.es6", - "line": 5, - "body": "\nP2: Typo in property name: `stopNotificiationsText` should be `stopNotificationsText` (extra 'i' in \"Notificiations\"). The matching template also has the same misspelling, so both should be corrected together.\n\n
\nPrompt for AI agents\n\n```text\nCheck if this issue is valid \u2014 if so, understand the root cause and fix it. At app/assets/javascripts/discourse/controllers/topic-unsubscribe.js.es6, line 5:\n\nTypo in property name: `stopNotificiationsText` should be `stopNotificationsText` (extra 'i' in \"Notificiations\"). The matching template also has the same misspelling, so both should be corrected together.\n\n\n@@ -0,0 +1,9 @@\n+\n+export default ObjectController.extend({\n+\n+ stopNotificiationsText: function() {\n+ return I18n.t(\"topic.unsubscribe.stop_notifications\", { title: this.get(\"model.fancyTitle\") });\n+ }.property(\"model.fancyTitle\"),\n\n```\n\n
\n\n```suggestion\n stopNotificationsText: function() {\n```\n\n\n \n \n \n \"Fix\n \n", - "created_at": "2026-03-08T04:13:06Z" - }, - { - "path": "config/locales/server.en.yml", - "line": 1855, - "body": "\nP2: The shared `unsubscribe_link` locale now requires `%{unsubscribe_url}`, which is not part of the default `template_args` in `MessageBuilder`. Unlike `user_preferences_url` (always initialized), `unsubscribe_url` is only provided by the topic-notification caller. If `add_unsubscribe_link` is ever set to true without passing `unsubscribe_url`, this will raise `I18n::MissingInterpolationArgument`. Consider either adding a default `unsubscribe_url` in `MessageBuilder#initialize` or splitting the topic-specific unsubscribe text into a separate locale key.\n\n
\nPrompt for AI agents\n\n```text\nCheck if this issue is valid \u2014 if so, understand the root cause and fix it. At config/locales/server.en.yml, line 1855:\n\nThe shared `unsubscribe_link` locale now requires `%{unsubscribe_url}`, which is not part of the default `template_args` in `MessageBuilder`. Unlike `user_preferences_url` (always initialized), `unsubscribe_url` is only provided by the topic-notification caller. If `add_unsubscribe_link` is ever set to true without passing `unsubscribe_url`, this will raise `I18n::MissingInterpolationArgument`. Consider either adding a default `unsubscribe_url` in `MessageBuilder#initialize` or splitting the topic-specific unsubscribe text into a separate locale key.\n\n\n@@ -1849,7 +1849,10 @@ en:\n+ unsubscribe_link: |\n+ To unsubscribe from these emails, visit your [user preferences](%{user_preferences_url}).\n+\n+ To stop receiving notifications about this particular topic, [click here](%{unsubscribe_url}).\n \n subject_re: \"Re: \"\n\n```\n\n
\n\n\n \n \n \n \"Fix\n \n", - "created_at": "2026-03-08T04:13:07Z" - }, - { - "path": "app/assets/javascripts/discourse/routes/topic-from-params.js.es6", - "line": 12, - "body": "\nP3: Use an arrow function instead of `const self = this`. The rest of this diff already adopts ES6 features (shorthand methods, `const`); the `self` capture pattern is the one leftover that arrow functions make unnecessary.\n\n
\nPrompt for AI agents\n\n```text\nCheck if this issue is valid \u2014 if so, understand the root cause and fix it. At app/assets/javascripts/discourse/routes/topic-from-params.js.es6, line 11:\n\nUse an arrow function instead of `const self = this`. The rest of this diff already adopts ES6 features (shorthand methods, `const`); the `self` capture pattern is the one leftover that arrow functions make unnecessary.\n\n\n@@ -2,32 +2,32 @@\n- var topicController = this.controllerFor('topic'),\n- topicProgressController = this.controllerFor('topic-progress'),\n- composerController = this.controllerFor('composer');\n+ const self = this,\n+ topic = this.modelFor('topic'),\n+ postStream = topic.get('postStream'),\n\n```\n\n
\n\n```suggestion\n const topic = this.modelFor('topic'),\n```\n\n\n \n \n \n \"Fix\n \n", - "created_at": "2026-03-08T04:13:07Z" - }, - { - "path": "config/locales/client.en.yml", - "line": 986, - "body": "\nP3: Inconsistent trailing punctuation: `stop_notifications` ends with a period but `change_notification_state` does not. Since both strings are in the same `unsubscribe` block and likely displayed together, the missing period is a visible inconsistency.\n\n
\nPrompt for AI agents\n\n```text\nCheck if this issue is valid \u2014 if so, understand the root cause and fix it. At config/locales/client.en.yml, line 986:\n\nInconsistent trailing punctuation: `stop_notifications` ends with a period but `change_notification_state` does not. Since both strings are in the same `unsubscribe` block and likely displayed together, the missing period is a visible inconsistency.\n\n\n@@ -981,6 +981,9 @@ en:\n topic:\n+ unsubscribe:\n+ stop_notifications: \"You will stop receiving notifications for {{title}}.\"\n+ change_notification_state: \"You can change your notification state\"\n filter_to: \"{{post_count}} posts in topic\"\n create: 'New Topic'\n\n```\n\n
\n\n```suggestion\n change_notification_state: \"You can change your notification state.\"\n```\n\n\n \n \n \n \"Fix\n \n", - "created_at": "2026-03-08T04:13:07Z" - }, - { - "path": null, - "line": null, - "body": "**10 issues found** across 18 files\n\n
\nPrompt for AI agents (unresolved issues)\n\n```text\n\nCheck if these issues are valid \u2014 if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.\n\n\n\n\n\nP2: The HTML class value `.previous-discussion` includes a leading dot, which is CSS selector syntax and should not appear in an HTML `class` attribute. This prevents CSS rules targeting `.previous-discussion` from matching this element.\n\n\n\n\n\nP2: `bottom: initial` is not supported in IE. Use `bottom: auto` instead \u2014 it's equivalent (since `auto` is the initial value of `bottom`) and has universal browser support.\n\n\n\n\n\nP3: Use an arrow function instead of `const self = this`. The rest of this diff already adopts ES6 features (shorthand methods, `const`); the `self` capture pattern is the one leftover that arrow functions make unnecessary.\n\n\n\n\n\nP2: Typo in property name: `stopNotificiationsText` should be `stopNotificationsText` (extra 'i' in \"Notificiations\"). The matching template also has the same misspelling, so both should be corrected together.\n\n\n\n\n\nP1: `TopicUser.find_by` can return `nil` if the user has no record for this topic, causing a `NoMethodError` on the next line. Add a nil guard or use `find_or_create_by` to handle the case where no `TopicUser` record exists.\n\n\n\n\n\nP1: The unsubscribe route uses GET but the controller action mutates state (`tu.save!`). GET requests must be idempotent per HTTP semantics. Since these links will appear in emails, email security scanners, link prefetchers, and anti-spam crawlers commonly follow GET links automatically \u2014 this would silently unsubscribe users without their intent. The GET route should render a confirmation page, and the actual state change should happen via a POST/PUT.\n\n\n\n\n\nP1: Bug: `user_id` is assigned `topic` instead of `user` in the fallback branch two lines above. When `track_visit!` is called with raw integer IDs (as it is from `TopicsController`), `user_id` receives the topic ID, so this query updates the wrong user's visit record. The fix is on the `user_id = \u2026` line: change `\u2026 : topic` to `\u2026 : user`.\n\n\n\n\n\nP3: Inconsistent trailing punctuation: `stop_notifications` ends with a period but `change_notification_state` does not. Since both strings are in the same `unsubscribe` block and likely displayed together, the missing period is a visible inconsistency.\n\n\n\n\n\nP1: The `title` is concatenated directly into HTML without escaping, which is an XSS risk. Use `Handlebars.Utils.escapeExpression(title)` to sanitize the value before inserting it into the buffer.\n\n\n\n\n\nP2: The shared `unsubscribe_link` locale now requires `%{unsubscribe_url}`, which is not part of the default `template_args` in `MessageBuilder`. Unlike `user_preferences_url` (always initialized), `unsubscribe_url` is only provided by the topic-notification caller. If `add_unsubscribe_link` is ever set to true without passing `unsubscribe_url`, this will raise `I18n::MissingInterpolationArgument`. Consider either adding a default `unsubscribe_url` in `MessageBuilder#initialize` or splitting the topic-specific unsubscribe text into a separate locale key.\n\n```\n\n
\n\nReply with feedback, questions, or to request a fix. Tag `@cubic-dev-ai` to re-run a review.\n\n", - "created_at": "2026-03-08T04:13:06Z" - }, - { - "path": null, - "line": null, - "body": "@cubic-dev-ai", "created_at": "2026-03-08T04:06:49Z" - }, - { - "path": null, - "line": null, - "body": "> @cubic-dev-ai\n\n@ashleyzhang01 I have started the AI code review. It will take a few minutes to complete.", - "created_at": "2026-03-08T04:07:09Z" } ] }, { "tool": "mesa", - "repo_name": "discourse__discourse-graphite__mesa__PR2__20260307", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__mesa__PR2__20260307/pull/1", + "repo_name": "grafana__grafana__mesa__PR76186__20260306", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__mesa__PR76186__20260306/pull/1", "review_comments": [ { - "path": "app/controllers/topics_controller.rb", - "line": 105, - "body": "![High](https://mesa-production-uploadbucketbucket-hzumnccc.s3.us-east-1.amazonaws.com/commentLabels/high-severity.svg)\n\nCritical nil safety issue: `find_by` can return nil if no TopicUser record exists for this user/topic combination. Line 107 will then raise NoMethodError when trying to access `tu.notification_level` on nil. Add a nil check: `tu = TopicUser.find_by(...) or TopicUser.create(...)` or return early with an error if tu is nil.\n\n\"Fix \u2022 \"Fix\n\n
\nPrompt for Agent\n\n```\nTask: Address review feedback left on GitHub.\nRepository: code-review-benchmark/discourse__discourse-graphite__mesa__PR2__20260307#1\nFile: app/controllers/topics_controller.rb#L105\nAction: Open this file location in your editor, inspect the highlighted code, and resolve the issue described below.\n\nFeedback:\nCritical nil safety issue: `find_by` can return nil if no TopicUser record exists for this user/topic combination. Line 107 will then raise NoMethodError when trying to access `tu.notification_level` on nil. Add a nil check: `tu = TopicUser.find_by(...) or TopicUser.create(...)` or return early with an error if tu is nil.\n```\n\n
", - "created_at": "2026-03-08T08:45:22Z" - }, - { - "path": "app/controllers/topics_controller.rb", - "line": 98, - "body": "![High](https://mesa-production-uploadbucketbucket-hzumnccc.s3.us-east-1.amazonaws.com/commentLabels/high-severity.svg)\n\nSecurity concern: This unsubscribe action relies on `current_user` from the active session without any signed token verification. This means if a user clicks the email link while logged in as a different user (or on a shared device), it will modify the wrong user's notification preferences. Email unsubscribe links should include a signed token parameter to verify the recipient's identity, independent of the active session. Consider implementing similar to how other email actions are secured in Discourse.\n\n\"Fix \u2022 \"Fix\n\n
\nPrompt for Agent\n\n```\nTask: Address review feedback left on GitHub.\nRepository: code-review-benchmark/discourse__discourse-graphite__mesa__PR2__20260307#1\nFile: app/controllers/topics_controller.rb#L98\nAction: Open this file location in your editor, inspect the highlighted code, and resolve the issue described below.\n\nFeedback:\nSecurity concern: This unsubscribe action relies on `current_user` from the active session without any signed token verification. This means if a user clicks the email link while logged in as a different user (or on a shared device), it will modify the wrong user's notification preferences. Email unsubscribe links should include a signed token parameter to verify the recipient's identity, independent of the active session. Consider implementing similar to how other email actions are secured in Discourse.\n```\n\n
", - "created_at": "2026-03-08T08:45:24Z" + "path": "pkg/plugins/log/logger.go", + "line": 49, + "body": "![Medium](https://mesa-production-uploadbucketbucket-hzumnccc.s3.us-east-1.amazonaws.com/commentLabels/medium-severity-v2.svg)\n\nType assertion to concrete `*log.ConcreteLogger` creates tight coupling to infrastructure internals. If the underlying logger implementation changes, this will silently fall back to `d.New()` (line 51), dropping all contextual attributes without error visibility. Consider returning the interface result directly, or introduce a stable adapter in `pkg/infra/log` to avoid concrete type dependencies.\n\n\"Fix \u2022 \"Fix\n\n
\nPrompt for Agent\n\n```\nTask: Address review feedback left on GitHub.\nRepository: code-review-benchmark/grafana__grafana__mesa__PR76186__20260306#1\nFile: pkg/plugins/log/logger.go#L49\nAction: Open this file location in your editor, inspect the highlighted code, and resolve the issue described below.\n\nFeedback:\nType assertion to concrete `*log.ConcreteLogger` creates tight coupling to infrastructure internals. If the underlying logger implementation changes, this will silently fall back to `d.New()` (line 51), dropping all contextual attributes without error visibility. Consider returning the interface result directly, or introduce a stable adapter in `pkg/infra/log` to avoid concrete type dependencies.\n```\n\n
", + "created_at": "2026-03-08T08:55:43Z" }, { - "path": "app/controllers/topics_controller.rb", - "line": 107, - "body": "![Medium](https://mesa-production-uploadbucketbucket-hzumnccc.s3.us-east-1.amazonaws.com/commentLabels/medium-severity-v2.svg)\n\nHTTP semantics violation: This action mutates state (changing notification_level) on a GET request, which violates HTTP idempotency principles. This can cause issues with browser prefetching, email client link scanning, and proxy caching. Consider either: (1) showing a confirmation page on GET and using POST/PUT for the actual state change, or (2) making this action truly idempotent by only performing the toggle on an explicit user action rather than on page load.\n\n\"Fix \u2022 \"Fix\n\n
\nPrompt for Agent\n\n```\nTask: Address review feedback left on GitHub.\nRepository: code-review-benchmark/discourse__discourse-graphite__mesa__PR2__20260307#1\nFile: app/controllers/topics_controller.rb#L107\nAction: Open this file location in your editor, inspect the highlighted code, and resolve the issue described below.\n\nFeedback:\nHTTP semantics violation: This action mutates state (changing notification_level) on a GET request, which violates HTTP idempotency principles. This can cause issues with browser prefetching, email client link scanning, and proxy caching. Consider either: (1) showing a confirmation page on GET and using POST/PUT for the actual state change, or (2) making this action truly idempotent by only performing the toggle on an explicit user action rather than on page load.\n```\n\n
", - "created_at": "2026-03-08T08:45:24Z" + "path": "pkg/plugins/log/fake.go", + "line": 47, + "body": "![Medium](https://mesa-production-uploadbucketbucket-hzumnccc.s3.us-east-1.amazonaws.com/commentLabels/medium-severity-v2.svg)\n\n`FromContext` returns a new `TestLogger` instance instead of the receiver (`f`). This breaks test observability\u2014tests using `FromContext` won't be able to verify logged calls/messages through the original TestLogger's captured state. Consider returning `f` (or a shallow clone sharing the same log sinks) to maintain instance continuity and test fidelity.\n\n\"Fix \u2022 \"Fix\n\n
\nPrompt for Agent\n\n```\nTask: Address review feedback left on GitHub.\nRepository: code-review-benchmark/grafana__grafana__mesa__PR76186__20260306#1\nFile: pkg/plugins/log/fake.go#L47\nAction: Open this file location in your editor, inspect the highlighted code, and resolve the issue described below.\n\nFeedback:\n`FromContext` returns a new `TestLogger` instance instead of the receiver (`f`). This breaks test observability\u2014tests using `FromContext` won't be able to verify logged calls/messages through the original TestLogger's captured state. Consider returning `f` (or a shallow clone sharing the same log sinks) to maintain instance continuity and test fidelity.\n```\n\n
", + "created_at": "2026-03-08T08:55:43Z" }, { "path": null, "line": null, - "body": "Performed full review of **56abd24...6669a2d**\n### Analysis\n\u2022 Security authorization model relies solely on active user session without signed token verification, creating vulnerability where email links opened by different logged-in users will modify the wrong user's preferences, and no protection against CSRF or email client link scanning\n\n\u2022 HTTP semantics violation: unsubscribe action mutates state (changes notification_level) on GET request, violating idempotency principles and vulnerable to browser prefetching, email client scanning, and proxy caching\u2014should use POST/PUT with confirmation page or signed token approach\n\n\u2022 Nil safety issue: controller accesses `tu.notification_level` without checking if TopicUser record exists, will raise NoMethodError when user has no TopicUser for the topic\n\n\u2022 Naming inconsistency: typo `stopNotificiationsText` (missing 'c') in both frontend controller and template introduces technical debt\n\n\n> [!TIP]\n>
\n> Help\n>\n> \n> **Slash Commands:**\n> - `/review` - Request a full code review\n> - `/review latest` - Review only changes since the last review\n> - `/describe` - Generate PR description. This will update the PR body or issue comment depending on your configuration\n> - `/help` - Get help with Mesa commands and configuration options\n> \n>
\n\n18 files reviewed | 3 comments | [Edit Agent Settings](https://wrangler.app.mesa.dev/code-review-benchmark/agents) \u2022 [Read Docs](https://docs.mesa.dev/) ", - "created_at": "2026-03-08T08:46:13Z" + "body": "Performed full review of **58ba11e...303cdc2**\n### Analysis\n\u2022 Middleware ordering may be incorrect: `LoggerMiddleware` executes before `ContextualLoggerMiddleware`, so logs won't contain expected contextual attributes (plugin ID, endpoint, datasource). Verify and likely reverse the ordering in pluginsintegration.go to ensure context enrichment precedes logging.\n\n\u2022 Hidden coupling to concrete logger types: `FromContext` type-asserts to `*log.ConcreteLogger`, creating brittle dependencies on infrastructure internals. If the underlying logger implementation changes, the code will silently degrade to a basic logger without contextual attributes.\n\n\u2022 Test logger breaks test observability: `TestLogger.FromContext` returns a fresh logger instance instead of maintaining the same instance, preventing tests from verifying that context-aware logging actually works and reducing confidence in the refactoring.\n\n\n> [!TIP]\n>
\n> Help\n>\n> \n> **Slash Commands:**\n> - `/review` - Request a full code review\n> - `/review latest` - Review only changes since the last review\n> - `/describe` - Generate PR description. This will update the PR body or issue comment depending on your configuration\n> - `/help` - Get help with Mesa commands and configuration options\n> \n>
\n\n8 files reviewed | 2 comments | [Edit Agent Settings](https://wrangler.app.mesa.dev/code-review-benchmark/agents) \u2022 [Read Docs](https://docs.mesa.dev/) ", + "created_at": "2026-03-08T08:56:27Z" }, { "path": null, "line": null, "body": "/review", - "created_at": "2026-03-08T08:31:17Z" + "created_at": "2026-03-08T08:32:07Z" } ] }, { "tool": "codeant", - "repo_name": "discourse__discourse-graphite__codeant__PR2__20260309", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__codeant__PR2__20260309/pull/1", + "repo_name": "grafana__grafana__codeant__PR76186__20260309", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__codeant__PR76186__20260309/pull/1", "review_comments": [ { "path": null, "line": null, "body": "@codeant", - "created_at": "2026-03-10T05:43:17Z" + "created_at": "2026-03-10T05:42:59Z" }, { "path": null, "line": null, - "body": "Question: \n\nAnswer:\nSummary\n- This PR adds a per-topic unsubscribe feature: a new TopicsController#unsubscribe action and route, frontend routes and templates for a topic unsubscribe page, changes to email building so notification emails include a topic-specific unsubscribe URL, and various small style/formatting cleanups.\n- Overall direction is good, but there are a few functional bugs, edge cases, and improvements to address before merging.\n\nProblems and suggested fixes\n\n1) Crash when TopicUser record is missing (critical)\n- Problem: In TopicsController#unsubscribe you do:\n tu = TopicUser.find_by(user_id: current_user.id, topic_id: params[:topic_id])\n if tu.notification_level > TopicUser.notification_levels[:regular]\n tu.notification_level = TopicUser.notification_levels[:regular]\n else\n tu.notification_level = TopicUser.notification_levels[:muted]\n end\n tu.save!\n\n If no TopicUser exists for that user/topic, tu will be nil and the code will raise NoMethodError.\n- Suggestion: Use TopicUser.change(...) (already used elsewhere) or create/fetch safely before mutating. Example fixes:\n - Use TopicUser.change(current_user.id, params[:topic_id], notification_level: TopicUser.notification_levels[:muted_or_regular]) \u2014 TopicUser.change handles create.\n - Or do a safe lookup and create if missing:\n tu = TopicUser.find_or_initialize_by(user_id: current_user.id, topic_id: params[:topic_id])\n ... set notification_level ...\n tu.save!\n- Prefer using TopicUser.change so \"observe_after_save_callbacks_for\" and MessageBus behaviors remain consistent.\n\n2) Unsubscribe URL should be absolute (email links / List-Unsubscribe)\n- The mailer now passes unsubscribe_url: post.topic.unsubscribe_url. topic.unsubscribe_url is defined as \"#{url}/unsubscribe\". Confirm what topic.url returns:\n - If topic.url returns a path (e.g., \"/t/slug/123\") then the unsubscribe URL will be relative; for emails List-Unsubscribe headers and links should usually be absolute (including host) to be robust.\n- Suggestion: Make unsubscribe_url absolute using Discourse.base_url or Discourse.base_uri (consistent with other url helpers), e.g.:\n def unsubscribe_url\n \"#{Discourse.base_url}#{relative_url}/unsubscribe\"\n end\n Or reuse existing helpers that build absolute topic links.\n\n3) Missing permission/visibility checks\n- TopicsController#unsubscribe constructs TopicView and redirects or renders based on slug matches, but ensure the guardian/permission logic used in show is applied to unsubscribe as well (TopicView.new(..., current_user) is used). I think the before_filter ensure_logged_in is applied to :unsubscribe, so only logged-in users can call it \u2014 that is good.\n- Also ensure users who cannot see a topic can't use the unsubscribe URL to discover metadata.\n\n4) Tests: more coverage needed\n- There is a spec change for MessageBuilder to pass unsubscribe_url when building the builder in the spec, which is good.\n- Add tests for TopicsController#unsubscribe:\n - when TopicUser exists: notification_level toggles as expected (e.g., if > regular -> becomes regular, else becomes muted).\n - when TopicUser does not exist: a TopicUser is created with the intended state (use TopicUser.change behavior).\n - redirect behavior when slug doesn't match and when request.format.json? is false with no slug.\n- Add an integration test to ensure notification emails include the topic-specific unsubscribe link and that List-Unsubscribe header is present and contains the absolute topic unsubscribe URL.\n\n5) Minor JS/typo issues\n- stopNotificiationsText is misspelled (Notificiations). The template and controller both use that misspelling so it works, but it's a typo. Consider renaming to stopNotificationsText for clarity (requires updating template and any references).\n- In topic-unsubscribe.js.es6 controller, .property() syntax is old Ember; if the codebase is using that style elsewhere it's probably fine, but consider modern computed property syntax if migrating Ember versions.\n\n6) style / formatting / small improvements\n- A few style refinements were applied (hash formatting and single-line if). Those are fine.\n- In notification.html.erb you added %{unsubscribe_link} into the HTML template. Confirm the text-only email templates also include unsubscribe link (server locale uses text_body_template too \u2014 you added server.en.yml change; check other locales).\n- Ensure the {% respond_instructions %} replacement logic still works in both HTML and text variants.\n\n7) Security/Spam headers\n- Email List-Unsubscribe header was covered in specs; ensure the header contains a valid angle-bracketed URL or mailto per List-Unsubscribe spec. If you're building a header elsewhere, confirm it contains the topic-specific URL and that it is absolute.\n\n8) Potential routing duplication/order\n- You added routes:\n get \"t/:slug/:topic_id/unsubscribe\" => \"topics#unsubscribe\", ...\n get \"t/:topic_id/unsubscribe\" => \"topics#unsubscribe\", ...\n These look correct. Ensure route ordering doesn't clash with other topic routes (seems OK).\n\n9) MessageBus broadcasts formatting consistency\n- You changed some MessageBus.publish calls to add spacing (stylistic). No functional issue, just consistent formatting.\n\nConcrete recommended code adjustments (examples)\n\n- Fix TopicsController#unsubscribe to be robust:\n def unsubscribe\n @topic_view = TopicView.new(params[:topic_id], current_user)\n if slugs_do_not_match || (!request.format.json? && params[:slug].blank?)\n return redirect_to @topic_view.topic.unsubscribe_url, status: 301\n end\n\n TopicUser.change(current_user.id, params[:topic_id], notification_level: new_level)\n perform_show_response\n end\n Where new_level is calculated from existing TopicUser if present (TopicUser.get) or default logic.\n\n- Make unsubscribe_url absolute in Topic model:\n def unsubscribe_url\n \"#{Discourse.base_url}#{relative_url}/unsubscribe\"\n end\n\n- Add nil-safe or create behavior:\n existing = TopicUser.get(@topic_view.topic, current_user)\n if existing\n new_level = existing.notification_level > TopicUser.notification_levels[:regular] ? TopicUser.notification_levels[:regular] : TopicUser.notification_levels[:muted]\n TopicUser.change(current_user.id, @topic_view.topic.id, notification_level: new_level)\n else\n # create with the intended state, probably muted\n TopicUser.change(current_user.id, @topic_view.topic.id, notification_level: TopicUser.notification_levels[:muted])\n end\n\n- Tests to add:\n - Controller spec for unsubscribe route states and TopicUser changes\n - Mailer spec to assert the List-Unsubscribe header includes absolute unsubscribe URL for that topic\n - Frontend acceptance test for topicUnsubscribe route to ensure page loads and toggle changes notification state\n\nOther small notes\n- The frontend route and templates look consistent and straightforward.\n- CSS additions are scoped and reasonable.\n- The email templates were tidied up \u2014 ensure replacements (%{respond_instructions} and %{unsubscribe_link}) are available for both HTML and plain text templates.\n\nIf you want, I can:\n- Suggest concrete unit test code for the controller and mailer.\n- Draft the small code edits (e.g., make unsubscribe_url absolute and make unsubscribe action use TopicUser.change) as a follow-up patch.\n\nOverall: the feature is close, but please fix the nil TopicUser issue and ensure the unsubscribe link in emails is an absolute URL and covered by tests.\n\n", - "created_at": "2026-03-10T05:44:13Z" + "body": "Question: \n\nAnswer:\nSummary of changes in this PR\n- Renamed the \"InstrumentationMiddleware\" to \"MetricsMiddleware\" (types, constructors, comments, tests).\n- Moved the code that enriches the context with a contextual logger out of the metrics middleware into a new dedicated ContextualLoggerMiddleware.\n- Logger interface changed:\n - Replaced \"interface{}\" with \"any\" in method signatures.\n - Added FromContext(ctx context.Context) Logger to Logger.\n- Updated the concrete grafana log wrapper and test logger to implement FromContext.\n- LoggerMiddleware now calls logger.FromContext(ctx).Info(...) so it uses the contextual logger placed into ctx by the new ContextualLoggerMiddleware.\n- CreateMiddlewares now includes NewMetricsMiddleware and NewContextualLoggerMiddleware in the middleware stack (ContextualLoggerMiddleware placed before LoggerMiddleware).\n- Minor API cleanups: use of \"any\" everywhere, comment updates.\n\nWhat looks good\n- Separation of concerns: contextual logger code is extracted into a dedicated middleware. This is clearer than having metrics middleware mix logging context manipulation with prometheus instrumentation.\n- Renaming Instrumentation -> Metrics is more accurate and readable.\n- Adding Logger.FromContext makes it explicit how to obtain contextual loggers and supports the new middleware layout.\n- Tests were updated to refer to new names (metrics_middleware_test.go).\n\nPotential issues and suggestions\n1) Order of middlewares in CreateMiddlewares\n - You added clientmiddleware.NewContextualLoggerMiddleware() before NewLoggerMiddleware and after NewMetricsMiddleware:\n - Ensure the intended order is that the ContextualLoggerMiddleware runs before LoggerMiddleware so LoggerMiddleware can call logger.FromContext(ctx) and get contextual attributes. The diff shows ContextualLoggerMiddleware placed before LoggerMiddleware which is correct \u2014 please double-check this ordering in runtime and tests.\n - Also consider whether MetricsMiddleware should be first (as earlier comment recommended instrumentation live at top). The current list places NewMetricsMiddleware first, then ContextualLoggerMiddleware, then LoggerMiddleware \u2014 that order should be validated to ensure instrumentation metrics still measure the desired durations, including whether instrumentation should add context or not.\n\n2) Logger.FromContext implementation in grafanaInfraLogWrapper\n - The implementation:\n concreteInfraLogger, ok := d.l.FromContext(ctx).(*log.ConcreteLogger)\n if !ok {\n return d.New()\n }\n return &grafanaInfraLogWrapper{ l: concreteInfraLogger }\n - This relies on d.l.FromContext(ctx) being available from the underlying infra logger. Ensure the underlying infra logger (pkg/infra/log) exposes FromContext and returns a concrete type *log.ConcreteLogger. If the underlying infra log package changes implementation or returns a different type, this type assertion could fail and you fall back to d.New() (base logger). That may be acceptable, but consider:\n - Documenting this fallback behavior.\n - If possible, rely on an interface method rather than a concrete type assertion (so you don't need to assert to *log.ConcreteLogger). For example, if the infra logger has a wrapper type that implements the same interface used here, return that directly.\n\n3) Test coverage\n - Add unit tests for the new ContextualLoggerMiddleware:\n - Verify that context attributes (pluginId, endpoint, dsName/dsUID when present, uname when present) are attached to the logger and that LoggerMiddleware actually logs those attributes via FromContext.\n - Test the case where DataSourceInstanceSettings is nil and where User is nil, to ensure no unexpected panics or missing attrs.\n - MetricsMiddleware tests: ensure they still measure duration and count correctly when the ContextualLoggerMiddleware is introduced (the previous test renamed accordingly; consider adding tests that ensure the metrics are still registered and updated).\n\n4) Logging parameters / fields\n - LoggerMiddleware logParams now includes: status, duration, eventName, time_before_plugin_request (and error on error). Previously it also included pluginId, endpoint, dsName, dsUID, uname, traceID. Those fields are now expected to be provided by the contextual logger in the context. Make sure:\n - ContextualLoggerMiddleware indeed adds all those fields (it does add endpoint, pluginId, dsName, dsUID, uname).\n - Tracing TraceID previously added by logger middleware was removed. If TraceID is still desirable on plugin logs, either:\n - ensure the tracing middleware adds the traceID to context and the contextual logger extracts it into fields, or\n - put traceID enrichment back or add to ContextualLoggerMiddleware.\n - If you want traceID included in logParams used by LoggerMiddleware, consider adding tracing.TraceIDFromContext(ctx, false) and append traceID here (or ensure the contextual logger includes it).\n\n5) Use of concrete names and comments\n - There are still references to plugin.instrumentation in CreateMiddlewares when calling log.New(\"plugin.instrumentation\") \u2014 since you renamed the middleware to metrics, consider if the logger name should be updated too (not a strict requirement).\n - Update comments where they still mention \"Instrumentation\" (I see most were updated, but double-check docstrings).\n\n6) Type changes to any vs interface{}\n - Using \"any\" is fine (Go 1.18+).\n - Make sure the repository consistently uses any in other places; the change is localized here so it's fine.\n\n7) Build/compile considerations\n - Ensure all packages that implement Logger now satisfy the new interface with FromContext. You updated grafanaInfraLogWrapper and TestLogger, and the PrettyLogger signatures were updated \u2014 verify there are no other implementations elsewhere that were missed (a simple repo build or `go test ./...` will catch them).\n - A possible missing import was resolved (added context in files that needed it). Make sure nothing else needs a new import.\n\nConcrete suggestions / examples\n- Consider changing grafanaInfraLogWrapper.FromContext to avoid asserting to *log.ConcreteLogger; if infra/log exposes an interface method that returns Logger-like type, use that. Example alternative:\n infraLogger := d.l.FromContext(ctx)\n if infraLogger == nil {\n return d.New()\n }\n return &grafanaInfraLogWrapper{ l: infraLogger }\n (This assumes types are compatible \u2014 adjust as needed.)\n\n- Add unit tests for ContextualLoggerMiddleware that assert:\n - When QueryData is called with a PluginContext containing DataSourceInstanceSettings and User, subsequent LoggerMiddleware logging includes dsName, dsUID, uname, pluginId and endpoint.\n - The middleware chain order works: MetricsMiddleware (instruments) -> ContextualLoggerMiddleware (adds attrs) -> LoggerMiddleware (reads from ctx via FromContext and logs).\n\n- If you want trace ID restored to logs, either:\n - Re-add tracing.TraceIDFromContext(ctx, false) into LoggerMiddleware logParams (it was intentionally removed \u2014 if that was intentional, no change needed), or\n - Add trace ID enrichment to the contextual logger middleware.\n\nFinal checklist before merge\n- Run go build / go test ./... locally or in CI to catch:\n - Any missed Logger implementations not updated for FromContext.\n - Any other compile errors from changed method names (Instrumentation -> Metrics).\n- Add tests for the new contextual middleware (happy path + edge cases).\n- Verify metrics names/labels and make sure prometheus registration doesn't panic on re-register (same as before, likely unchanged).\n\nIf you want, I can:\n- Run quick repository-wide searches for references to \"InstrumentationMiddleware\"/\"Instrumentation\" that may have been missed.\n- Try to run go test in the repo to find compiler errors (I can do that if you want me to run the repository).\n\n", + "created_at": "2026-03-10T05:43:51Z" } ] }, { "tool": "kodus", - "repo_name": "discourse__discourse-graphite__kodus__PR2__20260309", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__kodus__PR2__20260309/pull/1", + "repo_name": "grafana__grafana__kodus__PR76186__20260309", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__kodus__PR76186__20260309/pull/1", "review_comments": [ { - "path": "app/assets/javascripts/discourse/routes/topic-from-params.js.es6", - "line": 30, - "body": "![kody code-review](https://img.shields.io/badge/kody-code--review-312B4B?labelColor=C9BBF2) ![Bug](https://img.shields.io/badge/Bug-B71C1C) ![high](https://img.shields.io/badge/severity_level-high-6B6B92)\n\n\nNull pointer dereference. The `closestPostForPostNumber` method can return a null value, as explicitly noted in the preceding `TODO` comment. The subsequent line attempts to call `.get('post_number')` on this value without a null check. If `closestPost` is null, this will throw a `TypeError`, which is caught by the promise chain but prevents the topic from loading for the user.\n\n```javascript\n const closestPost = postStream.closestPostForPostNumber(params.nearPost || 1);\n\n if (!closestPost) {\n // Early exit or handle the case where no post is found.\n return;\n }\n\n const closest = closestPost.get('post_number'),\n progress = postStream.progressIndexOfPost(closestPost);\n```\n\n\n\n
\n\nPrompt for LLM\n\n```\n\nFile app/assets/javascripts/discourse/routes/topic-from-params.js.es6:\n\nLine 28 to 30:\n\nThe provided Javascript code, which is part of an Ember.js route, has a null pointer dereference bug. The function `closestPostForPostNumber` can return a null value, which is confirmed by a `TODO` comment in the code. However, the code immediately attempts to call the `.get()` method on the result without any null check. This will cause a `TypeError` and break the user flow for loading a topic. Explain the issue and suggest a fix that involves adding a null check after the call to `closestPostForPostNumber`.\n\nSuggested Code:\n\n const closestPost = postStream.closestPostForPostNumber(params.nearPost || 1);\n\n if (!closestPost) {\n // Early exit or handle the case where no post is found.\n return;\n }\n\n const closest = closestPost.get('post_number'),\n progress = postStream.progressIndexOfPost(closestPost);\n\n```\n\n
\n\n\nTalk to Kody by mentioning @kody\n\n\nWas this suggestion helpful? React with \ud83d\udc4d or \ud83d\udc4e to help Kody learn from this interaction.\n\n​\n​", - "created_at": "2026-03-10T06:27:40Z" - }, - { - "path": "app/assets/javascripts/discourse/routes/topic-unsubscribe.js.es6", - "line": 10, - "body": "![kody code-review](https://img.shields.io/badge/kody-code--review-312B4B?labelColor=C9BBF2) ![Cross File](https://img.shields.io/badge/Cross_File-9C27B0) ![medium](https://img.shields.io/badge/severity_level-medium-1A7BBE)\n\n\nRedundant data fetch on unsubscribe page load. The backend controller at `app/controllers/topics_controller.rb:115` preloads the topic data when rendering the page, but the frontend route at `app/assets/javascripts/discourse/routes/topic-unsubscribe.js.es6:6` immediately re-fetches the same data via a separate AJAX call (`PostStream.loadTopicView`). This results in an unnecessary network request, slowing down the page load. The model hook should be updated to leverage the preloaded data, for instance by using the Ember Data store.\n\n```javascript\n// app/assets/javascripts/discourse/routes/topic-unsubscribe.js.es6\n// Suggestion: Use the store to find the record, which will utilize preloaded data.\nimport PostStream from \"discourse/models/post-stream\";\n\nexport default Discourse.Route.extend({\n model(params) {\n // By using the store's find method, Ember will automatically use the preloaded\n // JSON from the initial page load instead of making a new network request.\n return this.store.find(\"topic\", params.id);\n },\n // ...\n});\n```\n\n\n\n
\n\nPrompt for LLM\n\n```\n\nFile app/assets/javascripts/discourse/routes/topic-unsubscribe.js.es6:\n\nLine 4 to 10:\n\nI have a Rails controller that preloads data for an Ember.js application, and an Ember route that is supposed to use that data. However, the Ember route is making a redundant API call to fetch the same data again. Here is the backend controller code in `app/controllers/topics_controller.rb` that preloads the data using `store_preloaded` and then renders the view. And here is the frontend route in `app/assets/javascripts/discourse/routes/topic-unsubscribe.js.es6` that uses `PostStream.loadTopicView` to fetch data, ignoring the preload. How should I refactor the Ember route's `model` hook to correctly use the preloaded data and avoid the unnecessary network request? The goal is to improve performance by using the data already sent in the initial page load.\n\nSuggested Code:\n\n// app/assets/javascripts/discourse/routes/topic-unsubscribe.js.es6\n// Suggestion: Use the store to find the record, which will utilize preloaded data.\nimport PostStream from \"discourse/models/post-stream\";\n\nexport default Discourse.Route.extend({\n model(params) {\n // By using the store's find method, Ember will automatically use the preloaded\n // JSON from the initial page load instead of making a new network request.\n return this.store.find(\"topic\", params.id);\n },\n // ...\n});\n\n```\n\n
\n\n\nTalk to Kody by mentioning @kody\n\n\nWas this suggestion helpful? React with \ud83d\udc4d or \ud83d\udc4e to help Kody learn from this interaction.\n\n​\n​", - "created_at": "2026-03-10T06:28:02Z" - }, - { - "path": "config/locales/server.en.yml", - "line": 1855, - "body": "![kody code-review](https://img.shields.io/badge/kody-code--review-312B4B?labelColor=C9BBF2) ![Bug](https://img.shields.io/badge/Bug-B71C1C) ![high](https://img.shields.io/badge/severity_level-high-6B6B92)\n\n\nMissing interpolation argument will cause email failures. The `unsubscribe_link` key is used in multiple email contexts, including generic digest emails which do not have a topic-specific context. The new version adds a requirement for the `%{unsubscribe_url}` variable, which is only available in topic-specific notifications. When a digest email is rendered, this variable will be missing, causing a fatal `I18n::MissingInterpolationArgument` error and preventing the email from being sent.\n\n```yaml\n unsubscribe_link: \"To unsubscribe from these emails, visit your [user preferences](%{user_preferences_url}).\"\n```\n\n\n\n
\n\nPrompt for LLM\n\n```\n\nFile config/locales/server.en.yml:\n\nLine 1852 to 1855:\n\nThe provided YAML localization file change modifies the `unsubscribe_link` string. This string is used as a footer in various emails sent by the application. The new version of the string introduces an interpolation variable `%{unsubscribe_url}`. However, based on the same file, this `unsubscribe_link` key is also used by the digest email template (`user_notifications.digest.unsubscribe`). Digest emails are generic and not tied to a single topic, so they won't have the `unsubscribe_url` context variable. Explain why this will cause an error during email rendering and suggest a fix. The fix should probably involve separating the generic unsubscribe link from the topic-specific one, perhaps by creating a new localization key and reverting this change.\n\nSuggested Code:\n\n unsubscribe_link: \"To unsubscribe from these emails, visit your [user preferences](%{user_preferences_url}).\"\n\n```\n\n
\n\n\nTalk to Kody by mentioning @kody\n\n\nWas this suggestion helpful? React with \ud83d\udc4d or \ud83d\udc4e to help Kody learn from this interaction.\n\n​\n​", - "created_at": "2026-03-10T06:28:15Z" + "path": "pkg/services/pluginsintegration/clientmiddleware/contextual_logger_middleware.go", + "line": 69, + "body": "![kody code-review](https://img.shields.io/badge/kody-code--review-312B4B?labelColor=C9BBF2) ![Bug](https://img.shields.io/badge/Bug-B71C1C) ![medium](https://img.shields.io/badge/severity_level-medium-1A7BBE)\n\n\nInconsistent instrumentation in the `ContextualLoggerMiddleware` where stream-related methods (`SubscribeStream`, `RunStream`) are not instrumented with contextual logging, unlike other methods (`QueryData`, `CallResource`, etc.). This leads to missing critical contextual information (pluginId, dsUID, etc.) in logs for streaming operations, making debugging and monitoring more difficult.\n\nThis issue appears in multiple locations:\n* pkg/services/pluginsintegration/clientmiddleware/contextual_logger_middleware.go: Lines 67-69\n* pkg/services/pluginsintegration/clientmiddleware/contextual_logger_middleware.go: Lines 59-69\nPlease instrument all stream-related methods (`SubscribeStream`, `RunStream`) with contextual logging by calling `instrumentContext` before passing the request to the next handler, similar to other methods in the middleware.\n\n\n```go\nfunc (m *ContextualLoggerMiddleware) RunStream(ctx context.Context, req *backend.RunStreamRequest, sender *backend.StreamSender) error {\n\tctx = instrumentContext(ctx, \"runStream\", req.PluginContext)\n\treturn m.next.RunStream(ctx, req, sender)\n}\n```\n\n\n\n
\n\nPrompt for LLM\n\n```\n\nFile pkg/services/pluginsintegration/clientmiddleware/contextual_logger_middleware.go:\n\nLine 67 to 69:\n\nThe `RunStream` method in `ContextualLoggerMiddleware` does not add contextual logging attributes to the context before passing the request to the next handler. This is inconsistent with other request-handling methods like `QueryData` and `CallResource` in the same middleware. As a result, any logs generated during the execution of a plugin's `RunStream` will be missing important context such as `pluginId`, `dsUID`, and `uname`, making debugging and monitoring of streaming plugins more difficult. Please modify the `RunStream` method to call `instrumentContext` and enrich the context, similar to how other methods in this file are handled. A suitable endpoint name would be \"runStream\".\n\nSuggested Code:\n\nfunc (m *ContextualLoggerMiddleware) RunStream(ctx context.Context, req *backend.RunStreamRequest, sender *backend.StreamSender) error {\n\tctx = instrumentContext(ctx, \"runStream\", req.PluginContext)\n\treturn m.next.RunStream(ctx, req, sender)\n}\n\n```\n\n
\n\n\nTalk to Kody by mentioning @kody\n\n\nWas this suggestion helpful? React with \ud83d\udc4d or \ud83d\udc4e to help Kody learn from this interaction.\n\n​\n​", + "created_at": "2026-03-10T06:06:24Z" }, { - "path": "config/routes.rb", - "line": 442, - "body": "![kody code-review](https://img.shields.io/badge/kody-code--review-312B4B?labelColor=C9BBF2) ![Bug](https://img.shields.io/badge/Bug-B71C1C) ![medium](https://img.shields.io/badge/severity_level-medium-1A7BBE)\n\n\nInconsistent parameter passing to a shared controller action. The route for topic summaries without a slug (`t/:topic_id/summary`) directs to the `topics#show` action but omits `defaults: {summary: true}`. The corresponding route that includes a slug (`t/:slug/:topic_id/summary`) sets this default. This discrepancy causes the `topics#show` action to receive `params[:summary]` as `nil` for slug-less URLs, likely breaking the intended summary view functionality.\n\n```suggestion\n get \"t/:topic_id/summary\" => \"topics#show\", defaults: {summary: true}, constraints: {topic_id: /\\d+/}\n```\n\n
\nWarning\n\nThis is an experimental feature that generates committable changes. Review the diff before applying. Results may be incorrect.\n
\n\n\n\n
\n\nPrompt for LLM\n\n```\n\nFile config/routes.rb:\n\nLine 442:\n\nI have two Ruby on Rails routes that both point to the same controller action, `topics#show`. One route is for URLs with a slug and the other is for URLs without a slug. Both URLs are for a 'summary' view.\n\nThe route with a slug is defined like this:\n`get \"t/:slug/:topic_id/summary\" => \"topics#show\", defaults: {summary: true}, constraints: {topic_id: /\\d+/}`\n\nThe route without a slug is defined like this:\n`get \"t/:topic_id/summary\" => \"topics#show\", constraints: {topic_id: /\\d+/}`\n\nNotice that the second route is missing `defaults: {summary: true}`. This means the `topics#show` action will receive `params[:summary]` as `true` for the first route, but `nil` for the second, leading to inconsistent behavior. Please correct the second route to be consistent with the first.\n\nSuggested Code:\n\n get \"t/:topic_id/summary\" => \"topics#show\", defaults: {summary: true}, constraints: {topic_id: /\\d+/} \n\n```\n\n
\n\n\nTalk to Kody by mentioning @kody\n\n\nWas this suggestion helpful? React with \ud83d\udc4d or \ud83d\udc4e to help Kody learn from this interaction.\n\n​\n​", - "created_at": "2026-03-10T06:28:30Z" + "path": "pkg/plugins/log/fake.go", + "line": 48, + "body": "![kody code-review](https://img.shields.io/badge/kody-code--review-312B4B?labelColor=C9BBF2) ![Bug](https://img.shields.io/badge/Bug-B71C1C) ![high](https://img.shields.io/badge/severity_level-high-6B6B92)\n\n\nThe `FromContext` method on the `TestLogger` mock creates and returns a new `TestLogger` instance via `NewTestLogger()`. This breaks the contract of a test logger, as any logs sent to the logger returned by `FromContext` will be recorded on this new, un-referenced instance, not the original instance (`f`) created in a test. Consequently, any test assertions on logs from the contextual logger will fail because the original logger never captured them. The method must return the receiver (`f`) to ensure logs are captured by the instance under test.\n\n```go\nfunc (f *TestLogger) FromContext(_ context.Context) Logger {\n\treturn f\n}\n```\n\n\n\n
\n\nPrompt for LLM\n\n```\n\nFile pkg/plugins/log/fake.go:\n\nLine 46 to 48:\n\nI have a mock logger in Go used for testing. It has a new method `FromContext` which is part of an interface it implements. The current implementation is `func (f *TestLogger) FromContext(_ context.Context) Logger { return NewTestLogger() }`. The purpose of the mock is to capture logs so that tests can assert that certain messages were logged. Explain why returning a new logger instance from `NewTestLogger()` is a bug in this context and provide the correct implementation. The goal is to ensure that logs sent to the logger returned by `FromContext` are captured by the original `TestLogger` instance.\n\nSuggested Code:\n\nfunc (f *TestLogger) FromContext(_ context.Context) Logger {\n\treturn f\n}\n\n```\n\n
\n\n\nTalk to Kody by mentioning @kody\n\n\nWas this suggestion helpful? React with \ud83d\udc4d or \ud83d\udc4e to help Kody learn from this interaction.\n\n​\n​", + "created_at": "2026-03-10T06:06:45Z" }, { - "path": "app/assets/javascripts/discourse/routes/app-route-map.js.es6", - "line": 13, - "body": "![kody code-review](https://img.shields.io/badge/kody-code--review-312B4B?labelColor=C9BBF2) ![Bug](https://img.shields.io/badge/Bug-B71C1C) ![critical](https://img.shields.io/badge/severity_level-critical-FF3D3D)\n\n\nRoute shadowing: the `topicUnsubscribe` route at `/t/:slug/:id/unsubscribe` will never be reached. The `topic` resource is defined before it, and its child route `fromParamsNear` has the dynamic path `/:nearPost`. When Ember's router processes `/t/some-slug/123/unsubscribe`, it matches the already-registered `topic` resource at `/t/:slug/:id`, then greedily matches the trailing segment `unsubscribe` as the `:nearPost` parameter, resolving the URL to `topic.fromParamsNear` with `nearPost='unsubscribe'` \u2014 the `topicUnsubscribe` route is never evaluated.\n\n```javascript\n this.route('topicUnsubscribe', { path: '/t/:slug/:id/unsubscribe' });\n this.resource('topic', { path: '/t/:slug/:id' }, function() {\n this.route('fromParams', { path: '/' });\n this.route('fromParamsNear', { path: '/:nearPost' });\n });\n this.resource('topicBySlug', { path: '/t/:slug' });\n```\n\n\n\n
\n\nPrompt for LLM\n\n```\n\nFile app/assets/javascripts/discourse/routes/app-route-map.js.es6:\n\nLine 13:\n\nI have an Ember.js route map where I'm adding a new static route at '/t/:slug/:id/unsubscribe'. The problem is that an existing resource named 'topic' is defined before my new route with the path '/t/:slug/:id', and it has a child route called 'fromParamsNear' with the dynamic path '/:nearPost'. Because Ember's router evaluates routes in definition order and dynamic segments match any string, when a user visits '/t/some-slug/123/unsubscribe', the router matches the 'topic' resource first and then matches the literal string 'unsubscribe' as the value for ':nearPost', so it routes to 'topic.fromParamsNear' instead of my 'topicUnsubscribe' route. The 'topicUnsubscribe' route is defined after the 'topic' resource so it is never reached. How should I reorder or restructure the routes so that '/t/:slug/:id/unsubscribe' is matched by 'topicUnsubscribe' and not by 'topic.fromParamsNear'?\n\nSuggested Code:\n\n this.route('topicUnsubscribe', { path: '/t/:slug/:id/unsubscribe' });\n this.resource('topic', { path: '/t/:slug/:id' }, function() {\n this.route('fromParams', { path: '/' });\n this.route('fromParamsNear', { path: '/:nearPost' });\n });\n this.resource('topicBySlug', { path: '/t/:slug' });\n\n```\n\n
\n\n\nTalk to Kody by mentioning @kody\n\n\nWas this suggestion helpful? React with \ud83d\udc4d or \ud83d\udc4e to help Kody learn from this interaction.\n\n​\n​", - "created_at": "2026-03-10T18:20:13Z" + "path": "pkg/services/pluginsintegration/clientmiddleware/logger_middleware.go", + "line": 36, + "body": "![kody code-review](https://img.shields.io/badge/kody-code--review-312B4B?labelColor=C9BBF2) ![Bug](https://img.shields.io/badge/Bug-B71C1C) ![high](https://img.shields.io/badge/severity_level-high-6B6B92)\n\n\nObservability regression in logging middleware. The refactoring of `logRequest` removes the `pluginCtx` parameter, which eliminates critical contextual information from plugin request logs, including `pluginId`, `dsName`, `dsUID`, and `uname`. The new implementation attempts to use a contextual logger with `m.logger.FromContext(ctx)`, but the calling functions (e.g., `QueryData`, `CallResource`) do not enrich the context with this information before calling `logRequest`. This results in a significant loss of observability, making it difficult to debug or monitor plugin behavior in production.\n\n```go\nfunc (m *LoggerMiddleware) logRequest(ctx context.Context, pluginCtx backend.PluginContext, endpoint string, fn func(ctx context.Context) error) error {\n\tstatus := statusOK\n\tstart := time.Now()\n\ttimeBeforePluginRequest := log.TimeSinceStart(ctx, start)\n\n\terr := fn(ctx)\n\tif err != nil {\n\t\tstatus = statusError\n\t\tif errors.Is(err, context.Canceled) {\n\t\t\tstatus = statusCancelled\n\t\t}\n\t}\n\n\tlogParams := []any{\n\t\t\"status\", status,\n\t\t\"duration\", time.Since(start),\n\t\t\"pluginId\", pluginCtx.PluginID,\n\t\t\"endpoint\", endpoint,\n\t\t\"eventName\", \"grafana-data-egress\",\n\t\t\"time_before_plugin_request\", timeBeforePluginRequest,\n\t}\n\n\tif pluginCtx.User != nil {\n\t\tlogParams = append(logParams, \"uname\", pluginCtx.User.Login)\n\t}\n\n\tif pluginCtx.DataSourceInstanceSettings != nil {\n\t\tlogParams = append(logParams, \"dsName\", pluginCtx.DataSourceInstanceSettings.Name)\n\t\tlogParams = append(logParams, \"dsUID\", pluginCtx.DataSourceInstanceSettings.UID)\n\t}\n\n\tif status == statusError {\n\t\tlogParams = append(logParams, \"error\", err)\n\t}\n\n\tm.logger.Info(\"Plugin Request Completed\", logParams...)\n\treturn err\n}\n```\n\n\n\n
\n\nPrompt for LLM\n\n```\n\nFile pkg/services/pluginsintegration/clientmiddleware/logger_middleware.go:\n\nLine 36:\n\nI have refactored a logging middleware in Go. The original `logRequest` function accepted a `pluginCtx` object and used it to add contextual fields like `pluginId`, `dsName`, and `uname` to the logs. My new version removes the `pluginCtx` parameter and instead uses a contextual logger via `m.logger.FromContext(ctx)`. However, I did not update the calling functions (like `QueryData`) to add the plugin context information to the `context.Context` object before passing it to `logRequest`. This has resulted in a loss of important information in our logs. Please review the provided code and suggest a fix. The goal is to ensure that all the original contextual information is present in the logs. You can either restore the old function signature and logic, or show how to correctly implement the contextual logger pattern by modifying the calling functions to enrich the context before the logging call.\n\nSuggested Code:\n\nfunc (m *LoggerMiddleware) logRequest(ctx context.Context, pluginCtx backend.PluginContext, endpoint string, fn func(ctx context.Context) error) error {\n\tstatus := statusOK\n\tstart := time.Now()\n\ttimeBeforePluginRequest := log.TimeSinceStart(ctx, start)\n\n\terr := fn(ctx)\n\tif err != nil {\n\t\tstatus = statusError\n\t\tif errors.Is(err, context.Canceled) {\n\t\t\tstatus = statusCancelled\n\t\t}\n\t}\n\n\tlogParams := []any{\n\t\t\"status\", status,\n\t\t\"duration\", time.Since(start),\n\t\t\"pluginId\", pluginCtx.PluginID,\n\t\t\"endpoint\", endpoint,\n\t\t\"eventName\", \"grafana-data-egress\",\n\t\t\"time_before_plugin_request\", timeBeforePluginRequest,\n\t}\n\n\tif pluginCtx.User != nil {\n\t\tlogParams = append(logParams, \"uname\", pluginCtx.User.Login)\n\t}\n\n\tif pluginCtx.DataSourceInstanceSettings != nil {\n\t\tlogParams = append(logParams, \"dsName\", pluginCtx.DataSourceInstanceSettings.Name)\n\t\tlogParams = append(logParams, \"dsUID\", pluginCtx.DataSourceInstanceSettings.UID)\n\t}\n\n\tif status == statusError {\n\t\tlogParams = append(logParams, \"error\", err)\n\t}\n\n\tm.logger.Info(\"Plugin Request Completed\", logParams...)\n\treturn err\n}\n\n```\n\n
\n\n\nTalk to Kody by mentioning @kody\n\n\nWas this suggestion helpful? React with \ud83d\udc4d or \ud83d\udc4e to help Kody learn from this interaction.\n\n​\n​", + "created_at": "2026-03-10T06:07:10Z" }, { - "path": "app/controllers/topics_controller.rb", - "line": 113, - "body": "![kody code-review](https://img.shields.io/badge/kody-code--review-312B4B?labelColor=C9BBF2) ![Bug](https://img.shields.io/badge/Bug-B71C1C) ![critical](https://img.shields.io/badge/severity_level-critical-FF3D3D)\n\n\nNull pointer dereference: `TopicUser.find_by` returns `nil` when the user has no existing TopicUser record (e.g., they received the email but never opened the topic). Line 107 then calls `tu.notification_level` on `nil`, raising `NoMethodError: undefined method 'notification_level' for nil:NilClass` and crashing the request.\n\n```ruby\n tu = TopicUser.find_by(user_id: current_user.id, topic_id: params[:topic_id])\n\n if tu.nil?\n # No existing record means default (regular) level \u2014 mute it\n TopicUser.change(current_user.id, params[:topic_id],\n notification_level: TopicUser.notification_levels[:muted])\n elsif tu.notification_level > TopicUser.notification_levels[:regular]\n TopicUser.change(current_user.id, params[:topic_id],\n notification_level: TopicUser.notification_levels[:regular])\n else\n TopicUser.change(current_user.id, params[:topic_id],\n notification_level: TopicUser.notification_levels[:muted])\n end\n```\n\n\n\n
\n\nPrompt for LLM\n\n```\n\nFile app/controllers/topics_controller.rb:\n\nLine 105 to 113:\n\nIn a Ruby on Rails controller action called `unsubscribe`, the code calls `TopicUser.find_by(user_id: current_user.id, topic_id: params[:topic_id])` and assigns the result to `tu`. It then immediately accesses `tu.notification_level` without any nil check. The `find_by` method returns nil when no matching record exists \u2014 this is the normal case for a user who received an email notification but has never opened the topic (so no TopicUser row was created for them). When `tu` is nil, calling `tu.notification_level` raises `NoMethodError: undefined method 'notification_level' for nil:NilClass`, crashing the action with a 500 error. The fix should guard against nil before accessing any attributes on `tu`. Also worth noting: all other places in the codebase that change the notification level use `TopicUser.change(user_id, topic_id, attrs)` (which also updates `notifications_changed_at`, `notifications_reason_id`, and publishes to MessageBus) rather than directly assigning attributes and calling `save!`, so the fix should use that method instead.\n\nSuggested Code:\n\n tu = TopicUser.find_by(user_id: current_user.id, topic_id: params[:topic_id])\n\n if tu.nil?\n # No existing record means default (regular) level \u2014 mute it\n TopicUser.change(current_user.id, params[:topic_id],\n notification_level: TopicUser.notification_levels[:muted])\n elsif tu.notification_level > TopicUser.notification_levels[:regular]\n TopicUser.change(current_user.id, params[:topic_id],\n notification_level: TopicUser.notification_levels[:regular])\n else\n TopicUser.change(current_user.id, params[:topic_id],\n notification_level: TopicUser.notification_levels[:muted])\n end\n\n```\n\n
\n\n\nTalk to Kody by mentioning @kody\n\n\nWas this suggestion helpful? React with \ud83d\udc4d or \ud83d\udc4e to help Kody learn from this interaction.\n\n​\n​", - "created_at": "2026-03-10T18:20:31Z" + "path": "pkg/plugins/log/fake.go", + "line": 47, + "body": "![kody code-review](https://img.shields.io/badge/kody-code--review-312B4B?labelColor=C9BBF2) ![Cross File](https://img.shields.io/badge/Cross_File-9C27B0) ![high](https://img.shields.io/badge/severity_level-high-6B6B92)\n\n\n`TestLogger.FromContext` (fake.go:46-48) returns `NewTestLogger()` \u2014 a completely independent, zero-value instance \u2014 instead of `f` (the receiver). `LoggerMiddleware.logRequest` (logger_middleware.go:58) now calls `m.logger.FromContext(ctx).Info(...)` to emit the \"Plugin Request Completed\" log. In test suites that inject a `*TestLogger` as the logger, this call reaches a discarded instance, so `InfoLogs.Calls` is never incremented on the original test logger and any assertion like `require.Equal(t, 1, logger.InfoLogs.Calls)` silently fails. The production implementation in `logger.go:48-55` correctly wraps the concrete infra logger and returns it, demonstrating the expected contract: `FromContext` should return the same logical logger enriched with context, not a new one.\n\n```suggestion\n\treturn f\n```\n\n
\nWarning\n\nThis is an experimental feature that generates committable changes. Review the diff before applying. Results may be incorrect.\n
\n\n\n\n
\n\nPrompt for LLM\n\n```\n\nFile pkg/plugins/log/fake.go:\n\nLine 46 to 48:\n\nIn a Go codebase I'm reviewing, the Logger interface now requires a FromContext(ctx context.Context) Logger method. Two implementations exist: the production wrapper in logger.go returns a properly enriched logger (wrapping the concrete infra logger), while the test fake in fake.go returns NewTestLogger() \u2014 a brand new, zero-value instance \u2014 instead of the receiver f. The logger middleware (logger_middleware.go) calls m.logger.FromContext(ctx).Info(...) to record every completed plugin request. When a *TestLogger is injected in tests, the Info call lands on the discarded new instance, so InfoLogs.Calls on the original logger is never incremented, causing test assertions to silently fail. What is the correct fix for the fake implementation so it satisfies the same contract as the production implementation?\n\nSuggested Code:\n\n// fake.go\nfunc (f *TestLogger) FromContext(_ context.Context) Logger {\n\treturn f\n}\n\n```\n\n
\n\n\nTalk to Kody by mentioning @kody\n\n\nWas this suggestion helpful? React with \ud83d\udc4d or \ud83d\udc4e to help Kody learn from this interaction.\n\n​\n​", + "created_at": "2026-03-10T18:19:24Z" }, { "path": null, "line": null, "body": "@kody start-review", - "created_at": "2026-03-10T06:02:21Z" + "created_at": "2026-03-10T06:02:12Z" }, { "path": null, "line": null, - "body": "## Code Review Completed! \ud83d\udd25\n\nThe code review was successfully completed based on your current configurations.\n\n\n\n
\nKody Guide: Usage and Configuration\n\n
\nInteracting with Kody\n\n- **Request a Review:** Ask Kody to review your PR manually by adding a comment with the `@kody start-review` command at the root of your PR.\n\n- **Validate Business Logic:** Ask Kody to validate your code against business rules by adding a comment with the `@kody -v business-logic` command.\n\n- **Provide Feedback:** Help Kody learn and improve by reacting to its comments with a \ud83d\udc4d for helpful suggestions or a \ud83d\udc4e if improvements are needed.\n\n
\n\n
\nCurrent Kody Configuration\n\n
\nReview Options\n\nThe following review options are enabled or disabled:\n\n| Options | Enabled |\n|-------------------------------|---------|\n| **Bug** | \u2705 |\n| **Performance** | \u2705 |\n| **Security** | \u2705 |\n| **Cross File** | \u2705 |\n| **Business Logic** | \u2705 |\n\n
\n\n**[Access your configuration settings here.](https://app.kodus.io/settings/code-review/global/general)**\n\n
\n
\n\n\n\n​", - "created_at": "2026-03-10T06:05:41Z" + "body": "## Code Review Completed! \ud83d\udd25\n\nThe code review was successfully completed based on your current configurations.\n\n\n\n
\nKody Guide: Usage and Configuration\n\n
\nInteracting with Kody\n\n- **Request a Review:** Ask Kody to review your PR manually by adding a comment with the `@kody start-review` command at the root of your PR.\n\n- **Validate Business Logic:** Ask Kody to validate your code against business rules by adding a comment with the `@kody -v business-logic` command.\n\n- **Provide Feedback:** Help Kody learn and improve by reacting to its comments with a \ud83d\udc4d for helpful suggestions or a \ud83d\udc4e if improvements are needed.\n\n
\n\n
\nCurrent Kody Configuration\n\n
\nReview Options\n\nThe following review options are enabled or disabled:\n\n| Options | Enabled |\n|-------------------------------|---------|\n| **Bug** | \u2705 |\n| **Performance** | \u2705 |\n| **Security** | \u2705 |\n| **Cross File** | \u2705 |\n| **Business Logic** | \u2705 |\n\n
\n\n**[Access your configuration settings here.](https://app.kodus.io/settings/code-review/global/general)**\n\n
\n
\n\n\n\n​", + "created_at": "2026-03-10T06:02:29Z" }, { "path": null, "line": null, "body": "@kody review", - "created_at": "2026-03-10T18:15:28Z" + "created_at": "2026-03-10T18:15:17Z" }, { "path": null, "line": null, - "body": "## Code Review Completed! \ud83d\udd25\n\nThe code review was successfully completed based on your current configurations.\n\n\n\n
\nKody Guide: Usage and Configuration\n\n
\nInteracting with Kody\n\n- **Request a Review:** Ask Kody to review your PR manually by adding a comment with the `@kody start-review` command at the root of your PR.\n\n- **Validate Business Logic:** Ask Kody to validate your code against business rules by adding a comment with the `@kody -v business-logic` command.\n\n- **Provide Feedback:** Help Kody learn and improve by reacting to its comments with a \ud83d\udc4d for helpful suggestions or a \ud83d\udc4e if improvements are needed.\n\n
\n\n
\nCurrent Kody Configuration\n\n
\nReview Options\n\nThe following review options are enabled or disabled:\n\n| Options | Enabled |\n|-------------------------------|---------|\n| **Bug** | \u2705 |\n| **Performance** | \u2705 |\n| **Security** | \u2705 |\n| **Cross File** | \u2705 |\n| **Business Logic** | \u2705 |\n\n
\n\n**[Access your configuration settings here.](https://app.kodus.io/settings/code-review/global/general)**\n\n
\n
\n\n\n\n​", - "created_at": "2026-03-10T18:15:37Z" + "body": "## Code Review Completed! \ud83d\udd25\n\nThe code review was successfully completed based on your current configurations.\n\n\n\n
\nKody Guide: Usage and Configuration\n\n
\nInteracting with Kody\n\n- **Request a Review:** Ask Kody to review your PR manually by adding a comment with the `@kody start-review` command at the root of your PR.\n\n- **Validate Business Logic:** Ask Kody to validate your code against business rules by adding a comment with the `@kody -v business-logic` command.\n\n- **Provide Feedback:** Help Kody learn and improve by reacting to its comments with a \ud83d\udc4d for helpful suggestions or a \ud83d\udc4e if improvements are needed.\n\n
\n\n
\nCurrent Kody Configuration\n\n
\nReview Options\n\nThe following review options are enabled or disabled:\n\n| Options | Enabled |\n|-------------------------------|---------|\n| **Bug** | \u2705 |\n| **Performance** | \u2705 |\n| **Security** | \u2705 |\n| **Cross File** | \u2705 |\n| **Business Logic** | \u2705 |\n\n
\n\n**[Access your configuration settings here.](https://app.kodus.io/settings/code-review/global/general)**\n\n
\n
\n\n\n\n​", + "created_at": "2026-03-10T18:15:27Z" } ] }, { "tool": "claude-code", - "repo_name": "discourse__discourse-graphite__claude-code__PR2__20260310", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__claude-code__PR2__20260310/pull/1", + "repo_name": "grafana__grafana__claude-code__PR76186__20260310", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__claude-code__PR76186__20260310/pull/1", "review_comments": [ { - "path": "app/controllers/topics_controller.rb", - "line": 28, - "body": "\ud83d\udd34 The `unsubscribe` action requires `ensure_logged_in` (line 28), but the unsubscribe URL is embedded in notification emails for users to click directly. When a logged-out user clicks this link, the `Discourse::NotLoggedIn` exception is caught by `application_controller.rb:97-104`, which silently redirects GET requests to `\"/\"` (the homepage) with no error message and no unsubscribe action taken. This makes the per-topic email unsubscribe feature non-functional for the most common case: users clicking the link from their email client while not logged in. Consider using a token-based approach similar to the existing `email/unsubscribe/:key` system.\n\n
\nExtended reasoning...\n\n## Bug Analysis\n\nThe new `unsubscribe` action in `TopicsController` is added to the `ensure_logged_in` filter list at line 28. This action is specifically designed to be reached via links in notification emails (the `unsubscribe_url` is passed through `user_notifications.rb` and rendered in `notification.html.erb` via the `unsubscribe_link` i18n string). The fundamental problem is that email links are clicked by users who are very often not logged into the forum in their browser.\n\n## Code Path for Logged-Out Users\n\nWhen a logged-out user clicks the unsubscribe link from an email:\n1. The request hits `TopicsController#unsubscribe`\n2. Before the action runs, the `ensure_logged_in` before_filter fires (`application_controller.rb:408-410`)\n3. Since `current_user` is nil, it raises `Discourse::NotLoggedIn`\n4. The `rescue_from Discourse::NotLoggedIn` handler at `application_controller.rb:97-104` catches this\n5. Since the request is a GET request (not JSON, not XHR), execution falls to line 103: `redirect_to path(\"/\")`\n6. The user is silently redirected to the homepage with **no feedback whatsoever**\n\n## Why This Is Not \"Standard Discourse Behavior\"\n\nOne verifier argued this is intentional because Discourse redirects to login and then back. However, that is **not** what happens here. Reading lines 97-104 of `application_controller.rb`, the handler for `NotLoggedIn` on GET HTML requests does `redirect_to path(\"/\")` \u2014 a plain redirect to the homepage root, not to a login page with a return URL. The user sees the homepage with no indication that they needed to log in or that their unsubscribe attempt failed. There is no redirect-back-after-login mechanism in this code path.\n\n## Concrete Example\n\n1. User Alice receives a notification email for topic \"Release Notes v2.0\" (topic_id: 42)\n2. The email contains a link: `https://forum.example.com/t/release-notes-v2-0/42/unsubscribe`\n3. Alice clicks the link in her email client (Gmail, Outlook, etc.)\n4. Her browser opens, but she is not logged into the forum\n5. The `ensure_logged_in` filter raises `NotLoggedIn`\n6. She is silently redirected to `https://forum.example.com/`\n7. Alice sees the forum homepage, has no idea why, and is still subscribed to the topic\n\n## Impact\n\nThis effectively makes the entire per-topic unsubscribe feature non-functional for the primary use case it was designed for. The existing Discourse email unsubscribe system (`email/unsubscribe/:key` at `config/routes.rb`) uses a unique token-based approach that works without authentication, specifically because email links must work without a login session. The new per-topic unsubscribe should follow the same pattern.\n\n## Suggested Fix\n\nEither: (a) Generate a unique unsubscribe token per user/topic and use token-based authentication similar to `EmailController#unsubscribe`, or (b) at minimum, redirect to a login page with a return URL so the user can authenticate and then be redirected back to complete the unsubscribe action.\n\n
", - "created_at": "2026-03-11T05:52:37Z" + "path": "pkg/plugins/log/fake.go", + "line": 48, + "body": "\ud83d\udd34 `TestLogger.FromContext` returns a new disconnected `TestLogger` via `NewTestLogger()` instead of returning the receiver (`f`). Since `LoggerMiddleware.logRequest` now calls `m.logger.FromContext(ctx).Info(...)`, any test using `TestLogger` to assert on `LoggerMiddleware` logging behavior will see zero calls \u2014 logs silently go to a throwaway instance. Fix: change `return NewTestLogger()` to `return f` on line 47.\n\n
\nExtended reasoning...\n\n## What the bug is\n\nThe newly added `TestLogger.FromContext` method in `pkg/plugins/log/fake.go` (lines 46-48) creates and returns a brand new `TestLogger` instance:\n\n```go\nfunc (f *TestLogger) FromContext(_ context.Context) Logger {\n return NewTestLogger()\n}\n```\n\nThis means the returned logger is completely disconnected from the original `TestLogger` that the caller holds a reference to.\n\n## How it manifests\n\n`LoggerMiddleware.logRequest` (in `logger_middleware.go`, line 62) was changed in this PR from `m.logger.Info(...)` to `m.logger.FromContext(ctx).Info(\"Plugin Request Completed\", logParams...)`. This is the key code path for logging plugin requests.\n\n**Step-by-step proof:**\n1. A test creates `logger := log.NewTestLogger()` and passes it to `NewLoggerMiddleware(cfg, logger)`\n2. The test makes a request (e.g., `QueryData`) through the middleware\n3. `logRequest` is called, which calls `m.logger.FromContext(ctx)` \u2014 this returns a *new* `TestLogger` via `NewTestLogger()`\n4. `.Info(\"Plugin Request Completed\", logParams...)` is called on this throwaway instance\n5. The test checks `logger.InfoLogs.Calls` \u2014 it is `0`, because the original `logger` was never written to\n6. The test incorrectly concludes no logging happened\n\n## Why existing code doesn't prevent it\n\nThe `TestLogger` struct correctly records calls in its `Info`, `Warn`, `Debug`, and `Error` methods by incrementing counters on the receiver. However, `FromContext` breaks this contract by returning a new instance that has its own independent counters. The `var _ Logger = (*TestLogger)(nil)` compile-time check only verifies interface satisfaction, not behavioral correctness.\n\n## Impact\n\n`TestLogger` is the standard test fake for the `Logger` interface in the plugins package. With this bug, it is impossible to write meaningful tests for `LoggerMiddleware` logging behavior \u2014 the primary use case for this fake. While no existing `logger_middleware_test.go` file exists yet (so no tests are currently broken), this defect in the test infrastructure makes it a trap for anyone writing such tests in the future.\n\n## Fix\n\nChange line 47 from `return NewTestLogger()` to `return f`:\n\n```go\nfunc (f *TestLogger) FromContext(_ context.Context) Logger {\n return f\n}\n```\n\nThis ensures that log calls made via `FromContext` are recorded on the same `TestLogger` instance that the test holds, preserving the ability to assert on log output.\n\n
", + "created_at": "2026-03-11T06:01:29Z" }, { - "path": "config/locales/server.en.yml", - "line": 1855, - "body": "\ud83d\udfe1 The `unsubscribe_link` i18n string now requires `%{unsubscribe_url}` interpolation, but `Email::MessageBuilder` provides no default value for it. While the only current internal caller (`send_notification_email`) passes `unsubscribe_url`, any plugin using `build_email` with `add_unsubscribe_link: true` without providing `unsubscribe_url` will raise `I18n::MissingInterpolationArgument`. Consider adding a default value for `unsubscribe_url` in the `@template_args` initialization in `message_builder.rb`.\n\n
\nExtended reasoning...\n\n## What the bug is\n\nThe `unsubscribe_link` translation in `config/locales/server.en.yml` was changed from a single line referencing only `%{user_preferences_url}` to a multi-line string that also references `%{unsubscribe_url}`:\n\n```yaml\nunsubscribe_link: |\n To unsubscribe from these emails, visit your [user preferences](%{user_preferences_url}).\n To stop receiving notifications about this particular topic, [click here](%{unsubscribe_url}).\n```\n\nThis introduces a new mandatory interpolation variable that was not previously required.\n\n## The code path\n\nIn `lib/email/message_builder.rb`, both `html_part` (line 68) and `body` (line 88) call `I18n.t('unsubscribe_link', template_args)` when `add_unsubscribe_link: true` is set. The `template_args` hash is initialized with only `site_name`, `base_url`, and `user_preferences_url`, then merged with `@opts`. The `unsubscribe_url` key only appears in `template_args` if the caller explicitly passes it in `@opts`.\n\n## Why existing code doesn't prevent it\n\nThe only current production caller that sets `add_unsubscribe_link: true` is `send_notification_email` in `app/mailers/user_notifications.rb` (line 294), which was updated in this PR to also pass `unsubscribe_url: post.topic.unsubscribe_url`. The test spec was similarly updated to pass `unsubscribe_url`. However, `Email::MessageBuilder` is a shared utility class -- there is no guard, default value, or documentation requiring callers to provide `unsubscribe_url`.\n\n## Step-by-step proof\n\n1. A Discourse plugin calls `build_email(user.email, body: 'hello', add_unsubscribe_link: true)` -- this was valid before this PR.\n2. `MessageBuilder` initializes `@template_args` as `{site_name: ..., base_url: ..., user_preferences_url: ...}` merged with opts. No `unsubscribe_url` key is present.\n3. In `body`, `I18n.t('unsubscribe_link', template_args)` is called.\n4. The translation expects `%{unsubscribe_url}` but the key is missing from `template_args`.\n5. Ruby I18n raises `I18n::MissingInterpolationArgument` (or renders `%{unsubscribe_url}` literally depending on I18n exception handler config), breaking the email.\n\n## Impact\n\nThis is a breaking change to the `Email::MessageBuilder` public API. Any Discourse plugin that previously used `add_unsubscribe_link: true` without passing `unsubscribe_url` will fail after this change. The core codebase itself is not affected since the only internal caller was updated.\n\n## Fix\n\nAdd a default value for `unsubscribe_url` in the `@template_args` initialization in `message_builder.rb`, e.g., defaulting to `user_preferences_url`. Alternatively, the translation could use `%{unsubscribe_url}` with a conditional or be split into two separate translation keys.\n\n
", - "created_at": "2026-03-11T05:52:37Z" + "path": "pkg/services/pluginsintegration/clientmiddleware/metrics_middleware_test.go", + "line": 78, + "body": "\ud83d\udfe1 Nit: The test function on line 21 is still named `TestInstrumentationMiddleware` but the struct was renamed to `MetricsMiddleware`. Should be `TestMetricsMiddleware` for consistency with the rest of the rename.\n\n
\nExtended reasoning...\n\n## What the bug is\n\nThis PR renames `InstrumentationMiddleware` to `MetricsMiddleware` across the codebase. The rename was applied thoroughly to the struct, its constructor (`newMetricsMiddleware`), the public constructor (`NewMetricsMiddleware`), all method receivers, and comments. However, the test function name on line 21 of `metrics_middleware_test.go` was missed \u2014 it still reads `func TestInstrumentationMiddleware(t *testing.T)` instead of `func TestMetricsMiddleware(t *testing.T)`.\n\n## How it manifests\n\nThe inconsistency is visible when looking at line 21 vs. line 78 of the test file. Line 78 correctly calls `newMetricsMiddleware(promRegistry, pluginsRegistry)`, showing the internal constructor was renamed. But the enclosing test function on line 21 still uses the old name `TestInstrumentationMiddleware`.\n\n## Step-by-step proof\n\n1. The PR renames `InstrumentationMiddleware` \u2192 `MetricsMiddleware` in `metrics_middleware.go`.\n2. The PR renames `newInstrumentationMiddleware` \u2192 `newMetricsMiddleware` (line 78 of the test file was updated).\n3. The PR renames `NewInstrumentationMiddleware` \u2192 `NewMetricsMiddleware` in the public API.\n4. But line 21 of `metrics_middleware_test.go` still reads `func TestInstrumentationMiddleware(t *testing.T)` \u2014 the old name.\n\n## Impact\n\nThis is purely cosmetic. The test compiles and runs correctly since Go test function names are arbitrary (they just need the `Test` prefix). However, it creates confusion: someone reading the test would see a reference to the old `InstrumentationMiddleware` name, making it harder to find the test via search or understand which middleware it covers.\n\n## Fix\n\nRename `TestInstrumentationMiddleware` to `TestMetricsMiddleware` on line 21 of `metrics_middleware_test.go`.\n\n
", + "created_at": "2026-03-11T06:01:29Z" }, { "path": null, "line": null, "body": "## Claude Code Review\n\nCode review was not run because your organization is out of credits.\n\nAn admin can add credits from the billing page in the Anthropic Console. Once credits are available, reopen this pull request to trigger a review.", - "created_at": "2026-03-11T05:22:13Z" + "created_at": "2026-03-11T05:21:55Z" } ] }, { "tool": "vercel", - "repo_name": "discourse__discourse-graphite__vercel__PR2__20260310", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__vercel__PR2__20260310/pull/1", + "repo_name": "grafana__grafana__vercel__PR76186__20260310", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__vercel__PR76186__20260310/pull/1", "review_comments": [] }, { "tool": "bito", - "repo_name": "discourse__discourse-graphite__bito__PR2__20260310", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__bito__PR2__20260310/pull/1", + "repo_name": "grafana__grafana__bito__PR76186__20260310", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__bito__PR76186__20260310/pull/1", "review_comments": [] }, { "tool": "sentry", - "repo_name": "discourse__discourse-graphite__sentry__PR2__20260310", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__sentry__PR2__20260310/pull/1", + "repo_name": "grafana__grafana__sentry__PR76186__20260310", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__sentry__PR76186__20260310/pull/1", "review_comments": [] }, { "tool": "linearb", - "repo_name": "discourse__discourse-graphite__linearb__PR2__20260309", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__linearb__PR2__20260309/pull/1", + "repo_name": "grafana__grafana__linearb__PR76186__20260309", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__linearb__PR76186__20260309/pull/1", "review_comments": [ { "path": null, "line": null, "body": "/gs review", - "created_at": "2026-03-10T06:13:49Z" + "created_at": "2026-03-10T06:13:41Z" } ] }, { "tool": "kodus-v2", - "repo_name": "discourse__discourse-graphite__kodus-v2__PR2__20260312", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__kodus-v2__PR2__20260312/pull/1", + "repo_name": "grafana__grafana__kodus-v2__PR76186__20260312", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__kodus-v2__PR76186__20260312/pull/1", "review_comments": [ { - "path": "app/controllers/topics_controller.rb", - "line": 113, - "body": "![kody code-review](https://img.shields.io/badge/kody-code--review-312B4B?labelColor=C9BBF2) ![Bug](https://img.shields.io/badge/Bug-B71C1C) ![critical](https://img.shields.io/badge/severity_level-critical-FF3D3D)\n\n\nNull pointer dereference in the `unsubscribe` action of `app/controllers/topics_controller.rb` (lines 105\u2013113): `TopicUser.find_by` returns `nil` when no record exists for the user/topic pair, yet `tu.notification_level` is called unconditionally on the result. This causes a `NoMethodError` in production for any user who reaches the unsubscribe URL without a prior `TopicUser` record (e.g., mentioned but never visited the topic).\n\nThis issue appears in multiple locations:\n* app/controllers/topics_controller.rb: Lines 105-113\n* app/controllers/topics_controller.rb: Lines 105-113\nAdd a nil guard after the `TopicUser.find_by` call \u2014 either raise a not-found error, return an appropriate response, or use `find_or_create_by` \u2014 before accessing any attributes on the result.\n\n\n```ruby\n tu = TopicUser.find_by(user_id: current_user.id, topic_id: params[:topic_id])\n raise Discourse::NotFound unless tu\n\n if tu.notification_level > TopicUser.notification_levels[:regular]\n tu.notification_level = TopicUser.notification_levels[:regular]\n else\n tu.notification_level = TopicUser.notification_levels[:muted]\n end\n\n tu.save!\n```\n\n\n\n
\n\nPrompt for LLM\n\n```\n\nFile app/controllers/topics_controller.rb:\n\nLine 105 to 113:\n\nIn a Ruby on Rails controller action called `unsubscribe`, the code calls `TopicUser.find_by(user_id: current_user.id, topic_id: params[:topic_id])` and immediately accesses `.notification_level` on the result without a nil check. `find_by` returns nil when no matching record exists. A user can reach this action by clicking an unsubscribe link in an email for a topic where they have no TopicUser record (for example, they were mentioned in the topic but never visited it). This causes a NoMethodError at runtime. Please add a nil guard on the TopicUser lookup result before accessing its attributes, either raising a not-found error or creating a default record.\n\nSuggested Code:\n\n tu = TopicUser.find_by(user_id: current_user.id, topic_id: params[:topic_id])\n raise Discourse::NotFound unless tu\n\n if tu.notification_level > TopicUser.notification_levels[:regular]\n tu.notification_level = TopicUser.notification_levels[:regular]\n else\n tu.notification_level = TopicUser.notification_levels[:muted]\n end\n\n tu.save!\n\n```\n\n
\n\n\nTalk to Kody by mentioning @kody\n\n\nWas this suggestion helpful? React with \ud83d\udc4d or \ud83d\udc4e to help Kody learn from this interaction.\n\n​\n​", - "created_at": "2026-03-13T01:26:03Z" - }, - { - "path": "app/assets/javascripts/discourse/routes/app-route-map.js.es6", - "line": 13, - "body": "![kody code-review](https://img.shields.io/badge/kody-code--review-312B4B?labelColor=C9BBF2) ![Cross File](https://img.shields.io/badge/Cross_File-9C27B0) ![high](https://img.shields.io/badge/severity_level-high-6B6B92)\n\n\nRoute path inconsistency between Ember client and Rails server: the Ember route map defines `topicUnsubscribe` with path `/t/:slug/:id/unsubscribe` (line 13), but the Rails route (`config/routes.rb` line 441) expects `/t/:topic_id/unsubscribe` (without slug) as a fallback. The Ember route passes `params.id` in `topic-unsubscribe.js.es6` (lines 5-6), while the Rails controller reads `params[:topic_id]`. This parameter name mismatch means the Ember route's `:id` segment will arrive at Rails as `params[:id]`, not `params[:topic_id]`, causing the `TopicView.new(params[:topic_id], ...)` call to receive `nil`.\n\n```javascript\nthis.route('topicUnsubscribe', { path: '/t/:slug/:topic_id/unsubscribe' });\n```\n\n\n\n
\n\nPrompt for LLM\n\n```\n\nFile app/assets/javascripts/discourse/routes/app-route-map.js.es6:\n\nLine 13:\n\nI have a route definition mismatch between my Ember.js frontend and Rails backend. The Ember route map in app-route-map.js.es6 defines the unsubscribe route as `/t/:slug/:id/unsubscribe`, extracting the topic identifier as `params.id`. However, the Rails route in config/routes.rb defines it as `get 't/:slug/:topic_id/unsubscribe' => 'topics#unsubscribe'`, which means Rails expects the parameter as `params[:topic_id]`. The controller action does `TopicView.new(params[:topic_id], current_user)`. Since the Ember route sends `:id` but Rails expects `:topic_id`, there is a parameter name mismatch. The Ember route file topic-unsubscribe.js.es6 also uses `params.id`. Should the Ember route parameter be renamed to `:topic_id` to match Rails, or should the Rails controller be adjusted?\n\nSuggested Code:\n\nthis.route('topicUnsubscribe', { path: '/t/:slug/:topic_id/unsubscribe' });\n\n```\n\n
\n\n\nTalk to Kody by mentioning @kody\n\n\nWas this suggestion helpful? React with \ud83d\udc4d or \ud83d\udc4e to help Kody learn from this interaction.\n\n​\n​", - "created_at": "2026-03-13T01:26:15Z" + "path": "pkg/plugins/log/fake.go", + "line": 48, + "body": "![kody code-review](https://img.shields.io/badge/kody-code--review-312B4B?labelColor=C9BBF2) ![Bug](https://img.shields.io/badge/Bug-B71C1C) ![high](https://img.shields.io/badge/severity_level-high-6B6B92)\n\n\n`FromContext` returns a new, disconnected `TestLogger` instead of the receiver. Any production code that calls `logger.FromContext(ctx)` and logs on the result will write to a discarded instance \u2014 the original `TestLogger` used for test assertions never sees those logs. Codebase context confirms tests like `steps_test.go` assert on `fakeLogger.ErrorLogs.Calls` after assigning `fakeLogger` to a struct field; if the code under test now obtains a logger via `FromContext`, the assertion `require.Equal(t, fakeLogger.ErrorLogs.Calls, 1)` silently passes with 0 calls, or fails, depending on whether the log path changed.\n\n```go\nfunc (f *TestLogger) FromContext(_ context.Context) Logger {\n\treturn f\n}\n```\n\n\n\n
\n\nPrompt for LLM\n\n```\n\nFile pkg/plugins/log/fake.go:\n\nLine 46 to 48:\n\nIn a Go test helper struct called TestLogger (in pkg/plugins/log/fake.go), a new method FromContext was added that returns a brand-new TestLogger via NewTestLogger() instead of returning the receiver (f). The purpose of TestLogger is to capture log calls (DebugLogs, InfoLogs, WarnLogs, ErrorLogs) so that tests can assert on them. When production code obtains a logger by calling FromContext on a TestLogger instance, it gets back a completely separate instance. Any logs written to that new instance are invisible to the test, which holds a reference to the original TestLogger. This means test assertions like require.Equal(t, fakeLogger.ErrorLogs.Calls, 1) will see 0 calls. The fix is to return the receiver f from FromContext so that all log writes are captured on the same instance the test is asserting against.\n\nSuggested Code:\n\nfunc (f *TestLogger) FromContext(_ context.Context) Logger {\n\treturn f\n}\n\n```\n\n
\n\n\nTalk to Kody by mentioning @kody\n\n\nWas this suggestion helpful? React with \ud83d\udc4d or \ud83d\udc4e to help Kody learn from this interaction.\n\n​\n​", + "created_at": "2026-03-13T01:24:55Z" }, { - "path": "lib/email/message_builder.rb", - "line": 69, - "body": "![kody code-review](https://img.shields.io/badge/kody-code--review-312B4B?labelColor=C9BBF2) ![Cross File](https://img.shields.io/badge/Cross_File-9C27B0) ![high](https://img.shields.io/badge/severity_level-high-6B6B92)\n\n\nUnsubscribe link rendering is now gated behind `@opts[:add_unsubscribe_link]` (line 61), but the `respond_instructions` substitution and `unsubscribe_link` substitution are both inside that conditional. The `server.en.yml` locale now includes `%{unsubscribe_url}` in the `unsubscribe_link` template (line 1855), and `user_notifications.rb` passes `unsubscribe_url` in `email_opts` (line 295). However, when `add_unsubscribe_link` is false or absent, the `%{respond_instructions}` placeholder in the HTML override will never be replaced either, leaving raw `%{respond_instructions}` text in the email body for any email that has an HTML override but doesn't add unsubscribe links.\n\n```ruby\nif response_instructions = @template_args[:respond_instructions]\n respond_instructions = PrettyText.cook(response_instructions).html_safe\n html_override.gsub!(\"%{respond_instructions}\", respond_instructions)\nend\n\nif @opts[:add_unsubscribe_link]\n unsubscribe_link = PrettyText.cook(I18n.t('unsubscribe_link', template_args)).html_safe\n html_override.gsub!(\"%{unsubscribe_link}\", unsubscribe_link)\nelse\n html_override.gsub!(\"%{unsubscribe_link}\", '')\nend\n```\n\n\n\n
\n\nPrompt for LLM\n\n```\n\nFile lib/email/message_builder.rb:\n\nLine 61 to 69:\n\nIn my Ruby email builder (lib/email/message_builder.rb), the html_part method performs placeholder substitution on an HTML email override. The code that replaces `%{respond_instructions}` and `%{unsubscribe_link}` placeholders is nested inside a conditional `if @opts[:add_unsubscribe_link]`. This means that when `add_unsubscribe_link` is false or not set, both placeholders remain as raw text in the final email HTML. The email notification template (app/views/email/notification.html.erb) always includes both `%{respond_instructions}` and `%{unsubscribe_link}` placeholders. The respond_instructions replacement should happen independently of the unsubscribe link logic. How should I restructure this conditional to ensure respond_instructions is always substituted while keeping the unsubscribe link conditional?\n\nSuggested Code:\n\nif response_instructions = @template_args[:respond_instructions]\n respond_instructions = PrettyText.cook(response_instructions).html_safe\n html_override.gsub!(\"%{respond_instructions}\", respond_instructions)\nend\n\nif @opts[:add_unsubscribe_link]\n unsubscribe_link = PrettyText.cook(I18n.t('unsubscribe_link', template_args)).html_safe\n html_override.gsub!(\"%{unsubscribe_link}\", unsubscribe_link)\nelse\n html_override.gsub!(\"%{unsubscribe_link}\", '')\nend\n\n```\n\n
\n\n\nTalk to Kody by mentioning @kody\n\n\nWas this suggestion helpful? React with \ud83d\udc4d or \ud83d\udc4e to help Kody learn from this interaction.\n\n​\n​", - "created_at": "2026-03-13T01:26:18Z" + "path": "pkg/services/pluginsintegration/clientmiddleware/contextual_logger_middleware.go", + "line": 37, + "body": "![kody code-review](https://img.shields.io/badge/kody-code--review-312B4B?labelColor=C9BBF2) ![Cross File](https://img.shields.io/badge/Cross_File-9C27B0) ![high](https://img.shields.io/badge/severity_level-high-6B6B92)\n\n\nTraceID dropped from \"Plugin Request Completed\" log entries. The old `logRequest` in `logger_middleware.go` explicitly extracted `traceID` via `tracing.TraceIDFromContext(ctx, false)` and appended it to `logParams`. That extraction was removed (lines 49\u201358 of new `logger_middleware.go`), and the replacement `instrumentContext` in `contextual_logger_middleware.go` (lines 27\u201337) only injects `endpoint`, `pluginId`, `dsName`, `dsUID`, and `uname`\u2014never `traceID`. Unless the infra-level `FromContext` automatically decorates with trace information, every plugin-request log line loses its correlation to distributed traces.\n\n```go\nfunc instrumentContext(ctx context.Context, endpoint string, pCtx backend.PluginContext) context.Context {\n\tp := []any{\"endpoint\", endpoint, \"pluginId\", pCtx.PluginID}\n\tif pCtx.DataSourceInstanceSettings != nil {\n\t\tp = append(p, \"dsName\", pCtx.DataSourceInstanceSettings.Name)\n\t\tp = append(p, \"dsUID\", pCtx.DataSourceInstanceSettings.UID)\n\t}\n\tif pCtx.User != nil {\n\t\tp = append(p, \"uname\", pCtx.User.Login)\n\t}\n\tif traceID := tracing.TraceIDFromContext(ctx, false); traceID != \"\" {\n\t\tp = append(p, \"traceID\", traceID)\n\t}\n\treturn log.WithContextualAttributes(ctx, p)\n}\n```\n\n\n\n
\n\nPrompt for LLM\n\n```\n\nFile pkg/services/pluginsintegration/clientmiddleware/contextual_logger_middleware.go:\n\nLine 27 to 37:\n\nI am refactoring a Go middleware stack for plugin request instrumentation. Previously, a single InstrumentationMiddleware handled both metrics and logging. It explicitly extracted traceID from the context via tracing.TraceIDFromContext and included it in log parameters for the 'Plugin Request Completed' log line. I split this into three middlewares: MetricsMiddleware (prometheus metrics only), ContextualLoggerMiddleware (injects contextual attributes into the context), and LoggerMiddleware (reads context and logs). The ContextualLoggerMiddleware has an instrumentContext function that adds endpoint, pluginId, dsName, dsUID, and uname to the context, but I forgot to also add the traceID. The LoggerMiddleware now relies entirely on logger.FromContext(ctx) to pick up all contextual attributes, so if traceID is not in the context, it will be missing from logs. Can you confirm this is indeed a regression and show me how to add traceID extraction back into the instrumentContext function in contextual_logger_middleware.go?\n\nSuggested Code:\n\nfunc instrumentContext(ctx context.Context, endpoint string, pCtx backend.PluginContext) context.Context {\n\tp := []any{\"endpoint\", endpoint, \"pluginId\", pCtx.PluginID}\n\tif pCtx.DataSourceInstanceSettings != nil {\n\t\tp = append(p, \"dsName\", pCtx.DataSourceInstanceSettings.Name)\n\t\tp = append(p, \"dsUID\", pCtx.DataSourceInstanceSettings.UID)\n\t}\n\tif pCtx.User != nil {\n\t\tp = append(p, \"uname\", pCtx.User.Login)\n\t}\n\tif traceID := tracing.TraceIDFromContext(ctx, false); traceID != \"\" {\n\t\tp = append(p, \"traceID\", traceID)\n\t}\n\treturn log.WithContextualAttributes(ctx, p)\n}\n\n```\n\n
\n\n\nTalk to Kody by mentioning @kody\n\n\nWas this suggestion helpful? React with \ud83d\udc4d or \ud83d\udc4e to help Kody learn from this interaction.\n\n​\n​", + "created_at": "2026-03-13T01:24:58Z" }, { "path": null, "line": null, "body": "@kody review", - "created_at": "2026-03-13T01:13:22Z" + "created_at": "2026-03-13T01:12:46Z" }, { "path": null, "line": null, "body": "@kody review", - "created_at": "2026-03-13T01:18:10Z" + "created_at": "2026-03-13T01:17:42Z" }, { "path": null, "line": null, "body": "@kody review", - "created_at": "2026-03-13T01:21:09Z" + "created_at": "2026-03-13T01:20:39Z" }, { "path": null, "line": null, - "body": "## Code Review Completed! \ud83d\udd25\n\nThe code review was successfully completed based on your current configurations.\n\n\n\n
\nKody Guide: Usage and Configuration\n\n
\nInteracting with Kody\n\n- **Request a Review:** Ask Kody to review your PR manually by adding a comment with the `@kody start-review` command at the root of your PR.\n\n- **Validate Business Logic:** Ask Kody to validate your code against business rules by adding a comment with the `@kody -v business-logic` command.\n\n- **Provide Feedback:** Help Kody learn and improve by reacting to its comments with a \ud83d\udc4d for helpful suggestions or a \ud83d\udc4e if improvements are needed.\n\n
\n\n
\nCurrent Kody Configuration\n\n
\nReview Options\n\nThe following review options are enabled or disabled:\n\n| Options | Enabled |\n|-------------------------------|---------|\n| **Bug** | \u2705 |\n| **Performance** | \u2705 |\n| **Security** | \u2705 |\n| **Cross File** | \u2705 |\n| **Business Logic** | \u2705 |\n\n
\n\n**[Access your configuration settings here.](https://app.kodus.io/settings/code-review/global/general)**\n\n
\n
\n\n\n\n​", - "created_at": "2026-03-13T01:21:24Z" + "body": "## Code Review Completed! \ud83d\udd25\n\nThe code review was successfully completed based on your current configurations.\n\n\n\n
\nKody Guide: Usage and Configuration\n\n
\nInteracting with Kody\n\n- **Request a Review:** Ask Kody to review your PR manually by adding a comment with the `@kody start-review` command at the root of your PR.\n\n- **Validate Business Logic:** Ask Kody to validate your code against business rules by adding a comment with the `@kody -v business-logic` command.\n\n- **Provide Feedback:** Help Kody learn and improve by reacting to its comments with a \ud83d\udc4d for helpful suggestions or a \ud83d\udc4e if improvements are needed.\n\n
\n\n
\nCurrent Kody Configuration\n\n
\nReview Options\n\nThe following review options are enabled or disabled:\n\n| Options | Enabled |\n|-------------------------------|---------|\n| **Bug** | \u2705 |\n| **Performance** | \u2705 |\n| **Security** | \u2705 |\n| **Cross File** | \u2705 |\n| **Business Logic** | \u2705 |\n\n
\n\n**[Access your configuration settings here.](https://app.kodus.io/settings/code-review/global/general)**\n\n
\n
\n\n\n\n​", + "created_at": "2026-03-13T01:20:47Z" } ] }, { "tool": "qodo-v2-2", - "repo_name": "discourse__discourse-graphite__qodo-v2-2__PR2__20260312", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-v2-2__PR2__20260312/pull/1", + "repo_name": "grafana__grafana__qodo-v2-2__PR76186__20260312", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__qodo-v2-2__PR76186__20260312/pull/1", "review_comments": [ - { - "path": "app/controllers/topics_controller.rb", - "line": 111, - "body": "\"Action\n\n1\\. Nil topicuser crash \ud83d\udc1e Bug \u2713 Correctness\n\n
\nTopicsController#unsubscribe dereferences tu.notification_level immediately after\nTopicUser.find_by(...); if the user has no TopicUser row for that topic, the action raises and the\nunsubscribe link 500s.\n
\n\n\n
\nAgent Prompt\n\n```\n### Issue description\n`TopicsController#unsubscribe` assumes a `TopicUser` row exists and calls `tu.notification_level` without a nil check, causing 500s when a user has never created a `topic_users` row for the topic.\n\n### Issue Context\n`TopicView.new(...)` does not create a `TopicUser` row. The canonical API for updating notification level is `TopicUser.change(...)`, which explicitly creates the row when missing and performs related bookkeeping.\n\n### Fix Focus Areas\n- app/controllers/topics_controller.rb[98-116]\n- app/models/topic_user.rb[74-115]\n- lib/topic_view.rb[31-71]\n```\n\n\u24d8 Copy this prompt and use it to remediate the issue with your preferred AI generation tools\n
", - "created_at": "2026-03-13T03:12:58Z" - }, - { - "path": "config/routes.rb", - "line": 442, - "body": "\"Action\n\n2\\. Csrfable get unsubscribe \ud83d\udc1e Bug \u26e8 Security\n\n
\nThe unsubscribe endpoint is routed as GET but mutates TopicUser.notification_level and saves,\nenabling cross-site requests to toggle a logged-in user\u2019s topic notification state.\n
\n\n\n
\nAgent Prompt\n\n```\n### Issue description\n`/t/.../unsubscribe` is a GET endpoint that updates `TopicUser` state, making it vulnerable to CSRF (e.g., any third-party page can trigger it for a logged-in user).\n\n### Issue Context\nOther topic notification state mutations (mute/unmute) use non-GET verbs. Email-driven unsubscribe typically needs either (a) a signed token in the URL, or (b) a GET confirmation page + POST action.\n\n### Fix Focus Areas\n- config/routes.rb[438-452]\n- app/controllers/topics_controller.rb[98-116]\n```\n\n\u24d8 Copy this prompt and use it to remediate the issue with your preferred AI generation tools\n
", - "created_at": "2026-03-13T03:12:58Z" - }, - { - "path": "app/assets/javascripts/discourse/routes/topic-unsubscribe.js.es6", - "line": 15, - "body": "\"Action\n\n3\\. Sets computed property \ud83d\udc1e Bug \u2713 Correctness\n\n
\nThe unsubscribe Ember route sets details.notificationReasonText to null, but\nnotificationReasonText is a read-only computed property; this will throw/assert and break the\nunsubscribe page transition.\n
\n\n\n
\nAgent Prompt\n\n```\n### Issue description\n`notificationReasonText` is a computed property on `TopicDetails` with no setter, so calling `set` on it will crash/assert.\n\n### Issue Context\nThe unsubscribe page wants the dropdown but not the explanatory long description. That should be controlled at the component/template level, not by mutating a computed property.\n\n### Fix Focus Areas\n- app/assets/javascripts/discourse/routes/topic-unsubscribe.js.es6[12-15]\n- app/assets/javascripts/discourse/models/topic-details.js.es6[43-58]\n- app/assets/javascripts/discourse/components/topic-notifications-button.js.es6[1-16]\n- app/assets/javascripts/discourse/templates/topic/unsubscribe.hbs[1-8]\n```\n\n\u24d8 Copy this prompt and use it to remediate the issue with your preferred AI generation tools\n
", - "created_at": "2026-03-13T03:12:58Z" - }, - { - "path": "app/assets/javascripts/discourse/routes/app-route-map.js.es6", - "line": 13, - "body": "\"Action\n\n4\\. Route/template mismatch \ud83d\udc1e Bug \u2713 Correctness\n\n
\nThe router defines a top-level topicUnsubscribe route, but the template was added as\ntemplates/topic/unsubscribe.hbs (nested route template); Ember will likely fail template\nresolution and render an error/blank page.\n
\n\n\n
\nAgent Prompt\n\n```\n### Issue description\n`topicUnsubscribe` is defined as a top-level route, but the template is named like a nested `topic.unsubscribe` template (`templates/topic/unsubscribe.hbs`). This mismatch likely prevents Ember from finding the template.\n\n### Issue Context\nThe codebase uses foldered templates for nested routes (e.g. `group.members` -> `templates/group/members.hbs`).\n\n### Fix Focus Areas\n- app/assets/javascripts/discourse/routes/app-route-map.js.es6[7-14]\n- app/assets/javascripts/discourse/templates/topic/unsubscribe.hbs[1-8]\n```\n\n\u24d8 Copy this prompt and use it to remediate the issue with your preferred AI generation tools\n
", - "created_at": "2026-03-13T03:12:58Z" - }, { "path": null, "line": null, "body": "/agentic_review --review_agent.comments_location_policy=inline --review_agent.inline_comments_severity_threshold = 2", - "created_at": "2026-03-13T03:03:33Z" + "created_at": "2026-03-13T03:03:20Z" }, { "path": null, "line": null, - "body": "\n

Code Review by Qodo

\n\ud83d\udc1e Bugs (4) \ud83d\udcd8 Rule violations (0) \ud83d\udcce Requirement gaps (0)\n\n\"Grey\n\n

Issue findings (4)

\n\nFindings have been published as inline review comments\n\n\"Grey\n\n
 \u24d8 Findings that repeat in the code will be published as multiple comments
\n\n\"Grey\n\n
\u24d8 The new review experience is currently in Beta. Learn more
\n\n\"Grey\n\n\n\n\n\"Qodo", - "created_at": "2026-03-13T03:03:40Z" + "body": "\n

Code Review by Qodo

\n\ud83d\udc1e Bugs (0) \ud83d\udcd8 Rule violations (0) \ud83d\udcce Requirement gaps (0)\n\n\"Grey\n\n\n\n

Great, no issues found!

\nQodo reviewed your code and found no material issues that require review\n\n\"Grey\n\n
\u24d8 The new review experience is currently in Beta. Learn more
\n\n\"Grey\n\n\n\n\n\"Qodo", + "created_at": "2026-03-13T03:03:28Z" } ] }, { "tool": "qodo-v22", - "repo_name": "discourse__discourse-graphite__qodo-v22__PR2__20260312", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-v22__PR2__20260312/pull/1", + "repo_name": "grafana__grafana__qodo-v22__PR76186__20260312", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__qodo-v22__PR76186__20260312/pull/1", "review_comments": [ { - "path": "app/controllers/topics_controller.rb", - "line": 113, - "body": "\"Action\n\n1\\. Nil topicuser crash \ud83d\udc1e Bug \u2713 Correctness\n\n
\nTopicsController#unsubscribe calls TopicUser.find_by and then dereferences tu.notification_level\nwithout handling a missing row or NULL notification_level, which can raise and break the unsubscribe\nlink. This is plausible because TopicUser.notification_level is nullable and TopicUser.change\nexplicitly creates missing rows.\n
\n\n\n
\nAgent Prompt\n\n```\n### Issue description\n`TopicsController#unsubscribe` assumes `TopicUser.find_by(...)` returns a record and that `notification_level` is non-NULL. In practice, a user may not yet have a `topic_users` row for a topic and `notification_level` can be NULL, causing a 500.\n\n### Issue Context\nThe model already provides `TopicUser.change` which creates the row when missing and standardizes notification change bookkeeping.\n\n### Fix Focus Areas\n- app/controllers/topics_controller.rb[98-116]\n- app/models/topic_user.rb[74-115]\n\n### Suggested fix\n- Replace the `find_by` + manual mutation with `TopicUser.change(current_user.id, params[:topic_id], notification_level: new_level)`.\n- Compute `current_level = tu&.notification_level || TopicUser.notification_levels[:regular]` before comparing, or just always set the intended target level.\n- Ensure `notifications_changed_at` / `notifications_reason_id` semantics match existing `TopicUser.change` behavior (ideally by using it).\n```\n\n\u24d8 Copy this prompt and use it to remediate the issue with your preferred AI generation tools\n
", - "created_at": "2026-03-13T07:10:34Z" - }, - { - "path": "config/routes.rb", - "line": 442, - "body": "\"Action\n\n2\\. Get mutates user state \ud83d\udc1e Bug \u26e8 Security\n\n
\nThe unsubscribe endpoint is exposed as GET but persists a notification_level change, which is\nvulnerable to CSRF and can be triggered unintentionally by email link prefetch/scanners. With Rails\nCSRF protection enabled, using GET for writes bypasses authenticity token protections.\n
\n\n\n
\nAgent Prompt\n\n```\n### Issue description\n`GET /t/.../unsubscribe` performs a write to `TopicUser`, which is CSRF/prefetch vulnerable.\n\n### Issue Context\nRails CSRF protection is enabled, but GET requests are not expected to mutate state and won't require an authenticity token.\n\n### Fix Focus Areas\n- config/routes.rb[434-442]\n- app/controllers/topics_controller.rb[98-116]\n\n### Suggested fix\n- Change the mutating endpoint to `POST`/`PUT` and require CSRF token.\n- Keep the email link as a GET that only renders a confirmation page (or includes a signed one-time token that is validated server-side before applying the change).\n- If keeping one-click behavior, implement a signed token parameter validated in `TopicsController#unsubscribe` before mutating state, and consider making it idempotent (set to muted/regular explicitly rather than toggling).\n```\n\n\u24d8 Copy this prompt and use it to remediate the issue with your preferred AI generation tools\n
", - "created_at": "2026-03-13T07:10:34Z" - }, - { - "path": "app/assets/javascripts/discourse/routes/app-route-map.js.es6", - "line": 13, - "body": "\"Action\n\n3\\. Unsubscribe template mismatch \ud83d\udc1e Bug \u2713 Correctness\n\n
\nThe Ember router defines a top-level topicUnsubscribe route, but the added template is\ntemplates/topic/unsubscribe.hbs and the route/controller don\u2019t override templateName, so the\nunsubscribe UI template will not be found/rendered. Additionally, the existing topic template has\nno {{outlet}}, so the new template also can\u2019t render as a nested child of topic.\n
\n\n\n
\nAgent Prompt\n\n```\n### Issue description\nThe new `topicUnsubscribe` route is top-level but the template was added at `templates/topic/unsubscribe.hbs`, with no `templateName`/`renderTemplate` override. This will prevent the unsubscribe UI from rendering.\n\n### Issue Context\nTop-level routes in this repo resolve templates at the root (example: `queued-posts` -> `templates/queued-posts.hbs`). The existing `topic` template does not include an `{{outlet}}`, so a nested `topic/unsubscribe` template won\u2019t render inside it either.\n\n### Fix Focus Areas\n- app/assets/javascripts/discourse/routes/app-route-map.js.es6[7-14]\n- app/assets/javascripts/discourse/routes/topic-unsubscribe.js.es6[1-23]\n- app/assets/javascripts/discourse/templates/topic/unsubscribe.hbs[1-8]\n- app/assets/javascripts/discourse/templates/topic.hbs[1-20]\n\n### Suggested fix options\n1) Make it a real top-level page:\n- Move/rename the template to `app/assets/javascripts/discourse/templates/topic-unsubscribe.hbs` (matching the route\u2019s dasherized name), and keep the top-level route.\n\n2) Make it nested under `topic`:\n- Define the route as `this.route('unsubscribe', { path: '/unsubscribe' })` within the `topic` resource, and add an `{{outlet}}` to `templates/topic.hbs` at the appropriate place so `templates/topic/unsubscribe.hbs` can render.\n\n3) Explicit mapping:\n- Keep current file locations but add `templateName: 'topic/unsubscribe'` via `renderTemplate()` in the route (or controller/view), ensuring it actually resolves.\n```\n\n\u24d8 Copy this prompt and use it to remediate the issue with your preferred AI generation tools\n
", - "created_at": "2026-03-13T07:10:34Z" + "path": "pkg/plugins/log/fake.go", + "line": 48, + "body": "\"Action\n\n1\\. Testlogger drops contextual logs \ud83d\udc1e Bug \u2713 Correctness\n\n
\nTestLogger.FromContext returns a new TestLogger instance, so any code that logs via\nlogger.FromContext(ctx) writes into a fresh logger and the original test logger never sees the call.\nThis breaks log assertions and can silently hide logging regressions for code paths like\nLoggerMiddleware that now always use FromContext.\n
\n\n\n
\nAgent Prompt\n\n```\n### Issue description\n`TestLogger.FromContext` returns a new logger instance, so calls like `logger.FromContext(ctx).Info(...)` do not update the original `TestLogger` held by a test.\n\n### Issue Context\n`LoggerMiddleware` now always logs via `m.logger.FromContext(ctx)`.\n\n### Fix Focus Areas\n- pkg/plugins/log/fake.go[46-48]\n- pkg/services/pluginsintegration/clientmiddleware/logger_middleware.go[49-59]\n```\n\n\u24d8 Copy this prompt and use it to remediate the issue with your preferred AI generation tools\n
", + "created_at": "2026-03-13T07:08:04Z" }, { "path": null, "line": null, "body": "/agentic_review", - "created_at": "2026-03-13T07:01:42Z" + "created_at": "2026-03-13T07:01:13Z" }, { "path": null, "line": null, - "body": "

Code Review by Qodo

\n\n\ud83d\udc1e Bugs (3) \ud83d\udcd8 Rule violations (0) \ud83d\udcce Requirement gaps (0)\n\n\"Grey\n\n
\n\n\"Action\n\n
\n 1. Nil TopicUser crash \ud83d\udc1e Bug \u2713 Correctness\n\n
\n\n>
\n>Description\n>
\n>\n>
\n>TopicsController#unsubscribe calls TopicUser.find_by and then dereferences tu.notification_level\n>without handling a missing row or NULL notification_level, which can raise and break the unsubscribe\n>link. This is plausible because TopicUser.notification_level is nullable and TopicUser.change\n>explicitly creates missing rows.\n>
\n>
\n\n>
\n>Code\n>
\n>\n>[app/controllers/topics_controller.rb[R105-113]](https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-v22__PR2__20260312/pull/1/files#diff-a672ba5c68f4e7842932ef4e0c0893ef55b981e205a2b85490c4ff4363760811R105-R113)\n>\n>```diff\n>+ tu = TopicUser.find_by(user_id: current_user.id, topic_id: params[:topic_id])\n>+\n>+ if tu.notification_level > TopicUser.notification_levels[:regular]\n>+ tu.notification_level = TopicUser.notification_levels[:regular]\n>+ else\n>+ tu.notification_level = TopicUser.notification_levels[:muted]\n>+ end\n>+\n>+ tu.save!\n>```\n>
\n\n>
\n>Evidence\n>
\n>\n>
\n>The unsubscribe action assumes a TopicUser record exists and that notification_level is non-NULL,\n>but the data model and helper APIs indicate both conditions can be false in normal operation\n>(nullable notification_level + records created on demand).\n>
\n>\n> [app/controllers/topics_controller.rb[98-116]](https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-v22__PR2__20260312/blob/6669a2d94d76eea3b99b8c476d12b1eb66726b07/app/controllers/topics_controller.rb/#L98-L116)\n> [app/models/topic_user.rb[8-13]](https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-v22__PR2__20260312/blob/6669a2d94d76eea3b99b8c476d12b1eb66726b07/app/models/topic_user.rb/#L8-L13)\n> [app/models/topic_user.rb[74-107]](https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-v22__PR2__20260312/blob/6669a2d94d76eea3b99b8c476d12b1eb66726b07/app/models/topic_user.rb/#L74-L107)\n>
\n\n>
\n>Agent prompt\n>
\n>\n>```\n>The issue below was found during a code review. Follow the provided context and guidance below and implement a solution\n>\n>### Issue description\n>`TopicsController#unsubscribe` assumes `TopicUser.find_by(...)` returns a record and that `notification_level` is non-NULL. In practice, a user may not yet have a `topic_users` row for a topic and `notification_level` can be NULL, causing a 500.\n>\n>### Issue Context\n>The model already provides `TopicUser.change` which creates the row when missing and standardizes notification change bookkeeping.\n>\n>### Fix Focus Areas\n>- app/controllers/topics_controller.rb[98-116]\n>- app/models/topic_user.rb[74-115]\n>\n>### Suggested fix\n>- Replace the `find_by` + manual mutation with `TopicUser.change(current_user.id, params[:topic_id], notification_level: new_level)`.\n>- Compute `current_level = tu&.notification_level || TopicUser.notification_levels[:regular]` before comparing, or just always set the intended target level.\n>- Ensure `notifications_changed_at` / `notifications_reason_id` semantics match existing `TopicUser.change` behavior (ideally by using it).\n>```\n> \u24d8 Copy this prompt and use it to remediate the issue with your preferred AI generation tools\n>
\n\n
\n
\n\n\n
\n 2. GET mutates user state \ud83d\udc1e Bug \u26e8 Security\n\n
\n\n>
\n>Description\n>
\n>\n>
\n>The unsubscribe endpoint is exposed as GET but persists a notification_level change, which is\n>vulnerable to CSRF and can be triggered unintentionally by email link prefetch/scanners. With Rails\n>CSRF protection enabled, using GET for writes bypasses authenticity token protections.\n>
\n>
\n\n>
\n>Code\n>
\n>\n>[config/routes.rb[R440-442]](https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-v22__PR2__20260312/pull/1/files#diff-959bc9abc46a55332bb64d5155a79323afa75a50ec1a2137ddd22d926f62c6c5R440-R442)\n>\n>```diff\n>+ get \"t/:slug/:topic_id/unsubscribe\" => \"topics#unsubscribe\", constraints: {topic_id: /\\d+/}\n>+ get \"t/:topic_id/unsubscribe\" => \"topics#unsubscribe\", constraints: {topic_id: /\\d+/}\n>+ get \"t/:topic_id/summary\" => \"topics#show\", constraints: {topic_id: /\\d+/}\n>```\n>
\n\n>
\n>Evidence\n>
\n>\n>
\n>Routes map unsubscribe to GET, and the controller action performs a database write. CSRF protection\n>is enabled globally, but GET requests are not protected by authenticity tokens by default, so state\n>changes can be triggered cross-site.\n>
\n>\n> [config/routes.rb[434-442]](https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-v22__PR2__20260312/blob/6669a2d94d76eea3b99b8c476d12b1eb66726b07/config/routes.rb/#L434-L442)\n> [app/controllers/topics_controller.rb[98-116]](https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-v22__PR2__20260312/blob/6669a2d94d76eea3b99b8c476d12b1eb66726b07/app/controllers/topics_controller.rb/#L98-L116)\n> [app/controllers/application_controller.rb[13-33]](https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-v22__PR2__20260312/blob/6669a2d94d76eea3b99b8c476d12b1eb66726b07/app/controllers/application_controller.rb/#L13-L33)\n>
\n\n>
\n>Agent prompt\n>
\n>\n>```\n>The issue below was found during a code review. Follow the provided context and guidance below and implement a solution\n>\n>### Issue description\n>`GET /t/.../unsubscribe` performs a write to `TopicUser`, which is CSRF/prefetch vulnerable.\n>\n>### Issue Context\n>Rails CSRF protection is enabled, but GET requests are not expected to mutate state and won't require an authenticity token.\n>\n>### Fix Focus Areas\n>- config/routes.rb[434-442]\n>- app/controllers/topics_controller.rb[98-116]\n>\n>### Suggested fix\n>- Change the mutating endpoint to `POST`/`PUT` and require CSRF token.\n>- Keep the email link as a GET that only renders a confirmation page (or includes a signed one-time token that is validated server-side before applying the change).\n>- If keeping one-click behavior, implement a signed token parameter validated in `TopicsController#unsubscribe` before mutating state, and consider making it idempotent (set to muted/regular explicitly rather than toggling).\n>```\n> \u24d8 Copy this prompt and use it to remediate the issue with your preferred AI generation tools\n>
\n\n
\n
\n\n\n
\n 3. Unsubscribe template mismatch \ud83d\udc1e Bug \u2713 Correctness\n\n
\n\n>
\n>Description\n>
\n>\n>
\n>The Ember router defines a top-level topicUnsubscribe route, but the added template is\n>templates/topic/unsubscribe.hbs and the route/controller don\u2019t override templateName, so the\n>unsubscribe UI template will not be found/rendered. Additionally, the existing topic template has\n>no {{outlet}}, so the new template also can\u2019t render as a nested child of topic.\n>
\n>
\n\n>
\n>Code\n>
\n>\n>[app/assets/javascripts/discourse/routes/app-route-map.js.es6[13]](https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-v22__PR2__20260312/pull/1/files#diff-b16ca8ad4e61ae3156c5018333ab78ceaf8cc26448af010f6849d2d20084237fR13-R13)\n>\n>```diff\n>+ this.route('topicUnsubscribe', { path: '/t/:slug/:id/unsubscribe' });\n>```\n>
\n\n>
\n>Evidence\n>
\n>\n>
\n>Top-level routes in this codebase use root-level templates (e.g., queued-posts ->\n>templates/queued-posts.hbs). Here, the route is top-level (topicUnsubscribe), but the template\n>was added under templates/topic/... and no code maps the route to that template; the topic\n>template also lacks an outlet for nested rendering.\n>
\n>\n> [app/assets/javascripts/discourse/routes/app-route-map.js.es6[7-14]](https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-v22__PR2__20260312/blob/6669a2d94d76eea3b99b8c476d12b1eb66726b07/app/assets/javascripts/discourse/routes/app-route-map.js.es6/#L7-L14)\n> [app/assets/javascripts/discourse/routes/topic-unsubscribe.js.es6[1-23]](https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-v22__PR2__20260312/blob/6669a2d94d76eea3b99b8c476d12b1eb66726b07/app/assets/javascripts/discourse/routes/topic-unsubscribe.js.es6/#L1-L23)\n> [app/assets/javascripts/discourse/templates/topic/unsubscribe.hbs[1-8]](https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-v22__PR2__20260312/blob/6669a2d94d76eea3b99b8c476d12b1eb66726b07/app/assets/javascripts/discourse/templates/topic/unsubscribe.hbs/#L1-L8)\n> [app/assets/javascripts/discourse/routes/app-route-map.js.es6[102-103]](https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-v22__PR2__20260312/blob/6669a2d94d76eea3b99b8c476d12b1eb66726b07/app/assets/javascripts/discourse/routes/app-route-map.js.es6/#L102-L103)\n> [app/assets/javascripts/discourse/templates/queued-posts.hbs[1-5]](https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-v22__PR2__20260312/blob/6669a2d94d76eea3b99b8c476d12b1eb66726b07/app/assets/javascripts/discourse/templates/queued-posts.hbs/#L1-L5)\n> [app/assets/javascripts/discourse/templates/topic.hbs[1-20]](https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-v22__PR2__20260312/blob/6669a2d94d76eea3b99b8c476d12b1eb66726b07/app/assets/javascripts/discourse/templates/topic.hbs/#L1-L20)\n>
\n\n>
\n>Agent prompt\n>
\n>\n>```\n>The issue below was found during a code review. Follow the provided context and guidance below and implement a solution\n>\n>### Issue description\n>The new `topicUnsubscribe` route is top-level but the template was added at `templates/topic/unsubscribe.hbs`, with no `templateName`/`renderTemplate` override. This will prevent the unsubscribe UI from rendering.\n>\n>### Issue Context\n>Top-level routes in this repo resolve templates at the root (example: `queued-posts` -> `templates/queued-posts.hbs`). The existing `topic` template does not include an `{{outlet}}`, so a nested `topic/unsubscribe` template won\u2019t render inside it either.\n>\n>### Fix Focus Areas\n>- app/assets/javascripts/discourse/routes/app-route-map.js.es6[7-14]\n>- app/assets/javascripts/discourse/routes/topic-unsubscribe.js.es6[1-23]\n>- app/assets/javascripts/discourse/templates/topic/unsubscribe.hbs[1-8]\n>- app/assets/javascripts/discourse/templates/topic.hbs[1-20]\n>\n>### Suggested fix options\n>1) Make it a real top-level page:\n>- Move/rename the template to `app/assets/javascripts/discourse/templates/topic-unsubscribe.hbs` (matching the route\u2019s dasherized name), and keep the top-level route.\n>\n>2) Make it nested under `topic`:\n>- Define the route as `this.route('unsubscribe', { path: '/unsubscribe' })` within the `topic` resource, and add an `{{outlet}}` to `templates/topic.hbs` at the appropriate place so `templates/topic/unsubscribe.hbs` can render.\n>\n>3) Explicit mapping:\n>- Keep current file locations but add `templateName: 'topic/unsubscribe'` via `renderTemplate()` in the route (or controller/view), ensuring it actually resolves.\n>```\n> \u24d8 Copy this prompt and use it to remediate the issue with your preferred AI generation tools\n>
\n\n
\n
\n\n\n\"Grey\n
\u24d8 The new review experience is currently in Beta. Learn more
\n\"Grey\n\n\n\n\n\"Qodo", - "created_at": "2026-03-13T07:01:48Z" + "body": "

Code Review by Qodo

\n\n\ud83d\udc1e Bugs (2) \ud83d\udcd8 Rule violations (0) \ud83d\udcce Requirement gaps (0)\n\n\"Grey\n\n
\n\n\"Action\n\n
\n 1. TestLogger drops contextual logs \ud83d\udc1e Bug \u2713 Correctness\n\n
\n\n>
\n>Description\n>
\n>\n>
\n>TestLogger.FromContext returns a new TestLogger instance, so any code that logs via\n>logger.FromContext(ctx) writes into a fresh logger and the original test logger never sees the call.\n>This breaks log assertions and can silently hide logging regressions for code paths like\n>LoggerMiddleware that now always use FromContext.\n>
\n>
\n\n>
\n>Code\n>
\n>\n>[pkg/plugins/log/fake.go[R46-48]](https://github.com/code-review-benchmark/grafana__grafana__qodo-v22__PR76186__20260312/pull/1/files#diff-2e193f6e338772fc8cac83ae9a80f3dbd280bfa319ab538dd14f21db7b90c64fR46-R48)\n>\n>```diff\n>+func (f *TestLogger) FromContext(_ context.Context) Logger {\n>+\treturn NewTestLogger()\n>+}\n>```\n>
\n\n>
\n>Evidence\n>
\n>\n>
\n>The new LoggerMiddleware logging path calls FromContext(ctx) before logging. TestLogger.FromContext\n>currently returns a brand-new logger, so the log call is recorded on a different instance than the\n>one the test holds.\n>
\n>\n> [pkg/plugins/log/fake.go[46-48]](https://github.com/code-review-benchmark/grafana__grafana__qodo-v22__PR76186__20260312/blob/303cdc2caf256aa5df660611120c2e3f7392365d/pkg/plugins/log/fake.go/#L46-L48)\n> [pkg/services/pluginsintegration/clientmiddleware/logger_middleware.go[49-59]](https://github.com/code-review-benchmark/grafana__grafana__qodo-v22__PR76186__20260312/blob/303cdc2caf256aa5df660611120c2e3f7392365d/pkg/services/pluginsintegration/clientmiddleware/logger_middleware.go/#L49-L59)\n>
\n\n>
\n>Agent prompt\n>
\n>\n>```\n>The issue below was found during a code review. Follow the provided context and guidance below and implement a solution\n>\n>### Issue description\n>`TestLogger.FromContext` returns a new logger instance, so calls like `logger.FromContext(ctx).Info(...)` do not update the original `TestLogger` held by a test.\n>\n>### Issue Context\n>`LoggerMiddleware` now always logs via `m.logger.FromContext(ctx)`.\n>\n>### Fix Focus Areas\n>- pkg/plugins/log/fake.go[46-48]\n>- pkg/services/pluginsintegration/clientmiddleware/logger_middleware.go[49-59]\n>```\n> \u24d8 Copy this prompt and use it to remediate the issue with your preferred AI generation tools\n>
\n\n
\n
\n\n\n
\n\n\"Remediation\n\n
\n 2. TraceID logging becomes optional \ud83d\udc1e Bug \u2727 Quality\n\n
\n\n>
\n>Description\n>
\n>\n>
\n>LoggerMiddleware no longer appends traceID directly and now relies on logger.FromContext(ctx), so\n>traceID is only logged when the tracing package has registered a contextual log provider.\n>tracing.InitializeTracerForTest does not register that provider, so plugin request logs in tests\n>(and any setups not calling tracing.ProvideService) lose trace correlation even when spans exist in\n>the context.\n>
\n>
\n\n>
\n>Code\n>
\n>\n>[pkg/services/pluginsintegration/clientmiddleware/logger_middleware.go[R49-59]](https://github.com/code-review-benchmark/grafana__grafana__qodo-v22__PR76186__20260312/pull/1/files#diff-84652ecfb68ac98b8a312312bb54394327cab0a5c0c3a0cc214c24d1ab2c0a08R49-R59)\n>\n>```diff\n> \tlogParams := []any{\n> \t\t\"status\", status,\n> \t\t\"duration\", time.Since(start),\n>-\t\t\"pluginId\", pluginCtx.PluginID,\n>-\t\t\"endpoint\", endpoint,\n> \t\t\"eventName\", \"grafana-data-egress\",\n> \t\t\"time_before_plugin_request\", timeBeforePluginRequest,\n> \t}\n>-\n>-\tif pluginCtx.User != nil {\n>-\t\tlogParams = append(logParams, \"uname\", pluginCtx.User.Login)\n>-\t}\n>-\n>-\ttraceID := tracing.TraceIDFromContext(ctx, false)\n>-\tif traceID != \"\" {\n>-\t\tlogParams = append(logParams, \"traceID\", traceID)\n>-\t}\n>-\n>-\tif pluginCtx.DataSourceInstanceSettings != nil {\n>-\t\tlogParams = append(logParams, \"dsName\", pluginCtx.DataSourceInstanceSettings.Name)\n>-\t\tlogParams = append(logParams, \"dsUID\", pluginCtx.DataSourceInstanceSettings.UID)\n>-\t}\n>-\n> \tif status == statusError {\n> \t\tlogParams = append(logParams, \"error\", err)\n> \t}\n>-\n>-\tm.logger.Info(\"Plugin Request Completed\", logParams...)\n>+\tm.logger.FromContext(ctx).Info(\"Plugin Request Completed\", logParams...)\n> \treturn err\n>```\n>
\n\n>
\n>Evidence\n>
\n>\n>
\n>LoggerMiddleware now logs through Logger.FromContext(ctx) without adding trace fields itself; the\n>traceID key is only injected if a tracing contextual log provider has been registered. That provider\n>is registered in tracing.ProvideService, but the test helper InitializeTracerForTest does not\n>register it, so FromContext will not add traceID in tests.\n>
\n>\n> [pkg/services/pluginsintegration/clientmiddleware/logger_middleware.go[36-59]](https://github.com/code-review-benchmark/grafana__grafana__qodo-v22__PR76186__20260312/blob/303cdc2caf256aa5df660611120c2e3f7392365d/pkg/services/pluginsintegration/clientmiddleware/logger_middleware.go/#L36-L59)\n> [pkg/infra/tracing/tracing.go[85-102]](https://github.com/code-review-benchmark/grafana__grafana__qodo-v22__PR76186__20260312/blob/303cdc2caf256aa5df660611120c2e3f7392365d/pkg/infra/tracing/tracing.go/#L85-L102)\n> [pkg/infra/tracing/test_helper.go[17-30]](https://github.com/code-review-benchmark/grafana__grafana__qodo-v22__PR76186__20260312/blob/303cdc2caf256aa5df660611120c2e3f7392365d/pkg/infra/tracing/test_helper.go/#L17-L30)\n> [pkg/services/pluginsintegration/clientmiddleware/contextual_logger_middleware.go[26-37]](https://github.com/code-review-benchmark/grafana__grafana__qodo-v22__PR76186__20260312/blob/303cdc2caf256aa5df660611120c2e3f7392365d/pkg/services/pluginsintegration/clientmiddleware/contextual_logger_middleware.go/#L26-L37)\n>
\n\n>
\n>Agent prompt\n>
\n>\n>```\n>The issue below was found during a code review. Follow the provided context and guidance below and implement a solution\n>\n>### Issue description\n>`LoggerMiddleware` now relies on `logger.FromContext(ctx)` to include trace correlation fields. However, the traceID contextual provider is only registered in `tracing.ProvideService`, while `tracing.InitializeTracerForTest` does not register it; as a result, traceID is missing from plugin request logs in tests/custom setups even when a span exists in `ctx`.\n>\n>### Issue Context\n>`ContextualLoggerMiddleware` only adds plugin/datasource/user attributes via `log.WithContextualAttributes` and does not add traceID.\n>\n>### Fix Focus Areas\n>- pkg/services/pluginsintegration/clientmiddleware/logger_middleware.go[36-59]\n>- pkg/infra/tracing/test_helper.go[17-30]\n>- pkg/infra/tracing/tracing.go[85-102]\n>- pkg/services/pluginsintegration/clientmiddleware/contextual_logger_middleware.go[26-37]\n>```\n> \u24d8 Copy this prompt and use it to remediate the issue with your preferred AI generation tools\n>
\n\n
\n
\n\n\n\"Grey\n
\u24d8 The new review experience is currently in Beta. Learn more
\n\"Grey\n\n\n\n\n\"Qodo", + "created_at": "2026-03-13T07:01:20Z" } ] }, { "tool": "qodo-extended", - "repo_name": "discourse__discourse-graphite__qodo-extended__PR2__20260312", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-extended__PR2__20260312/pull/1", + "repo_name": "grafana__grafana__qodo-extended__PR76186__20260312", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__qodo-extended__PR76186__20260312/pull/1", "review_comments": [ { - "path": "app/controllers/topics_controller.rb", - "line": 111, - "body": "\"Action\n\n1\\. Nil topicuser crashes \ud83d\udc1e Bug \u2713 Correctness\n\n
\nTopicsController#unsubscribe calls tu.notification_level even when TopicUser.find_by returns nil,\ncausing a 500 for users without a topic_users row. It can also raise when notification_level is NULL\nbecause the code compares nil to an integer.\n
\n\n\n
\nAgent Prompt\n\n```\n### Issue description\n`TopicsController#unsubscribe` assumes a `TopicUser` row exists and that `notification_level` is non-NULL. If either is untrue, the action raises and the unsubscribe page returns 500.\n\n### Issue Context\nThe model code indicates `notification_level` can be NULL (COALESCE usage). The controller uses `find_by` and then compares/reads `notification_level` without guarding.\n\n### Fix Focus Areas\n- app/controllers/topics_controller.rb[98-115]\n- app/models/topic_user.rb[74-107]\n\n### Suggested direction\n- Replace the direct `find_by` + manual mutation with `TopicUser.change(current_user.id, params[:topic_id], notification_level: ...)`, selecting the desired new level based on a safe current level (e.g., `tu&.notification_level || TopicUser.notification_levels[:regular]`).\n- If keeping `find_by`, initialize when nil and treat nil `notification_level` as `:regular` before comparing.\n```\n\n\u24d8 Copy this prompt and use it to remediate the issue with your preferred AI generation tools\n
", - "created_at": "2026-03-13T09:04:29Z" - }, - { - "path": "config/routes.rb", - "line": 441, - "body": "\"Action\n\n2\\. Get unsubscribe enables csrf \ud83d\udc1e Bug \u26e8 Security\n\n
\nThe unsubscribe action is routed via GET but mutates database state (changes and saves\nTopicUser.notification_level). Since Rails skips CSRF verification for GET requests, any external\npage can silently unsubscribe a logged-in user via an embedded image tag or link, and email client\nprefetchers will also trigger the mutation.\n
\n\n\n
\nAgent Prompt\n\n```\n## Issue description\nThe unsubscribe action uses GET but mutates server state, violating HTTP semantics and bypassing CSRF protection. This allows silent unsubscription via embedded images or link prefetchers.\n\n## Issue Context\nRails skips CSRF verification for GET requests by design. The unsubscribe link is embedded in emails where prefetchers commonly follow links.\n\n## Fix Focus Areas\n- config/routes.rb[440-441]\n- app/controllers/topics_controller.rb[98-116]\n```\n\n\u24d8 Copy this prompt and use it to remediate the issue with your preferred AI generation tools\n
", - "created_at": "2026-03-13T09:04:29Z" - }, - { - "path": "app/assets/javascripts/discourse/components/dropdown-button.js.es6", - "line": 29, - "body": "\"Remediation\n\n3\\. Xss via unescaped title \ud83d\udc1e Bug \u26e8 Security\n\n
\nThe title property is concatenated directly into an HTML string via `buffer.push("<h4\nclass='title'>" + title + "</h4>")` without any escaping, enabling cross-site scripting if the title\ncontains user-controlled content such as a topic title with embedded HTML.\n
\n\n\n
\nAgent Prompt\n\n```\n## Issue description\nThe `title` property is inserted into HTML via string concatenation without escaping, allowing XSS if the value contains user-controlled HTML.\n\n## Issue Context\nThe `dropdown-button` component is a generic reusable component. The `renderString` method builds HTML manually.\n\n## Fix Focus Areas\n- app/assets/javascripts/discourse/components/dropdown-button.js.es6[29-29]\n```\n\n\u24d8 Copy this prompt and use it to remediate the issue with your preferred AI generation tools\n
", - "created_at": "2026-03-13T09:04:29Z" + "path": "pkg/plugins/log/fake.go", + "line": 48, + "body": "\"Action\n\n1\\. Testlogger.fromcontext discards receiver state \ud83d\udc1e Bug \u2713 Correctness\n\n
\nTestLogger.FromContext returns a brand-new disconnected TestLogger via NewTestLogger() instead\nof the receiver. Since LoggerMiddleware.logRequest now calls\nm.logger.FromContext(ctx).Info(...), any test injecting a TestLogger will silently lose all log\noutput to an ephemeral instance, making log assertions always see zero recorded logs.\n
\n\n\n
\nAgent Prompt\n\n```\n## Issue description\n`TestLogger.FromContext` returns a brand-new `TestLogger` via `NewTestLogger()`, discarding the receiver. This means any logs written through the returned logger are lost to an ephemeral instance that no test code holds a reference to.\n\n## Issue Context\n`LoggerMiddleware.logRequest` calls `m.logger.FromContext(ctx).Info(...)`. In tests, `m.logger` is a `TestLogger`. Since `FromContext` returns a new disconnected instance, `Info` logs go to the new instance and the original `TestLogger`'s `InfoLogs` field remains empty.\n\n## Fix Focus Areas\n- pkg/plugins/log/fake.go[46-48]\n```\n\n\u24d8 Copy this prompt and use it to remediate the issue with your preferred AI generation tools\n
", + "created_at": "2026-03-13T08:58:54Z" }, { "path": null, "line": null, "body": "/agentic_review", - "created_at": "2026-03-13T08:47:23Z" + "created_at": "2026-03-13T08:47:14Z" }, { "path": null, "line": null, - "body": "

Code Review by Qodo

\n\n\ud83d\udc1e Bugs (3) \ud83d\udcd8 Rule violations (0) \ud83d\udcce Requirement gaps (0)\n\n\"Grey\n\n
\n\n\"Action\n\n
\n 1. Nil TopicUser crashes \ud83d\udc1e Bug \u2713 Correctness\n\n
\n\n>
\n>Description\n>
\n>\n>
\n>TopicsController#unsubscribe calls tu.notification_level even when TopicUser.find_by returns nil,\n>causing a 500 for users without a topic_users row. It can also raise when notification_level is NULL\n>because the code compares nil to an integer.\n>
\n>
\n\n>
\n>Code\n>
\n>\n>[app/controllers/topics_controller.rb[R105-111]](https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-extended__PR2__20260312/pull/1/files#diff-a672ba5c68f4e7842932ef4e0c0893ef55b981e205a2b85490c4ff4363760811R105-R111)\n>\n>```diff\n>+ tu = TopicUser.find_by(user_id: current_user.id, topic_id: params[:topic_id])\n>+\n>+ if tu.notification_level > TopicUser.notification_levels[:regular]\n>+ tu.notification_level = TopicUser.notification_levels[:regular]\n>+ else\n>+ tu.notification_level = TopicUser.notification_levels[:muted]\n>+ end\n>```\n>
\n\n>
\n>Evidence\n>
\n>\n>
\n>The unsubscribe action fetches a TopicUser row with find_by and immediately dereferences\n>notification_level with no nil guard. Separately, TopicUser code explicitly treats\n>notification_level as nullable via COALESCE(..., :regular), proving NULL is a supported state and\n>making the integer comparison unsafe.\n>
\n>\n> [app/controllers/topics_controller.rb[98-115]](https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-extended__PR2__20260312/blob/6669a2d94d76eea3b99b8c476d12b1eb66726b07/app/controllers/topics_controller.rb/#L98-L115)\n> [app/models/topic_user.rb[8-13]](https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-extended__PR2__20260312/blob/6669a2d94d76eea3b99b8c476d12b1eb66726b07/app/models/topic_user.rb/#L8-L13)\n> [app/models/topic_user.rb[68-72]](https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-extended__PR2__20260312/blob/6669a2d94d76eea3b99b8c476d12b1eb66726b07/app/models/topic_user.rb/#L68-L72)\n>
\n\n>
\n>Agent prompt\n>
\n>\n>```\n>The issue below was found during a code review. Follow the provided context and guidance below and implement a solution\n>\n>### Issue description\n>`TopicsController#unsubscribe` assumes a `TopicUser` row exists and that `notification_level` is non-NULL. If either is untrue, the action raises and the unsubscribe page returns 500.\n>\n>### Issue Context\n>The model code indicates `notification_level` can be NULL (COALESCE usage). The controller uses `find_by` and then compares/reads `notification_level` without guarding.\n>\n>### Fix Focus Areas\n>- app/controllers/topics_controller.rb[98-115]\n>- app/models/topic_user.rb[74-107]\n>\n>### Suggested direction\n>- Replace the direct `find_by` + manual mutation with `TopicUser.change(current_user.id, params[:topic_id], notification_level: ...)`, selecting the desired new level based on a safe current level (e.g., `tu&.notification_level || TopicUser.notification_levels[:regular]`).\n>- If keeping `find_by`, initialize when nil and treat nil `notification_level` as `:regular` before comparing.\n>```\n> \u24d8 Copy this prompt and use it to remediate the issue with your preferred AI generation tools\n>
\n\n
\n
\n\n\n
\n 2. GET unsubscribe enables CSRF \ud83d\udc1e Bug \u26e8 Security\n\n
\n\n>
\n>Description\n>
\n>\n>
\n>The unsubscribe action is routed via GET but mutates database state (changes and saves\n>TopicUser.notification_level). Since Rails skips CSRF verification for GET requests, any external\n>page can silently unsubscribe a logged-in user via an embedded image tag or link, and email client\n>prefetchers will also trigger the mutation.\n>
\n>
\n\n>
\n>Code\n>
\n>\n>[config/routes.rb[R440-441]](https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-extended__PR2__20260312/pull/1/files#diff-959bc9abc46a55332bb64d5155a79323afa75a50ec1a2137ddd22d926f62c6c5R440-R441)\n>\n>```diff\n>+ get \"t/:slug/:topic_id/unsubscribe\" => \"topics#unsubscribe\", constraints: {topic_id: /\\d+/}\n>+ get \"t/:topic_id/unsubscribe\" => \"topics#unsubscribe\", constraints: {topic_id: /\\d+/}\n>```\n>
\n\n>
\n>Evidence\n>
\n>\n>
\n>The routes are defined as GET (config/routes.rb lines 440-441), and the controller action writes to\n>the database (topics_controller.rb lines 107-113 with tu.save!). Rails does not verify CSRF tokens\n>on GET requests. An attacker can craft `<img\n>src='https://forum.example.com/t/slug/123/unsubscribe'>` to silently change notification settings.\n>Email client link prefetchers will also trigger the state change when users receive the unsubscribe\n>email.\n>
\n>\n> [config/routes.rb[440-441]](https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-extended__PR2__20260312/blob/6669a2d94d76eea3b99b8c476d12b1eb66726b07/config/routes.rb/#L440-L441)\n> [app/controllers/topics_controller.rb[107-113]](https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-extended__PR2__20260312/blob/6669a2d94d76eea3b99b8c476d12b1eb66726b07/app/controllers/topics_controller.rb/#L107-L113)\n>
\n\n>
\n>Agent prompt\n>
\n>\n>```\n>The issue below was found during a code review. Follow the provided context and guidance below and implement a solution\n>\n>## Issue description\n>The unsubscribe action uses GET but mutates server state, violating HTTP semantics and bypassing CSRF protection. This allows silent unsubscription via embedded images or link prefetchers.\n>\n>## Issue Context\n>Rails skips CSRF verification for GET requests by design. The unsubscribe link is embedded in emails where prefetchers commonly follow links.\n>\n>## Fix Focus Areas\n>- config/routes.rb[440-441]\n>- app/controllers/topics_controller.rb[98-116]\n>```\n> \u24d8 Copy this prompt and use it to remediate the issue with your preferred AI generation tools\n>
\n\n
\n
\n\n\n
\n\n\"Remediation\n\n
\n 3. XSS via unescaped title \ud83d\udc1e Bug \u26e8 Security\n\n
\n\n>
\n>Description\n>
\n>\n>
\n>The title property is concatenated directly into an HTML string via `buffer.push("<h4\n>class='title'>" + title + "</h4>")` without any escaping, enabling cross-site scripting if the title\n>contains user-controlled content such as a topic title with embedded HTML.\n>
\n>
\n\n>
\n>Code\n>
\n>\n>[app/assets/javascripts/discourse/components/dropdown-button.js.es6[29]](https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-extended__PR2__20260312/pull/1/files#diff-7d81c91fa56796637c237b445239de5663a7808892aa7808f55be51d849bd188R29-R29)\n>\n>```diff\n>+ buffer.push(\"

\" + title + \"

\");\n>```\n>
\n\n>
\n>Evidence\n>
\n>\n>
\n>The renderString method builds HTML via raw string concatenation with no escaping applied to the\n>title value. This component is used generically (e.g., in the new topic-unsubscribe template which\n>renders topic-notifications-button). If the title property contains HTML from a user-controlled\n>source like a topic title, it will be rendered as raw HTML in the DOM.\n>
\n>\n> [app/assets/javascripts/discourse/components/dropdown-button.js.es6[27-30]](https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-extended__PR2__20260312/blob/6669a2d94d76eea3b99b8c476d12b1eb66726b07/app/assets/javascripts/discourse/components/dropdown-button.js.es6/#L27-L30)\n>
\n\n>
\n>Agent prompt\n>
\n>\n>```\n>The issue below was found during a code review. Follow the provided context and guidance below and implement a solution\n>\n>## Issue description\n>The `title` property is inserted into HTML via string concatenation without escaping, allowing XSS if the value contains user-controlled HTML.\n>\n>## Issue Context\n>The `dropdown-button` component is a generic reusable component. The `renderString` method builds HTML manually.\n>\n>## Fix Focus Areas\n>- app/assets/javascripts/discourse/components/dropdown-button.js.es6[29-29]\n>```\n> \u24d8 Copy this prompt and use it to remediate the issue with your preferred AI generation tools\n>
\n\n
\n
\n\n\n\"Grey\n
\u24d8 The new review experience is currently in Beta. Learn more
\n\"Grey\n\n\n\n\n\"Qodo", - "created_at": "2026-03-13T08:47:30Z" + "body": "

Code Review by Qodo

\n\n\ud83d\udc1e Bugs (1) \ud83d\udcd8 Rule violations (0) \ud83d\udcce Requirement gaps (0)\n\n\"Grey\n\n
\n\n\"Action\n\n
\n 1. TestLogger.FromContext discards receiver state \ud83d\udc1e Bug \u2713 Correctness\n\n
\n\n>
\n>Description\n>
\n>\n>
\n>TestLogger.FromContext returns a brand-new disconnected TestLogger via NewTestLogger() instead\n>of the receiver. Since LoggerMiddleware.logRequest now calls\n>m.logger.FromContext(ctx).Info(...), any test injecting a TestLogger will silently lose all log\n>output to an ephemeral instance, making log assertions always see zero recorded logs.\n>
\n>
\n\n>
\n>Code\n>
\n>\n>[pkg/plugins/log/fake.go[R46-48]](https://github.com/code-review-benchmark/grafana__grafana__qodo-extended__PR76186__20260312/pull/1/files#diff-2e193f6e338772fc8cac83ae9a80f3dbd280bfa319ab538dd14f21db7b90c64fR46-R48)\n>\n>```diff\n>+func (f *TestLogger) FromContext(_ context.Context) Logger {\n>+\treturn NewTestLogger()\n>+}\n>```\n>
\n\n>
\n>Evidence\n>
\n>\n>
\n>The LoggerMiddleware.logRequest method now writes logs via m.logger.FromContext(ctx).Info(...). In\n>production, grafanaInfraLogWrapper.FromContext correctly wraps the underlying ConcreteLogger. But\n>TestLogger.FromContext returns NewTestLogger() \u2014 a completely new instance with no connection to\n>the receiver f. Any test that holds a reference to a TestLogger, injects it into LoggerMiddleware,\n>and then checks f.InfoLogs after a request will find zero logs because the output went to the\n>ephemeral logger returned by FromContext.\n>
\n>\n> [pkg/plugins/log/fake.go[46-48]](https://github.com/code-review-benchmark/grafana__grafana__qodo-extended__PR76186__20260312/blob/303cdc2caf256aa5df660611120c2e3f7392365d/pkg/plugins/log/fake.go/#L46-L48)\n> [pkg/services/pluginsintegration/clientmiddleware/logger_middleware.go[58]](https://github.com/code-review-benchmark/grafana__grafana__qodo-extended__PR76186__20260312/blob/303cdc2caf256aa5df660611120c2e3f7392365d/pkg/services/pluginsintegration/clientmiddleware/logger_middleware.go/#L58-L58)\n> [pkg/plugins/log/logger.go[48-55]](https://github.com/code-review-benchmark/grafana__grafana__qodo-extended__PR76186__20260312/blob/303cdc2caf256aa5df660611120c2e3f7392365d/pkg/plugins/log/logger.go/#L48-L55)\n>
\n\n>
\n>Agent prompt\n>
\n>\n>```\n>The issue below was found during a code review. Follow the provided context and guidance below and implement a solution\n>\n>## Issue description\n>`TestLogger.FromContext` returns a brand-new `TestLogger` via `NewTestLogger()`, discarding the receiver. This means any logs written through the returned logger are lost to an ephemeral instance that no test code holds a reference to.\n>\n>## Issue Context\n>`LoggerMiddleware.logRequest` calls `m.logger.FromContext(ctx).Info(...)`. In tests, `m.logger` is a `TestLogger`. Since `FromContext` returns a new disconnected instance, `Info` logs go to the new instance and the original `TestLogger`'s `InfoLogs` field remains empty.\n>\n>## Fix Focus Areas\n>- pkg/plugins/log/fake.go[46-48]\n>```\n> \u24d8 Copy this prompt and use it to remediate the issue with your preferred AI generation tools\n>
\n\n
\n
\n\n\n\"Grey\n
\u24d8 The new review experience is currently in Beta. Learn more
\n\"Grey\n\n\n\n\n\"Qodo", + "created_at": "2026-03-13T08:47:21Z" } ] }, { "tool": "cubic-v2", - "repo_name": "discourse__discourse-graphite__cubic-v2__PR2__20260313", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__cubic-v2__PR2__20260313/pull/1", + "repo_name": "grafana__grafana__cubic-v2__PR76186__20260313", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__cubic-v2__PR76186__20260313/pull/1", "review_comments": [ { - "path": "config/locales/client.en.yml", - "line": 985, - "body": "\nP1: Rendering `{{title}}` inside this HTML translation creates an XSS path on the unsubscribe page because the title is interpolated and inserted with triple-stash without escaping.\n\n
\nPrompt for AI agents\n\n```text\nCheck if this issue is valid \u2014 if so, understand the root cause and fix it. At config/locales/client.en.yml, line 985:\n\nRendering `{{title}}` inside this HTML translation creates an XSS path on the unsubscribe page because the title is interpolated and inserted with triple-stash without escaping.\n\n\n@@ -981,6 +981,9 @@ en:\n \n topic:\n+ unsubscribe:\n+ stop_notifications: \"You will stop receiving notifications for {{title}}.\"\n+ change_notification_state: \"You can change your notification state\"\n filter_to: \"{{post_count}} posts in topic\"\n\n```\n\n
\n\n\n \n \n \n \"Fix\n \n", - "created_at": "2026-03-13T14:08:44Z" + "path": "pkg/services/pluginsintegration/clientmiddleware/contextual_logger_middleware.go", + "line": 41, + "body": "\nP1: Accessing `req.PluginContext` without a nil guard will panic if `req` is nil. The `LoggerMiddleware` in this same package guards against nil requests in all four methods \u2014 this middleware should do the same.\n\n
\nPrompt for AI agents\n\n```text\nCheck if this issue is valid \u2014 if so, understand the root cause and fix it. At pkg/services/pluginsintegration/clientmiddleware/contextual_logger_middleware.go, line 39:\n\nAccessing `req.PluginContext` without a nil guard will panic if `req` is nil. The `LoggerMiddleware` in this same package guards against nil requests in all four methods \u2014 this middleware should do the same.\n\n\n@@ -0,0 +1,69 @@\n+\treturn log.WithContextualAttributes(ctx, p)\n+}\n+\n+func (m *ContextualLoggerMiddleware) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {\n+\tctx = instrumentContext(ctx, endpointQueryData, req.PluginContext)\n+\treturn m.next.QueryData(ctx, req)\n\n```\n\n
\n\n```suggestion\nfunc (m *ContextualLoggerMiddleware) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {\n\tif req == nil {\n\t\treturn m.next.QueryData(ctx, req)\n\t}\n\tctx = instrumentContext(ctx, endpointQueryData, req.PluginContext)\n\treturn m.next.QueryData(ctx, req)\n}\n```\n\n\n \n \n \n \"Fix\n \n", + "created_at": "2026-03-13T14:04:27Z" + }, + { + "path": "pkg/services/pluginsintegration/clientmiddleware/contextual_logger_middleware.go", + "line": 28, + "body": "\nP2: The contextual log attributes omit `traceID`. Since this middleware now owns the contextual logging setup (moved out of the instrumentation/metrics middleware), consider including `traceID` from `tracing.TraceIDFromContext` so that plugin request logs emitted by `LoggerMiddleware` still carry trace correlation.\n\n
\nPrompt for AI agents\n\n```text\nCheck if this issue is valid \u2014 if so, understand the root cause and fix it. At pkg/services/pluginsintegration/clientmiddleware/contextual_logger_middleware.go, line 28:\n\nThe contextual log attributes omit `traceID`. Since this middleware now owns the contextual logging setup (moved out of the instrumentation/metrics middleware), consider including `traceID` from `tracing.TraceIDFromContext` so that plugin request logs emitted by `LoggerMiddleware` still carry trace correlation.\n\n\n@@ -0,0 +1,69 @@\n+\n+// instrumentContext adds a contextual logger with plugin and request details to the given context.\n+func instrumentContext(ctx context.Context, endpoint string, pCtx backend.PluginContext) context.Context {\n+\tp := []any{\"endpoint\", endpoint, \"pluginId\", pCtx.PluginID}\n+\tif pCtx.DataSourceInstanceSettings != nil {\n+\t\tp = append(p, \"dsName\", pCtx.DataSourceInstanceSettings.Name)\n\n```\n\n
\n\n```suggestion\n\tp := []any{\"endpoint\", endpoint, \"pluginId\", pCtx.PluginID}\n\tif traceID := tracing.TraceIDFromContext(ctx, true); traceID != \"\" {\n\t\tp = append(p, \"traceID\", traceID)\n\t}\n```\n\n\n \n \n \n \"Fix\n \n", + "created_at": "2026-03-13T14:04:28Z" }, { - "path": "app/assets/javascripts/discourse/templates/topic/unsubscribe.hbs", - "line": 1, - "body": "\nP1: This template is named for a nested `topic.unsubscribe` route, but the router/controller/view use the top-level `topicUnsubscribe` route. The new page content will not be picked up unless the template is renamed to match that route.\n\n
\nPrompt for AI agents\n\n```text\nCheck if this issue is valid \u2014 if so, understand the root cause and fix it. At app/assets/javascripts/discourse/templates/topic/unsubscribe.hbs, line 1:\n\nThis template is named for a nested `topic.unsubscribe` route, but the router/controller/view use the top-level `topicUnsubscribe` route. The new page content will not be picked up unless the template is renamed to match that route.\n\n\n@@ -0,0 +1,8 @@\n+
\n+

\n+ {{{stopNotificiationsText}}}\n\n```\n\n

\n\n\n \n \n \n \"Fix\n \n", - "created_at": "2026-03-13T14:08:44Z" + "path": "pkg/plugins/log/fake.go", + "line": 47, + "body": "\nP2: Returning a fresh `TestLogger` here drops all log writes made through `FromContext`, so tests can no longer observe logs emitted by code paths that now use contextual logging.\n\n
\nPrompt for AI agents\n\n```text\nCheck if this issue is valid \u2014 if so, understand the root cause and fix it. At pkg/plugins/log/fake.go, line 47:\n\nReturning a fresh `TestLogger` here drops all log writes made through `FromContext`, so tests can no longer observe logs emitted by code paths that now use contextual logging.\n\n\n@@ -41,6 +43,10 @@ func (f *TestLogger) Error(msg string, ctx ...any) {\n }\n \n+func (f *TestLogger) FromContext(_ context.Context) Logger {\n+\treturn NewTestLogger()\n+}\n+\n\n```\n\n
\n\n```suggestion\n\treturn f\n```\n\n\n \n \n \n \"Fix\n \n", + "created_at": "2026-03-13T14:04:28Z" }, { "path": null, "line": null, - "body": "**2 issues found** across 18 files\n\n
\nPrompt for AI agents (unresolved issues)\n\n```text\n\nCheck if these issues are valid \u2014 if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.\n\n\n\n\n\nP1: Rendering `{{title}}` inside this HTML translation creates an XSS path on the unsubscribe page because the title is interpolated and inserted with triple-stash without escaping.\n\n\n\n\n\nP1: This template is named for a nested `topic.unsubscribe` route, but the router/controller/view use the top-level `topicUnsubscribe` route. The new page content will not be picked up unless the template is renamed to match that route.\n\n```\n\n
\n\nReply with feedback, questions, or to request a fix. Tag `@cubic-dev-ai` to re-run a review.\n\n", - "created_at": "2026-03-13T14:08:44Z" + "body": "**3 issues found** across 8 files\n\n
\nPrompt for AI agents (unresolved issues)\n\n```text\n\nCheck if these issues are valid \u2014 if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.\n\n\n\n\n\nP2: The contextual log attributes omit `traceID`. Since this middleware now owns the contextual logging setup (moved out of the instrumentation/metrics middleware), consider including `traceID` from `tracing.TraceIDFromContext` so that plugin request logs emitted by `LoggerMiddleware` still carry trace correlation.\n\n\nP1: Accessing `req.PluginContext` without a nil guard will panic if `req` is nil. The `LoggerMiddleware` in this same package guards against nil requests in all four methods \u2014 this middleware should do the same.\n\n\n\n\n\nP2: Returning a fresh `TestLogger` here drops all log writes made through `FromContext`, so tests can no longer observe logs emitted by code paths that now use contextual logging.\n\n```\n\n
\n\nReply with feedback, questions, or to request a fix. Tag `@cubic-dev-ai` to re-run a review.\n\n", + "created_at": "2026-03-13T14:04:27Z" }, { "path": null, "line": null, "body": "@cubic-dev-ai", - "created_at": "2026-03-13T14:00:21Z" + "created_at": "2026-03-13T13:59:45Z" }, { "path": null, "line": null, "body": "> @cubic-dev-ai\n\n@ashleyzhang01 I have started the AI code review. It will take a few minutes to complete.", - "created_at": "2026-03-13T14:00:31Z" + "created_at": "2026-03-13T14:01:02Z" } ] }, { "tool": "qodo-extended-summary", - "repo_name": "discourse__discourse-graphite__qodo-extended-summary__PR2__20260313", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-extended-summary__PR2__20260313/pull/1", + "repo_name": "grafana__grafana__qodo-extended-summary__PR76186__20260313", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__qodo-extended-summary__PR76186__20260313/pull/1", "review_comments": [ { "path": null, "line": null, "body": "/agentic_review --review_agent.comments_location_policy=\"summary\"", - "created_at": "2026-03-13T13:55:35Z" + "created_at": "2026-03-13T13:55:01Z" }, { "path": null, "line": null, - "body": "

Code Review by Qodo

\n\n\ud83d\udc1e Bugs (4) \ud83d\udcd8 Rule violations (0) \ud83d\udcce Requirement gaps (0)\n\n\"Grey\n\n
\n\n\"Action\n\n
\n 1. Nil TopicUser crash \ud83d\udc1e Bug \u26ef Reliability\n\n
\n\n>
\n>Description\n>
\n>\n>
\n>TopicsController#unsubscribe dereferences tu.notification_level and tu.save! even when\n>TopicUser.find_by returns nil or when notification_level is NULL, causing a 500 instead of\n>toggling state. This will break the unsubscribe flow for users without an existing topic_users row\n>for that topic.\n>
\n>
\n\n>
\n>Code\n>
\n>\n>[app/controllers/topics_controller.rb[R105-113]](https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-extended-summary__PR2__20260313/pull/1/files#diff-a672ba5c68f4e7842932ef4e0c0893ef55b981e205a2b85490c4ff4363760811R105-R113)\n>\n>```diff\n>+ tu = TopicUser.find_by(user_id: current_user.id, topic_id: params[:topic_id])\n>+\n>+ if tu.notification_level > TopicUser.notification_levels[:regular]\n>+ tu.notification_level = TopicUser.notification_levels[:regular]\n>+ else\n>+ tu.notification_level = TopicUser.notification_levels[:muted]\n>+ end\n>+\n>+ tu.save!\n>```\n>
\n\n>
\n>Evidence\n>
\n>\n>
\n>The unsubscribe action assumes a TopicUser row exists and that notification_level is non-null. The\n>codebase already has an upsert-style helper (TopicUser.change) that explicitly creates a TopicUser\n>when update_all affects 0 rows, proving absence of a row is a supported/expected state.\n>
\n>\n> [app/controllers/topics_controller.rb[98-116]](https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-extended-summary__PR2__20260313/blob/6669a2d94d76eea3b99b8c476d12b1eb66726b07/app/controllers/topics_controller.rb/#L98-L116)\n> [app/models/topic_user.rb[95-107]](https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-extended-summary__PR2__20260313/blob/6669a2d94d76eea3b99b8c476d12b1eb66726b07/app/models/topic_user.rb/#L95-L107)\n>
\n\n>
\n>Agent prompt\n>
\n>\n>```\n>The issue below was found during a code review. Follow the provided context and guidance below and implement a solution\n>\n>### Issue description\n>`TopicsController#unsubscribe` can raise `NoMethodError` (tu is nil) or fail comparison when `tu.notification_level` is NULL.\n>\n>### Issue Context\n>The codebase already supports creating a `TopicUser` record when missing via `TopicUser.change`.\n>\n>### Fix Focus Areas\n>- app/controllers/topics_controller.rb[98-116]\n>- app/models/topic_user.rb[80-118]\n>```\n> \u24d8 Copy this prompt and use it to remediate the issue with your preferred AI generation tools\n>
\n\n
\n
\n\n\n
\n 2. Unsubscribe requires login \ud83d\udc1e Bug \u2713 Correctness\n\n
\n\n>
\n>Description\n>
\n>\n>
\n>The unsubscribe link added to notification emails routes to TopicsController#unsubscribe, but that\n>action is protected by ensure_logged_in, so logged-out users clicking from email cannot\n>unsubscribe. This defeats the primary email-driven unsubscribe path.\n>
\n>
\n\n>
\n>Code\n>
\n>\n>[app/controllers/topics_controller.rb[R24-28]](https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-extended-summary__PR2__20260313/pull/1/files#diff-a672ba5c68f4e7842932ef4e0c0893ef55b981e205a2b85490c4ff4363760811R24-R28)\n>\n>```diff\n> :bulk,\n> :reset_new,\n> :change_post_owners,\n>- :bookmark]\n>+ :bookmark,\n>+ :unsubscribe]\n>```\n>
\n\n>
\n>Evidence\n>
\n>\n>
\n>The controller explicitly requires authentication for the unsubscribe action, and ensure_logged_in\n>raises when there is no current_user, preventing the email link from functioning for typical\n>email-client sessions (no active web session).\n>
\n>\n> [app/controllers/topics_controller.rb[8-28]](https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-extended-summary__PR2__20260313/blob/6669a2d94d76eea3b99b8c476d12b1eb66726b07/app/controllers/topics_controller.rb/#L8-L28)\n> [app/controllers/application_controller.rb[408-410]](https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-extended-summary__PR2__20260313/blob/6669a2d94d76eea3b99b8c476d12b1eb66726b07/app/controllers/application_controller.rb/#L408-L410)\n>
\n\n>
\n>Agent prompt\n>
\n>\n>```\n>The issue below was found during a code review. Follow the provided context and guidance below and implement a solution\n>\n>### Issue description\n>The per-topic unsubscribe endpoint is gated by `ensure_logged_in`, so users clicking from email while logged out cannot complete the unsubscribe.\n>\n>### Issue Context\n>Email links often originate from clients without session cookies; the controller currently raises `Discourse::NotLoggedIn` when `current_user` is absent.\n>\n>### Fix Focus Areas\n>- app/controllers/topics_controller.rb[6-33]\n>- app/controllers/topics_controller.rb[98-116]\n>- app/controllers/application_controller.rb[408-410]\n>```\n> \u24d8 Copy this prompt and use it to remediate the issue with your preferred AI generation tools\n>
\n\n
\n
\n\n\n
\n 3. State modification via GET request \ud83d\udc1e Bug \u26e8 Security\n\n
\n\n>
\n>Description\n>
\n>\n>
\n>The unsubscribe action modifies TopicUser.notification_level and calls save!, but is routed\n>via HTTP GET. Rails CSRF protection does not cover GET requests, allowing attackers to forge\n>unsubscribe requests via embedded <img> tags or link prefetching on any page a logged-in user\n>visits.\n>
\n>
\n\n>
\n>Code\n>
\n>\n>[config/routes.rb[R440-441]](https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-extended-summary__PR2__20260313/pull/1/files#diff-959bc9abc46a55332bb64d5155a79323afa75a50ec1a2137ddd22d926f62c6c5R440-R441)\n>\n>```diff\n>+ get \"t/:slug/:topic_id/unsubscribe\" => \"topics#unsubscribe\", constraints: {topic_id: /\\d+/}\n>+ get \"t/:topic_id/unsubscribe\" => \"topics#unsubscribe\", constraints: {topic_id: /\\d+/}\n>```\n>
\n\n>
\n>Evidence\n>
\n>\n>
\n>The routes use get for the unsubscribe endpoint, but the controller action performs a database\n>write (tu.save!). Rails' protect_from_forgery only verifies CSRF tokens on non-GET methods. An\n>attacker can embed <img src='https://forum.example.com/t/slug/123/unsubscribe'> on any external page\n>to silently unsubscribe logged-in users from topics.\n>
\n>\n> [config/routes.rb[440-441]](https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-extended-summary__PR2__20260313/blob/6669a2d94d76eea3b99b8c476d12b1eb66726b07/config/routes.rb/#L440-L441)\n> [app/controllers/topics_controller.rb[105-113]](https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-extended-summary__PR2__20260313/blob/6669a2d94d76eea3b99b8c476d12b1eb66726b07/app/controllers/topics_controller.rb/#L105-L113)\n>
\n\n>
\n>Agent prompt\n>
\n>\n>```\n>The issue below was found during a code review. Follow the provided context and guidance below and implement a solution\n>\n>## Issue description\n>The unsubscribe routes use HTTP GET but the controller action modifies database state. GET requests bypass Rails CSRF protection, enabling cross-site request forgery attacks.\n>\n>## Issue Context\n>Rails' `protect_from_forgery` only validates CSRF tokens on POST/PUT/DELETE/PATCH requests. State-modifying actions must not use GET to prevent CSRF via image tags, link prefetching, etc.\n>\n>## Fix Focus Areas\n>- config/routes.rb[440-441]\n>- app/controllers/topics_controller.rb[98-116]\n>```\n> \u24d8 Copy this prompt and use it to remediate the issue with your preferred AI generation tools\n>
\n\n
\n
\n\n\n
\n\n\"Remediation\n\n
\n 4. Duplicated respond_instructions in emails \ud83d\udc1e Bug \u2713 Correctness\n\n
\n\n>
\n>Description\n>
\n>\n>
\n>When context_posts are present, the notification email template contains %{respond_instructions}\n>both inside the conditional block (line 6) and unconditionally at the bottom (line 19). Since\n>message_builder.rb uses gsub! which replaces all occurrences, the respond instructions text will\n>appear twice in the rendered email.\n>
\n>
\n\n>
\n>Code\n>
\n>\n>[app/views/email/notification.html.erb[R5-19]](https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-extended-summary__PR2__20260313/pull/1/files#diff-1b8508fec1c958364487379c8a844933b9459839945ac9932edcd336e140dfc5R5-R19)\n>\n>```diff\n>+ <% if context_posts.present? %>\n>+ \n>+\n>+
\n> \n>- <% context_posts.each do |p| %>\n>- <%= render :partial => 'email/post', :locals => {:post => p} %>\n>+

<%= t \"user_notifications.previous_discussion\" %>

\n>+\n>+ <% context_posts.each do |p| %>\n>+ <%= render partial: 'email/post', locals: { post: p } %>\n>+ <% end %>\n> <% end %>\n>-<% end %>\n> \n>-
\n>+
\n>+\n>+ \n>```\n>
\n\n>
\n>Evidence\n>
\n>\n>
\n>The ERB template renders both %{respond_instructions} placeholders into the HTML string when\n>context_posts are present. Then message_builder's gsub! replaces ALL occurrences of the placeholder,\n>causing the instructions to appear twice in the final email.\n>
\n>\n> [app/views/email/notification.html.erb[5-19]](https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-extended-summary__PR2__20260313/blob/6669a2d94d76eea3b99b8c476d12b1eb66726b07/app/views/email/notification.html.erb/#L5-L19)\n> [lib/email/message_builder.rb[63-64]](https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-extended-summary__PR2__20260313/blob/6669a2d94d76eea3b99b8c476d12b1eb66726b07/lib/email/message_builder.rb/#L63-L64)\n>
\n\n>
\n>Agent prompt\n>
\n>\n>```\n>The issue below was found during a code review. Follow the provided context and guidance below and implement a solution\n>\n>## Issue description\n>The email template has two `%{respond_instructions}` placeholders, and `gsub!` replaces all of them, causing duplication when context posts are present.\n>\n>## Issue Context\n>The old template also had this pattern but the refactored version preserves it. Only one instance of respond_instructions should appear in the final email.\n>\n>## Fix Focus Areas\n>- app/views/email/notification.html.erb[5-7]\n>```\n> \u24d8 Copy this prompt and use it to remediate the issue with your preferred AI generation tools\n>
\n\n
\n
\n\n\n\"Grey\n
\u24d8 The new review experience is currently in Beta. Learn more
\n\"Grey\n\n\n\n\n\"Qodo", - "created_at": "2026-03-13T13:55:41Z" + "body": "

Code Review by Qodo

\n\n\ud83d\udc1e Bugs (2) \ud83d\udcd8 Rule violations (0) \ud83d\udcce Requirement gaps (0)\n\n\"Grey\n\n
\n\n\"Action\n\n
\n 1. TestLogger.FromContext loses log state \ud83d\udc1e Bug \u2713 Correctness\n\n
\n\n>
\n>Description\n>
\n>\n>
\n>TestLogger.FromContext returns a brand-new TestLogger instance (NewTestLogger()), discarding all\n>accumulated state. Since LoggerMiddleware.logRequest now calls m.logger.FromContext(ctx).Info(...),\n>the log entry goes to this throwaway instance rather than the original TestLogger that tests hold a\n>reference to, making it impossible to assert on InfoLogs.Calls, InfoLogs.Message, or InfoLogs.Ctx.\n>
\n>
\n\n>
\n>Code\n>
\n>\n>[pkg/plugins/log/fake.go[R46-48]](https://github.com/code-review-benchmark/grafana__grafana__qodo-extended-summary__PR76186__20260313/pull/1/files#diff-2e193f6e338772fc8cac83ae9a80f3dbd280bfa319ab538dd14f21db7b90c64fR46-R48)\n>\n>```diff\n>+func (f *TestLogger) FromContext(_ context.Context) Logger {\n>+\treturn NewTestLogger()\n>+}\n>```\n>
\n\n>
\n>Evidence\n>
\n>\n>
\n>TestLogger.FromContext returns NewTestLogger() (a fresh zero-state instance).\n>LoggerMiddleware.logRequest calls m.logger.FromContext(ctx).Info(...), so the log entry is written\n>to the throwaway instance, not the original TestLogger. Any test that holds a reference to the\n>original TestLogger and asserts on InfoLogs.Calls will see 0 instead of 1.\n>
\n>\n> [pkg/plugins/log/fake.go[46-48]](https://github.com/code-review-benchmark/grafana__grafana__qodo-extended-summary__PR76186__20260313/blob/303cdc2caf256aa5df660611120c2e3f7392365d/pkg/plugins/log/fake.go/#L46-L48)\n> [pkg/services/pluginsintegration/clientmiddleware/logger_middleware.go[58-58]](https://github.com/code-review-benchmark/grafana__grafana__qodo-extended-summary__PR76186__20260313/blob/303cdc2caf256aa5df660611120c2e3f7392365d/pkg/services/pluginsintegration/clientmiddleware/logger_middleware.go/#L58-L58)\n>
\n\n>
\n>Agent prompt\n>
\n>\n>```\n>The issue below was found during a code review. Follow the provided context and guidance below and implement a solution\n>\n>## Issue description\n>TestLogger.FromContext returns a brand-new TestLogger instance, discarding all accumulated state. Any code that calls `logger.FromContext(ctx).Info(...)` will log to a throwaway instance, making test assertions on `InfoLogs.Calls`, `InfoLogs.Message`, or `InfoLogs.Ctx` impossible.\n>\n>## Issue Context\n>The `LoggerMiddleware.logRequest` method was changed to call `m.logger.FromContext(ctx).Info(...)`. When tests use a `TestLogger`, the `FromContext` call creates a new instance, so the log entry is never captured by the original `TestLogger` that tests hold a reference to.\n>\n>## Fix Focus Areas\n>- pkg/plugins/log/fake.go[46-48]\n>```\n> \u24d8 Copy this prompt and use it to remediate the issue with your preferred AI generation tools\n>
\n\n
\n
\n\n\n
\n\n\"Remediation\n\n
\n 2. traceID silently dropped from logs \ud83d\udc1e Bug \u2727 Quality\n\n
\n\n>
\n>Description\n>
\n>\n>
\n>The PR removes the traceID field from the 'Plugin Request Completed' log line. The old\n>logRequest explicitly extracted the trace ID via tracing.TraceIDFromContext and appended it to\n>logParams, but this was removed in the refactor and the new\n>ContextualLoggerMiddleware.instrumentContext does not add it either, breaking trace-to-log\n>correlation.\n>
\n>
\n\n>
\n>Code\n>
\n>\n>[pkg/services/pluginsintegration/clientmiddleware/logger_middleware.go[L61-64]](https://github.com/code-review-benchmark/grafana__grafana__qodo-extended-summary__PR76186__20260313/pull/1/files#diff-84652ecfb68ac98b8a312312bb54394327cab0a5c0c3a0cc214c24d1ab2c0a08L61-L64)\n>\n>```diff\n>-\ttraceID := tracing.TraceIDFromContext(ctx, false)\n>-\tif traceID != \"\" {\n>-\t\tlogParams = append(logParams, \"traceID\", traceID)\n>-\t}\n>```\n>
\n\n>
\n>Evidence\n>
\n>\n>
\n>The old logger_middleware.go extracted traceID and appended it to logParams. The new code removes\n>this entirely. The new contextual_logger_middleware.go instrumentContext function only adds\n>endpoint, pluginId, dsName, dsUID, and uname \u2014 no traceID. This means the 'Plugin Request Completed'\n>log line no longer carries the traceID, breaking the ability to correlate logs with distributed\n>traces.\n>
\n>\n> [pkg/services/pluginsintegration/clientmiddleware/contextual_logger_middleware.go[27-37]](https://github.com/code-review-benchmark/grafana__grafana__qodo-extended-summary__PR76186__20260313/blob/303cdc2caf256aa5df660611120c2e3f7392365d/pkg/services/pluginsintegration/clientmiddleware/contextual_logger_middleware.go/#L27-L37)\n> [pkg/services/pluginsintegration/clientmiddleware/logger_middleware.go[49-57]](https://github.com/code-review-benchmark/grafana__grafana__qodo-extended-summary__PR76186__20260313/blob/303cdc2caf256aa5df660611120c2e3f7392365d/pkg/services/pluginsintegration/clientmiddleware/logger_middleware.go/#L49-L57)\n>
\n\n>
\n>Agent prompt\n>
\n>\n>```\n>The issue below was found during a code review. Follow the provided context and guidance below and implement a solution\n>\n>## Issue description\n>The `traceID` field was removed from the 'Plugin Request Completed' log line during this refactor. This breaks trace-to-log correlation for plugin requests.\n>\n>## Issue Context\n>The old `logRequest` method in `logger_middleware.go` called `tracing.TraceIDFromContext(ctx, false)` and appended the result to `logParams`. This was removed in the refactor. The new `instrumentContext` in `contextual_logger_middleware.go` does not add traceID to the context attributes.\n>\n>## Fix Focus Areas\n>- pkg/services/pluginsintegration/clientmiddleware/logger_middleware.go[36-59]\n>- pkg/services/pluginsintegration/clientmiddleware/contextual_logger_middleware.go[27-37]\n>```\n> \u24d8 Copy this prompt and use it to remediate the issue with your preferred AI generation tools\n>
\n\n
\n
\n\n\n\"Grey\n
\u24d8 The new review experience is currently in Beta. Learn more
\n\"Grey\n\n\n\n\n\"Qodo", + "created_at": "2026-03-13T13:55:09Z" } ] }, { "tool": "macroscope", - "repo_name": "discourse__discourse-graphite__macroscope__PR2__20260309", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__macroscope__PR2__20260309/pull/1", + "repo_name": "grafana__grafana__macroscope__PR76186__20260309", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__macroscope__PR76186__20260309/pull/1", "review_comments": [ { - "path": "app/controllers/topics_controller.rb", - "line": 113, - "body": "\ud83d\udd34 **Critical** `controllers/topics_controller.rb:98`\n\n`TopicUser.find_by` on line 105 returns `nil` when the user has never interacted with the topic, but line 107 immediately calls `tu.notification_level` without a nil check, causing `NoMethodError` when the record doesn't exist. Consider adding a nil guard or creating the `TopicUser` record before accessing `notification_level`.\n\n```diff\n def unsubscribe\n @topic_view = TopicView.new(params[:topic_id], current_user)\n \n if slugs_do_not_match || (!request.format.json? && params[:slug].blank?)\n return redirect_to @topic_view.topic.unsubscribe_url, status: 301\n end\n \n tu = TopicUser.find_by(user_id: current_user.id, topic_id: params[:topic_id])\n \n+ if tu.nil?\n+ TopicUser.create!(user_id: current_user.id, topic_id: params[:topic_id].to_i, notification_level: TopicUser.notification_levels[:regular])\n+ tu = TopicUser.find_by(user_id: current_user.id, topic_id: params[:topic_id])\n+ end\n+\n if tu.notification_level > TopicUser.notification_levels[:regular]\n tu.notification_level = TopicUser.notification_levels[:regular]\n else\n```\n\n\n
\n\ud83d\ude80 Reply \"fix it for me\" or copy this AI Prompt for your agent:\n\n```text\nIn file app/controllers/topics_controller.rb around lines 98-113:\n\n`TopicUser.find_by` on line 105 returns `nil` when the user has never interacted with the topic, but line 107 immediately calls `tu.notification_level` without a nil check, causing `NoMethodError` when the record doesn't exist. Consider adding a nil guard or creating the `TopicUser` record before accessing `notification_level`.\n\nEvidence trail:\napp/controllers/topics_controller.rb lines 98-116 (viewed at REVIEWED_COMMIT) shows the `unsubscribe` method with `tu = TopicUser.find_by(...)` on line 105 and `tu.notification_level` on line 107. git_diff between MERGE_BASE and REVIEWED_COMMIT confirms this is new code added in the PR. Rails `find_by` returns nil when no record matches - this is standard Rails behavior documented at https://api.rubyonrails.org/classes/ActiveRecord/FinderMethods.html#method-i-find_by\n```\n
", - "created_at": "2026-03-18T20:50:29Z" + "path": "pkg/plugins/log/fake.go", + "line": 48, + "body": "\ud83d\udfe0 **High** `log/fake.go:46`\n\n`TestLogger.FromContext` returns a new `*TestLogger` instance, so logs written through the returned logger are recorded on that ephemeral instance and never appear on the original logger's `InfoLogs`, `DebugLogs`, etc. Tests calling `FromContext` cannot verify their logs because the captured logs are discarded. Return `f` to keep logs on the same test logger instance.\n\n```suggestion\nfunc (f *TestLogger) FromContext(_ context.Context) Logger {\n\treturn f\n}\n```\n\n\n
\n\ud83d\ude80 Reply \"fix it for me\" or copy this AI Prompt for your agent:\n\n```text\nIn file pkg/plugins/log/fake.go around lines 46-48:\n\n`TestLogger.FromContext` returns a new `*TestLogger` instance, so logs written through the returned logger are recorded on that ephemeral instance and never appear on the original logger's `InfoLogs`, `DebugLogs`, etc. Tests calling `FromContext` cannot verify their logs because the captured logs are discarded. Return `f` to keep logs on the same test logger instance.\n\nEvidence trail:\npkg/plugins/log/fake.go lines 46-48 (FromContext returns NewTestLogger()), lines 14-16 (NewTestLogger creates new instance), lines 7-12 (TestLogger struct with log fields). Interface definition at pkg/plugins/log/ifaces.go lines 22-23.\n```\n
", + "created_at": "2026-03-18T20:46:20Z" }, { "path": null, "line": null, "body": "@macroscope-app review", - "created_at": "2026-03-18T20:27:39Z" + "created_at": "2026-03-18T20:27:31Z" } ] } @@ -55625,4 +43765,4 @@ } ] } -} \ No newline at end of file +} diff --git a/offline/results/openai_gpt-5.2/candidates.json b/offline/results/openai_gpt-5.2/candidates.json index 67a86fc..6479590 100644 --- a/offline/results/openai_gpt-5.2/candidates.json +++ b/offline/results/openai_gpt-5.2/candidates.json @@ -12021,5601 +12021,116 @@ } ] }, - "https://github.com/ai-code-review-evaluation/discourse-graphite/pull/9": { - "gemini": [], - "claude": [], - "qodo": [ - { - "text": "I18n.fallbacks.ensure_loaded! is executed on every request in set_locale, causing unnecessary per-request work and potentially repeated filesystem loads", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "FallbackLocaleList relies on mutable global state (SiteSetting.default_locale and I18n.locale) while being assigned to global I18n.fallbacks, which may behave incorrectly under multi-threaded servers where I18n.locale is per-thread but I18n.fallbacks is shared", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "FallbackLocaleList.ensure_loaded! may load/modify shared I18n structures unsafely in concurrent environments", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "ensure_loaded! tracks loaded locales in @loaded_locales without synchronization, allowing concurrent threads to race when loading the same locale or observe partially updated state", - "path": null, - "line": null, - "source": "extracted" - } - ], - "bugbot": [ - { - "text": "FallbackLocaleList#[] mixes string and symbol locale values in the fallback chain, so Ruby's uniq does not deduplicate equivalent locales (e.g., \"en\" vs :en)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "When user locale matches the default locale, the fallback chain retains duplicates (e.g., [\"en\", :en]) causing the same locale to be processed twice in ensure_loaded!", - "path": null, - "line": null, - "source": "extracted" - } - ], - "copilot": [ - { - "text": "ensure_loaded! reads and potentially modifies @loaded_locales without using the LOAD_MUTEX used by load_locale, creating a race condition in multi-threaded access", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "FallbackLocaleList#[] calls SiteSetting.default_locale.to_sym without guarding for SiteSetting/default_locale availability during early initialization, which can raise and break startup", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "ensure_loaded! depends on I18n.locale and its fallbacks, but I18n.locale may be unset when the initializer runs, causing incorrect behavior or errors if accessed too early", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Initializer load order is being managed via a comment (\"order: after 02-freedom_patches.rb\"), but Rails loads initializers alphabetically; relying on the comment can lead to incorrect load order unless the file is renamed with a numeric prefix", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Calling I18n.fallbacks.ensure_loaded! assumes I18n.fallbacks is always present; if I18n.fallbacks is nil/unset, this will raise NoMethodError without a guard", - "path": null, - "line": null, - "source": "extracted" - } - ], - "augment": [ - { - "text": "Loading I18n fallbacks via ApplicationController#set_locale only covers controller requests; background jobs/mailers that use I18n may not load fallback locales because they don\u2019t pass through set_locale and TranslateAccelerator only loads config.locale", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "TranslateAccelerator caches I18n.translate results keyed only by config.locale even though the fallback chain depends on SiteSetting.default_locale; changing the site default locale at runtime can cause stale cached fallback strings until I18n.reload!/cache reset", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "TranslateAccelerator @loaded_locales tracking treats string vs symbol locales (e.g., \"en\" vs :en) as different, leading to duplicate entries and repeated locale loads; locale should be normalized before checking/adding", - "path": null, - "line": null, - "source": "extracted" - } - ], - "propel": [ - { - "text": "Fallback locales are only loaded via ApplicationController#set_locale, so non-controller contexts (background jobs, mailers, rake tasks) never load fallback locales and can raise missing translation errors even when translations exist", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The lazy-loading I18n.translate accelerator only loads config.locale, so when translations fall back to SiteSetting.default_locale or :en those locales may remain unloaded and fallback lookups raise I18n::MissingTranslation / I18n::MissingTranslationData", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Calling I18n.fallbacks.ensure_loaded! only in the controller path means fallback locales are not preloaded for non-controller codepaths; the fallback loading logic should be moved into the I18n layer (e.g., translate hook or FallbackLocaleList#[]) so every translation call preloads the fallback chain", - "path": null, - "line": null, - "source": "extracted" - } - ], - "coderabbit": [ - { - "text": "FallbackLocaleList#[] calls SiteSetting.default_locale.to_sym without guarding against nil, which can raise NoMethodError in edge cases", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "config/initializers/i18n.rb uses send(:include, ...) to mix in modules where direct include is public and preferred (I18n::Backend::Simple.send(:include, I18n::Backend::Pluralization))", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "config/initializers/i18n.rb uses send(:include, ...) to mix in modules where direct include is public and preferred (I18n.backend.class.send(:include, I18n::Backend::Fallbacks))", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "protect_from_forgery is not configured with with: :exception, leaving a CSRF protection configuration warning", - "path": null, - "line": null, - "source": "extracted" - } - ], - "greptile": [ - { - "text": "FallbackLocaleList#[] accesses SiteSetting.default_locale during early initialization, which may fail if called before the database/SiteSetting is available", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Fallback locale chain construction lacks nil-safety/error handling around SiteSetting.default_locale, risking exceptions during boot (should rescue/compact or defer access)", - "path": null, - "line": null, - "source": "extracted" - } - ], - "kg": [], - "qodo-v2": [ - { - "text": "FallbackLocaleList#[] returns a mixed array of String and Symbol locales, causing the translation lazy-loader to treat \"en\" and :en as different and load the same locale multiple times, growing @loaded_locales with duplicates", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Fallback locales are preloaded only in ApplicationController#set_locale, so non-controller locale switches (e.g., services/jobs using I18n.with_locale) may not preload fallbacks, making fallback behavior depend on prior execution order in the process", - "path": null, - "line": null, - "source": "extracted" - } - ], - "devin": [ - { - "text": "LRU cache key in translate_accelerator.rb does not include SiteSetting.default_locale, so when the dynamic fallback chain changes (via FallbackLocaleList), cached translations can become stale after the site default locale is updated", - "path": null, - "line": null, - "source": "extracted" - } - ], - "mra-max": [ - { - "text": "Repository has no test files; needs a dedicated test suite for i18n fallback changes (FallbackLocaleList semantics, ApplicationController#set_locale, lazy-loading via I18n.fallbacks, translation loading via translate accelerator, idempotent locale loading, and production fallback regressions)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "ApplicationController#set_locale now calls I18n.fallbacks.ensure_loaded!, which may eagerly load fallback locales and increase startup/request time and memory usage", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Eager loading fallbacks in set_locale is a behavioral change that may cause regressions if locales are misconfigured or locale loading fails in certain environments", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Per-request preload_json performs potentially heavy DB work on typical HTML GET requests, inflating latency and increasing DB load/contestion in high-traffic scenarios", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Removing default I18n fallbacks setting from Cloud66 production config changes runtime fallback behavior and may cause missing translations in production", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Removing I18n fallbacks enabling from config/environments/production.rb may regress translation availability if the new fallback mechanism isn\u2019t initialized early enough", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Removing I18n fallbacks enabling from config/environments/profile.rb may cause translation misses or environment-specific translation differences", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "New custom i18n initializer (FallbackLocaleList, ensure_loaded! integration, pluralization/fallback modules) is a major architectural change that risks I18n gem compatibility issues", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Custom i18n initializer may have thread-safety risks due to altered fallback/loader behavior", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Custom i18n initializer may mis-load translations if locales are not loaded in the expected order", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "translate_accelerator ensure_loaded!(locale) may load the same locale twice when locale is passed as both string and symbol, duplicating work", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Duplicate locale loads (string vs symbol) can cause subtle timing/consistency issues in multithreaded servers", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "No tests verifying thread-safety/concurrency behavior for the new i18n fallback/loading changes", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "No tests verifying correctness of fallback order/behavior across locales for the new fallback implementation", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "No tests covering environment-specific behavior differences (prod vs non-prod) for fallbacks after config changes", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "No tests covering per-request preloading behavior/performance impact", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Server-side i18n fallback behavior changed but client-side i18n.js still disables/configures fallbacks differently, risking inconsistent translation resolution between server and client", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Public API/behavior change (server vs client fallback parity) is not reflected in documentation, risking integrator confusion", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Removal of explicit i18n/backend/pluralization patch file may cause inconsistency or double-loading if not aligned with the new initializer", - "path": null, - "line": null, - "source": "extracted" - } - ], - "greptile-v4": [ - { - "text": "`translate` only loads the current locale and does not load fallback locales, so fallback lookups can silently fail when fallback locale files aren\u2019t loaded", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Fallback locales are only preloaded via `ApplicationController#set_locale`, so any `I18n.translate` usage outside controller requests (background jobs, rake tasks, mailers, console) won\u2019t have fallbacks loaded and will return missing translations", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "`FallbackLocaleList#[]` dynamically accesses `SiteSetting.default_locale` without a guard, which can raise (e.g., `NameError`) or return an unexpected value during early boot/initializer contexts before `SiteSetting` is available", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "`ensure_loaded!` initializes and checks `@loaded_locales` outside `LOAD_MUTEX`, creating a TOCTOU race where multiple threads may redundantly call `load_locale`, adding unnecessary overhead", - "path": null, - "line": null, - "source": "extracted" - } - ], - "mra-nano": [ - { - "text": "FallbackLocaleList does not guard against nil I18n.locale, which can crash in ensure_loaded!", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "FallbackLocaleList depends on SiteSetting.default_locale during initialization, risking incorrect fallback chains if settings are not yet loaded (boot-order issue)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "I18n.fallbacks.ensure_loaded! is called in ApplicationController#set_locale on every request, coupling request handling to dynamic locale loading and risking performance issues", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Calling I18n.fallbacks.ensure_loaded! in set_locale relies on a custom fallback loader and may load translations inconsistently or break if locales are missing or I18n semantics change", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Removal of config.i18n.fallbacks = true from environment files shifts fallback control to the initializer, so fallbacks may be absent if the initializer loads too late or fails", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Translate accelerator: @loaded_locales may be nil when load_locale calls @loaded_locales.include?(locale), causing NoMethodError on first invocation", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Translate accelerator: translate checks @loaded_locales.include?(config.locale) without initializing @loaded_locales, potentially crashing on the first translation request", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Pluralization backend inclusion moved from config/initializers/pluralization.rb to config/initializers/i18n.rb, changing initialization order and risking pluralization being loaded before I18n is fully configured or breaking behavior", - "path": null, - "line": null, - "source": "extracted" - } - ], - "macroscope": [] - }, - "https://github.com/grafana/grafana/pull/76186": { - "gemini": [ - { - "text": "SubscribeStream is not instrumented with a contextual logger despite having access to PluginContext, leading to missing contextual log fields (e.g., pluginId)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "RunStream is not instrumented with a contextual logger despite having access to PluginContext, leading to missing contextual log fields (e.g., pluginId)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "A test function name is not aligned with the file rename and should be renamed for consistency", - "path": null, - "line": null, - "source": "extracted" - } - ], - "claude": [], - "qodo": [ - { - "text": "TestLogger.FromContext returns a brand-new logger instance, potentially dropping previously accumulated logger state/context and masking production bugs; it should preserve existing context (e.g., return f or f.New(...))", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "grafanaInfraLogWrapper.FromContext depends on a type assertion to *log.ConcreteLogger; if the underlying logger is a different implementation it falls back to d.New() and may unexpectedly discard contextual attributes", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Contextual logging enrichment is moved to a separate middleware while LoggerMiddleware consumes logger.FromContext(ctx), creating a fragile dependency on middleware ordering/inclusion; missing or misordered middleware can silently degrade log fields", - "path": null, - "line": null, - "source": "extracted" - } - ], - "bugbot": [ - { - "text": "`TestLogger.FromContext` creates a new `TestLogger` via `NewTestLogger()` instead of returning the receiver or sharing state, causing logs to be written to an orphan instance", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Because `FromContext` returns a fresh logger, `LoggerMiddleware.logRequest` logging calls are not recorded on the original `TestLogger`, preventing tests from verifying that logging occurred", - "path": null, - "line": null, - "source": "extracted" - } - ], - "copilot": [], - "augment": [ - { - "text": "QueryData and other request methods dereference req without checking for nil, so a nil request propagating to this middleware would cause a panic", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "TestLogger.FromContext returns a new logger instance, so logs written via m.logger.FromContext(ctx).Info(...) are not recorded on the original TestLogger instance that tests assert against", - "path": null, - "line": null, - "source": "extracted" - } - ], - "coderabbit": [ - { - "text": "ContextualLoggerMiddleware dereferences req.PluginContext unconditionally in QueryData, CallResource, CheckHealth, and CollectMetrics, which can panic when req is nil", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "ContextualLoggerMiddleware does not guard against req.PluginContext being nil before passing it to instrumentContext, which can panic when PluginContext is nil", - "path": null, - "line": null, - "source": "extracted" - } - ], - "greptile": [], - "kg": [], - "qodo-v2": [ - { - "text": "TestLogger.FromContext returns a new TestLogger instance, dropping previously captured log state (InfoLogs/WarnLogs/etc.) and violating the Logger contract to preserve existing context plus the given context", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "LoggerMiddleware now relies on logger.FromContext(ctx) for traceID enrichment, but the traceID contextual log provider is only registered when tracing.ProvideService is called, so in tests or non-standard setups plugin request logs can silently miss trace IDs", - "path": null, - "line": null, - "source": "extracted" - } - ], - "devin": [ - { - "text": "TestLogger.FromContext returns a new disconnected TestLogger instance (NewTestLogger()) instead of the receiver, causing logs to be recorded on a throwaway logger rather than the one referenced by tests", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Because TestLogger.FromContext returns a new instance, log assertions in tests (e.g., checking InfoLogs.Calls/messages) can silently show zero calls even when logging occurred via logger.FromContext(ctx).Info(...), breaking the test double contract", - "path": null, - "line": null, - "source": "extracted" - } - ], - "mra-max": [ - { - "text": "Logger interface is a breaking public API change: variadic params changed to ctx ...any and new FromContext(context.Context) method added, requiring all external implementors to update", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "ContextualLoggerMiddleware lacks nil checks and dereferences req.PluginContext in multiple methods, risking panics on nil requests", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "InstrumentationMiddleware was renamed/refactored to MetricsMiddleware (including constructor rename), causing large churn and requiring downstream updates", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "MetricsMiddleware uses unsafe type assertions to prometheus.ExemplarObserver/ExemplarAdder without ok-checks, which can panic if exemplars aren\u2019t supported by the underlying metric implementation", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "MetricsMiddleware.QueryData dereferences req without a nil check, so a nil *backend.QueryDataRequest will panic", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Middleware ordering places ContextualLoggerMiddleware after MetricsMiddleware, so metrics/log instrumentation may miss contextual attributes; ContextualLoggerMiddleware should run before MetricsMiddleware", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "grafanaInfraLogWrapper.FromContext performs a potentially unsafe type assertion to *log.ConcreteLogger and fallback behavior may yield unexpected nil/default logger; should use ok-check and ensure a valid logger is returned", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "metrics_middleware_test.go keeps the test name TestInstrumentationMiddleware while constructing MetricsMiddleware, creating confusing/misaligned test naming", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Branch contains a merge commit from origin/main, increasing risk of merge/conflict issues in the change set", - "path": null, - "line": null, - "source": "extracted" - } - ], - "greptile-v4": [ - { - "text": "TestLogger.FromContext returns a new detached TestLogger instance, so logs emitted via logger.FromContext(ctx).Info/Warn/Error are written to a throwaway logger and tests asserting on the original TestLogger\u2019s captured logs will see zero calls", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "ContextualLoggerMiddleware handlers dereference req.PluginContext without guarding against req == nil, so a nil request can panic before LoggerMiddleware\u2019s existing nil-request guard is reached (needs nil checks in QueryData/CallResource/CheckHealth/CollectMetrics)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "metrics_middleware_test.go test function name was not updated after renaming InstrumentationMiddleware to MetricsMiddleware (still named TestInstrumentationMiddleware), causing inconsistency and reduced discoverability", - "path": null, - "line": null, - "source": "extracted" - } - ], - "mra-nano": [ - { - "text": "Need to confirm NewContextualLoggerMiddleware is called/wired in pluginsintegration.go and covered by tests", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Instrumentation logic should use logger.FromContext for contextual logging; verify it does so", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "TestLogger in pkg/plugins/log/fake.go mutates ErrorLogs fields (Calls/Message/Ctx) without synchronization, risking data races/flaky tests when logging concurrently", - "path": null, - "line": null, - "source": "extracted" - } - ], - "macroscope": [ - { - "text": "TestLogger.FromContext returns a new *TestLogger instance, so logs written via the returned logger are captured on an ephemeral logger and never appear in the original logger's InfoLogs/DebugLogs/etc., preventing tests from verifying logs; it should return the existing receiver (f) instead.", - "path": null, - "line": null, - "source": "extracted" - } - ] - }, - "https://github.com/ai-code-review-evaluation/discourse-graphite/pull/10": { - "gemini": [ - { - "text": "Using `where(id: params[:id]).first` to fetch a single record can return `nil` and cause `NoMethodError` when passed to `save_host(nil)`; use `find(params[:id])` to raise `ActiveRecord::RecordNotFound` and return a proper 404", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Using `where(id: params[:id]).first` in the destroy path can return `nil` and cause `NoMethodError` on `host.destroy`; use `find(params[:id])` to raise `ActiveRecord::RecordNotFound` and return a proper 404", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Directly assigning model attributes from `params` risks mass-assignment vulnerabilities; use strong parameters to whitelist permitted attributes", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Migration code accessing `execute(...)[0]['id']` is unsafe when the query returns no rows, leading to `NoMethodError` and migration failure; handle empty results safely (e.g., `first&.[]('id')`)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "SQL query in the migration uses string interpolation to insert values, creating SQL injection risk; use parameter binding or quoting (e.g., `connection.quote`)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Client code manually sets the category after saving because the server response only includes `category_id`; a more robust approach is for the server to embed the full category object in the response so the relationship updates automatically", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "`saveChanges` action appears to be dead code: it calls a server-side `update` action that is a no-op and there is no UI trigger; remove the unused client action and corresponding server route/action", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Inline `style` attributes are used for table layout, reducing maintainability; replace with CSS classes and stylesheet rules", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Host validation regex allows an optional path `(\\/.*)?` but a `before_validation` hook strips paths, making that regex portion redundant and confusing; remove the path-matching part", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Serializer methods `id`, `host`, and `category_id` are redundant because `attributes :id, :host, :category_id` already defines them; remove the explicit method definitions", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "An explicit `id` method is redundant because `attributes :id` already defines it; remove the method definition", - "path": null, - "line": null, - "source": "extracted" - } - ], - "claude": [ - { - "text": "spec/fabricators/category_fabricator.rb was overwritten with embeddable_host content, removing/destroying category fabricators like Fabricator(:category) and Fabricator(:diff_category)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Controller uses deprecated where().first pattern instead of the codebase-standard find_by()", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Controller accesses params[:embeddable_host] directly without strong parameters (missing require/permit)", - "path": null, - "line": null, - "source": "extracted" - } - ], - "qodo": [ - { - "text": "Migration `db/migrate/20150818190757_create_embeddable_hosts.rb` interpolates `h` directly into an INSERT SQL string, allowing SQL injection or broken migrations if the value contains quotes/malicious SQL; should use quoting/parameterized inserts", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Controller `app/controllers/admin/embeddable_hosts_controller.rb` assigns `params[:embeddable_host][:host]` directly without explicit sanitization/normalization and assumes nested params exist, which can cause unsafe input handling or crashes when params are missing", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "`update` action fetches host with `where(...).first` and calls `save_host(host)` without handling the record-not-found case, risking `NoMethodError` and inconsistent responses; should return 404 when missing", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "`destroy` action fetches host with `where(...).first` and calls `host.destroy` without handling the record-not-found case, risking `NoMethodError` and inconsistent responses; should return 404 when missing", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "`save_host` assumes `params[:embeddable_host]` is present and dereferences it directly, which can raise exceptions if the nested params are absent", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Model `before_validation` callback calls `sub!` on `self.host` without guarding against `nil`/blank, which can raise exceptions when `host` is unset; should add presence validation and/or skip callback when blank", - "path": null, - "line": null, - "source": "extracted" - } - ], - "bugbot": [ - { - "text": "Contents of spec/fabricators/category_fabricator.rb and spec/fabricators/embeddable_host_fabricator.rb are swapped, placing the wrong fabricator definitions in each file", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Admin::EmbeddableHostsController update and destroy actions do not check whether the fetched EmbeddableHost exists before calling save_host/host.destroy, risking NoMethodError on nil", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "EmbeddableHost.record_for_host lowercases the database column but not the host parameter, so case-insensitive host matching can fail when the input host contains uppercase letters", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "EmbeddableHost before_validation callback calls host.sub! without guarding against nil, causing a crash when host is missing instead of producing a validation error", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Migration 20150818190757_create_embeddable_hosts.rb assumes execute(...)[0]['id'] exists; if the query returns no rows (embed_category not configured or no matching category), it crashes with NoMethodError", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "embeddable-host.js delete action calls destroyRecord() without a catch handler, so request failures are silently swallowed and the user gets no feedback", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "store.js plural *_ids hydration branch deletes the original key unconditionally even if hydration fails, potentially losing the original IDs and replacing them with undefined values", - "path": null, - "line": null, - "source": "extracted" - } - ], - "copilot": [ - { - "text": "`update` and `destroy` use `EmbeddableHost.where(...).first` and immediately dereference the result; when no record exists this can raise `NoMethodError` instead of returning a 404-style error (should use `find` or handle `nil`)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Fabricators are in non-conventional files: `category`-related fabricators were moved into `embeddable_host_fabricator.rb` while `category_fabricator.rb` now defines `:embeddable_host`, reducing maintainability/discoverability", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Test description string contains a typo: `'with an emeddable host'` should be `'with an embeddable host'`", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Store test is inconsistent: it calls `store.find('fruit', 2)` but asserts colors for fruit id 1; the test passes only because the pretender route always returns `fruits[0]` regardless of requested id (fixture/route/requested id should be aligned)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Migration assumes SQL queries always return rows; `execute(...)[0]` can be `nil` if settings/rows are missing, causing `['id']`/`['value']` to raise (should check presence/`cmd_tuples` and handle missing data safely)", - "path": null, - "line": null, - "source": "extracted" - } - ], - "propel": [ - { - "text": "before_validation mutates self.host with sub! even when host is nil/blank, causing NoMethodError and a 500 instead of allowing validation errors", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "EmbeddableHost.record_for_host compares lower(host) to an undowncased extracted host, so mixed-case referrer hosts never match allow-listed hosts", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Admin embeddable hosts update action doesn\u2019t handle missing/invalid :id, so save_host can receive nil and raise NoMethodError, returning 500 instead of a proper 404/422", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Admin embeddable hosts destroy action doesn\u2019t handle missing/invalid :id, so calling destroy on nil raises NoMethodError, returning 500 instead of a proper 404/422", - "path": null, - "line": null, - "source": "extracted" - } - ], - "augment": [ - { - "text": "Controller actions call save_host(host) / host.destroy without handling the case where params[:id] does not match a record, so host can be nil and raise a 500 instead of returning a 404/not-found response", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Model host normalization calls sub! when host is nil/blank, which can raise before validations run and turn an invalid request into a 500 instead of a validation error", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "record_for_host matches against uri.host (no port) while the model allows saving host values with :port, so stored entries like example.com:3000 will never match and will be treated as disallowed", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Migration inserts host values via string interpolation, which can break on quotes and is unsafe, and it bypasses model host normalization (scheme/path stripping), potentially creating records that will never match record_for_host", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Client sets host.category from the input categoryId instead of using the saved record\u2019s returned category_id, so when categoryId is blank/undefined the UI may show no category even though the server defaulted/persisted uncategorized", - "path": null, - "line": null, - "source": "extracted" - } - ], - "baz": [ - { - "text": "`before_validation` calls `self.host.sub!` without guarding against `host` being nil/blank, causing `NoMethodError` on admin POST/PUT when `embeddable_host` params omit `host` before validation can reject it", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Migration copies raw `site_settings.embeddable_hosts` entries (including scheme/path) into `embeddable_hosts.host` without normalization, so `EmbeddableHost.record_for_host` (which compares `URI(host).host`) will not match migrated values and previously allowed hosts become disallowed after migration", - "path": null, - "line": null, - "source": "extracted" - } - ], - "coderabbit": [ - { - "text": "Icon-only Save/Cancel/Edit/Delete d-button instances in embeddable-host.hbs lack accessible labels (no title/aria label) for screen readers", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Proposed i18n keys for button titles (admin.embedding.save/cancel/edit/delete) are missing from locale files, so titles would not resolve", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Store hydration logic for *_ids assumes obj[k] is an array and calls obj[k].map, which will throw if the API returns null or a non-array", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Admin::EmbeddableHostsController update action can pass nil host into save_host when an invalid id is provided, leading to NoMethodError", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Admin::EmbeddableHostsController destroy action can call destroy on nil when an invalid id is provided, leading to NoMethodError", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "EmbeddableHost host validation regex restricts TLD length to 5 characters, rejecting valid modern TLDs (e.g., .museum, .community)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "EmbeddableHost before_validation callback calls sub! on host without guarding nil/blank, which can raise and 500 instead of returning validation errors", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Migration resolving category_id uses execute(...)[0] without checking for empty results, which can raise IndexError and abort the migration if rows are missing", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Migration backfill interpolates host strings directly into SQL without quoting/escaping, breaking on quotes/special characters and posing SQL-injection risk", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Migration backfill does not skip blank/whitespace host lines produced by splitting, potentially inserting invalid/empty hosts", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Fabricator(:private_category) uses update! inside after_build, causing persistence even for Fabricate.build (unexpected side effect)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Fabricator(:private_category) assumes transients[:group] is present and calls transients[:group].id, which will raise if group is not provided", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Spec files contain extra blank lines that violate RuboCop Layout/EmptyLinesAroundBlockBody and may fail CI linting", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "RSpec context description contains a typo: 'emeddable' should be 'embeddable'", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Store test requests store.find('fruit', 2) but Pretender mock /fruits/:id always returns fruits[0] (id 1), making the test misleading/accidentally passing", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "spec/fabricators/category_fabricator.rb filename no longer matches its contents (contains Fabricator(:embeddable_host)), which is misleading and inconsistent with conventions", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Admin embeddable-host component cancel action removes new host from array but does not delete/unload the unsaved record from the store, leaving orphaned store entries", - "path": null, - "line": null, - "source": "extracted" - } - ], - "greptile": [ - { - "text": "SQL injection vulnerability in migration: `h` from `embeddable_hosts` site setting is interpolated into raw SQL without escaping/quoting", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Migration can crash when no matching category is found because the category lookup query may return an empty result set and code assumes a row exists", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "`EmbeddableHost` model normalization can raise `NoMethodError` because `self.host`/`host` can be nil when calling string methods", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Admin embeddable hosts controller update/save path can crash due to missing nil check when no `EmbeddableHost` is found for `params[:id]`", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Admin embeddable hosts controller destroy path can crash due to missing nil check when no `EmbeddableHost` is found for `params[:id]`", - "path": null, - "line": null, - "source": "extracted" - } - ], - "kg": [ - { - "text": "spec/fabricators/category_fabricator.rb defines an embeddable_host fabricator instead of category fabricators; its content appears swapped with spec/fabricators/embeddable_host_fabricator.rb, breaking tests that rely on Fabricate(:category)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "spec/fabricators/embeddable_host_fabricator.rb defines a category fabricator instead of an embeddable_host fabricator; its content appears swapped with spec/fabricators/category_fabricator.rb", - "path": null, - "line": null, - "source": "extracted" - } - ], - "qodo-v2": [ - { - "text": "Migration uses create_table(..., force: true), which can drop/recreate the table and cause data loss if the migration is re-run or partially applied", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Migration interpolates unescaped host values into raw SQL INSERT statements, which can break on quotes and enables SQL injection via stored site_settings values", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Migration indexes execute(...)[0] results without guarding for missing rows, which can raise errors when queries return no results", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Admin::EmbeddableHostsController update/destroy fetch records with where(...).first and then operate on a possibly nil record, causing 500s on missing ids", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Admin::EmbeddableHostsController save_host assumes params[:embeddable_host] (and nested keys) exist, causing exceptions on malformed/missing request payloads", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Admin destroy action does not handle destroy failures (always returns success), potentially hiding errors", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "EmbeddableHost before_validation callback calls sub! on self.host without nil/blank guarding, causing exceptions before validations can add errors", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Admin UI component overwrites host.category after save using local categoryId, which can be blank/null and clears the hydrated category in the UI until reload", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Client store hydration treats any *_ids field as an array and blindly calls .map, which can throw if the value is not an array", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Client store hydration deletes the original *_ids field even when hydration cannot succeed (missing embedded collection), silently dropping ids and producing undefined entries", - "path": null, - "line": null, - "source": "extracted" - } - ], - "devin": [ - { - "text": "Migration can crash with NoMethodError by unconditionally accessing result[0]['id'] when the embed_category setting is missing or no category matches (query returns zero rows)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Migration builds an INSERT SQL string by interpolating unescaped host values (h) from site_settings, which can break on quotes and enables SQL injection; should use quoting/parameterization", - "path": null, - "line": null, - "source": "extracted" - } - ], - "mra-max": [ - { - "text": "Ember REST adapter for embedding uses path \"/admin/customize/embedding\" but server routes expose \"/customize/embedding\", likely causing 404s for embedding edits", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Routes map \"customize/embedding\" to a non-namespaced EmbeddingController, but the implemented controller is Admin::EmbeddingController, likely causing misrouting/404s", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Admin REST adapter changes are broad and touch many files, increasing risk of merge conflicts and admin UI regressions", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "New embeddable-host Ember component contains substantial UI/state/DOM logic, increasing risk of UI regressions and brittle event handling", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Admin::EmbeddableHostsController introduces new create/update/destroy endpoints that may lack sufficient permission checks and strong parameter/data validation", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Admin::EmbeddingController adds a sensitive admin endpoint (show/update) that must strictly enforce authentication/authorization to prevent unauthorized access", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Embed controller now relies on EmbeddableHost.host_allowed?(request.referer); if EmbeddableHost data isn\u2019t seeded/migrations haven\u2019t run, legitimate embedding may be blocked", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "EmbeddableHost host normalization/validation and category association could diverge from expectations, risking data integrity or incorrect allowlist behavior", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Topic#expandable_first_post? no longer requires embeddable hosts to be present, changing behavior and potentially enabling embedding UI/behavior when no hosts are configured", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "TopicEmbed.import now depends on EmbeddableHost.record_for_host to assign category_id; failures/incomplete host data can cause incorrect category assignment or import behavior", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Topic embedding/retrieval logic now depends on DB-backed EmbeddableHost allowlist; if DB state is out of sync, host validation may behave incorrectly", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Migration seeds embeddable_hosts using raw SQL string interpolation of host values, creating SQL injection/broken SQL risk if host contains quotes/special characters", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "TopicRetriever invalid_host? now uses EmbeddableHost.host_allowed?, adding runtime dependency on DB-backed allowlist that can fail if DB/config is not in sync", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Admin controller specs for Admin::EmbeddableHostsController only test inheritance; missing tests for create/update/destroy behavior, validation errors, and JSON responses", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Admin controller specs for Admin::EmbeddingController only test inheritance; missing tests for show/update/saveChanges behavior and serializer/persistence responses", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Admin REST adapter model type normalization uses replace('_','-') (first underscore only); models with multiple underscores may not match ADMIN_MODELS and may route to wrong base path", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Store hydration for keys ending in _ids may mishandle types/keys with multiple underscores due to first-underscore-only normalization, causing missing/incorrect relationship hydration", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Removal of server locale key (embed_category) may break templates expecting that translation, causing missing i18n keys", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Removing embeddable_hosts/embed_category from site_settings introduces a migration boundary; environments that don\u2019t run migrations may lose configuration or behave unexpectedly", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Test coverage for the end-to-end editing/saving flow of embeddable hosts appears limited, risking undetected integration issues between Ember UI and Rails controllers", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Tests previously relying on SiteSetting embeddable host behavior were removed/reworked; test environment must ensure migrations/DB-backed config are present to avoid false assumptions", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "TopicEmbed specs lack edge-case coverage (e.g., nil category_id, multiple hosts, different URLs), leaving potential bugs untested", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "No CHANGELOG entry documents the embedding feature/admin endpoints/migrations, reducing visibility of breaking changes", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Admin embedding template condition uses truthiness of embeddable_hosts; empty array is truthy, so an empty table header may render when there are no hosts", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "New EmbeddableHost fabricator/specs must align with model validations/associations; mismatches could cause flaky tests", - "path": null, - "line": null, - "source": "extracted" - } - ], - "greptile-v4": [ - { - "text": "spec/fabricators/category_fabricator.rb defines Fabricator(:embeddable_host) instead of category fabricators (file content does not match filename)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "spec/fabricators/embeddable_host_fabricator.rb contains category fabricators (:category, :diff_category, :happy_category, :private_category) instead of the embeddable host fabricator (file content does not match filename)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "db/migrate/20150818190757_create_embeddable_hosts.rb builds a raw INSERT with host values string-interpolated, creating a SQL injection/syntax error risk for hosts containing quotes", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "app/models/embeddable_host.rb before_validation calls self.host.sub!(...) without a nil check, causing NoMethodError when host is blank/nil", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "app/controllers/admin/embeddable_hosts_controller.rb update action does not handle record-not-found (EmbeddableHost.where(...).first can be nil), leading to NoMethodError", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "app/controllers/admin/embeddable_hosts_controller.rb destroy action does not handle record-not-found (EmbeddableHost.where(...).first can be nil), leading to NoMethodError", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "app/assets/javascripts/admin/components/embeddable-host.js.es6 delete action calls destroyRecord() without a .catch() handler, so deletion errors are silently swallowed", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "app/assets/javascripts/discourse/adapters/rest.js.es6 uses String.replace('_','-') which only replaces the first underscore, breaking normalization for model names with multiple underscores", - "path": null, - "line": null, - "source": "extracted" - } - ], - "mra-nano": [ - { - "text": "DB migration creates embeddable_hosts table, migrates data from site_settings, then deletes old site_settings keys, making the change one-way and risking missing embed data if migration fails or is skipped", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Embedding feature changes span frontend adapter, backend controller, and routes, increasing risk of merge conflicts, permission gaps, and regressions", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Admin embedding management update endpoint is a no-op (PUT /admin/customize/embedding does not persist changes), while the UI triggers an update on save", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "EmbedController#ensure_embeddable uses an unusual rescue placement after the method end, which may cause syntax/runtime issues or incorrect exception handling for invalid referer URLs", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "EmbeddableHost model couples host_allowed? validation with TopicEmbed.import categorization; missing host records can lead to nil category assignment or silent failures during import", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Topic#expandable_first_post? removed the embeddable_hosts presence check, potentially expanding embed behavior beyond configured hosts and causing unexpected expandability conditions", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "New Ember embeddable-host component/template changes combined with adapter/serializer changes risk frontend/backend binding mismatches and UI regressions", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "REST adapter admin path detection relies on brittle type normalization (underscore-to-dash) and ADMIN_MODELS matching, which can misroute requests if Ember Data type naming differs", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Store _hydrateEmbedded now handles *_ids and assigns hydrated arrays to pluralized keys, changing the in-memory association shape and potentially breaking callers expecting the old singular key/shape", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Removing embeddable_hosts and embed_category from site_settings.yml risks new installs having neither defaults nor DB records if migrations are skipped or fail", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Embedding endpoint sets X-Frame-Options to ALLOWALL, disabling clickjacking protection and enabling iframe embedding of sensitive UI flows", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Topic retrieval/embedding validation now relies on EmbeddableHost.host_allowed? instead of SiteSetting checks; call sites and tests may not fully cover the new validation path", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Admin::EmbeddableHostsController and Admin::EmbeddingController REST actions lack meaningful test coverage for authorization, params edge cases, and serialization behavior", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Embed controller tests were rewritten to use EmbeddableHost; coverage may be missing for single-host vs multi-host scenarios and referer edge cases (invalid/missing referer)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Test suite introduces duplicate/fragile Fabricator(:category) definitions across files, risking conflicts or unintended fabricator usage", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "EmbeddableHost model specs do not cover invalid host format validations or belongs_to :category association edge cases (e.g., nil category_id behavior)", - "path": null, - "line": null, - "source": "extracted" - } - ], - "macroscope": [ - { - "text": "admin/embeddable_hosts_controller.rb update: EmbeddableHost lookup can return nil for missing id, nil is passed to save_host and then host.host= is called, causing NoMethodError; should return 404 when not found", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "db/migrate/20150818190757_create_embeddable_hosts.rb: execute(...)[0] can return nil when the uncategorized_category_id site setting is missing, and accessing ['value'] on nil raises NoMethodError", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "spec/fabricators/embeddable_host_fabricator.rb: transients[:group] has no default and can be nil, so transients[:group].id raises NoMethodError when fabricating :private_category without providing :group", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "app/models/embeddable_host.rb record_for_host: bare hostnames like \"example.com\" fail because URI(host).host is nil without a scheme, causing the method to return false; should handle inputs without scheme (e.g., by prepending https://)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "app/models/embeddable_host.rb record_for_host: query compares lower(host) to an un-downcased parameter, so lookups can fail when uri.host contains uppercase letters; should downcase the parameter", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "admin/embedding_controller.rb update: action ignores params and performs no update, yet returns the same 200 response as show, misleading callers; should implement update logic or return an error/not supported", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "db/migrate/20150818190757_create_embeddable_hosts.rb: host value h is interpolated into SQL without escaping, so single quotes in host cause SQL syntax errors (and potential injection); should use quote(h)", - "path": null, - "line": null, - "source": "extracted" - } - ] - }, - "https://github.com/ai-code-review-evaluation/discourse-graphite/pull/7": { - "gemini": [ - { - "text": "Light theme lightness changed from 30% to 70%, making the color too light on a light background and causing poor contrast/readability", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Dark theme uses scale-color($secondary, $lightness: 30%), producing a too-dark color on a dark background and causing poor contrast (should use a lighter value like 70%)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Inconsistent .custom-message-length color between desktop and mobile stylesheets (desktop uses light theme lightness 70% while mobile uses 30%)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Desktop .custom-message-length light theme lightness 70% has low contrast on light background; should match mobile\u2019s 30% for better readability", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Light theme color for .group-member-info .name changed from 30% to 50%, introducing an unintended visual/contrast change (also present in mobile/user.scss)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "h3 light theme lightness changed from 20% to 50%, reducing contrast compared to desktop/topic-post.scss which uses 20%; should be consistent and keep 20% for readability", - "path": null, - "line": null, - "source": "extracted" - } - ], - "claude": [ - { - "text": "app/assets/stylesheets/desktop/topic-post.scss line 291: dark-light-choose conversion inverted the lightness values (used primary 70% / secondary 30%) instead of preserving the original 30% for primary and using 70% for secondary, causing incorrect link color hierarchy", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "app/assets/stylesheets/common/base/_topic-list.scss line 115: missed wrapping `scale-color($primary, $lightness: 50%)` with `dark-light-choose(...)`, leaving one instance without dark theme support", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "app/assets/stylesheets/mobile/topic-post.scss ~178-182: `scale-color($primary, $lightness: 20%)` was transformed to 50%/50% instead of 20%/80%, breaking the established conversion pattern and contrast", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "app/assets/stylesheets/desktop/topic-post.scss ~892: transformation unintentionally changed the CSS property from `border: 1px solid scale-color(...)` to `background-color: dark-light-choose(...)`, altering the visual effect from border to background", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "app/assets/stylesheets/desktop/user.scss ~518-522: `scale-color($primary, $lightness: 30%)` was transformed to 50%/50% instead of 30%/70%, creating inconsistent styling", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "app/assets/stylesheets/mobile/user.scss ~493-497: `scale-color($primary, $lightness: 30%)` was transformed to 50%/50% instead of 30%/70%, creating inconsistent styling", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "app/assets/stylesheets/mobile/modal.scss ~98-102: `scale-color($primary, $lightness: 70%)` was transformed to 30%/70% instead of 70%/30%, inverting the intended light/dark theme mapping", - "path": null, - "line": null, - "source": "extracted" - } - ], - "qodo": [ - { - "text": "Mixed and inconsistent $lightness values in new dark-light-choose(...) usages may cause insufficient contrast/readability between light and dark themes (notably for borders and helper text); needs validation against the design system", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Repeated dark-light-choose(scale-color(...)) patterns for muted/subtle text are duplicated across many elements, risking divergence and making future theme tweaks harder; should be centralized via a shared variable/mixin/token", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Disabled button hover styling changes text color via dark-light-choose(...), which may make disabled buttons appear enabled or reduce legibility, especially in dark theme; needs confirmation/testing", - "path": null, - "line": null, - "source": "extracted" - } - ], - "bugbot": [ - { - "text": "Light theme link color for `.topic-meta-data h5 a` in embedded posts was unintentionally changed: original `scale-color($primary, $lightness: 30%)` but `dark-light-choose` now uses `70%`, making it much lighter than before", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Light theme color for `.custom-message-length` was unintentionally changed: original `scale-color($primary, $lightness: 70%)` but `dark-light-choose` now uses `30%`, making it much darker than before", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Light theme color for `.group-member-info .name` was unintentionally changed: original `scale-color($primary, $lightness: 30%)` but `dark-light-choose` now uses `50%`, affecting both desktop and mobile stylesheets", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Light theme color for `.topic-map h3` heading was unintentionally changed: original `scale-color($primary, $lightness: 20%)` but `dark-light-choose` now uses `50%`, making the heading significantly lighter than before", - "path": null, - "line": null, - "source": "extracted" - } - ], - "copilot": [ - { - "text": "Several SCSS color conversions changed the light theme lightness from the original `scale-color($primary, $lightness: 30%)` to `50%`, unintentionally altering light theme appearance; should use `dark-light-choose(scale-color($primary, $lightness: 30%), scale-color($secondary, $lightness: 70%))` instead", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "A SCSS color conversion changed the light theme lightness from the original `scale-color($primary, $lightness: 20%)` to `50%`, unintentionally altering light theme appearance; should use `dark-light-choose(scale-color($primary, $lightness: 20%), scale-color($secondary, $lightness: 80%))` instead", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "A SCSS color conversion changed the light theme lightness from the original `scale-color($primary, $lightness: 30%)` to `70%`, altering appearance and making it inconsistent with the dark theme value of `30%`; should use `dark-light-choose(scale-color($primary, $lightness: 30%), scale-color($secondary, $lightness: 70%))` instead", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "A SCSS color conversion changed the light theme lightness from the original `scale-color($primary, $lightness: 70%)` to `30%`, unintentionally altering light theme appearance; should use `dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%))` instead", - "path": null, - "line": null, - "source": "extracted" - } - ], - "augment": [ - { - "text": "`dark-light-choose` is being used in a way that unintentionally changes the light-theme color (e.g., from `scale-color($primary, $lightness: 30%)` to `scale-color($primary, $lightness: 70%)`), causing a large and possibly unintended contrast shift", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "In `.group-member-info .name`, using `dark-light-choose` changes the light-mode color from `scale-color($primary, $lightness: 30%)` to `scale-color($primary, $lightness: 50%)`, which may be unintended and inconsistent with the pattern of keeping light theme unchanged while adjusting only dark theme", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "In `mobile/modal.scss`, the `dark-light-choose` argument order appears to swap the intended light and dark values (light changes from `scale-color($primary, $lightness: 70%)` to `scale-color($primary, $lightness: 30%)`, and vice-versa for dark)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "In mobile `topic-post.scss`, the light-theme `h3` color is changed from `scale-color($primary, $lightness: 20%)` to `scale-color($primary, $lightness: 50%)` due to `dark-light-choose` returning its first argument in light themes; this may be unintended", - "path": null, - "line": null, - "source": "extracted" - } - ], - "propel": [ - { - "text": "Wrapping the color in `dark-light-choose` changed the light-theme value from `scale-color($primary, $lightness: 70%)` to `scale-color($primary, $lightness: 30%)`, making the custom message length hint much darker and reducing readability", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The mobile modal\u2019s custom message length hint color is now inconsistent with the desktop modal, which still uses 70% lightening for the light theme", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The `dark-light-choose` usage should preserve the original 70% light-theme lightening and only add a dark-theme fallback, rather than altering the light-theme value", - "path": null, - "line": null, - "source": "extracted" - } - ], - "baz": [ - { - "text": "In the light theme, the topic-meta link color was changed to scale-color($primary, $lightness: 70%) due to wrapping in dark-light-choose, making links nearly white and reducing contrast; the light branch should preserve the previous 30% lightness.", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "For .custom-message-length, the light-theme color was changed to scale-color($primary, $lightness: 30%) instead of the previous 70%, making helper text unexpectedly darker; the light branch should keep 70% while adding a dark branch.", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Light-theme h3 color was changed to scale-color($primary, $lightness: 50%) instead of the previous 20% when moving to dark-light-choose, brightening header text and regressing contrast; the light branch should keep 20% while still providing a dark branch.", - "path": null, - "line": null, - "source": "extracted" - } - ], - "coderabbit": [ - { - "text": "In app/assets/stylesheets/mobile/compose.scss (#file-uploading, around lines 30-37), the text color still uses scale-color($secondary, $lightness: 50%) instead of the new dark-light-choose pattern used by the sibling #draft-status, causing inconsistent theme handling and potential poor visibility in dark themes", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "In app/assets/stylesheets/mobile/compose.scss (.wmd-input, around lines 174-176), the text color uses darken($primary, 40%), which may reduce readability when $primary is already dark; consider switching to dark-light-choose for consistent theme support", - "path": null, - "line": null, - "source": "extracted" - } - ], - "greptile": [ - { - "text": "In app/assets/stylesheets/desktop/topic-post.scss line 291, the dark-light-choose lightness values are flipped: light theme should keep scale-color($primary, $lightness: 30%) and dark theme should use scale-color($secondary, $lightness: 70%) (inverted), not the opposite", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "In app/assets/stylesheets/desktop/user.scss line 522, an original scale-color($primary, $lightness: 30%) was incorrectly changed to 50% instead of using 30% for light and 70% (inverted) for dark with $secondary", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "In app/assets/stylesheets/desktop/user.scss line 527, an original scale-color($primary, $lightness: 30%) was incorrectly changed to 50% instead of using 30% for light and 70% (inverted) for dark with $secondary", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "In app/assets/stylesheets/mobile/user.scss line 497, an original scale-color($primary, $lightness: 30%) was incorrectly changed to 50% instead of using 30% for light and 70% (inverted) for dark with $secondary", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "In app/assets/stylesheets/mobile/user.scss line 502, an original scale-color($primary, $lightness: 30%) was incorrectly changed to 50% instead of using 30% for light and 70% (inverted) for dark with $secondary", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "In app/assets/stylesheets/mobile/modal.scss line 102, the .custom-message-length color inversion is inconsistent with desktop: desktop uses (70%, 30%) while mobile uses (30%, 70%); they should match", - "path": null, - "line": null, - "source": "extracted" - } - ], - "kg": [], - "qodo-v2": [ - { - "text": "In app/assets/stylesheets/desktop/topic-post.scss, .topic-meta-data h5 a uses dark-light-choose with a too-light light-theme value (primary 70% instead of the intended darker ~30%), reducing contrast and becoming inconsistent with base author link styling", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "In app/assets/stylesheets/mobile/modal.scss, .custom-message-length has light/dark lightness values inverted (light theme uses 30% and dark theme uses 70%), likely an accidental swap during dark-light-choose conversion and a contrast regression vs desktop", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "In app/assets/stylesheets/mobile/topic-post.scss, .topic-map h3 uses a 50%/50% lightness pair instead of matching desktop\u2019s stronger 20%/80%, reducing emphasis/contrast and creating inconsistency between mobile and desktop", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "In app/assets/stylesheets/desktop/user.scss (and similarly mobile/user.scss), .group-member-info .name uses a weaker 50%/50% lightness pair instead of the base name styling\u2019s stronger 30%/70%, making group member names less prominent and inconsistent", - "path": null, - "line": null, - "source": "extracted" - } - ], - "devin": [ - { - "text": "In desktop/topic-post.scss for `.embedded-posts .topic-meta-data h5 a`, the `dark-light-choose` light/dark values are swapped, changing light-theme link color from `scale-color($primary, $lightness: 30%)` to `70%` and making embedded post meta-data links/author names hard to read on light themes", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "In mobile/modal.scss for `.custom-message-length`, the `dark-light-choose` light/dark values are swapped, changing light-theme color from `scale-color($primary, $lightness: 70%)` to `30%` and inverting intended contrast on both light and dark themes", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "In mobile/topic-post.scss for `.topic-map h3`, the `dark-light-choose` transformation changes the light-theme lightness from `20%` to `50%` (and uses `50%` for both branches), making headings less prominent and inconsistent with the desktop equivalent", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "In desktop/user.scss for `.group-member-info .name`, the `dark-light-choose` transformation changes the light-theme lightness from `30%` to `50%`, making group member names lighter and less readable on light themes", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "In mobile/user.scss for `.group-member-info .name`, the `dark-light-choose` transformation changes the light-theme lightness from `30%` to `50%`, making group member names lighter on light themes", - "path": null, - "line": null, - "source": "extracted" - } - ], - "mra-max": [ - { - "text": "Sweeping migration of color functions in admin_base.scss (scale-color to dark-light-choose(scale-color(...), ...)) creates high churn and risk of visual regressions, merge conflicts, and potential SCSS compile issues if theme variables are missing", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Widespread replacement of scale-color($primary, ...) with dark-light-choose(scale-color($primary,...), scale-color($secondary,...)) across core/global SCSS files may shift global styling behavior and requires thorough regression testing due to large ripple potential", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "In admin_base.scss, .setting.overridden h3 uses scale-color($highlight, $lightness: -50%) instead of the dark-light-choose pattern, risking inconsistent theming/contrast in dark mode", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "admin_base.scss contains multiple scale-color(...) usages not wrapped in dark-light-choose (e.g., tertiary/danger backgrounds), creating inconsistent theming and potential dark-mode breakage", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Possible SCSS syntax/typo in _topic-list.scss where a rule appears truncated as 'color: dark-ligh', which would cause a compile-time error if present", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "_topic-list.scss has a color rule using scale-color($primary, $lightness: 50%) without dark-light-choose, inconsistent with surrounding theming approach", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Header/navigation SCSS underwent large-scale dark-light-choose color changes, increasing risk of visual regressions and theme drift", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "In header.scss, some dark-light-choose calls use a $primary-derived value as an option such that dark mode may still resolve to a $primary-derived color instead of the intended $secondary-derived color (e.g., around line 301)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "In header.scss, another similar dark-light-choose usage may cause dark mode to derive from $primary rather than $secondary (e.g., around line 332)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Centralizing theming decisions in the dark-light-choose Sass function (based on brightness($primary)/brightness($secondary)) tightly couples UI colors to theme palette/brightness logic, so changes to tokens or brightness logic can cascade broadly", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Desktop topic-list SCSS mass refactor to dark-light-choose may cause inconsistent theming across breakpoints if $secondary is not consistently defined across themes", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Mobile topic-list SCSS mass refactor to dark-light-choose increases churn and risk of mobile rendering regressions", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Lack of explicit automated visual regression and/or color-contrast testing for the new dark/light color logic increases risk of undetected UI regressions", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Overall patch scope is very broad across desktop/mobile/admin styles, increasing regression risk and warranting comprehensive testing (visual/UI/accessibility)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "dark-light-choose depends on theme_variables imports; if the import graph/build context lacks theme_variables, builds may fail or colors may compute unexpectedly (build fragility)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "PR description is insufficient to explain rationale, scope, affected files, and testing performed, making review/maintenance harder", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Repository lacks a CHANGELOG entry/file despite docs referencing changelog practices; theming/token changes should be documented for consumers", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Partial adoption of the new theming scheme leaves some rules using direct tokens (e.g., color: $primary) alongside dark-light-choose, creating inconsistent color strategy within the same file/theme", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Static uses of $header_primary in topic-post.scss are not wrapped in dark-light-choose, potentially leading to inconsistent dark-theme behavior if theme variables change", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "No tests were found that explicitly cover dark-theme behavior or validate the new dark-light-choose-based CSS outputs, leaving changes unguarded", - "path": null, - "line": null, - "source": "extracted" - } - ], - "greptile-v4": [ - { - "text": "desktop/topic-post.scss: In `.topic-meta-data h5 a`, the light-theme color lightness was accidentally changed from `scale-color($primary, $lightness: 30%)` to `scale-color($primary, $lightness: 70%)` in the first argument of `dark-light-choose`, causing an unintended light-mode link color regression", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "mobile/modal.scss: In `.custom-message-length`, the `dark-light-choose(light, dark)` arguments are swapped (light uses 30% and dark uses 70%), diverging from the desktop rule and producing incorrect colors in both light and dark modes on mobile", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "desktop/user.scss: In `.name`, the light-theme lightness was changed from 30% to 50% in the first argument of `dark-light-choose`, unintentionally lightening the text in light mode", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "mobile/user.scss: In `.name`, the light-theme lightness was changed from 30% to 50% in the first argument of `dark-light-choose`, matching the desktop bug and altering light-mode rendering", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "mobile/topic-post.scss: In `.media-body h3`, the light-theme lightness was changed from 20% to 50% in the first argument of `dark-light-choose`, making the heading too light and inconsistent with the correct desktop implementation", - "path": null, - "line": null, - "source": "extracted" - } - ], - "macroscope": [ - { - "text": "In desktop/topic-post.scss, the dark-light-choose call for .topic-meta-data h5 a uses lightness: 70% for the light theme instead of the intended/original 30%, making embedded post links too light and less visible in light themes", - "path": null, - "line": null, - "source": "extracted" - } - ] - }, - "https://github.com/ai-code-review-evaluation/discourse-graphite/pull/8": { - "gemini": [ - { - "text": "Backend controller reads group ID from params[:group_id] but the route provides it in params[:id], causing the action to fail; should use :id", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Frontend pagination totalPages calculation is wrong when user_count is an exact multiple of limit (e.g., 50/50 yields 2 instead of 1); should use Math.ceil(user_count/limit)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "addMembers action leaves a TODO to clear the input; usernames field should be cleared after adding members for better UX", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "CSS uses !important, which hurts maintainability/debugging; should avoid it by adjusting selector specificity or structure", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "add_members backend action performs one DB query per username (N+1); should fetch users in a single query (e.g., User.where(username: ...))", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Test uses PUT to hit remove_member but the route is DELETE; test should use xhr :delete", - "path": null, - "line": null, - "source": "extracted" - } - ], - "claude": [ - { - "text": "Removed TODO comment about fixing the root cause of group member truncation without clearly addressing whether the underlying bug was actually fixed", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Introduced a new TODO in admin-group.js to clear the input after adding members, but the functionality is not implemented", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Regression: groups_controller.rb now paginates (limits) members for non-automatic groups, reintroducing truncation that a recent commit explicitly prevented", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Test spec uses the wrong HTTP method for remove_member (uses PUT/xhr :put but the route expects DELETE/xhr :delete), so the test may not exercise the intended behavior", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Pagination calculation error in admin-group.js totalPages formula can return an extra empty page", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "group.js function returns undefined instead of a promise when name is empty, causing inconsistent return type and breaking promise chaining", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Test suite change removed a pending pagination test, meaning the truncation regression would not be caught by tests", - "path": null, - "line": null, - "source": "extracted" - } - ], - "qodo": [ - { - "text": "Client-controlled `limit` param in `GroupsController#members` is not capped, enabling potentially very large queries/responses (DoS risk)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Pagination `totalPages` calculation uses `Math.floor(user_count / limit) + 1`, causing an extra page when `user_count` is an exact multiple of `limit` (off-by-one)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "`next` pagination action clamps `offset` to `user_count`, which can produce an empty page; last valid offset should typically be `user_count - limit` (or last page start)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "`members` action uses `offset = params[:offset].to_i` without a sane default/validation, which can lead to unexpected pagination behavior", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "`add_members` splits comma-separated usernames without trimming whitespace, leading to failed lookups or surprising behavior for inputs like `\" bob,alice \"`", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "`add_members` does not deduplicate usernames, causing unnecessary repeated lookups/add attempts", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "`add_members` does not handle empty tokens from the split (e.g., trailing commas), causing unnecessary lookups", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "`remove_member` deletes membership via `group.users.delete(user_id)` directly; may bypass intended group add/remove APIs (callbacks/validations) and should be confirmed as intended", - "path": null, - "line": null, - "source": "extracted" - } - ], - "bugbot": [ - { - "text": "`totalPages` uses `Math.floor(user_count / limit) + 1`, causing an off-by-one extra page when `user_count` is a multiple of `limit`; should use `Math.ceil(user_count / limit)`", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Off-by-one `totalPages` calculation keeps the pagination \"next\" button enabled on the real last page, allowing navigation to an empty page", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "`remove_member` calls `group.users.delete(user_id)` with an integer ID, but the association `delete` expects a User object (or should use the Group model's `remove(user)`), so the user may not be removed from the group", - "path": null, - "line": null, - "source": "extracted" - } - ], - "copilot": [ - { - "text": "totalPages calculation uses Math.floor(user_count/limit)+1, which over-counts when user_count is an exact multiple of limit; should use Math.ceil (handling the 0 case)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Pagination next action clamps offset to user_count, allowing offset past the last valid page start and producing empty result sets; should clamp to lastPageStart = max(user_count - limit, 0)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "create action forces group.visible to false when the visible param is omitted, overriding the model default (visible true) and unintentionally creating invisible groups", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "create action ignores alias_level even though the client sends it, so alias level is not persisted", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "update action unconditionally sets group.visible from a string comparison, so omitting visible in a partial update resets it to false; should only update when the param key is present/normalize booleans", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "add_members uses group.add(user) (persisting join rows immediately) and then relies on group.save for success, so it can return an error even though memberships were already added; should avoid group.save and make join changes atomic (e.g., transaction)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "remove_member uses group.users.delete(user_id) on a has_many :through, which can bypass GroupUser destroy callbacks and break the user_count counter_cache; should destroy join rows via group.group_users...destroy_all (and handle primary_group cleanup)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "findMembers returns undefined when name is empty, but callers treat it like a promise; should consistently return a resolved promise (or a consistent null contract)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "findMembers offset clamping allows offset === user_count, which yields an empty page and breaks pagination math; should clamp to maxOffset = max(user_count - limit, 0)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "A database query is executed inside a loop and should be hoisted to a single query outside the loop (e.g., fetch users by username once and index them)", - "path": null, - "line": null, - "source": "extracted" - } - ], - "augment": [ - { - "text": "`totalPages` calculation uses `Math.floor(user_count / limit) + 1`, which over-counts pages when `user_count` is an exact multiple of `limit` (e.g., 50/50 -> 2 pages instead of 1)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Over-counted `totalPages` allows `next` to set `offset` to `user_count`, producing an empty last page and incorrect `currentPage/totalPages` display", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "`automatic` may not be in scope in the group member item view template; if it resolves against the member context it can be `undefined`, causing the remove control to appear for automatic groups", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "If the remove control is shown for automatic groups, clicking remove may trigger a failing request (likely 422) with no UI error handling", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "`offset = params[:offset].to_i` can yield negative offsets and `limit` can be <= 0, leading to invalid/raising ActiveRecord `.limit(...).offset(...)` queries; inputs should be clamped/sanitized", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Controller spec uses `xhr :put` for `remove_member` even though the route is `DELETE /admin/groups/:group_id/members`, so the spec may pass while the real route/verb behaves differently", - "path": null, - "line": null, - "source": "extracted" - } - ], - "propel": [ - { - "text": "Add/remove member routes are defined outside a `member` block, generating `/admin/groups/members` without a group id, while the Ember client calls `/admin/groups/${id}/members.json`, causing 404s", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "In `add_members`, the controller requires `params[:group_id]` but the client only provides the group id in the URL (`params[:id]`), triggering `ActionController::ParameterMissing` and returning 400", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "In `remove_member`, the controller requires `params[:group_id]` but the client only provides the group id in the URL (`params[:id]`), triggering `ActionController::ParameterMissing` and returning 400", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Pagination total pages calculation uses `Math.floor(user_count / limit) + 1`, which overcounts when `user_count` is an exact multiple of `limit`, leaving the \u201cnext\u201d button enabled and allowing navigation to an empty page", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Server-side username parsing for adding members does not trim whitespace from comma-separated usernames (e.g. \"bob, alice\"), causing `User.find_by_username` to miss users and silently skip adding them", - "path": null, - "line": null, - "source": "extracted" - } - ], - "baz": [ - { - "text": "Group create action never assigns alias_level from params before saving, so non-default alias_level set in the UI is silently dropped on create", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "totalPages calculation uses Math.floor(user_count/limit)+1, which overcounts when user_count is an exact multiple of limit and shows a phantom extra page", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "next pagination clamps offset to user_count, allowing offset==user_count on the last page and causing the backend to return zero members (blank page)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "members endpoint always paginates with limit/offset but the public group/members template has no pagination controls, making members beyond the first page unreachable for non-automatic groups", - "path": null, - "line": null, - "source": "extracted" - } - ], - "coderabbit": [ - { - "text": "totalPages computed property has an off-by-one error when user_count is an exact multiple of limit due to using Math.floor(...)+1 instead of Math.ceil(...)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "next() pagination clamps offset to user_count, allowing offset == user_count and resulting in navigation to an empty members page; should clamp to the last valid offset", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "add_members splits usernames by comma without trimming whitespace or rejecting blank entries, causing valid usernames with spaces (or empty items) to be skipped during lookup", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "groups_controller pagination uses limit and offset directly from params without bounds checking, allowing negative or excessively large values that can cause heavy/unbounded or odd queries", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "admin/groups_controller_spec remove_member test uses PUT instead of the DELETE verb required by the route, so it doesn't validate correct HTTP method usage", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "group/members.hbs template guard checks model but iterates over members, so headers can render without rows when members is undefined/empty; guard should check members", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "group/members.hbs contains a stray closing tag without a matching opening tag, producing invalid HTML and potential rendering issues", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "routes define add/remove member endpoints as member routes passing :id, but controller actions require :group_id, causing missing parameter errors at runtime", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "admin_base.scss hardcodes colors (#333, #aaa) instead of using SCSS variables, reducing theme consistency and potentially impacting accessibility across color schemes", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "admin-group.js addMembers action does not clear the usernames input after adding, increasing risk of accidental repeated submissions", - "path": null, - "line": null, - "source": "extracted" - } - ], - "greptile": [ - { - "text": "Controller spec uses the wrong HTTP method (PUT) for the remove_member route/action; it should send DELETE to match the route and controller action", - "path": null, - "line": null, - "source": "extracted" - } - ], - "kg": [], - "qodo-v2": [ - { - "text": "Admin group pagination calculates totalPages as Math.floor(user_count/limit)+1, which overcounts when user_count is an exact multiple of limit and creates an extra blank members page", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Pagination can navigate to offset == user_count, resulting in an empty members list and incorrect/confusing next/prev state", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Admin::GroupsController#remove_member removes membership via group.users.delete(user_id) and group.save, bypassing the existing GroupUser destroy/callback/counter-cache semantics used elsewhere", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "remove_member does not trigger the primary_group_id cleanup path (destroy_deletions/@deletions), leaving User.primary_group_id pointing to a group the user is no longer a member of", - "path": null, - "line": null, - "source": "extracted" - } - ], - "devin": [ - { - "text": "totalPages calculation uses Math.floor(user_count / limit) + 1, causing an off-by-one error when user_count is evenly divisible by limit (should use Math.ceil)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Inflated totalPages prevents showingLast (currentPage === totalPages) from being true on the real last page, leaving the Next button enabled and allowing navigation to a phantom empty final page", - "path": null, - "line": null, - "source": "extracted" - } - ], - "mra-max": [ - { - "text": "admin-group.js.es6 refactor introduces risk of UI state desynchronization with server data due to new pagination helpers and separate usernames input", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "admin-group.js.es6 save/create path changes may introduce edge cases or incorrect persistence behavior", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "admin_group_route.js removed afterModel and calls model.findMembers() in setupController without awaiting, causing a potential race where UI renders before members load", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "admin/group.hbs template rewrite may break dependent UI/components relying on previous markup/IDs/wiring", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Group.findMembers returns undefined when group name is blank, breaking callers that expect a Promise", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Group.findMembers switched to /members.json with limit/offset, potentially breaking callers expecting the previous endpoint/contract and requiring backend support for the new payload", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "admin-group-selector.hbs template appears removed/empty, which can cause runtime errors or missing UI if still referenced", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Admin::GroupsController server-side API refactor changes create/update semantics and adds add_members/remove_member, risking backward compatibility with existing clients/integrations", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "New Admin::GroupsController membership actions require careful authorization checks; missing/incorrect checks would be a security/permission bug", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "GroupsController changes member listing/pagination: posts now returns meta (total/limit/offset) and default limit changed to 50, potentially breaking client expectations", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Routes reorganize member endpoints and add collection actions (e.g., refresh_automatic_groups), requiring client updates and potentially breaking existing route consumers", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Non-admin GroupsController spec still targets :posts while implementation uses a members endpoint, so tests may fail or not cover the intended API/payload", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "group_member.hbs uses '#unless automatic' but 'automatic' may be on the parent group, so context scoping could incorrectly show/hide the remove button", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Admin groups specs were reorganized around new endpoints/behaviors; risk that edge cases are not covered or tests may not match actual behavior", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "CSS changes for Groups UI could cause layout regressions across admin screens", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Translation placeholder text change ('add users' to 'enter username') may break UI tests or expectations due to string mismatch", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "New i18n keys may be missing in other locales/clients relying on older keys, leading to missing translations", - "path": null, - "line": null, - "source": "extracted" - } - ], - "greptile-v4": [ - { - "text": "Controller actions `add_members` and `remove_member` require `:group_id`, but the member routes provide the group key as `params[:id]`, causing `ActionController::ParameterMissing` on real requests", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Controller specs for `add_members`/`remove_member` inject params directly and bypass routing, masking the `group_id` vs `id` route/controller mismatch that will fail in production", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Pagination `totalPages` calculation uses `Math.floor(user_count / limit) + 1`, which overcounts pages when `user_count` is an exact multiple of `limit` (off-by-one)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Off-by-one `totalPages` bug makes `showingLast` false on the real last page, allowing navigation to an empty next page", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "`alias_level` parameter is not applied in the `create` action, so the user-selected alias level is silently dropped and defaults to 0", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Spec for `remove_member` uses `xhr :put` even though the route is `DELETE /admin/groups/:id/members`, so the test uses the wrong HTTP verb and can\u2019t catch routing/method issues", - "path": null, - "line": null, - "source": "extracted" - } - ], - "mra-nano": [ - { - "text": "Potential mismatch between rewritten admin-group.js.es6 controller logic and group.hbs template conditional rendering (e.g., member management gated by 'automatic' flag)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Removing afterModel and moving member loading to setupController via model.findMembers() may cause race conditions or incomplete UI state if members load after render", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Asynchronous member fetching from a new endpoint may fail, leaving the view in an incomplete state without proper error handling", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "New pagination fields (limit, offset, user_count) in Group model may not align with frontend expectations, risking incorrect pagination behavior", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Mismatch between newly added admin group routes and controller actions could lead to 404/422 errors in group management flows", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "GroupsController#members API response changed from a plain array to an object with members and pagination metadata, risking client incompatibility if not updated accordingly", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Test suite reorganization/new endpoint coverage may not match actual implementation, potentially masking integration regressions", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Updated client.en UI strings (e.g., selector_placeholder and new group management labels) may not align with the new UI behavior, causing confusing or incorrect messaging", - "path": null, - "line": null, - "source": "extracted" - } - ], - "macroscope": [ - { - "text": "`params.require(:usernames)` may return an Array for `usernames[]=...` submissions, but the code calls `.split(\",\")` on it, causing `NoMethodError` on Array", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "In `group/members.hbs`, the template iterates `members` (`{{#each m in members}}`) but no controller proxies that property, so it resolves to undefined and renders an empty list instead of using `model.members`", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "In `models/group.js`, `findMembers` returns `undefined` when `name` is empty, breaking promise chaining (`.then()` TypeError); it should return a resolved promise (e.g., `Ember.RSVP.resolve([])`) for API consistency", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "In `admin/groups_controller_spec.rb`, one `:remove_member` test uses `xhr :put` while another uses `xhr :delete`; the PUT test likely passes due to routing/404 rather than intended behavior, and both should use DELETE", - "path": null, - "line": null, - "source": "extracted" - } - ] - }, - "https://github.com/ai-code-review-evaluation/discourse-graphite/pull/3": { - "gemini": [ - { - "text": "Potential race condition when incrementing match_count: concurrent requests can read the same value and overwrite each other on save, leading to incorrect match_count", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Missing newline at end of Ruby source file, which violates common Ruby style conventions and can cause tool issues", - "path": null, - "line": null, - "source": "extracted" - } - ], - "claude": [ - { - "text": "BlockedEmail.should_block? uses a non-atomic read-modify-write update of match_count/last_match_at (record.match_count += 1; save), causing race conditions and lost updates under concurrent requests", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "BlockedEmail.should_block? performs database writes (increments match_count/updates last_match_at) during validation, introducing validation side effects and potentially persisting stats even when the overall user creation/validation later fails (stat pollution/transaction inconsistency)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "BlockedEmail.should_block? uses save instead of save!, allowing silent failure if the record fails validations and making the method behave as if the update succeeded", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "lib/validators/email_validator.rb is missing a trailing newline at end of file", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "lib/validators/email_validator.rb uses the low-precedence 'and' keyword in a conditional instead of '&&', which is inconsistent with codebase conventions and can lead to precedence-related bugs", - "path": null, - "line": null, - "source": "extracted" - } - ], - "qodo": [ - { - "text": "users_controller#create exposes detailed validation errors and returned values (including email) in failure JSON responses, which may leak information or allow sensitive attributes to be exposed if the sliced set changes", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "BlockedEmail.should_block? updates match_count/last_match_at via read-modify-write, which can lose increments under concurrent signups (race condition)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "BlockedEmail.should_block? uses record.save, which can fail due to validations and silently skip the statistics update; should use an update path that can\u2019t fail silently (e.g., atomic update/update_all or locking)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Email uniqueness is only enforced at the model level and may allow duplicates that differ only by case/normalization; should canonicalize and/or enforce case-insensitive uniqueness (ideally with a DB index)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Client-side caching of rejected emails does not normalize casing/whitespace, so equivalent emails may bypass the cache or be treated inconsistently", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Client-side rejectedEmails cache can grow unbounded across repeated attempts; should limit size or clear it appropriately", - "path": null, - "line": null, - "source": "extracted" - } - ], - "copilot": [ - { - "text": "Race condition in the read-modify-write update of match_count (lines 14-16) can produce inaccurate values when concurrent requests check the same email, because the operation is not atomic and increments can be lost/overwritten", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Calling save without error handling can silently ignore persistence failures, potentially leading to inconsistent data", - "path": null, - "line": null, - "source": "extracted" - } - ], - "bugbot": [ - { - "text": "Client-side validation shows the generic \"Please enter a valid email address\" (I18n.t('user.email.invalid')) for emails added to rejectedEmails after server-side validation failure, which misleads users because the rejection may be due to blocking/taken/disallowed domain rather than invalid format", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "BlockedEmail.should_block? uses a case-sensitive lookup (BlockedEmail.where(email: email)) without normalizing case, so blocked emails may not match the same address with different casing (e.g., spam@EXAMPLE.COM)", - "path": null, - "line": null, - "source": "extracted" - } - ], - "augment": [ - { - "text": "`email_in_restriction_setting?` uses `value =~ regexp` and will raise an exception when `value` is nil (e.g., missing email param) during validation when whitelist/blacklist settings are enabled", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "`validate_each` / `email_in_restriction_setting?` lacks a guard for blank/nil values, causing validation-time exceptions instead of cleanly failing presence validation", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "`BlockedEmail.should_block?` mutates persisted state (`match_count`/`last_match_at`) but is called from email validation, which may run multiple times per request, inflating stats and causing extra writes for a single signup attempt", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "`pushObject(result.values.email)` can push null/undefined/blank into `rejectedEmails` if the server response omits or blanks `values.email`, leaving non-string entries in the cache", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Computed property dependency on `rejectedEmails.@each` may not recompute on array membership changes (especially for primitive string items), so updates via `pushObject` might not trigger recomputation depending on Ember version", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Missing regression test coverage to ensure the email validator does not raise when `value` is nil/blank, particularly when whitelist/blacklist settings are present and `BlockedEmail` is consulted", - "path": null, - "line": null, - "source": "extracted" - } - ], - "propel": [ - { - "text": "Race condition in BlockedEmail match_count/last_match_at updates due to read-modify-write without locking, causing lost increments and inaccurate statistics under concurrent signups", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Ember computed property emailValidation incorrectly depends on rejectedEmails.@each, which doesn\u2019t recompute when plain-string items are added/removed, so pushing a rejected email doesn\u2019t invalidate the field until the user edits it", - "path": null, - "line": null, - "source": "extracted" - } - ], - "baz": [ - { - "text": "BlockedEmail.should_block? performs a case-sensitive exact match (BlockedEmail.where(email: email)) so blocked emails with different casing (e.g., foo@ vs Foo@) are not detected", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "BlockedEmail records are stored with whatever casing the admin entered while User.email is stored with the user\u2019s original casing, leading to inconsistent comparisons and missed blocks", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "EmailValidator passes the raw email value to BlockedEmail.should_block? without normalizing (downcase/strip), so uppercase or whitespace variants bypass the block list", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Postgres string equality is case-sensitive, so the email block validator may never fire for case-variant addresses unless both stored and queried values are canonicalized", - "path": null, - "line": null, - "source": "extracted" - } - ], - "coderabbit": [ - { - "text": "BlockedEmail.should_block? performs a case-sensitive email lookup, allowing blocked emails to bypass by changing letter case (e.g., User@Example.com vs user@example.com)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Proposed fix using Email.downcase() is ineffective because Email.downcase only lowercases the domain portion, so it still won\u2019t match emails that differ by local-part casing", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "BlockedEmail model does not normalize stored emails to lowercase before validation/save, leading to inconsistent storage and comparisons", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "BlockedEmail.should_block? does not normalize the input email to a fully lowercase string before querying (should use email.to_s.downcase)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "BlockedEmail email uniqueness is not enforced case-insensitively, allowing duplicates that differ only by case unless a case-insensitive validator/DB index is used", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "EmailValidator\u2019s domain restriction regex is missing an end-of-string anchor, allowing suffix-domain bypasses (e.g., user@example.com.evil.com matching example.com)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "EmailValidator builds the domain-matching regex using unsafe/insufficient escaping (should use Regexp.escape on the setting rather than manual dot replacement)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "RuboCop offense: extra blank lines at the start/end of the describe block in spec/components/validators/email_validator_spec.rb (Layout/EmptyLinesAroundBlockBody)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Client-side create_account_controller uses a generic translation key (user.email.invalid) for blocked emails instead of the more specific user.email.blocked message", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Client-side rejectedEmails array can accumulate duplicate entries if the same blocked email is submitted multiple times", - "path": null, - "line": null, - "source": "extracted" - } - ], - "greptile": [ - { - "text": "Race condition: updating match_count without locking can lose increments under concurrent requests in BlockedEmail.should_block?", - "path": null, - "line": null, - "source": "extracted" - } - ], - "kg": [ - { - "text": "BlockedEmail.where(email: email).first performs a case-sensitive email match, allowing blocked emails to be bypassed by changing letter case (e.g., Bad@spam.com vs bad@spam.com)", - "path": null, - "line": null, - "source": "extracted" - } - ], - "qodo-v2": [ - { - "text": "BlockedEmail.should_block? uses an exact string match (no canonicalization), so case/whitespace variants of a blocked email can bypass blocking", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Exact-match blocked email lookup allows duplicate BlockedEmail entries that differ only by case/whitespace", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "EmailValidator calls BlockedEmail.should_block? during validation, and should_block? performs DB writes (updates match_count/last_match_at and saves), creating side effects in a validator", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "User signup validations run multiple times (e.g., valid? then save), causing blocked email match_count to be double-incremented and adding unnecessary DB write load", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "EmailValidator unconditionally queries BlockedEmail, which can raise errors during deploy if the blocked_emails table hasn't been migrated yet (missing table_exists? guard)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "BlockedEmail.should_block? increments match_count via read-modify-write (match_count += 1; save), which is non-atomic and can lose increments under concurrent requests", - "path": null, - "line": null, - "source": "extracted" - } - ], - "devin": [ - { - "text": "BlockedEmail.should_block? is invoked twice per signup because UsersController#create calls user.valid? and then user.save (which re-runs validations), causing match_count and last_match_at to be updated twice per attempt", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "BlockedEmail.should_block? increments match_count via a non-atomic read-modify-write (load record, increment in Ruby, save), which can lose updates under concurrent requests", - "path": null, - "line": null, - "source": "extracted" - } - ], - "greptile-v4": [ - { - "text": "BlockedEmail.should_block? increments match_count via non-atomic read-modify-write, causing lost updates/race conditions under concurrent requests", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Ember Classic controller defines rejectedEmails array on the class prototype (extend), making it shared mutable state across all controller instances and leaking data between sessions", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Client-side email validation always shows user.email.invalid for cached rejected emails, ignoring the server-provided user.email.blocked reason and resulting in inconsistent/misleading error messages", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "File lib/validators/email_validator.rb is missing a trailing newline at end of file", - "path": null, - "line": null, - "source": "extracted" - } - ], - "mra-nano": [ - { - "text": "User email validation is conditional on email_changed?, which may not run on new record creation and can allow blocked/restricted emails to bypass server-side validation during signup", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "CreateAccountController introduces a new rejectedEmails state and related short-circuit/push logic across multiple locations, increasing churn and risk of regressions/inconsistent signup state management", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "UsersController create action returns JSON errors including user.errors.to_hash and echoes attempted user attributes (name/username/email) back to the client, leaking sensitive input and enabling user/email enumeration", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "BlockedEmail.should_block? returns nil when no matching record exists instead of explicitly returning false, breaking the expected boolean contract and failing specs", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "EmailValidator/email_in_restriction_setting? does not guard against nil email values and may raise NoMethodError when performing value =~ regexp", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The i18n translation for the blocked email message is added under email.blocked but the code looks up user.email.blocked, so the translation key may not resolve and the error message may be missing or incorrect", - "path": null, - "line": null, - "source": "extracted" - } - ], - "macroscope": [ - { - "text": "BlockedEmail.should_block? uses a non-atomic read-modify-write to increment match_count (read, increment in Ruby, save), causing lost updates under concurrent requests; should use an atomic update like increment!(:match_count)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Email whitelist regex /@(#{domains})/ is missing an end-of-string anchor, allowing substring matches (e.g., user@allowed.com.malicious.org passes when only allowed.com is whitelisted); should anchor with \\z", - "path": null, - "line": null, - "source": "extracted" - } - ] - }, - "https://github.com/ai-code-review-evaluation/discourse-graphite/pull/5": { - "gemini": [ - { - "text": "Some components still use `float` properties that may conflict with the newly applied flexbox layouts, leading to inconsistent or unpredictable behavior", - "path": null, - "line": null, - "source": "extracted" - } - ], - "claude": [ - { - "text": "The @mixin order($val) in mixins.scss incorrectly maps order to -webkit-box-ordinal-group/-moz-box-ordinal-group without adding 1, causing wrong ordering in old flexbox implementations due to 1-based vs 0-based indexing differences", - "path": null, - "line": null, - "source": "extracted" - } - ], - "qodo": [ - { - "text": "Flexbox mixins use legacy vendor syntaxes where some properties/values are not equivalent across implementations (e.g., -webkit-box-align and -webkit-box-ordinal-group), risking inconsistent layouts across target browsers", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Using align-items(baseline) via the mixin may not map correctly to legacy flexbox implementations, potentially breaking baseline alignment in some browsers", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Using order() via the mixin may not map correctly to legacy flexbox implementations (e.g., -webkit-box-ordinal-group semantics differ), potentially producing incorrect element ordering", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Changing .contents to a flex container and positioning .panel with margin-left:auto plus order(3) may cause header child ordering/spacing regressions across header states (docked/undocked)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Removing float-based positioning in the header may break existing markup assumptions and cause layout regressions", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Switching .small-action to flex and changing .small-action-desc padding/paragraph margins may alter vertical rhythm and text wrapping, especially on narrow viewports or with long custom messages", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Avatar/description alignment and multi-line behavior in .small-action may be affected by the new flex/padding/margin changes, potentially causing misalignment", - "path": null, - "line": null, - "source": "extracted" - } - ], - "bugbot": [ - { - "text": "The button inside .small-action uses float: right for right alignment, but after .small-action becomes a flex container the float is ignored, causing the button to lose right alignment and appear inline instead", - "path": null, - "line": null, - "source": "extracted" - } - ], - "copilot": [ - { - "text": "Line comment formatting is inconsistent: missing a space after `//` (should be `// Flexbox`)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Trailing whitespace exists at the end of the `-ms-flex-align: $alignment;` line and should be removed", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "SCSS property formatting issue: missing space after colon (should be `align-items: $alignment;`)", - "path": null, - "line": null, - "source": "extracted" - } - ], - "augment": [ - { - "text": "Legacy -webkit-box/-moz-box flexbox fallback uses *-box-ordinal-group with a 0-based $val even though the property is 1-based, causing item order to be shifted by 1 in older browsers", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "-ms-align-items is not a valid/recognized MS flexbox property for IE10/11 (should be -ms-flex-align), so it may emit invalid CSS and hinder debugging", - "path": null, - "line": null, - "source": "extracted" - } - ], - "propel": [ - { - "text": "The order mixin emits the same $val for legacy -webkit-box-ordinal-group/-moz-box-ordinal-group and modern order, but legacy implementations are one-based while order is zero-based, causing elements to render one position earlier in old WebKit/Gecko builds (e.g., order(2) appears as order 1).", - "path": null, - "line": null, - "source": "extracted" - } - ], - "baz": [ - { - "text": ".panel is styled as if it were a flex item (using margin-left:auto/@include order) but it is not a direct child of a flex container because it sits inside .contents > .row and .row is not display:flex", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Because .panel is not a flex child and clearfix behavior applies, the login panel drops below .title for logged-out users instead of staying right-aligned", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Layout should be fixed by making the actual .row a flex container or reverting to float-based positioning (e.g., float:right) so the login panel remains on the right", - "path": null, - "line": null, - "source": "extracted" - } - ], - "coderabbit": [ - { - "text": "In the SCSS `order($val)` mixin, `-webkit-box-ordinal-group` and `-moz-box-ordinal-group` are set to `$val` directly even though these legacy properties are 1-based, causing an off-by-one ordering bug in older WebKit/Firefox; they should use `$val + 1` while keeping modern `order`/`-ms-flex-order` values unchanged", - "path": null, - "line": null, - "source": "extracted" - } - ], - "greptile": [ - { - "text": "order() mixin passes $val directly to -webkit-box-ordinal-group and -moz-box-ordinal-group even though those properties require 1-indexed values; if $val is 0 it will render incorrectly in older browsers", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Formatting issue: missing space after a colon in the align-items() mixin (line 122)", - "path": null, - "line": null, - "source": "extracted" - } - ], - "kg": [ - { - "text": "Invalid CSS property `-ms-align-items` is used even though it does not exist; it should be removed (IE10 uses `-ms-flex-align`, which is already present) to avoid potential CSS parsing issues", - "path": null, - "line": null, - "source": "extracted" - } - ], - "qodo-v2": [ - { - "text": "Logged-out/server-rendered header layout regression: `.panel` no longer right-aligns (and may drop below the title) because `float: right` was removed and replaced with flex-only properties (`margin-left: auto`, `order`) while the parent Bootstrap `.row` is not a flex container", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Flexbox alignment mixin incomplete for legacy Mozilla box model: `flexbox()`/`inline-flex()` emit `display: -moz-box`/`-moz-inline-box` but `align-items()` does not emit `-moz-box-align`, causing incorrect alignment when falling back to the -moz box display mode (e.g., `.badge-wrapper.bullet` baseline alignment)", - "path": null, - "line": null, - "source": "extracted" - } - ], - "devin": [ - { - "text": "In the `order` mixin, `-webkit-box-ordinal-group` (and `-moz-box-ordinal-group`) incorrectly uses `$val` directly even though the old 2009 flexbox property is 1-indexed, causing off-by-one ordering differences vs standard `order` (should be `$val + 1`).", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "`@include order(2)` on `.extra-info-wrapper` has no effect because `.extra-info-wrapper` is not a direct child of the flex container `.contents` (it is wrapped by an Ember component div), so the element is not reordered as intended.", - "path": null, - "line": null, - "source": "extracted" - } - ], - "mra-max": [ - { - "text": "header.scss uses mixins (e.g., flexbox(), order()) without explicitly importing foundation/mixins, so compiling header.scss in isolation may fail", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Many components still use floats, so partial migration to flexbox can cause inconsistent layouts across pages/breakpoints until floats are removed or a consistent flex context is established", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "CHANGELOG.md is missing or not updated to document foundation/mixins.scss changes and new flexbox mixins, making consumer migration/release notes unclear", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Header migration changes .title from float-based layout to using flexbox()/align-items(center), potentially altering child/sibling flow and breaking alignment if the parent is not a flex container", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "New flexbox mixins added to foundation/mixins.scss (flexbox, inline-flex, align-items, order) are now widely depended on; any naming/compilation/inconsistent application issues could cause build failures or inconsistent styling", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "common.scss imports foundation mixins before foundation variables, creating an import-order mismatch that could break if mixins rely on variables and makes dependency order harder to reason about", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": ".small-action is converted into a flex container (flexbox + align-items), which may change layout/spacing of its children that previously relied on float/inline-block behavior (e.g., .topic-avatar)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "topic-post.scss small-action flexbox change introduces dependency on flex container behavior; if internal structure still relies on floats or breakpoint assumptions, layout may regress", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "topic.scss adds order(2) and line-height: 1.5 to .extra-info-wrapper, which can change flex positioning and vertical spacing and affect alignment with badges/title", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "topic.scss uses order(2) on .extra-info-wrapper, which has no effect unless an ancestor is a flex container, potentially causing misalignment if the parent isn\u2019t flex", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "badges.css.scss replaces direct inline-flex/align-items with new mixins (inline-flex(), align-items(baseline)), introducing dependency on the new mixins and potentially subtle rendering/layout changes for inline badges", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "PR description lacks required detail (migration steps, testing plan, follow-up work) per CONTRIBUTING.md, hindering review and maintenance", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "No Sass-specific tests are present to validate mixin/layout behavior, increasing risk of undetected styling regressions", - "path": null, - "line": null, - "source": "extracted" - } - ], - "greptile-v4": [ - { - "text": "The align-items mixin includes an invalid/dead CSS property `-ms-align-items` that is not supported by IE and should be removed (use `-ms-flex-align` instead, which is already present)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The order mixin incorrectly passes the same value to `-webkit-box-ordinal-group`/`-moz-box-ordinal-group` as modern `order`, causing an off-by-one mismatch because old-spec ordinal-group is 1-based while `order` is 0-based", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "In `.small-action` converted to a flex container, `button { float: right }` is ignored for flex items, causing the delete/remove button to lose right alignment and creating a layout regression", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "In `.small-action` converted to a flex container, `.topic-avatar { float: left }` is ignored for flex items, so the intended float-based positioning no longer applies", - "path": null, - "line": null, - "source": "extracted" - } - ], - "mra-nano": [ - { - "text": "In header.scss, removing/moving the closing brace after .contents likely changes selector nesting so .title may become nested under .contents (e.g., .contents .title) instead of the original header selector, potentially breaking layout for callers relying on the old selector", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "In header.scss, .title was changed from float-based layout to a flex container via mixins, which is a behavioral layout change that may alter header structure/positioning", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "In topic-post.scss/topic.scss, partial float-to-flex migration mixes flexbox regions (e.g., small-action) with float-based elements (e.g., .category), risking alignment/spacing regressions and unpredictable layout behavior", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "In topic-post.scss/topic.scss, padding values shifting to percent-based may change spacing and cause layout regressions", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "In topic.scss, adding order(2) to .extra-info-wrapper may be ineffective if the parent is not a flex container, causing the intended reordering to be ignored and layout to diverge", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "In topic.scss badges selectors, a stray comma in the selector list (\"...span.badge-category, {\") is a syntax error that can break CSS parsing and prevent subsequent rules from applying", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "New foundational flexbox mixins added to foundation/mixins.scss (flexbox/inline-flex/align-items/order) have wide-reaching styling impact and could introduce cross-browser behavior changes due to vendor-prefixed declarations", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Lack of visual/regression or component tests covering the header/topic layout changes increases risk of shipping undetected layout regressions", - "path": null, - "line": null, - "source": "extracted" - } - ], - "macroscope": [] - }, - "https://github.com/ai-code-review-evaluation/discourse-graphite/pull/6": { - "gemini": [ - { - "text": "website_name method repeatedly parses the website URI (URI(website.to_s) called multiple times) instead of parsing once and reusing a local variable, causing unnecessary overhead", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Test coverage is missing for the case where the website and Discourse instance are different subdomains of the same parent domain (e.g., www.example.com vs forum.example.com), leaving the elsif branch untested", - "path": null, - "line": null, - "source": "extracted" - } - ], - "claude": [], - "qodo": [ - { - "text": "User-supplied model.website is rendered directly into an href, which could allow URL injection (e.g., javascript:) if server-side validation/sanitization does not restrict schemes/formats", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Serializer conditional include hook is named include_website_name (missing expected ?/convention), which may cause website_name to be included/excluded incorrectly", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "website_name repeatedly reparses the URL with URI(website.to_s) instead of parsing once and reusing the result, adding overhead and potential inconsistencies", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "website_name uses `rescue nil` for control flow when parsing the URL, masking invalid URI errors instead of handling them explicitly", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "website_name may behave incorrectly for URL edge cases (uppercase hosts, trailing dots, ports, IDNs/punycode, URLs without a path) and should be validated/handled", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Template renders model.website into href while displaying model.website_name; upstream sanitization/validation must prevent dangerous schemes or malformed URLs from becoming clickable links", - "path": null, - "line": null, - "source": "extracted" - } - ], - "bugbot": [ - { - "text": "Conditional serializer method is misnamed: `include_website_name` is missing the required `?` suffix (`include_website_name?`), so Active Model Serializers won't call it and `website_name` will always be serialized instead of conditionally omitted", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "`website_name` bypasses existing trust-level/privacy protections because it is not included in `untrusted_attributes`, potentially exposing a user's website domain when `website` is intentionally hidden", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "`website_name` performs repeated expensive computations (multiple `URI(website.to_s)` parses and repeated `split('.')` calls) that should be computed once and stored in local variables to improve readability and performance", - "path": null, - "line": null, - "source": "extracted" - } - ], - "copilot": [ - { - "text": "`include_website_name` method is missing the trailing `?`, so ActiveModel::Serializer will not treat it as the inclusion hook and the inclusion logic will not run", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Because the inclusion hook is not recognized, `website_name` will be serialized whenever it has a value, even when it should be hidden (e.g., when `website` is blank or other inclusion conditions apply)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "`website_name` derives from `website` but is not gated as an untrusted attribute, so users whose `website` is hidden via `untrusted_attributes` / `restrict_user_fields?` can still leak their website through `website_name` (privacy issue, e.g., TL0 viewed anonymously)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Specs for `website_name` only cover the happy path and do not assert that `website_name` is hidden for TL0 users viewed anonymously when untrusted attributes are filtered, risking privacy regressions", - "path": null, - "line": null, - "source": "extracted" - } - ], - "augment": [ - { - "text": "`website_name` logic doesn\u2019t handle the case where the instance host is the apex domain (e.g. `example.com`) but the user website is a subdomain (e.g. `www.example.com/user`), causing it to return only `www.example.com` and drop the path", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Unclear/possibly incorrect behavior: for subdomains of the instance domain, `website_name` may need to include the URL path to meet the PR goal of treating them as \u201csame domain\u201d", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Serializer conditional hook is misnamed `include_website_name` instead of `include_website_name?`, so it may not gate serialization and could cause `website_name` to be emitted unconditionally", - "path": null, - "line": null, - "source": "extracted" - } - ], - "propel": [ - { - "text": "include_website_name does not check scope.restrict_user_fields?(object), so website_name can be serialized for restricted/anonymous views and leak a TL0 user's website domain/path even when website is suppressed", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Using \".\" << website_host mutates the \".\" string literal, causing the suffix to grow across requests (and potentially raising FrozenError with frozen_string_literal), leading to incorrect host comparison logic", - "path": null, - "line": null, - "source": "extracted" - } - ], - "baz": [ - { - "text": "Hostname matching logic fails when Discourse host is the base domain (example.com) but the user website is on a subdomain (www.example.com), so no branch matches and the URL path (/user) is not appended for same-site subdomain URLs", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "User serializer defines include_website_name without a trailing '?', so it is not treated as a predicate and does not gate the website_name attribute, causing website_name to be serialized even when website is hidden", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Because website_name is still serialized while website is hidden, the template renders a link using model.website even though it is absent, leaking the website host to anonymous/TL0 users", - "path": null, - "line": null, - "source": "extracted" - } - ], - "coderabbit": [ - { - "text": "`include_website_name` predicate method is missing the required trailing `?`, so ActiveModelSerializers will not invoke it and `website_name` will be serialized unconditionally", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "`website_name` can be exposed to restricted/untrusted viewers (e.g., anonymous/TL0 viewing other profiles) because inclusion is not properly gated by `restrict_user_fields?`/`untrusted_attributes`, potentially leaking private data", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "RuboCop: `website_name` method has excessive ABC size (Metrics/AbcSize)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "RuboCop: `website_name` method cyclomatic complexity is too high (Metrics/CyclomaticComplexity)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "RuboCop: `website_name` method perceived complexity is too high (Metrics/PerceivedComplexity)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "RuboCop: uses `rescue` in modifier form in `website_name` (Style/RescueModifier)", - "path": null, - "line": null, - "source": "extracted" - } - ], - "greptile": [ - { - "text": "Query parameters and URL fragments are stripped from the displayed website path because only `URI(...).path` is used (should include query/fragment via `request_uri` or manual concatenation)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "`URI(website.to_s)` is parsed multiple times instead of being parsed once and reused", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Subdomain/domain matching logic is incorrect for multi-level TLDs (e.g., `.co.uk`, `.com.au`) and for domains with different segment counts (e.g., `example.co.uk` vs `forum.example.com`)", - "path": null, - "line": null, - "source": "extracted" - } - ], - "kg": [], - "qodo-v2": [ - { - "text": "`website_name` is serialized without applying the TL0/anonymous `untrusted_attributes` restriction, leaking a TL0 user's website text to anonymous users even when `website` is hidden", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Serializer defines `include_website_name` instead of the expected `include_website_name?`, so the conditional include hook won't run and `website_name` may be included unexpectedly", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "User profile template guards rendering on `model.website_name` but uses `model.website` for the ``, which can produce a broken/empty link when `website` is omitted/hidden", - "path": null, - "line": null, - "source": "extracted" - } - ], - "devin": [ - { - "text": "Method `include_website_name` is missing the required `?` suffix (`include_website_name?`), so ActiveModel::Serializer never calls it as an inclusion guard and `website_name` is always serialized", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Because the `website_name` inclusion guard is not invoked, `website_name` is serialized even when `website` is blank/nil, adding an unnecessary `nil` field to the JSON output", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Broken `include_website_name?` guard causes a privacy leak: `website` is in `untrusted_attributes` (hidden for anonymous TL0 profile views) but `website_name` is not, so the user\u2019s website hostname can be exposed to anonymous viewers", - "path": null, - "line": null, - "source": "extracted" - } - ], - "mra-max": [ - { - "text": "User profile template binds href directly to user-provided model.website without validating/whitelisting URL schemes, allowing javascript: URLs and potential client-side XSS/redirect", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "UserSerializer exposes website_name (including full URL path like example.com/user) to anonymous/TL0 users, potentially leaking internal routing structure and user data", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Front-end removed client-side computed websiteName and now relies on model.website_name; if website_name is not consistently available across serializers/scopes this can cause UI inconsistencies", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Serializer gating pattern is inconsistent: website_name is not included in untrusted_attributes and uses include_website_name without the conventional include_website_name? method generated by the macro, risking incorrect inclusion/exclusion behavior", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Access-control/gating for website_name may be misaligned with untrusted_attributes policy (website gated via untrusted_attributes while website_name is gated separately via website.present?), increasing risk of inconsistent exposure or leakage", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "website_name computation depends on Discourse.current_hostname for domain reasoning, which may behave unexpectedly across multisite/configurations and affect correctness", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Serializer spec tests for website_name are tightly coupled to Discourse.current_hostname stubs and exact string outputs, making them brittle/flaky if hostname canonicalization or URL parsing changes", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Missing CHANGELOG entry documenting the new website_name field exposure and its privacy/security implications", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "PR description is too terse and does not explain the change, rationale, or privacy/security impacts", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Documentation is missing for the new website_name field (semantics, privacy/gating rules, example payloads)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Inline code comments mention a property rename (websiteName to profileBackground) and may be out of sync with actual runtime behavior, causing maintainer confusion", - "path": null, - "line": null, - "source": "extracted" - } - ], - "greptile-v4": [ - { - "text": "URI(website.to_s) is parsed multiple times within website_name (up to three times), causing redundant work and less clear code", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "rescue nil only guards the first URI parse; subsequent URI(website.to_s) calls can raise unhandled exceptions if the URI is invalid", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "String concatenation uses mutating << on a string literal (\".\" << website_host), which is non-idiomatic and potentially confusing; prefer + for one-off concatenation", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "include_website_name guard method is missing the conventional ? suffix, which may prevent ActiveModelSerializers from invoking it and could cause website_name to be serialized unconditionally", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Test coverage is missing for the sibling-subdomain branch of website_name logic (e.g., www.example.com vs forum.example.com), leaving that path untested", - "path": null, - "line": null, - "source": "extracted" - } - ], - "mra-nano": [ - { - "text": "Frontend controller removed legacy websiteName property while backend serializer now provides website_name, risking backward compatibility for clients still using model.websiteName", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Client code must be updated to consume website_name from the serialized User API payload to render user URLs correctly", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "User-provided website URL is bound directly into an href without validating/whitelisting URL schemes, allowing javascript: URLs and creating an XSS/phishing risk", - "path": null, - "line": null, - "source": "extracted" - } - ], - "macroscope": [ - { - "text": "`include_website_name` method is missing the trailing `?`, so the serializer framework never calls it to determine attribute inclusion", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "As a result of the missing `?`, `website_name` is always serialized even when `website` is blank", - "path": null, - "line": null, - "source": "extracted" - } - ] - }, - "https://github.com/ai-code-review-evaluation/discourse-graphite/pull/4": { - "gemini": [ - { - "text": "RSS feed content is imported and rendered as raw HTML via TopicEmbed.import (cook_method: :raw_html) without sanitization, creating a critical XSS vulnerability", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Origin validation using discourseUrl.indexOf(e.origin) === -1 is insecure and can be bypassed with substring matches; origin should be parsed and compared exactly", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "X-Frame-Options header value ALLOWALL is non-standard and may be ignored, leaving the page vulnerable to clickjacking; should use a supported directive (e.g., ALLOW-FROM with a safe origin)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Using open/open-uri with an admin-configured URL from site settings can enable SSRF, local file inclusion, or command execution (e.g., file:// or |cmd); should use a strict HTTP/HTTPS client", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Interpolating url directly into an HTML tag without proper escaping can allow XSS (e.g., javascript: URLs); URL should be HTML-escaped/safely encoded before insertion into attributes", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Using open/open-uri with user-influenced embed_url (from TopicRetriever) can enable SSRF, local file inclusion, or command execution; should restrict to http/https and use a safer HTTP client", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "JavaScript assumes an element with id discourse-comments always exists; if missing, appending to null will throw a TypeError and break the script", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Jobs::PollFeed.new.execute({}) runs synchronously in TopicRetriever and may cause long request times/timeouts when triggered via web request; should be made asynchronous or more efficient", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Auto-reloading the page every 30 seconds can create an infinite reload loop if the background job fails or is slow, harming UX and increasing server load; should add a retry limit/backoff", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Using full request.referer as the postMessage target origin is overly broad; should construct and use the exact origin (scheme + host) for safer messaging", - "path": null, - "line": null, - "source": "extracted" - } - ], - "claude": [ - { - "text": "Origin validation in app/assets/javascripts/embed.js uses indexOf and can be bypassed (e.g., example.com.evil.com passes), allowing improper postMessage origin checks", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "app/controllers/embed_controller.rb sets X-Frame-Options to invalid value \"ALLOWALL\", disabling/negating clickjacking protection and allowing embedding from any origin instead of the configured host", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Invalid X-Frame-Options value may be ignored by browsers, causing default restrictive behavior that breaks intended iframe embedding functionality", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "app/views/layouts/embed.html.erb interpolates request.referer without escaping, enabling potential JavaScript injection/XSS if the Referer header is attacker-controlled", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Using request.referer as postMessage targetOrigin is unsafe without strict validation, potentially sending messages to untrusted origins", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Remote URL open() calls in app/jobs/scheduled/poll_feed.rb and app/models/topic_embed.rb lack error handling, timeouts, and cleanup, risking hanging jobs, resource exhaustion, and unhandled exceptions", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "open() usage for fetching remote URLs may enable SSRF if URL validation is insufficient", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "TopicEmbed.import in app/models/topic_embed.rb has a TOCTOU race between checking for an existing embed and creating one, leading to duplicates or constraint violations", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "URL validation regex in app/models/topic_embed.rb and app/jobs/scheduled/poll_feed.rb only checks http/https prefix and is too permissive, allowing malformed URLs and potential SSRF (e.g., localhost/metadata IPs)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "lib/topic_retriever.rb host validation can be bypassed via userinfo/URL parsing tricks (e.g., embeddable_host@internal.server), enabling SSRF to internal hosts", - "path": null, - "line": null, - "source": "extracted" - } - ], - "qodo": [ - { - "text": "Imported/remote embed content is stored and rendered as raw HTML (cook_method raw_html and rendering raw post.cooked), bypassing normal sanitization and enabling XSS", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Imported HTML may include unsafe constructs (e.g., javascript: links, dangerous attributes/tags/entities, data URIs) that are not sufficiently stripped by the current allowlists", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "TopicEmbed.import_remote fetches arbitrary URLs via open(url).read, enabling SSRF against internal network resources", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Remote fetching via open(...).read lacks hardening such as explicit timeouts, response size limits, redirect limits, and IP-range allow/deny lists, risking hangs and resource exhaustion", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Jobs::PollFeed fetches SiteSetting.feed_polling_url via open(...) without the same hardening, enabling SSRF and resource exhaustion", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "EmbedController sets X-Frame-Options to ALLOWALL, weakening framing protections and enabling clickjacking", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "EmbedController relies on request.referer host matching for access control, but Referer can be absent/spoofed and is bypass-prone for origin validation", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Framing policy should prefer CSP frame-ancestors with explicit allowed origins and stricter origin validation rather than the current X-Frame-Options/Referer approach", - "path": null, - "line": null, - "source": "extracted" - } - ], - "bugbot": [ - { - "text": "RSS feed polling job crashes when an RSS item lacks a `content` field because the code calls `i.content.scrub` on nil instead of falling back to `description` or handling missing content", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "`feed_key` method in `poll_feed.rb` is defined but never used (dead code / leftover unused Redis key generation)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Topic creation/import can crash or create invalid topics when RSS items have no title because `i.title` is passed to `TopicEmbed.import` without a nil check", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Embed iframe `postMessage` origin validation is weak because it uses substring matching (`discourseUrl.indexOf(e.origin) === -1`), allowing bypass with attacker-controlled prefix domains (e.g., example.co vs example.com)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Updating an existing embed can crash if the embedded post was deleted because `embed.post` may be nil and is passed into `PostRevisor.new(post).revise!` without a nil check", - "path": null, - "line": null, - "source": "extracted" - } - ], - "copilot": [ - { - "text": "`posts.cook_method` column default is set to `1`, which conflicts with `Post.cook_methods = Enum.new(:regular, :raw_html)` and makes new posts default to `:raw_html`, bypassing the rendering/sanitization pipeline for regular user posts", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Existing rows may already have the wrong `cook_method` due to the incorrect default and should be backfilled so only explicitly imported/embed posts use `:raw_html`", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "`postMessage` uses `request.referer` (full URL including path) as `targetOrigin`, but browsers compare only scheme/host/port, so including the path can prevent message delivery in some user agents", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Code calls `open(url)` without explicitly requiring `'open-uri'`, which can cause `open` to treat the URL as a local filename if `open-uri` hasn\u2019t been loaded elsewhere", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "RSS/Atom import assumes `i.content` is always present and calls `.scrub` unconditionally, which can raise `nil.scrub` for feeds that only provide `description` or other fields and crash the job", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Imported feed content may include non-UTF-8 data and should be encoded/handled safely before scrubbing/unescaping to avoid errors", - "path": null, - "line": null, - "source": "extracted" - } - ], - "augment": [ - { - "text": "Origin validation using `discourseUrl.indexOf(e.origin)` is insecure and can be bypassed by attacker-controlled origins that include the Discourse URL as a substring (e.g., `https://discourse.example.com.evil.com`), allowing untrusted resize messages", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "`postMessage` `targetOrigin` is set to `request.referer` (a full URL with path/query) instead of an origin (`scheme://host[:port]`), which can throw in browsers and break the iframe resize handshake", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Feed polling can crash because some feed items have `i.content == nil`, so calling `i.content.scrub` raises and breaks polling; needs a guard or fallback field (e.g., description)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "`url` is interpolated directly into a raw HTML `` string without escaping, so quotes/unexpected characters can break markup and enable injection/XSS", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "When `cook_method` is `:raw_html`, `cook` returns raw content and bypasses sanitization; if imported content isn't strictly sanitized upstream, this creates an XSS risk on rendered pages", - "path": null, - "line": null, - "source": "extracted" - } - ], - "propel": [ - { - "text": "Using the full request.referer (including path/query) as postMessage targetOrigin is invalid/mismatched, causing the resize message to be discarded and the iframe height to never adjust; must send only scheme://host[:port] (or fallback to \"*\")", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Feed polling job calls scrub on i.content without handling nil; when a feed item lacks content (only has description), this raises NoMethodError and aborts the job\u2014should fallback to description or skip blank entries before scrub", - "path": null, - "line": null, - "source": "extracted" - } - ], - "baz": [ - { - "text": "SimpleRSS::Item#content is nil for most RSS feeds (only populated for Atom ), so calling i.content.scrub raises NoMethodError and crashes the poll job before importing posts", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "RSS import should fall back to i.description/i.content_encoded/i.summary and guard against nil before scrubbing/unescaping HTML", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "parent.postMessage is given request.referer (full URL with path/query) as targetOrigin, so the browser rejects it and the resize message never reaches the parent; should pass only the origin (scheme+host+port)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "TopicRetriever#fetch_http silently returns when SiteSetting.embed_by_username is blank or the user cannot be found, leaving embeds stuck loading; should surface an error or require a valid username before enqueuing retrieval", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "EmbedController#best enqueues retrieval for any embed_url even when its host mismatches SiteSetting.embeddable_host, but TopicRetriever#invalid_host? then aborts, causing the loader view to loop indefinitely; should validate host/handle invalid URLs and render an error or raise before enqueueing", - "path": null, - "line": null, - "source": "extracted" - } - ], - "coderabbit": [ - { - "text": "embed.js assumes the #discourse-comments container exists; if missing, comments.appendChild(iframe) will throw", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "embed.js postMessageReceived validates origin using discourseUrl.indexOf(e.origin), which can be spoofed via substring matches; origin should be compared strictly to the parsed URL origin", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "embed.js postMessageReceived trusts e.data.height without parsing/validation; non-numeric or invalid values could be applied to iframe.height", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "poll_feed.rb uses open/open-uri with SiteSetting.feed_polling_url, creating an SSRF risk (and potential open-uri command-injection footgun) if the setting is abused; should use a safer HTTP client with scheme/host/IP allow/deny controls", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "poll_feed.rb calls i.content.scrub without guarding for nil; nil content will raise NoMethodError", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "poll_feed.rb defines a feed_key method that appears unused/incomplete and should be removed or wired up", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "post.rb cook returns raw HTML when cook_method is raw_html, allowing untrusted HTML to bypass sanitization and potentially cause stored XSS", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "topic_embed.rb import interpolates url directly into an tag without HTML-escaping; a crafted URL could break attributes and inject HTML/JS", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "topic_embed.rb import appends to contents without guarding for nil; nil contents will raise on <<", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "topic_embed.rb updates embed.content_sha1 even if PostRevisor#revise! fails, potentially desynchronizing state and blocking future updates", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "topic_embed.rb import_remote fetches remote content with open(url).read without timeouts, risking indefinite hangs", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "topic_embed.rb import_remote does not validate URL scheme before fetching, allowing non-http(s) URLs to be used", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "embed/best.html.erb contains invalid ERB/Ruby syntax '<%- end if %>', causing a template rendering syntax error", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "create_topic_embeds migration uses create_table force: true, which can drop an existing table and cause data loss", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "create_top_topics migration uses create_table force: true, which can drop an existing table and cause data loss", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "topic_retriever.rb uses setnx followed by expire (non-atomic); a crash between calls can leave a key without TTL and permanently throttle retrieval", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "topic_retriever.rb calls SiteSetting.embed_by_username.downcase without guarding for blank/nil, which can raise and crash the job", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "disqus.thor sets both raw and cooked to p[:cooked]; raw should be source text (e.g., markdown), not HTML, which can break future re-cooking", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "disqus.thor uses Date.parse for created_at, losing time-of-day precision; should use DateTime.parse/Time.parse to preserve full timestamp", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "post_creator.rb persists cook_method without validating/whitelisting enum values, risking invalid cook_method values and inconsistent behavior", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "add_cook_method_to_posts migration adds a column with default and null: false in one step, which can cause long locks/rewrites on large tables; should be a safer multi-step migration", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "spec/models/topic_embed_spec.rb uses deprecated RSpec matcher be_true; should use 'be true'", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "app/views/embed/best.html.erb interpolates avatar URL directly into an img src attribute; should use image_tag/escaping to avoid attribute injection issues", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "spec/controllers/embed_controller_spec.rb places assertions in an after block, which is an anti-pattern and can mask failures; assertions should be in each test", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "poll_feed.rb lacks error handling around network fetch and feed parsing; exceptions can crash the job (and with retry: false may fail silently without logging)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "embed.css.scss repeats the literal 92px value in multiple places; should be extracted to a SCSS variable for maintainability", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "embed_controller.rb does not handle potential exceptions from TopicView.new, which could cause 500 errors for embeds", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "topic_embed.rb absolutize_urls duplicates similar logic for anchors and images; should be refactored to reduce duplication/complexity", - "path": null, - "line": null, - "source": "extracted" - } - ], - "greptile": [ - { - "text": "XSS vulnerability: `url` parameter is interpolated into an HTML string without sanitization/escaping, allowing crafted URLs to inject JavaScript", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "SSRF vulnerability: `open(url)` can fetch arbitrary, unvalidated URLs, enabling access to internal services (localhost/private IPs/metadata endpoints)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Insecure postMessage: `request.referer` is used as `targetOrigin` without validation, allowing attacker-controlled referer to receive iframe messages/data", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "SSRF vulnerability: RSS feed polling uses `open()` on user-controlled `SiteSetting.feed_polling_url` without validation or domain restrictions", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Potential nil exception: `i.content` may be nil for some RSS items, causing `scrub` to fail", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Weak origin validation in `embed.js`: using `indexOf` to validate `e.origin` can be bypassed via subdomain/prefix tricks, enabling XSS or message spoofing", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Missing error handling for network failures during URL fetching (e.g., `open()` calls)", - "path": null, - "line": null, - "source": "extracted" - } - ], - "kg": [], - "qodo-v2": [ - { - "text": "app/views/embed/best.html.erb contains invalid ERB syntax (<%- end if %>), causing a template compilation error and breaking /embed/best rendering", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "embed layout uses request.referer as postMessage targetOrigin even though referer is often a full URL (path/query) and not a valid origin, so postMessage can throw and iframe resizing can fail", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "request.referer (an unescaped request header) is interpolated directly into JavaScript, creating an injection risk", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "EmbedController#ensure_embeddable validates only the referer host rather than a full origin (scheme+host+port), allowing non-origin referer strings to pass validation", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "app/assets/javascripts/embed.js validates message origins via substring check (discourseUrl.indexOf(e.origin)), which is not a robust origin comparison and can allow spoofed resize messages", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "embed.js assumes the #discourse-comments mount element exists and will throw if it is missing", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Post#cook returns raw HTML unchanged when cook_method is raw_html, bypassing PrettyText sanitization", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "TopicEmbed.import uses cook_method: raw_html for external/RSS content, enabling storage of unsanitized attacker-controlled HTML", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "app/views/embed/best.html.erb renders imported cooked content via raw post.cooked, enabling stored XSS when combined with raw_html cooking", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "PollFeed calls i.content.scrub without guarding against nil, which can raise NoMethodError and abort the job", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "PollFeed has sidekiq_options retry: false, so transient feed format issues can prevent imports until the next scheduled run", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "TopicEmbed.import assumes contents is a mutable String, which can break if a non-mutable or nil value is passed from feed parsing", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "TopicEmbed.import_remote fetches remote content with open(url).read without explicitly requiring open-uri, making behavior dependent on load order", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "TopicEmbed.import_remote uses open(url).read without timeouts, risking hangs and unpredictable slowdowns in background jobs", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "TopicEmbed.import_remote uses open(url).read without error handling/rescue, so network/parse failures can crash jobs", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "TopicRetriever synchronously runs Jobs::PollFeed.new.execute({}) during each retrieval when feed polling is enabled, defeating the hourly schedule and causing repeated expensive feed downloads under load", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "A historical migration (db/migrate/20131223171005_create_top_topics.rb) was modified, which is risky for already-deployed environments", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The modified migration uses create_table ... force: true, which can drop and recreate the table on rerun (reset/redo), risking data loss", - "path": null, - "line": null, - "source": "extracted" - } - ], - "devin": [ - { - "text": "Invalid ERB/Ruby syntax `<%- end if %>` (missing condition) in `app/views/embed/best.html.erb` causes a `SyntaxError` and crashes template rendering", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "`app/jobs/scheduled/poll_feed.rb` calls `i.content.scrub` without nil-check/fallback; feeds without `content` (using `description`) cause `NoMethodError` and crash the polling job", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "XSS risk in `app/views/layouts/embed.html.erb` by interpolating unescaped `request.referer` into a single-quoted JavaScript string; a `'` in the referer can break out and inject script", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "`TopicEmbed#import` mutates the caller-provided `contents` string in-place via `<<`, potentially causing unexpected side effects and repeated footer accumulation on reuse", - "path": null, - "line": null, - "source": "extracted" - } - ], - "mra-max": [ - { - "text": "Embed view sets X-Frame-Options to ALLOWALL, disabling clickjacking protections and allowing framing from any site despite host whitelisting", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Embed controller 'best' action now enqueues RetrieveTopic asynchronously and renders a loading view, changing behavior from prior synchronous TopicRetriever usage and potentially breaking callers relying on synchronous retrieval", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Embed controller spec expects synchronous TopicRetriever invocation, but production code enqueues a background job instead (test/code intent mismatch)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "RetrieveTopic.import_remote does not rescue network or Readability parsing errors, so the job can crash or fail without meaningful error reporting", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Post.cook bypasses the normal cooking/sanitization pipeline when cook_method is raw_html, returning raw content directly (security/design risk)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "TopicEmbed.import_remote calls open(url) without requiring 'open-uri', creating a hidden dependency that can cause runtime failures", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Embed view renders post.cooked via raw while imported posts may use raw_html, creating XSS/content-injection risk if content is not sanitized on import", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Migration create_topic_embeds uses force: true, which can drop an existing table and cause data loss in production", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "PostRevisor update_post allows validate: false via skip_validations option, potentially bypassing validations and persisting invalid data unexpectedly", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "CHANGELOG.md not updated to document the new embedding feature and related changes", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "embed.js assumes #discourse-comments element and global variables (discourseUrl/discourseEmbedUrl) exist without guards, causing potential runtime errors", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "PollFeed job uses open-uri and SimpleRSS.parse without rescue/timeout handling, so network/invalid feed errors can break scheduled jobs", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Embed layout uses request.referer as postMessage target origin, which can be nil/spoofed and may not match the real embedding origin (security/correctness risk)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Adding non-null posts.cook_method column with default enum value can break existing posts if enum mappings change or existing values are unexpected, altering cooking/rendering semantics", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Disqus Thor task removed category option, potentially changing categorization behavior and causing regressions in import flow", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "TopicRetriever depends on Redis for throttling and URL host checks, which can cause runtime issues if Redis is unavailable or throttling behaves unexpectedly (especially outside tests)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "PollFeed feed parsing and per-item import behavior is not adequately exercised in tests, risking unverified end-to-end behavior", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "RetrieveTopic job behavior is not directly unit tested, leaving the async retrieval path unverified (e.g., no_throttle propagation)", - "path": null, - "line": null, - "source": "extracted" - } - ], - "greptile-v4": [ - { - "text": "Unescaped `request.referer` is interpolated into a JavaScript string literal, enabling XSS via a crafted Referer header", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "`postMessage` origin validation uses `discourseUrl.indexOf(e.origin)` (substring/prefix match) instead of exact origin comparison, allowing bypass by prefix origins", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "ERB template contains `<%- end if %>`, producing invalid Ruby (`end if` without a condition) and causing a `SyntaxError` that breaks rendering", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Controller spec mocks `TopicRetriever.new`/`retrieve`, but the controller enqueues `Jobs.enqueue(:retrieve_topic, ...)` instead, so the test has unmet expectations and fails", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "RSS polling job calls `.scrub` on `i.content` without a nil guard; items without `content` cause `NoMethodError` and crash the polling loop", - "path": null, - "line": null, - "source": "extracted" - } - ], - "mra-nano": [ - { - "text": "EmbedController#ensure_embeddable relies on Referer host validation and raises InvalidAccess on mismatched/invalid referer, which can block legitimate embeds and is a fragile access-control mechanism", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "EmbedController#ensure_embeddable sets X-Frame-Options to ALLOWALL, weakening clickjacking protection for the embed response", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "TopicEmbed.import_remote calls open(url).read without requiring 'open-uri', causing NoMethodError in environments where open-uri isn\u2019t auto-loaded and breaking HTTP fetching", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "embed/best.html.erb renders external content with raw post.cooked, bypassing escaping/sanitization and enabling XSS if cooked content contains malicious HTML/script", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Migration create_topic_embeds uses force: true when creating the table, which can drop an existing table and cause data loss", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Embed URL host validation is insufficient: embed_url isn\u2019t sanitized upfront and host checks lack DNS/IP-level SSRF protections, allowing potential access to internal/private addresses (especially in background job flows without referer gating)", - "path": null, - "line": null, - "source": "extracted" - } - ], - "macroscope": [ - { - "text": "app/models/topic_embed.rb: import_remote passes user-controlled url directly to Kernel#open before validation, enabling open-uri command injection (e.g., '|ls')", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "app/jobs/scheduled/poll_feed.rb: i.content can be nil for feeds without , so calling .scrub on it raises NoMethodError and crashes the job; should fall back to i.description when content is blank", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "lib/tasks/disqus.thor: switching to TopicEmbed.import_remote drops the created_at: Date.parse(t[:created_at]) parameter, causing imported topics to get the current timestamp and lose original Disqus thread creation dates", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "lib/topic_retriever.rb: using $redis.setnx and $redis.expire as separate commands can leave a key without TTL if the process crashes between them, permanently blocking retrieval; should set NX+EX atomically", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "lib/tasks/disqus.thor: TopicEmbed.import_remote/open(url).read network failures (OpenURI::HTTPError, SocketError, Timeout::Error, Errno::ECONNREFUSED) are unhandled, crashing the import task mid-run and leaving partial imports", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "db/migrate/20131223171005_create_top_topics.rb: create_table force: true can silently drop the existing top_topics table and destroy data if rerun; should not use force: true", - "path": null, - "line": null, - "source": "extracted" - } - ] - }, - "https://github.com/ai-code-review-evaluation/discourse-graphite/pull/1": { - "gemini": [ - { - "text": "Ruby method `self.downsize` is defined twice; the later definition overwrites the earlier one, breaking callers that pass `max_width` and `max_height` arguments", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "New `downsize` method name conflicts with existing API; it should be renamed (e.g., `downsize_by_dimension_string`) to avoid breaking change", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Client-side code hardcodes `maxSizeKB` to `10 * 1024` (10MB), ignoring site settings for maximum file size and causing client/server validation mismatch", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Client-side code also hardcodes the same `maxSizeKB` value on another line (line 246), again bypassing site settings", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Server-side maximum download size for a URL is hardcoded to `10.megabytes` instead of using a configurable site setting", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Downsizing retry attempt count is hardcoded to `5` and should be extracted to a constant or made configurable", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Downsizing percentage is hardcoded to `80%` and should be extracted to a constant or made configurable", - "path": null, - "line": null, - "source": "extracted" - } - ], - "claude": [ - { - "text": "utilities.js hardcodes a 10MB file size limit instead of using SiteSettings.max_image_size_kb, breaking configurability and test expectations", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "uploads_controller.rb while loop checks tempfile.size, but Tempfile.size is cached and not refreshed after downsizing, so the loop may always run 5 times regardless of the new file size", - "path": null, - "line": null, - "source": "extracted" - } - ], - "qodo": [ - { - "text": "FileHelper.download allows downloading up to 10MB from user-provided URLs, increasing SSRF/resource exhaustion risk (bandwidth/CPU)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "FileHelper.download may not sufficiently restrict internal IP ranges, redirects, or URL schemes; needs allowlist/denylist controls for remote URL fetching", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "OptimizedImage.downsize is defined twice with different signatures, so the later definition overwrites the earlier one in Ruby", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Overwritten downsize method can break callers expecting the (from, to, max_width, max_height, opts) variant, causing runtime errors or incorrect downsizing", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Server downloads API-provided URLs with a hard-coded 10MB limit that can conflict with SiteSetting.max_image_size_kb (download may exceed configured limit or be capped unexpectedly)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Downsizing call passes \"80%\" as the dimensions argument, which may not match what downsize_instructions expects (typically WxH), so it may not actually reduce file size as intended", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Client-side upload size check and 413 messaging are hard-coded to 10MB, ignoring site settings (max_*_size_kb / max_image_size_kb)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Client/server upload size limits can become inconsistent, causing confusing UX where the browser blocks uploads the server would accept (or vice versa)", - "path": null, - "line": null, - "source": "extracted" - } - ], - "bugbot": [ - { - "text": "Duplicate `OptimizedImage.downsize` method definitions cause the later definition to overwrite the earlier one in Ruby, breaking the original `downsize(max_width, max_height)` API", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Existing caller `resize_emoji.rb` invokes `OptimizedImage.downsize(..., 100, 100, opts)` with 4 positional arguments, which will now raise `ArgumentError` because only the `dimensions`-based signature remains", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Client-side file size validation uses a hardcoded 10MB limit instead of the type-specific `max_{type}_size_kb` site setting", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Because of the hardcoded client limit, attachments larger than `max_attachment_size_kb` can pass client validation but be rejected by the server during upload, creating a confusing UX", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Auto-downsize passes `tempfile.path` (without original extension) to `OptimizedImage.downsize`, so animated GIF detection based on matching the path against `/\\.GIF$/i` fails", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "With GIF detection failing, animated GIFs are processed with `downsize_instructions` that extracts only the first frame (`[0]`), destroying the animation", - "path": null, - "line": null, - "source": "extracted" - } - ], - "copilot": [ - { - "text": "API URL-download size cap is hardcoded to 10MB and ignores site settings (max_image_size_kb/max_attachment_size_kb), changing behavior and increasing bandwidth/CPU risk", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Download cap should be derived from the relevant site setting (or only apply 10MB to images while respecting max_attachment_size_kb for non-images)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Downsize retry loop ignores the return value of OptimizedImage.downsize; failures will retry without progress and still proceed to Upload.create_for, which then rejects the file as too large", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Downsize loop should break early and/or return an error (and optionally log) when downsize returns false to aid debugging", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "No controller spec coverage for new behavior that accepts an oversized image and auto-downsizes it below SiteSetting.max_image_size_kb", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "HTTP 413 error UI now hardcodes max_size_kb to 10MB, which can mislead users because 413 is often driven by server/request limits", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "413 handling should prefer a server-provided message/limit or fall back to the appropriate site setting so the UI reflects the real constraint", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "OptimizedImage.downsize is defined twice; the later definition overrides the earlier one, breaking callers expecting the (from,to,max_width,max_height,opts) signature (e.g., resize_emoji job) and likely causing argument/opts errors", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Downsize/optimize argument convention changed (width/height to geometry string) without sufficient test coverage for downsize invocation paths", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Missing/insufficient OptimizedImage specs to cover both legacy downsize(path,path,w,h,opts) calls and new geometry-string usage, risking silent breakage", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Client-side upload size validation is hardcoded to 10MB and no longer respects per-type site settings, allowing attempts the server will reject and showing incorrect limits", - "path": null, - "line": null, - "source": "extracted" - } - ], - "augment": [ - { - "text": "Client-side upload validation hardcodes maxSizeKB to 10MB, ignoring per-type max_*_size_kb site settings and causing client/server limit mismatches (accepting files server rejects or rejecting files allowed by admins)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The 10MB pre-downsize cap is applied to attachment uploads even though it appears intended only for temporary image pre-downsize limiting", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "A fixed 10.megabytes cap for URL upload downloads ignores SiteSetting.max_image_size_kb, allowing configuration bypass or blocking larger configured limits", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The same 10MB limit constant is duplicated in multiple places, increasing risk of client/server limit drift", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The iterative downsize loop (0.8 factor for 5 iterations) may not reduce large images enough to fall under max_image_size_kb, allowing oversized images to proceed to Upload.create_for despite promises of automatic reduction", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "If OptimizedImage.downsize fails and returns false, the current loop keeps retrying and can still allow the oversized file to continue instead of handling the failure appropriately", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "OptimizedImage defines self.downsize twice; the later definition overrides the former and changes the method signature, likely causing ArgumentError for existing callers passing max_width/max_height (e.g., resize_emoji job) and breaking emoji resizing", - "path": null, - "line": null, - "source": "extracted" - } - ], - "propel": [ - { - "text": "Ruby method `downsize` is defined twice, so the first definition is overwritten and calls using `(width, height, opts)` misinterpret `height` as the `opts` hash, producing incomplete ImageMagick dimensions and breaking thumbnail generation", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Client-side upload size validation is hard-coded to 10MB instead of using the configurable `max__size_kb` site setting, causing mismatches where allowed uploads are blocked or disallowed uploads are attempted", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The error message for HTTP 413 responses always reports a fixed 10MB limit instead of reflecting the configured `max_image_size_kb`, leading to misleading user-facing messaging", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Remote upload/download size limit is capped at 10MB regardless of `SiteSetting.max_image_size_kb`, causing remote imports to fail on sites configured to allow larger images and aborting before downsizing can run", - "path": null, - "line": null, - "source": "extracted" - } - ], - "baz": [ - { - "text": "`downsize` is defined twice with different signatures, so the second definition overrides the first and breaks callers that pass `max_width, max_height`", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Client-side upload validation uses a hardcoded `10 * 1024` KB limit instead of `Discourse.SiteSettings['max_' + type + '_size_kb']`, causing uploads to be rejected even when site settings allow larger sizes", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "HTTP 413 error handler hardcodes `max_size_kb = 10 * 1024`, so the displayed error message always claims a 10MB limit even if the configured/server limit differs", - "path": null, - "line": null, - "source": "extracted" - } - ], - "coderabbit": [ - { - "text": "JavaScript: Declaring a variable directly inside a switch case (case 413) without wrapping the case body in a block can leak scope and allow other cases to access the declaration", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Ruby: Using `tempfile.size` in the downsizing loop condition is incorrect because the size can be cached/stale after in-place modification, potentially preventing the loop from terminating correctly", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Ruby: Passing `tempfile.size` to `Upload.create_for` can record an incorrect (stale) file size after downsizing; should stat the file (e.g., `File.size(tempfile.path)`) before passing size", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Ruby: Performing image downsizing in-place by using the same input and output path is risky; a failure mid-write can corrupt/lose the original file (should write to a new temp file and replace atomically)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Ruby: `OptimizedImage.downsize` is defined twice; the second definition shadows the first (Ruby has no method overloading), breaking callers expecting the other signature", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "JavaScript: Hardcoding the max upload size to 10 MB removes per-type (image vs attachment) size limit flexibility that may be expected from site settings", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Ruby: Using `rescue nil` as a modifier when downloading a file silently swallows all exceptions; explicit error handling/logging would improve debuggability", - "path": null, - "line": null, - "source": "extracted" - } - ], - "greptile": [ - { - "text": "Duplicate method definition: `OptimizedImage.downsize` is defined twice, and the second definition overrides the first", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "`OptimizedImage.downsize` call passes a percentage string (\"80%\") where the original signature expects numeric dimensions, creating a signature/usage mismatch and ambiguity", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Frontend uses a hardcoded 10MB upload limit that does not match server-side `SiteSetting.max_image_size_kb`, leading to inconsistent validation and possible server-side rejection after upload", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Uploads downsizing loop modifies the file in-place but does not refresh/reload `tempfile.size`, so the loop condition never changes and can repeatedly run until attempts are exhausted (effectively an infinite/always-true loop)", - "path": null, - "line": null, - "source": "extracted" - } - ], - "kg": [], - "qodo-v2": [ - { - "text": "`OptimizedImage.downsize` is defined twice, so the later definition overrides the earlier one and changes the method arity/signature", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Existing callers (e.g., `Jobs::ResizeEmoji`) that call `OptimizedImage.downsize(from, to, max_width, max_height, opts)` will raise `ArgumentError` at runtime due to the signature change to `(from, to, dimensions, opts={})`", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Remote URL uploads use a hardcoded 10MB download cap, ignoring per-type size limits configured via `SiteSettings`, leading to inconsistent behavior and wasted downloads", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The file download loop can exceed the intended maximum size by up to one chunk because it checks the cap before reading/writing the next chunk", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The auto-downsize loop in `UploadsController` does not check whether downsizing succeeded (ignores the boolean result), risking repeated expensive processing and failures", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "After the auto-downsize loop, there is no guard to ensure the final file size is actually under `max_image_size_kb` before proceeding", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The auto-downsize loop always uses `SiteSetting.allow_animated_thumbnails`, which can incorrectly strip animation for avatar uploads when `allow_animated_avatars` should apply", - "path": null, - "line": null, - "source": "extracted" - } - ], - "devin": [ - { - "text": "Duplicate `OptimizedImage.downsize` method definitions in `app/models/optimized_image.rb` cause the later 3-arg version to overwrite the existing 4-arg version (Ruby has no method overloading)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "`app/jobs/regular/resize_emoji.rb:14` calls `OptimizedImage.downsize(path, path, 100, 100, opts)` with 5 positional args, which will raise `ArgumentError` after the method is overwritten to accept only 3..4 args", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "`lib/tasks/uploads.rake:347` also calls `OptimizedImage.downsize` with separate width/height args and will similarly break due to the overwritten method signature", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Client-side upload validation in `validateUploadedFile` (`app/assets/javascripts/discourse/lib/utilities.js:182`) hardcodes a 10MB limit, ignoring admin-configured `max_image_size_kb` / `max_attachment_size_kb` settings and losing per-type limits", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The upload validation error message (around `utilities.js:184`) will display `10240` KB instead of the configured max size because of the hardcoded 10MB value", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Existing JS tests (`test/javascripts/lib/utilities-test.js.es6:52-61`) will fail because they expect site-setting-based limits (e.g., `max_image_size_kb = 5`) but the code now uses a hardcoded 10240KB limit", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "`displayErrorForUpload` 413 handler (`app/assets/javascripts/discourse/lib/utilities.js:246`) hardcodes 10MB, causing the 'file too large' message to always show 10240 KB regardless of actual server/site configuration", - "path": null, - "line": null, - "source": "extracted" - } - ], - "mra-max": [ - { - "text": "Client-side image size limit is hard-coded to 10MB in JS utilities instead of using site-wide SiteSettings, causing config drift across environments", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Frontend upload validation uses a hard-coded 10MB limit rather than SiteSetting.max_image_size_kb, risking mismatch with server limits and user-facing errors", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "413 (Payload Too Large) error handling path hard-codes a 10MB limit, duplicating logic and increasing drift from backend configuration", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Server-side image handling includes hard-coded 10MB limits (e.g., UploadsController download path uses 10.megabytes) instead of consistently respecting SiteSetting.max_image_size_kb", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Async upload processing defers create_upload to a background task that relies on current_user, which may be nil/unauthenticated when the job runs, causing permission issues or exceptions", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "UploadsController#create_upload can download arbitrary remote URLs for API requests, enabling SSRF-style access to internal resources if not restricted", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Remote URL download path in UploadsController rescues all errors to nil, silently swallowing failures and making upload problems hard to detect", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "OptimizedImage.downsize signature change (dimensions string vs width/height args) can break existing call sites such as resize_emoji.rb, causing runtime errors in jobs/thumbnail generation", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "OptimizedImage resizing/downsize refactor introduces breaking API surface changes that may regress other code paths still using the old method signatures", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Image processing code uses broad `rescue nil` (e.g., FastImage/ImageOptim), masking failures and potentially leaving corrupted or partially processed uploads", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Gravatar fetching uses insecure HTTP URL and downloads without explicit TLS protections, enabling MITM and mixed-content/security issues", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "FileHelper.download uses open-uri without strict hostname allowlisting/SSRF protections (and limited SSL/redirect controls), allowing fetching of restricted/internal resources", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Auto-downsize loop for oversized images lacks dedicated tests for API URL uploads, iteration behavior (up to 5 attempts), and failure cases when downsizing never meets the limit", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "OptimizedImage tests do not assert that the new dimensions string is correctly formed/passed in all branches (e.g., animated vs non-animated), risking regressions", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "No tests cover the new hard-coded 10MB client validation threshold or the server-side 413 error path behavior in the UI", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Potential remaining call-site inconsistencies across the codebase/tests due to the OptimizedImage API contract change, risking runtime/test failures", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "OptimizedImage remote fetch path uses `rescue nil` around FileHelper.download, masking network/download failures and leaving partial state", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "JS site-settings test defaults (e.g., max_image_size_kb) no longer drive client behavior due to the hard-coded frontend limit, causing test/runtime drift", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "utilities-test.js expectations based on mutating Discourse.SiteSettings.max_image_size_kb may fail or become flaky because the code now uses a hard-coded 10MB limit", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "CHANGELOG entry is missing for the new automatic downsize feature and related behavior changes", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "PR description lacks meaningful summary, rationale, and testing instructions", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "README/high-level documentation is missing for the new automatic image downsize behavior and the introduced hard-coded limit", - "path": null, - "line": null, - "source": "extracted" - } - ], - "greptile-v4": [ - { - "text": "Two `self.downsize` methods with different arities are defined; in Ruby the later definition overrides the earlier one, making the 4-argument version dead code", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Existing callers passing four positional args to `downsize(from, to, max_width, max_height)` will be routed to the 3-arg method, binding `max_height` to `opts` and causing `TypeError` (or incorrect behavior) when `opts` is treated as a Hash", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Using `tempfile.size` after ImageMagick writes/replaces the file returns a stale size because it stats the open file descriptor (old inode), not the current path", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The downsize retry loop\u2019s condition uses stale `tempfile.size`, so it may run all iterations even after the file is already under the size limit", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "`Upload.create_for` is passed `tempfile.size`, so the persisted upload filesize can be the original pre-downsize size rather than the actual downsized size", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Client-side upload validation hardcodes a 10MB limit for all upload types, bypassing admin-configured limits for non-image attachments (e.g., `max_attachment_size_kb`)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Passing a percentage dimensions string like `\"80%\"` into the animated GIF resize path produces invalid gifsicle geometry (`--resize-fit 80%>`), causing animated GIF downsizing to fail silently", - "path": null, - "line": null, - "source": "extracted" - } - ], - "mra-nano": [ - { - "text": "Client-side file size validation and 413 error handling in app/assets/javascripts/discourse/lib/utilities.js use a hard-coded 10MB limit instead of SiteSettings.max_image_size_kb, causing frontend/backend upload limit discrepancies and misleading error messages", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "UploadsController URL upload flow in app/controllers/uploads_controller.rb downloads remote resources with a hard-coded 10.megabytes cap (FileHelper.download) instead of using SiteSetting.max_image_size_kb, preventing per-site tuning and diverging from existing patterns", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "app/jobs/regular/resize_emoji.rb calls OptimizedImage.downsize(path, path, 100, 100, opts) using the old numeric width/height signature, which no longer matches the refactored API and will fail at runtime", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "OptimizedImage downsize/resize methods in app/models/optimized_image.rb were refactored to accept a single dimensions string, breaking existing call sites that still pass separate numeric width/height arguments and causing runtime failures", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Upload#create_for in app/models/upload.rb has inconsistent compatibility with the new OptimizedImage.resize signature; other code/specs still use the old call pattern, requiring consistent updates or an adapter for backward compatibility", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "JS tests in test/javascripts/helpers/site-settings.js set Discourse.SiteSettings.max_image_size_kb expecting validation to honor it, but production JS uses a fixed 10MB threshold, causing test/behavior drift and unreliable test results", - "path": null, - "line": null, - "source": "extracted" - } - ], - "macroscope": [ - { - "text": "Duplicate `downsize` method definitions cause the second definition to overwrite the first because Ruby does not support method overloading", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Callers using the 4-argument `downsize(from, to, max_width, max_height)` signature will have arguments misassigned (`max_width` -> `dimensions`, `max_height` -> `opts`), leading to incorrect behavior", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "`optimize` accesses `opts[:allow_animation]` but `opts` may be an Integer due to the overwritten method signature, causing `TypeError: no implicit conversion of Symbol into Integer`", - "path": null, - "line": null, - "source": "extracted" - } - ] - }, - "https://github.com/ai-code-review-evaluation/discourse-graphite/pull/2": { - "gemini": [ - { - "text": "TopicUser.find_by may return nil for users who have never interacted with the topic, causing NoMethodError when accessing tu.notification_level and potentially a server error", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Function name typo: stopNotificiationsText should be stopNotificationsText, and its usage in topic/unsubscribe.hbs must be updated accordingly", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Template typo: reference uses stopNotificiationsText but should use stopNotificationsText to match the corrected controller/component name", - "path": null, - "line": null, - "source": "extracted" - } - ], - "claude": [ - { - "text": "topics_controller.rb unsubscribe action uses TopicUser.find_by and then accesses attributes without nil-checking, risking NoMethodError when no TopicUser record exists", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "lib/email/message_builder.rb does not use the provided per-topic unsubscribe_url for the List-Unsubscribe header, so it still points to the global user preferences URL", - "path": null, - "line": null, - "source": "extracted" - } - ], - "qodo": [ - { - "text": "`topics#unsubscribe` is reachable via GET and mutates server-side state (topic notification level), violating HTTP semantics and enabling unintended triggers (e.g., CSRF, link prefetchers/email scanners)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "`topics#unsubscribe` explicitly skips XHR checks (`skip_before_filter :check_xhr`), potentially weakening protections for a state-changing action", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "`unsubscribe` assumes `current_user` is present; if unauthenticated, `current_user.id` will raise", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "`unsubscribe` assumes a `TopicUser` row exists; if none exists, `tu` is nil and `tu.notification_level` will raise", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Computed property name `stopNotificiationsText` is misspelled (notifications), increasing maintenance cost and potential confusion", - "path": null, - "line": null, - "source": "extracted" - } - ], - "bugbot": [ - { - "text": "`unsubscribe` action calls `tu.notification_level` on a `TopicUser.find_by` result without checking for nil, causing a `NoMethodError` when no TopicUser record exists for the user/topic", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Ember route/template naming mismatch: route is `topicUnsubscribe` (top-level) but template is located at `templates/topic/unsubscribe.hbs` instead of `templates/topic-unsubscribe.hbs`, so the template won't resolve and the unsubscribe page may render blank/broken", - "path": null, - "line": null, - "source": "extracted" - } - ], - "copilot": [ - { - "text": "unsubscribe method uses find_by for TopicUser and may get nil when no record exists, leading to nil method calls; should guard for nil or use TopicUser.change to create missing records", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "unsubscribe_url included in emails is a relative path but needs to be a full absolute URL; should prepend Discourse.base_url", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "New unsubscribe controller action lacks test coverage; tests should be added for handling missing TopicUser records, notification level changes, slug mismatch redirects, and authentication requirement", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Trailing comma after the last property definition should be removed to match conventions and avoid issues in older JavaScript environments", - "path": null, - "line": null, - "source": "extracted" - }, + "https://github.com/grafana/grafana/pull/76186": { + "gemini": [ { - "text": "Typo in property name: stopNotificiationsText should be stopNotificationsText in the JS/controller code", + "text": "SubscribeStream is not instrumented with a contextual logger despite having access to PluginContext, leading to missing contextual log fields (e.g., pluginId)", "path": null, "line": null, "source": "extracted" }, { - "text": "Template references the misspelled stopNotificiationsText; must be updated to stopNotificationsText to match the corrected property name", - "path": null, - "line": null, - "source": "extracted" - } - ], - "augment": [ - { - "text": "Ember route `topicUnsubscribe` is declared as a top-level route but its template is located at `templates/topic/unsubscribe.hbs` (resolving to `topic.unsubscribe`), so Ember may not find/render the intended template and the unsubscribe page may render blank/incorrectly", + "text": "RunStream is not instrumented with a contextual logger despite having access to PluginContext, leading to missing contextual log fields (e.g., pluginId)", "path": null, "line": null, "source": "extracted" }, { - "text": "`TopicUser.find_by(...)` can return `nil`, and accessing `tu.notification_level` without guarding will raise an exception, potentially causing 500 errors on the unsubscribe URL", + "text": "A test function name is not aligned with the file rename and should be renamed for consistency", "path": null, "line": null, "source": "extracted" } ], - "propel": [ + "claude": [], + "qodo": [ { - "text": "Unsubscribe flow can raise NoMethodError when TopicUser.find_by returns nil and code calls tu.notification_level", + "text": "TestLogger.FromContext returns a brand-new logger instance, potentially dropping previously accumulated logger state/context and masking production bugs; it should preserve existing context (e.g., return f or f.New(...))", "path": null, "line": null, "source": "extracted" }, { - "text": "Missing topic_users record causes unsubscribe request to 500, preventing users from opting out", + "text": "grafanaInfraLogWrapper.FromContext depends on a type assertion to *log.ConcreteLogger; if the underlying logger is a different implementation it falls back to d.New() and may unexpectedly discard contextual attributes", "path": null, "line": null, "source": "extracted" }, { - "text": "Code does not guard against or initialize a TopicUser record (and default notification_level) before reading/writing its attributes", + "text": "Contextual logging enrichment is moved to a separate middleware while LoggerMiddleware consumes logger.FromContext(ctx), creating a fragile dependency on middleware ordering/inclusion; missing or misordered middleware can silently degrade log fields", "path": null, "line": null, "source": "extracted" } ], - "baz": [ + "bugbot": [ { - "text": "topics#unsubscribe uses TopicUser.find_by(...) and then accesses tu.notification_level without handling tu being nil, causing a 500 for users without a TopicUser record when visiting /unsubscribe", + "text": "`TestLogger.FromContext` creates a new `TestLogger` via `NewTestLogger()` instead of returning the receiver or sharing state, causing logs to be written to an orphan instance", "path": null, "line": null, "source": "extracted" }, { - "text": "topics#unsubscribe does not fetch-or-create/upsert a TopicUser row before toggling notification_level (should use find_or_initialize_by or TopicUser.change/upsert helper and save)", + "text": "Because `FromContext` returns a fresh logger, `LoggerMiddleware.logRequest` logging calls are not recorded on the original `TestLogger`, preventing tests from verifying that logging occurred", "path": null, "line": null, "source": "extracted" } ], - "coderabbit": [ - { - "text": "Multiline array passed to skip_before_filter is missing a trailing comma after the last element, triggering a Style/TrailingCommaInArrayLiteral lint warning and breaking style consistency", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "topics#unsubscribe is not idempotent because it toggles notification_level, which can accidentally re-subscribe/flip state (e.g., due to email link prefetching)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "topics#unsubscribe does not handle TopicUser.find_by returning nil, leading to potential nil dereference when accessing tu.notification_level; should find-or-create the TopicUser record", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "topics#unsubscribe has overly high ABC complexity (Metrics/AbcSize), indicating it should be refactored/simplified", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Email notification template renders respond_instructions twice when context_posts are present, causing duplicate instructions in the email", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Email notification template uses a malformed CSS class value with a leading dot (class='.previous-discussion'), preventing CSS selectors from matching", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Unsubscribe is a state-changing action exposed via GET routes, which is unsafe; mutation should be moved to POST/PUT or protected (e.g., confirmation page or signed token)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Spec for List-Unsubscribe header only asserts presence and does not verify it uses the provided unsubscribe_url, risking regressions to default URLs", - "path": null, - "line": null, - "source": "extracted" - }, + "copilot": [], + "augment": [ { - "text": "Typo in template binding stopNotificiationsText should be corrected to stopNotificationsText for clarity/consistency", + "text": "QueryData and other request methods dereference req without checking for nil, so a nil request propagating to this middleware would cause a panic", "path": null, "line": null, "source": "extracted" }, { - "text": "Controller property name stopNotificiationsText is misspelled and should be renamed to stopNotificationsText to match the template and improve readability", + "text": "TestLogger.FromContext returns a new logger instance, so logs written via m.logger.FromContext(ctx).Info(...) are not recorded on the original TestLogger instance that tests assert against", "path": null, "line": null, "source": "extracted" } ], - "greptile": [ - { - "text": "Missing nil check for TopicUser.find_by result; find_by can return nil and subsequent save! will crash when no TopicUser record exists", - "path": null, - "line": null, - "source": "extracted" - }, + "coderabbit": [ { - "text": "XSS vulnerability in unsubscribe template due to using triple braces {{{...}}} which renders unescaped HTML and allows malicious topic titles to inject scripts", + "text": "ContextualLoggerMiddleware dereferences req.PluginContext unconditionally in QueryData, CallResource, CheckHealth, and CollectMetrics, which can panic when req is nil", "path": null, "line": null, "source": "extracted" }, { - "text": "Typo in function/identifier name: 'Notificiations' should be 'Notifications' (e.g., stopNotificiationsText -> stopNotificationsText)", + "text": "ContextualLoggerMiddleware does not guard against req.PluginContext being nil before passing it to instrumentContext, which can panic when PluginContext is nil", "path": null, "line": null, "source": "extracted" } ], - "kg": [ + "greptile": [], + "kg": [], + "qodo-v2": [ { - "text": "Typo in property name in `app/assets/javascripts/discourse/controllers/topic-unsubscribe.js.es6`: `stopNotificiationsText` should be `stopNotificationsText`", + "text": "TestLogger.FromContext returns a new TestLogger instance, dropping previously captured log state (InfoLogs/WarnLogs/etc.) and violating the Logger contract to preserve existing context plus the given context", "path": null, "line": null, "source": "extracted" }, { - "text": "Typo in property name in `app/assets/javascripts/discourse/templates/topic/unsubscribe.hbs`: `stopNotificiationsText` should be `stopNotificationsText`", + "text": "LoggerMiddleware now relies on logger.FromContext(ctx) for traceID enrichment, but the traceID contextual log provider is only registered when tracing.ProvideService is called, so in tests or non-standard setups plugin request logs can silently miss trace IDs", "path": null, "line": null, "source": "extracted" } ], - "qodo-v2": [ - { - "text": "Unsubscribe endpoint changes notification state via a GET request, making it non-idempotent and CSRF-vulnerable (can be triggered cross-site for logged-in users, e.g., via an tag)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Unsubscribe action uses TopicUser.find_by followed by direct mutation and save!, which can crash (500) when the TopicUser row is missing", - "path": null, - "line": null, - "source": "extracted" - }, + "devin": [ { - "text": "Directly updating TopicUser and calling save! bypasses the canonical TopicUser.change path (upsert, reason/timestamps, MessageBus publish), leading to inconsistent notification state/UI updates", + "text": "TestLogger.FromContext returns a new disconnected TestLogger instance (NewTestLogger()) instead of the receiver, causing logs to be recorded on a throwaway logger rather than the one referenced by tests", "path": null, "line": null, "source": "extracted" }, { - "text": "Frontend unsubscribe route tries to hide notification reason text by setting details.notificationReasonText to null, but notificationReasonText is a computed property that still returns a translated string, so the reason text will still appear / the set is ineffective", - "path": null, - "line": null, - "source": "extracted" - } - ], - "devin": [ - { - "text": "unsubscribe action does not handle missing TopicUser record: TopicUser.find_by can return nil and the next line calls tu.notification_level, causing NoMethodError/500 when user has no TopicUser for the topic (e.g., unsubscribe link from email)", + "text": "Because TestLogger.FromContext returns a new instance, log assertions in tests (e.g., checking InfoLogs.Calls/messages) can silently show zero calls even when logging occurred via logger.FromContext(ctx).Info(...), breaking the test double contract", "path": null, "line": null, "source": "extracted" @@ -17623,91 +12138,55 @@ ], "mra-max": [ { - "text": "Unsubscribe endpoints are implemented as GET routes even though they change user state (notification level), violating REST/safe-HTTP semantics", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "State-changing unsubscribe via GET is vulnerable to CSRF because GET requests are not protected by Rails CSRF tokens by default and can be triggered by link/image visits", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "TopicsController#unsubscribe is excluded from the :check_xhr before filter, bypassing XHR/CSRF-related protections and increasing CSRF exposure", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Topic#unsubscribe_url builds \"#{url}/unsubscribe\" but Topic may not define an instance method `url`, risking NoMethodError at runtime", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Topic#unsubscribe_url string-concatenation URL construction is brittle (may not handle route/host/locale changes correctly)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Per-topic unsubscribe UI interpolates model.fancyTitle into a translation and renders it as HTML, creating an XSS risk if fancyTitle is not sanitized", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "The unsubscribe template uses triple-mustache ({{{stopNotificiationsText}}}) to render unescaped HTML, amplifying XSS risk from interpolated title/translation content", + "text": "Logger interface is a breaking public API change: variadic params changed to ctx ...any and new FromContext(context.Context) method added, requiring all external implementors to update", "path": null, "line": null, "source": "extracted" }, { - "text": "topic-from-params.js assumes closestPost is non-null and calls methods on it, risking a null dereference when closestPost is null", + "text": "ContextualLoggerMiddleware lacks nil checks and dereferences req.PluginContext in multiple methods, risking panics on nil requests", "path": null, "line": null, "source": "extracted" }, { - "text": "New per-topic unsubscribe route (topicUnsubscribe) expands the public navigation/API surface and increases maintenance/misuse risk, especially since it mutates state", + "text": "InstrumentationMiddleware was renamed/refactored to MetricsMiddleware (including constructor rename), causing large churn and requiring downstream updates", "path": null, "line": null, "source": "extracted" }, { - "text": "New unsubscribe routes (slug-based and slugless) may create route ambiguity/collisions and require careful route resolution", + "text": "MetricsMiddleware uses unsafe type assertions to prometheus.ExemplarObserver/ExemplarAdder without ok-checks, which can panic if exemplars aren\u2019t supported by the underlying metric implementation", "path": null, "line": null, "source": "extracted" }, { - "text": "TopicsController#unsubscribe expands public controller surface and may be missing explicit authentication/authorization checks beyond ensure_logged_in", + "text": "MetricsMiddleware.QueryData dereferences req without a nil check, so a nil *backend.QueryDataRequest will panic", "path": null, "line": null, "source": "extracted" }, { - "text": "Unsubscribe links are used in emails but the unsubscribe endpoint requires login (ensure_logged_in), potentially breaking expected email unsubscribe UX", + "text": "Middleware ordering places ContextualLoggerMiddleware after MetricsMiddleware, so metrics/log instrumentation may miss contextual attributes; ContextualLoggerMiddleware should run before MetricsMiddleware", "path": null, "line": null, "source": "extracted" }, { - "text": "Email template renders raw translated HTML via raw(t(...)), which can enable XSS if translation strings are untrusted or editable", + "text": "grafanaInfraLogWrapper.FromContext performs a potentially unsafe type assertion to *log.ConcreteLogger and fallback behavior may yield unexpected nil/default logger; should use ok-check and ensure a valid logger is returned", "path": null, "line": null, "source": "extracted" }, { - "text": "Email builder now substitutes %{unsubscribe_link} using unsubscribe_url, increasing coupling and risk of incorrect link rendering if unsubscribe_url is missing/incorrect", + "text": "metrics_middleware_test.go keeps the test name TestInstrumentationMiddleware while constructing MetricsMiddleware, creating confusing/misaligned test naming", "path": null, "line": null, "source": "extracted" }, { - "text": "Client/server locale interpolation for topic.unsubscribe translations may be inconsistent (e.g., {{title}} vs passed args), risking broken copy rendering", + "text": "Branch contains a merge commit from origin/main, increasing risk of merge/conflict issues in the change set", "path": null, "line": null, "source": "extracted" @@ -17715,37 +12194,19 @@ ], "greptile-v4": [ { - "text": "TopicsController#unsubscribe calls .notification_level on a nil TopicUser when no user/topic record exists, causing NoMethodError and crashing the unsubscribe flow", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Unsubscribe action mutates notification state on a GET request before showing any confirmation UI, allowing prefetch/crawlers/accidental clicks to cause irreversible changes", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Computed property name is misspelled as stopNotificiationsText (extra 'i'), creating an inconsistency/maintenance hazard despite matching the template today", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "unsubscribe.hbs uses triple-mustache {{{stopNotificiationsText}}} to render an I18n string interpolated with user-controlled model.fancyTitle, enabling potential XSS via unescaped HTML injection", + "text": "TestLogger.FromContext returns a new detached TestLogger instance, so logs emitted via logger.FromContext(ctx).Info/Warn/Error are written to a throwaway logger and tests asserting on the original TestLogger\u2019s captured logs will see zero calls", "path": null, "line": null, "source": "extracted" }, { - "text": "List-Unsubscribe email header omits the new per-topic unsubscribe URL, reducing compliance/utility for email clients that rely on the header", + "text": "ContextualLoggerMiddleware handlers dereference req.PluginContext without guarding against req == nil, so a nil request can panic before LoggerMiddleware\u2019s existing nil-request guard is reached (needs nil checks in QueryData/CallResource/CheckHealth/CollectMetrics)", "path": null, "line": null, "source": "extracted" }, { - "text": "Locale string now includes %{unsubscribe_url}; any email rendering that sets add_unsubscribe_link: true without providing unsubscribe_url will raise a missing interpolation error", + "text": "metrics_middleware_test.go test function name was not updated after renaming InstrumentationMiddleware to MetricsMiddleware (still named TestInstrumentationMiddleware), causing inconsistency and reduced discoverability", "path": null, "line": null, "source": "extracted" @@ -17753,85 +12214,19 @@ ], "mra-nano": [ { - "text": "Frontend Ember route uses ':id' while Rails expects ':topic_id' for unsubscribe route, risking param mismatch", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Unsubscribe feature changes span many layers (frontend/backend/locales), increasing merge conflict risk due to high churn", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Dropdown component concatenates topic title into HTML without escaping, creating potential XSS if titles contain HTML/script", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "No frontend spec covers HTML escaping/XSS behavior for topic titles in the dropdown component", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "TopicsController#unsubscribe assumes TopicUser exists; find_by may return nil and subsequent notification_level access/save will raise NoMethodError", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Unsubscribe nil-safety is inconsistent with other controllers that use nil-safe patterns (e.g., .try), increasing risk of crashes", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Routes expose unsubscribe as GET even though it mutates state, creating CSRF risk and violating REST/Rails conventions", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Client renders fancyTitle via triple-mustache after Emoji.unescape with no explicit client-side sanitization step, relying solely on server escaping", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "No tests cover unauthenticated access/redirect behavior for the unsubscribe action", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Topic#unsubscribe_url has no unit tests to validate URL construction", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Email message builder needs additional tests for edge cases when unsubscribe_url is present vs absent (headers/body rendering)", - "path": null, - "line": null, - "source": "extracted" - }, - { - "text": "Misspelled property name 'stopNotificiationsText' in controller/template is a readability and refactor hazard", + "text": "Need to confirm NewContextualLoggerMiddleware is called/wired in pluginsintegration.go and covered by tests", "path": null, "line": null, "source": "extracted" }, { - "text": "Email payload passes unsubscribe_url; templates should handle missing values and ensure the URL can\u2019t be abused to unsubscribe another user", + "text": "Instrumentation logic should use logger.FromContext for contextual logging; verify it does so", "path": null, "line": null, "source": "extracted" }, { - "text": "Locale strings embed %{unsubscribe_url}, coupling templates to this variable; translations must provide it and be validated", + "text": "TestLogger in pkg/plugins/log/fake.go mutates ErrorLogs fields (Calls/Message/Ctx) without synchronization, risking data races/flaky tests when logging concurrently", "path": null, "line": null, "source": "extracted" @@ -17839,7 +12234,7 @@ ], "macroscope": [ { - "text": "`TopicUser.find_by` in `unsubscribe` can return `nil` for users with no existing `TopicUser` record, but the code immediately calls `tu.notification_level` without a nil check, causing a `NoMethodError`.", + "text": "TestLogger.FromContext returns a new *TestLogger instance, so logs written via the returned logger are captured on an ephemeral logger and never appear in the original logger's InfoLogs/DebugLogs/etc., preventing tests from verifying logs; it should return the existing receiver (f) instead.", "path": null, "line": null, "source": "extracted" @@ -23744,4 +18139,4 @@ } ] } -} \ No newline at end of file +} diff --git a/offline/results/openai_gpt-5.2/evaluations.json b/offline/results/openai_gpt-5.2/evaluations.json index 7934278..6bbcf56 100644 --- a/offline/results/openai_gpt-5.2/evaluations.json +++ b/offline/results/openai_gpt-5.2/evaluations.json @@ -45457,33 +45457,43 @@ "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__macroscope__PR79265__20260309/pull/1" } }, - "https://github.com/ai-code-review-evaluation/discourse-graphite/pull/9": { + "https://github.com/grafana/grafana/pull/76186": { "gemini": { "skipped": false, "true_positives": [], - "false_positives": [], + "false_positives": [ + { + "candidate": "SubscribeStream is not instrumented with a contextual logger despite having access to PluginContext, leading to missing contextual log fields (e.g., pluginId)" + }, + { + "candidate": "RunStream is not instrumented with a contextual logger despite having access to PluginContext, leading to missing contextual log fields (e.g., pluginId)" + }, + { + "candidate": "A test function name is not aligned with the file rename and should be renamed for consistency" + } + ], "false_negatives": [ { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" + "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", + "severity": "High" }, { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", + "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", "severity": "Low" } ], "errors": [], - "total_candidates": 0, + "total_candidates": 3, "total_golden": 2, "tp": 0, - "fp": 0, + "fp": 3, "fn": 2, "errors_count": 0, "precision": 0.0, "recall": 0.0, "tool": "gemini", - "repo_name": "discourse__discourse-graphite__gemini__PR9__20260128", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__gemini__PR9__20260128/pull/1" + "repo_name": "grafana__grafana__gemini__PR76186__20260128", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__gemini__PR76186__20260128/pull/1" }, "claude": { "skipped": false, @@ -45491,11 +45501,11 @@ "false_positives": [], "false_negatives": [ { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" + "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", + "severity": "High" }, { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", + "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", "severity": "Low" } ], @@ -45509,8 +45519,8 @@ "precision": 0.0, "recall": 0.0, "tool": "claude", - "repo_name": "discourse__discourse-graphite__claude__PR9__20260127", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__claude__PR9__20260127/pull/1" + "repo_name": "grafana__grafana__claude__PR76186__20260127", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__claude__PR76186__20260127/pull/1" }, "graphite": { "skipped": false, @@ -45518,11 +45528,11 @@ "false_positives": [], "false_negatives": [ { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" + "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", + "severity": "High" }, { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", + "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", "severity": "Low" } ], @@ -45536,193 +45546,172 @@ "precision": 0.0, "recall": 0.0, "tool": "graphite", - "repo_name": "discourse__discourse-graphite__graphite__PR9__20260123", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__graphite__PR9__20260123/pull/1" + "repo_name": "grafana__grafana__graphite__PR76186__20260123", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__graphite__PR76186__20260123/pull/1" }, "qodo": { "skipped": false, - "true_positives": [ - { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low", - "matched_candidate": "ensure_loaded! tracks loaded locales in @loaded_locales without synchronization, allowing concurrent threads to race when loading the same locale or observe partially updated state", - "confidence": 0.94, - "reasoning": "The golden issue is a thread-safety problem with the lazily maintained @loaded_locales. The candidate explicitly states @loaded_locales is updated without synchronization, leading to races and inconsistent/partial state under concurrency, which is the same underlying concern." - } - ], + "true_positives": [], "false_positives": [ { - "candidate": "I18n.fallbacks.ensure_loaded! is executed on every request in set_locale, causing unnecessary per-request work and potentially repeated filesystem loads" + "candidate": "TestLogger.FromContext returns a brand-new logger instance, potentially dropping previously accumulated logger state/context and masking production bugs; it should preserve existing context (e.g., return f or f.New(...))" + }, + { + "candidate": "grafanaInfraLogWrapper.FromContext depends on a type assertion to *log.ConcreteLogger; if the underlying logger is a different implementation it falls back to d.New() and may unexpectedly discard contextual attributes" + }, + { + "candidate": "Contextual logging enrichment is moved to a separate middleware while LoggerMiddleware consumes logger.FromContext(ctx), creating a fragile dependency on middleware ordering/inclusion; missing or misordered middleware can silently degrade log fields" } ], "false_negatives": [ { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", + "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", + "severity": "High" + }, + { + "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", "severity": "Low" } ], "errors": [], - "total_candidates": 4, + "total_candidates": 3, "total_golden": 2, - "tp": 1, - "fp": 1, - "fn": 1, + "tp": 0, + "fp": 3, + "fn": 2, "errors_count": 0, - "precision": 0.25, - "recall": 0.5, + "precision": 0.0, + "recall": 0.0, "tool": "qodo", - "repo_name": "discourse__discourse-graphite__qodo__PR9__20260123", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo__PR9__20260123/pull/1" + "repo_name": "grafana__grafana__qodo__PR76186__20260123", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__qodo__PR76186__20260123/pull/1" }, "bugbot": { "skipped": false, - "true_positives": [ + "true_positives": [], + "false_positives": [ { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", - "severity": "Low", - "matched_candidate": "FallbackLocaleList#[] mixes string and symbol locale values in the fallback chain, so Ruby's uniq does not deduplicate equivalent locales (e.g., \"en\" vs :en)", - "confidence": 0.9, - "reasoning": "Both comments point to the same underlying problem: locales may be represented as Strings or Symbols, and without normalizing (e.g., converting to a consistent type), equivalent locales like \"en\" and :en are treated as different, leading to duplicate entries/double-loading or failed deduplication." + "candidate": "`TestLogger.FromContext` creates a new `TestLogger` via `NewTestLogger()` instead of returning the receiver or sharing state, causing logs to be written to an orphan instance" + }, + { + "candidate": "Because `FromContext` returns a fresh logger, `LoggerMiddleware.logRequest` logging calls are not recorded on the original `TestLogger`, preventing tests from verifying that logging occurred" } ], - "false_positives": [], "false_negatives": [ { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", + "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", + "severity": "High" + }, + { + "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", "severity": "Low" } ], "errors": [], "total_candidates": 2, "total_golden": 2, - "tp": 1, - "fp": 0, - "fn": 1, + "tp": 0, + "fp": 2, + "fn": 2, "errors_count": 0, - "precision": 0.5, - "recall": 0.5, + "precision": 0.0, + "recall": 0.0, "tool": "bugbot", - "repo_name": "discourse__discourse-graphite__bugbot__PR9__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__bugbot__PR9__20260122/pull/1" + "repo_name": "grafana__grafana__bugbot__PR76186__20260122", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__bugbot__PR76186__20260122/pull/1" }, "copilot": { "skipped": false, - "true_positives": [ - { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low", - "matched_candidate": "ensure_loaded! reads and potentially modifies @loaded_locales without using the LOAD_MUTEX used by load_locale, creating a race condition in multi-threaded access", - "confidence": 0.93, - "reasoning": "The golden issue is a thread-safety problem around lazy initialization/access of @loaded_locales. The candidate explicitly describes a race condition where ensure_loaded! reads/modifies @loaded_locales without the mutex used elsewhere, which is the same underlying thread-safety concern." - } - ], - "false_positives": [ - { - "candidate": "FallbackLocaleList#[] calls SiteSetting.default_locale.to_sym without guarding for SiteSetting/default_locale availability during early initialization, which can raise and break startup" - }, - { - "candidate": "ensure_loaded! depends on I18n.locale and its fallbacks, but I18n.locale may be unset when the initializer runs, causing incorrect behavior or errors if accessed too early" - }, + "true_positives": [], + "false_positives": [], + "false_negatives": [ { - "candidate": "Initializer load order is being managed via a comment (\"order: after 02-freedom_patches.rb\"), but Rails loads initializers alphabetically; relying on the comment can lead to incorrect load order unless the file is renamed with a numeric prefix" + "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", + "severity": "High" }, { - "candidate": "Calling I18n.fallbacks.ensure_loaded! assumes I18n.fallbacks is always present; if I18n.fallbacks is nil/unset, this will raise NoMethodError without a guard" - } - ], - "false_negatives": [ - { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", + "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", "severity": "Low" } ], "errors": [], - "total_candidates": 5, + "total_candidates": 0, "total_golden": 2, - "tp": 1, - "fp": 4, - "fn": 1, + "tp": 0, + "fp": 0, + "fn": 2, "errors_count": 0, - "precision": 0.2, - "recall": 0.5, + "precision": 0.0, + "recall": 0.0, "tool": "copilot", - "repo_name": "discourse__discourse-graphite__copilot__PR9__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__copilot__PR9__20260122/pull/1" + "repo_name": "grafana__grafana__copilot__PR76186__20260122", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__copilot__PR76186__20260122/pull/1" }, "augment": { "skipped": false, "true_positives": [ { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", - "severity": "Low", - "matched_candidate": "TranslateAccelerator @loaded_locales tracking treats string vs symbol locales (e.g., \"en\" vs :en) as different, leading to duplicate entries and repeated locale loads; locale should be normalized before checking/adding", - "confidence": 0.95, - "reasoning": "Both comments identify the same issue: locale values are not normalized, so passing a locale as a String vs Symbol causes the loaded-locales check/tracking to treat them as different and can lead to duplicate loading. The candidate explicitly mentions normalizing before checking/adding, matching the golden intent." + "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", + "severity": "High", + "matched_candidate": "QueryData and other request methods dereference req without checking for nil, so a nil request propagating to this middleware would cause a panic", + "confidence": 0.9, + "reasoning": "The candidate states that QueryData and other request methods dereference req without a nil check, leading to a panic if a nil request reaches the middleware. This matches the golden issue about ContextualLoggerMiddleware methods panicking on nil requests due to accessing req.PluginContext without checking req for nil (regression detail omitted but same core bug)." } ], "false_positives": [ { - "candidate": "Loading I18n fallbacks via ApplicationController#set_locale only covers controller requests; background jobs/mailers that use I18n may not load fallback locales because they don\u2019t pass through set_locale and TranslateAccelerator only loads config.locale" - }, - { - "candidate": "TranslateAccelerator caches I18n.translate results keyed only by config.locale even though the fallback chain depends on SiteSetting.default_locale; changing the site default locale at runtime can cause stale cached fallback strings until I18n.reload!/cache reset" + "candidate": "TestLogger.FromContext returns a new logger instance, so logs written via m.logger.FromContext(ctx).Info(...) are not recorded on the original TestLogger instance that tests assert against" } ], "false_negatives": [ { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", + "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", "severity": "Low" } ], "errors": [], - "total_candidates": 3, + "total_candidates": 2, "total_golden": 2, "tp": 1, - "fp": 2, + "fp": 1, "fn": 1, "errors_count": 0, - "precision": 0.3333333333333333, + "precision": 0.5, "recall": 0.5, "tool": "augment", - "repo_name": "discourse__discourse-graphite__augment__PR9__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__augment__PR9__20260122/pull/1" + "repo_name": "grafana__grafana__augment__PR76186__20260122", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__augment__PR76186__20260122/pull/1" }, "propel": { "skipped": false, "true_positives": [], "false_positives": [ { - "candidate": "Fallback locales are only loaded via ApplicationController#set_locale, so non-controller contexts (background jobs, mailers, rake tasks) never load fallback locales and can raise missing translation errors even when translations exist" - }, - { - "candidate": "The lazy-loading I18n.translate accelerator only loads config.locale, so when translations fall back to SiteSetting.default_locale or :en those locales may remain unloaded and fallback lookups raise I18n::MissingTranslation / I18n::MissingTranslationData" - }, - { - "candidate": "Calling I18n.fallbacks.ensure_loaded! only in the controller path means fallback locales are not preloaded for non-controller codepaths; the fallback loading logic should be moved into the I18n layer (e.g., translate hook or FallbackLocaleList#[]) so every translation call preloads the fallback chain" + "candidate": "/propel review" } ], "false_negatives": [ { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" + "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", + "severity": "High" }, { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", + "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", "severity": "Low" } ], "errors": [], - "total_candidates": 3, + "total_candidates": 1, "total_golden": 2, "tp": 0, - "fp": 3, + "fp": 1, "fn": 2, "errors_count": 0, "precision": 0.0, "recall": 0.0, "tool": "propel", - "repo_name": "discourse__discourse-graphite__propel__PR9__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__propel__PR9__20260122/pull/1" + "repo_name": "grafana__grafana__propel__PR76186__20260122", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__propel__PR76186__20260122/pull/1" }, "baz": { "skipped": false, @@ -45734,11 +45723,11 @@ ], "false_negatives": [ { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" + "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", + "severity": "High" }, { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", + "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", "severity": "Low" } ], @@ -45752,82 +45741,66 @@ "precision": 0.0, "recall": 0.0, "tool": "baz", - "repo_name": "discourse__discourse-graphite__baz__PR9__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__baz__PR9__20260122/pull/1" + "repo_name": "grafana__grafana__baz__PR76186__20260122", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__baz__PR76186__20260122/pull/1" }, "coderabbit": { "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "FallbackLocaleList#[] calls SiteSetting.default_locale.to_sym without guarding against nil, which can raise NoMethodError in edge cases" - }, - { - "candidate": "config/initializers/i18n.rb uses send(:include, ...) to mix in modules where direct include is public and preferred (I18n::Backend::Simple.send(:include, I18n::Backend::Pluralization))" - }, - { - "candidate": "config/initializers/i18n.rb uses send(:include, ...) to mix in modules where direct include is public and preferred (I18n.backend.class.send(:include, I18n::Backend::Fallbacks))" - }, + "true_positives": [ { - "candidate": "protect_from_forgery is not configured with with: :exception, leaving a CSRF protection configuration warning" + "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", + "severity": "High", + "matched_candidate": "ContextualLoggerMiddleware dereferences req.PluginContext unconditionally in QueryData, CallResource, CheckHealth, and CollectMetrics, which can panic when req is nil", + "confidence": 0.96, + "reasoning": "The candidate issue states that ContextualLoggerMiddleware unconditionally dereferences req.PluginContext in the listed methods, causing a panic when req is nil. This is the same core bug described in the golden comment (nil request not checked before accessing PluginContext)." } ], + "false_positives": [], "false_negatives": [ { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" - }, - { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", + "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", "severity": "Low" } ], "errors": [], - "total_candidates": 4, + "total_candidates": 2, "total_golden": 2, - "tp": 0, - "fp": 4, - "fn": 2, + "tp": 1, + "fp": 0, + "fn": 1, "errors_count": 0, - "precision": 0.0, - "recall": 0.0, + "precision": 0.5, + "recall": 0.5, "tool": "coderabbit", - "repo_name": "discourse__discourse-graphite__coderabbit__PR9__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__coderabbit__PR9__20260122/pull/1" + "repo_name": "grafana__grafana__coderabbit__PR76186__20260122", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__coderabbit__PR76186__20260122/pull/1" }, "greptile": { "skipped": false, "true_positives": [], - "false_positives": [ - { - "candidate": "FallbackLocaleList#[] accesses SiteSetting.default_locale during early initialization, which may fail if called before the database/SiteSetting is available" - }, - { - "candidate": "Fallback locale chain construction lacks nil-safety/error handling around SiteSetting.default_locale, risking exceptions during boot (should rescue/compact or defer access)" - } - ], + "false_positives": [], "false_negatives": [ { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" + "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", + "severity": "High" }, { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", + "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", "severity": "Low" } ], "errors": [], - "total_candidates": 2, + "total_candidates": 0, "total_golden": 2, "tp": 0, - "fp": 2, + "fp": 0, "fn": 2, "errors_count": 0, "precision": 0.0, "recall": 0.0, "tool": "greptile", - "repo_name": "discourse__discourse-graphite__greptile__PR9__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__greptile__PR9__20260122/pull/1" + "repo_name": "grafana__grafana__greptile__PR76186__20260122", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__greptile__PR76186__20260122/pull/1" }, "kg": { "skipped": false, @@ -45835,11 +45808,11 @@ "false_positives": [], "false_negatives": [ { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" + "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", + "severity": "High" }, { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", + "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", "severity": "Low" } ], @@ -45853,29 +45826,29 @@ "precision": 0.0, "recall": 0.0, "tool": "kg", - "repo_name": "discourse__discourse-graphite__kg__PR9__20260202", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__kg__PR9__20260202/pull/1" + "repo_name": "grafana__grafana__kg__PR76186__20260202", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__kg__PR76186__20260202/pull/1" }, "qodo-v2": { "skipped": false, "true_positives": [ { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", + "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", "severity": "Low", - "matched_candidate": "FallbackLocaleList#[] returns a mixed array of String and Symbol locales, causing the translation lazy-loader to treat \"en\" and :en as different and load the same locale multiple times, growing @loaded_locales with duplicates", - "confidence": 0.9, - "reasoning": "Both point to the same underlying problem: locales may be represented as String vs Symbol, and without normalization the loader treats them as distinct, leading to duplicate/double-loading. The candidate gives a concrete source (mixed array from FallbackLocaleList#[]) and the resulting duplicate @loaded_locales, which aligns with the golden comment\u2019s normalization concern." + "matched_candidate": "LoggerMiddleware now relies on logger.FromContext(ctx) for traceID enrichment, but the traceID contextual log provider is only registered when tracing.ProvideService is called, so in tests or non-standard setups plugin request logs can silently miss trace IDs", + "confidence": 0.84, + "reasoning": "Both point to missing traceID enrichment in plugin request logs after refactoring: the golden says traceID extraction/adding was removed and the new contextual middleware doesn\u2019t add it; the candidate says relying on logger.FromContext without the traceID provider being registered can cause logs to miss trace IDs. Same underlying concern: trace IDs are not reliably present in plugin request logging due to changed middleware/contextual logging setup." } ], "false_positives": [ { - "candidate": "Fallback locales are preloaded only in ApplicationController#set_locale, so non-controller locale switches (e.g., services/jobs using I18n.with_locale) may not preload fallbacks, making fallback behavior depend on prior execution order in the process" + "candidate": "TestLogger.FromContext returns a new TestLogger instance, dropping previously captured log state (InfoLogs/WarnLogs/etc.) and violating the Logger contract to preserve existing context plus the given context" } ], "false_negatives": [ { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" + "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", + "severity": "High" } ], "errors": [], @@ -45888,17845 +45861,615 @@ "precision": 0.5, "recall": 0.5, "tool": "qodo-v2", - "repo_name": "discourse__discourse-graphite__qodo-v2__PR9__20260227", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-v2__PR9__20260227/pull/1" + "repo_name": "grafana__grafana__qodo-v2__PR76186__20260227", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__qodo-v2__PR76186__20260227/pull/1" }, "devin": { "skipped": false, "true_positives": [], "false_positives": [ { - "candidate": "LRU cache key in translate_accelerator.rb does not include SiteSetting.default_locale, so when the dynamic fallback chain changes (via FallbackLocaleList), cached translations can become stale after the site default locale is updated" + "candidate": "TestLogger.FromContext returns a new disconnected TestLogger instance (NewTestLogger()) instead of the receiver, causing logs to be recorded on a throwaway logger rather than the one referenced by tests" + }, + { + "candidate": "Because TestLogger.FromContext returns a new instance, log assertions in tests (e.g., checking InfoLogs.Calls/messages) can silently show zero calls even when logging occurred via logger.FromContext(ctx).Info(...), breaking the test double contract" } ], "false_negatives": [ { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" + "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", + "severity": "High" }, { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", + "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", "severity": "Low" } ], "errors": [], - "total_candidates": 1, + "total_candidates": 2, "total_golden": 2, "tp": 0, - "fp": 1, + "fp": 2, "fn": 2, "errors_count": 0, "precision": 0.0, "recall": 0.0, "tool": "devin", - "repo_name": "discourse__discourse-graphite__devin__PR9__20260302", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__devin__PR9__20260302/pull/1" + "repo_name": "grafana__grafana__devin__PR76186__20260302", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__devin__PR76186__20260302/pull/1" }, "mra-max": { "skipped": false, "true_positives": [ { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", - "severity": "Low", - "matched_candidate": "translate_accelerator ensure_loaded!(locale) may load the same locale twice when locale is passed as both string and symbol, duplicating work", + "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", + "severity": "High", + "matched_candidate": "ContextualLoggerMiddleware lacks nil checks and dereferences req.PluginContext in multiple methods, risking panics on nil requests", "confidence": 0.93, - "reasoning": "Both comments flag the same underlying issue: locale values should be normalized (e.g., String vs Symbol) to prevent ensure_loaded!/loading logic from treating them as different and doing duplicate loads/work." + "reasoning": "The candidate notes that ContextualLoggerMiddleware is missing nil checks and dereferences req.PluginContext across multiple methods, which can panic on nil requests. This is the same core issue as the golden comment (panic due to accessing req.PluginContext without nil guard)." } ], "false_positives": [ { - "candidate": "Repository has no test files; needs a dedicated test suite for i18n fallback changes (FallbackLocaleList semantics, ApplicationController#set_locale, lazy-loading via I18n.fallbacks, translation loading via translate accelerator, idempotent locale loading, and production fallback regressions)" - }, - { - "candidate": "ApplicationController#set_locale now calls I18n.fallbacks.ensure_loaded!, which may eagerly load fallback locales and increase startup/request time and memory usage" - }, - { - "candidate": "Eager loading fallbacks in set_locale is a behavioral change that may cause regressions if locales are misconfigured or locale loading fails in certain environments" - }, - { - "candidate": "Per-request preload_json performs potentially heavy DB work on typical HTML GET requests, inflating latency and increasing DB load/contestion in high-traffic scenarios" - }, - { - "candidate": "Removing default I18n fallbacks setting from Cloud66 production config changes runtime fallback behavior and may cause missing translations in production" - }, - { - "candidate": "Removing I18n fallbacks enabling from config/environments/production.rb may regress translation availability if the new fallback mechanism isn\u2019t initialized early enough" - }, - { - "candidate": "Removing I18n fallbacks enabling from config/environments/profile.rb may cause translation misses or environment-specific translation differences" - }, - { - "candidate": "New custom i18n initializer (FallbackLocaleList, ensure_loaded! integration, pluralization/fallback modules) is a major architectural change that risks I18n gem compatibility issues" - }, - { - "candidate": "Custom i18n initializer may have thread-safety risks due to altered fallback/loader behavior" - }, - { - "candidate": "Custom i18n initializer may mis-load translations if locales are not loaded in the expected order" + "candidate": "Logger interface is a breaking public API change: variadic params changed to ctx ...any and new FromContext(context.Context) method added, requiring all external implementors to update" }, { - "candidate": "No tests verifying thread-safety/concurrency behavior for the new i18n fallback/loading changes" + "candidate": "InstrumentationMiddleware was renamed/refactored to MetricsMiddleware (including constructor rename), causing large churn and requiring downstream updates" }, { - "candidate": "No tests verifying correctness of fallback order/behavior across locales for the new fallback implementation" + "candidate": "MetricsMiddleware uses unsafe type assertions to prometheus.ExemplarObserver/ExemplarAdder without ok-checks, which can panic if exemplars aren\u2019t supported by the underlying metric implementation" }, { - "candidate": "No tests covering environment-specific behavior differences (prod vs non-prod) for fallbacks after config changes" + "candidate": "MetricsMiddleware.QueryData dereferences req without a nil check, so a nil *backend.QueryDataRequest will panic" }, { - "candidate": "No tests covering per-request preloading behavior/performance impact" + "candidate": "Middleware ordering places ContextualLoggerMiddleware after MetricsMiddleware, so metrics/log instrumentation may miss contextual attributes; ContextualLoggerMiddleware should run before MetricsMiddleware" }, { - "candidate": "Server-side i18n fallback behavior changed but client-side i18n.js still disables/configures fallbacks differently, risking inconsistent translation resolution between server and client" + "candidate": "grafanaInfraLogWrapper.FromContext performs a potentially unsafe type assertion to *log.ConcreteLogger and fallback behavior may yield unexpected nil/default logger; should use ok-check and ensure a valid logger is returned" }, { - "candidate": "Public API/behavior change (server vs client fallback parity) is not reflected in documentation, risking integrator confusion" + "candidate": "metrics_middleware_test.go keeps the test name TestInstrumentationMiddleware while constructing MetricsMiddleware, creating confusing/misaligned test naming" }, { - "candidate": "Removal of explicit i18n/backend/pluralization patch file may cause inconsistency or double-loading if not aligned with the new initializer" + "candidate": "Branch contains a merge commit from origin/main, increasing risk of merge/conflict issues in the change set" } ], "false_negatives": [ { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", + "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", "severity": "Low" } ], "errors": [], - "total_candidates": 19, + "total_candidates": 9, "total_golden": 2, "tp": 1, - "fp": 17, + "fp": 8, "fn": 1, "errors_count": 0, - "precision": 0.05263157894736842, + "precision": 0.1111111111111111, "recall": 0.5, "tool": "mra-max", - "repo_name": "discourse__discourse-graphite__mra-max__PR9__20260306", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__mra-max__PR9__20260306/pull/1" + "repo_name": "grafana__grafana__mra-max__PR76186__20260306", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__mra-max__PR76186__20260306/pull/1" }, "greptile-v4": { "skipped": false, "true_positives": [ { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low", - "matched_candidate": "`ensure_loaded!` initializes and checks `@loaded_locales` outside `LOAD_MUTEX`, creating a TOCTOU race where multiple threads may redundantly call `load_locale`, adding unnecessary overhead", + "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", + "severity": "High", + "matched_candidate": "ContextualLoggerMiddleware handlers dereference req.PluginContext without guarding against req == nil, so a nil request can panic before LoggerMiddleware\u2019s existing nil-request guard is reached (needs nil checks in QueryData/CallResource/CheckHealth/CollectMetrics)", "confidence": 0.93, - "reasoning": "The golden comment flags a thread-safety problem with the lazy initialization/use of @loaded_locales. The candidate describes a TOCTOU race due to checking/initializing @loaded_locales outside the mutex, allowing multiple threads to load redundantly. This is the same underlying thread-safety issue around lazy @loaded_locales." + "reasoning": "The candidate describes the same bug: ContextualLoggerMiddleware methods dereference req.PluginContext without checking if req is nil, causing a panic before the downstream LoggerMiddleware nil guard. It also names the affected handlers and suggests adding nil checks, matching the golden issue (including the regression context implicitly)." } ], "false_positives": [ { - "candidate": "`translate` only loads the current locale and does not load fallback locales, so fallback lookups can silently fail when fallback locale files aren\u2019t loaded" - }, - { - "candidate": "Fallback locales are only preloaded via `ApplicationController#set_locale`, so any `I18n.translate` usage outside controller requests (background jobs, rake tasks, mailers, console) won\u2019t have fallbacks loaded and will return missing translations" + "candidate": "TestLogger.FromContext returns a new detached TestLogger instance, so logs emitted via logger.FromContext(ctx).Info/Warn/Error are written to a throwaway logger and tests asserting on the original TestLogger\u2019s captured logs will see zero calls" }, { - "candidate": "`FallbackLocaleList#[]` dynamically accesses `SiteSetting.default_locale` without a guard, which can raise (e.g., `NameError`) or return an unexpected value during early boot/initializer contexts before `SiteSetting` is available" + "candidate": "metrics_middleware_test.go test function name was not updated after renaming InstrumentationMiddleware to MetricsMiddleware (still named TestInstrumentationMiddleware), causing inconsistency and reduced discoverability" } ], "false_negatives": [ { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", + "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", "severity": "Low" } ], "errors": [], - "total_candidates": 4, + "total_candidates": 3, "total_golden": 2, "tp": 1, - "fp": 3, + "fp": 2, "fn": 1, "errors_count": 0, - "precision": 0.25, + "precision": 0.3333333333333333, "recall": 0.5, "tool": "greptile-v4", - "repo_name": "discourse__discourse-graphite__greptile-v4__PR9__20260306", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__greptile-v4__PR9__20260306/pull/1" + "repo_name": "grafana__grafana__greptile-v4__PR76186__20260306", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__greptile-v4__PR76186__20260306/pull/1" }, "mra-nano": { "skipped": false, "true_positives": [], "false_positives": [ { - "candidate": "FallbackLocaleList does not guard against nil I18n.locale, which can crash in ensure_loaded!" + "candidate": "Need to confirm NewContextualLoggerMiddleware is called/wired in pluginsintegration.go and covered by tests" }, { - "candidate": "FallbackLocaleList depends on SiteSetting.default_locale during initialization, risking incorrect fallback chains if settings are not yet loaded (boot-order issue)" + "candidate": "Instrumentation logic should use logger.FromContext for contextual logging; verify it does so" }, { - "candidate": "I18n.fallbacks.ensure_loaded! is called in ApplicationController#set_locale on every request, coupling request handling to dynamic locale loading and risking performance issues" - }, + "candidate": "TestLogger in pkg/plugins/log/fake.go mutates ErrorLogs fields (Calls/Message/Ctx) without synchronization, risking data races/flaky tests when logging concurrently" + } + ], + "false_negatives": [ { - "candidate": "Calling I18n.fallbacks.ensure_loaded! in set_locale relies on a custom fallback loader and may load translations inconsistently or break if locales are missing or I18n semantics change" + "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", + "severity": "High" }, { - "candidate": "Removal of config.i18n.fallbacks = true from environment files shifts fallback control to the initializer, so fallbacks may be absent if the initializer loads too late or fails" - }, + "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", + "severity": "Low" + } + ], + "errors": [], + "total_candidates": 3, + "total_golden": 2, + "tp": 0, + "fp": 3, + "fn": 2, + "errors_count": 0, + "precision": 0.0, + "recall": 0.0, + "tool": "mra-nano", + "repo_name": "grafana__grafana__mra-nano__PR76186__20260306", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__mra-nano__PR76186__20260306/pull/1" + }, + "mra-ultra": { + "skipped": false, + "true_positives": [], + "false_positives": [ { - "candidate": "Translate accelerator: @loaded_locales may be nil when load_locale calls @loaded_locales.include?(locale), causing NoMethodError on first invocation" + "candidate": "MetricsMiddleware accesses pluginRegistry (m.pluginRegistry.Plugin) without explicit synchronization, risking a data race if the registry implementation is not thread-safe under concurrent requests" }, { - "candidate": "Translate accelerator: translate checks @loaded_locales.include?(config.locale) without initializing @loaded_locales, potentially crashing on the first translation request" + "candidate": "TestLogger in pkg/plugins/log/fake.go mutates shared internal fields (e.g., InfoLogs/ErrorLogs, Calls, Message, Ctx) in Info/Error methods without synchronization, risking data races when used concurrently" }, { - "candidate": "Pluralization backend inclusion moved from config/initializers/pluralization.rb to config/initializers/i18n.rb, changing initialization order and risking pluralization being loaded before I18n is fully configured or breaking behavior" + "candidate": "WithContextualAttributes appends new log attributes to existing context-stored attributes, allowing unbounded accumulation across middleware layers and potentially increasing exposure of sensitive fields in logs" } ], "false_negatives": [ { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" + "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", + "severity": "High" }, { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", + "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", "severity": "Low" } ], "errors": [], - "total_candidates": 8, + "total_candidates": 3, "total_golden": 2, "tp": 0, - "fp": 8, + "fp": 3, "fn": 2, "errors_count": 0, "precision": 0.0, "recall": 0.0, - "tool": "mra-nano", - "repo_name": "discourse__discourse-graphite__mra-nano__PR9__20260306", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__mra-nano__PR9__20260306/pull/1" + "tool": "mra-ultra", + "repo_name": "grafana__grafana__mra-ultra__PR76186__20260306", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__mra-ultra__PR76186__20260306/pull/1" }, - "mra-ultra": { + "mra-b": { "skipped": false, "true_positives": [ { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low", - "matched_candidate": "`ensure_loaded!` checks `@loaded_locales` outside the mutex guarding `load_locale`, allowing concurrent threads to race and trigger redundant/inconsistent locale loading", - "confidence": 0.93, - "reasoning": "Both describe a thread-safety problem around the lazy initialization/loading of @loaded_locales: the candidate explicitly notes the check occurs outside the mutex, enabling races during locale loading, which matches the golden issue." + "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", + "severity": "High", + "matched_candidate": "ContextualLoggerMiddleware dereferences request pointer fields without checking if req is nil, risking a nil-pointer panic", + "confidence": 0.9, + "reasoning": "The candidate states that ContextualLoggerMiddleware dereferences request pointer fields without a nil check, leading to a potential nil-pointer panic. This matches the golden issue about panics when req is nil due to accessing req.PluginContext without checking req first (even if it doesn\u2019t list the specific methods or mention regression)." } ], "false_positives": [ { - "candidate": "Removed explicit `config.i18n.fallbacks = true` from production environment configs, so fallbacks may be missing on first requests if the new initializer isn\u2019t loaded early in all startup paths" - }, - { - "candidate": "`I18n.fallbacks.ensure_loaded!` is called on every request in `set_locale`, adding IO/locale-loading overhead on the hot path" - }, - { - "candidate": "Per-request `I18n.fallbacks.ensure_loaded!` can raise runtime errors if the method is unavailable/miswired during early boot or in tests" - }, - { - "candidate": "`FallbackLocaleList` computes fallbacks using `SiteSetting.default_locale`, which may hit the database when `[]` is called at runtime, risking failures if requests occur before DB availability during boot/preload" - }, - { - "candidate": "Fallback list logic risks unsafe initialization because it may access the DB too early; it should avoid DB access at init time or lazy-load safely" + "candidate": "Adding FromContext(context.Context) to the exported Logger interface is a breaking API change that will cause compile errors for existing Logger implementations that are not updated" }, { - "candidate": "Pluralization initializer was folded into `i18n.rb`, risking incorrect load order across environments if not preserved" + "candidate": "TestLogger.New and TestLogger.FromContext discard the provided context and return a fresh logger, violating the new Logger interface contract" }, { - "candidate": "Translation loader builds a regex with unescaped, user-influenced `locale` (`Regexp.new(\"\\\\.#{locale}\\\\.yml$\")`), enabling crafted locale values to manipulate file matching/which translations are loaded" + "candidate": "MetricsMiddleware dereferences request pointer fields without checking if req is nil, risking a nil-pointer panic" }, { - "candidate": "Locale-based regex matching can lead to unintended translation file loads, potential information leakage of translations, or performance issues due to loading extra files" + "candidate": "grafanaInfraLogWrapper.FromContext type-asserts to *log.ConcreteLogger and, on failure, returns d.New(), which drops contextual attributes from ctx and can lose contextual logging data" }, { - "candidate": "Missing mitigations for user-controlled locale in translation loading (e.g., whitelist validation and `Regexp.escape(locale)` before embedding in regex)" + "candidate": "LoggerMiddleware now relies on logger.FromContext(ctx) for request/plugin details, so if contextual attributes are not injected into ctx before it runs (e.g., due to middleware ordering changes), logs will miss expected context information" } ], "false_negatives": [ { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", + "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", "severity": "Low" } ], "errors": [], - "total_candidates": 10, + "total_candidates": 6, "total_golden": 2, "tp": 1, - "fp": 9, + "fp": 5, "fn": 1, "errors_count": 0, - "precision": 0.1, + "precision": 0.16666666666666666, "recall": 0.5, - "tool": "mra-ultra", - "repo_name": "discourse__discourse-graphite__mra-ultra__PR9__20260306", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__mra-ultra__PR9__20260306/pull/1" + "tool": "mra-b", + "repo_name": "grafana__grafana__mra-b__PR76186__20260307", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__mra-b__PR76186__20260307/pull/1" }, - "mra-b": { + "mra-a": { "skipped": false, "true_positives": [ { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low", - "matched_candidate": "translate_accelerator.load_locale checks @loaded_locales.include? inside the mutex without ensuring @loaded_locales is initialized, so calling load_locale directly can raise NoMethodError when @loaded_locales is nil", - "confidence": 0.78, - "reasoning": "The golden issue is a thread-safety problem caused by lazy initialization of @loaded_locales. The candidate points out that @loaded_locales may be nil when checked (leading to NoMethodError) and notes the check occurs inside a mutex without ensuring initialization. This is essentially the same underlying problem: unsafe/lazy handling of @loaded_locales in concurrent/direct-call scenarios." + "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", + "severity": "High", + "matched_candidate": "ContextualLoggerMiddleware methods (QueryData/CallResource/CheckHealth/CollectMetrics) dereference req (e.g., req.PluginContext) without nil checks, so a nil request can panic and cause DoS for plugin requests", + "confidence": 0.93, + "reasoning": "The candidate flags that ContextualLoggerMiddleware methods dereference req (including req.PluginContext) without nil checks, leading to a panic when req is nil. This is the same core issue described in the golden comment (nil request causes panic due to direct access of req.PluginContext)." } ], "false_positives": [ { - "candidate": "Removing config.i18n.fallbacks = true from production environment makes fallback behavior depend solely on config/initializers/i18n.rb, so fallbacks may be absent in production if that initializer doesn\u2019t run or fails" - }, - { - "candidate": "config/initializers/i18n.rb has no numeric prefix, so its load order relative to 02-freedom_patches.rb is not guaranteed despite comments assuming it loads after" - }, - { - "candidate": "If the i18n initializer loads before freedom_patches, I18n.ensure_loaded! may be undefined, causing fallback loading to fail (potentially silently)" + "candidate": "Adding FromContext(ctx context.Context) Logger to the public Logger interface is a source-level breaking change for any external implementations that haven\u2019t added the new method" }, { - "candidate": "ApplicationController#set_locale calls I18n.fallbacks.ensure_loaded! on every request, adding per-request overhead and risking performance regression" + "candidate": "Splitting context enrichment into a separate ContextualLoggerMiddleware introduces an implicit middleware ordering dependency; if it\u2019s absent or misordered, LoggerMiddleware will silently miss expected log fields (plugin/endpoint/user/datasource/traceID)" }, { - "candidate": "Calling I18n.fallbacks.ensure_loaded! during request handling can trigger file/DB loads on first access, increasing latency and side-effect risk" + "candidate": "grafanaInfraLogWrapper.FromContext uses a concrete type assertion to *log.ConcreteLogger and falls back to d.New() on failure, which can drop context-enriched attributes when the returned logger isn\u2019t that concrete type" }, { - "candidate": "Per-request ensure_loaded! increases the surface area for race conditions and can raise if invoked before freedom_patches initialization" + "candidate": "TestLogger.FromContext ignores the provided context and returns a fresh logger, reducing test fidelity by not propagating context-derived logging attributes" } ], "false_negatives": [ { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", + "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", "severity": "Low" } ], "errors": [], - "total_candidates": 8, + "total_candidates": 5, "total_golden": 2, "tp": 1, - "fp": 6, + "fp": 4, "fn": 1, "errors_count": 0, - "precision": 0.125, + "precision": 0.2, "recall": 0.5, - "tool": "mra-b", - "repo_name": "discourse__discourse-graphite__mra-b__PR9__20260307", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__mra-b__PR9__20260307/pull/1" + "tool": "mra-a", + "repo_name": "grafana__grafana__mra-a__PR76186__20260307", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__mra-a__PR76186__20260307/pull/1" }, - "mra-a": { + "entelligence": { "skipped": false, "true_positives": [], "false_positives": [ { - "candidate": "FallbackLocaleList#[] calls SiteSetting.default_locale.to_sym without guarding for nil, which can raise NoMethodError during boot/requests if default_locale is unset" + "candidate": "Adding `FromContext` to the `Logger` interface is a breaking change that will cause existing `Logger` implementations to fail to compile unless they are updated or a separate interface (e.g., `ContextLogger`) is introduced." }, { - "candidate": "translate_accelerator.load_locale interpolates the locale directly into Regexp.new without escaping/validation, enabling regex injection/ReDoS and potential RegexpError crashes" + "candidate": "`newMetricsMiddleware` duplicates `newInstrumentationMiddleware` code, creating redundant implementations instead of reusing or refactoring shared logic." }, { - "candidate": "translate_accelerator.translate checks @loaded_locales.include?(config.locale) without ensuring @loaded_locales is initialized, risking NoMethodError when @loaded_locales is nil" + "candidate": "Prometheus metric registration logic is duplicated between the old and new middleware implementations, which can panic at runtime if both register the same metrics against the same `prometheus.Registerer`." }, { - "candidate": "ApplicationController#set_locale calls I18n.fallbacks.ensure_loaded! on every request using a user-controllable locale, which can trigger expensive translation file loading (I/O/CPU) and enable DoS/resource exhaustion" - } - ], - "false_negatives": [ + "candidate": "`NewMetricsMiddleware` duplicates `NewInstrumentationMiddleware`, leaving redundant constructors instead of completing the rename or reusing the existing function." + }, { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" + "candidate": "`MetricsMiddleware.pluginTarget` duplicates `InstrumentationMiddleware.pluginTarget`, indicating shared logic should be extracted into a helper to avoid duplication." }, { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", - "severity": "Low" + "candidate": "`MetricsMiddleware.instrumentPluginRequestSize` duplicates `InstrumentationMiddleware.instrumentPluginRequestSize`, indicating shared Prometheus observation logic should be consolidated or the old middleware removed." } ], - "errors": [], - "total_candidates": 4, - "total_golden": 2, - "tp": 0, - "fp": 4, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "mra-a", - "repo_name": "discourse__discourse-graphite__mra-a__PR9__20260307", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__mra-a__PR9__20260307/pull/1" - }, - "entelligence": { - "skipped": false, - "true_positives": [], - "false_positives": [], "false_negatives": [ { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" + "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", + "severity": "High" }, { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", + "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", "severity": "Low" } ], "errors": [], - "total_candidates": 0, + "total_candidates": 6, "total_golden": 2, "tp": 0, - "fp": 0, + "fp": 6, "fn": 2, "errors_count": 0, "precision": 0.0, "recall": 0.0, "tool": "entelligence", - "repo_name": "discourse__discourse-graphite__entelligence__PR9__20260306", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__entelligence__PR9__20260306/pull/1" + "repo_name": "grafana__grafana__entelligence__PR76186__20260306", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__entelligence__PR76186__20260306/pull/1" }, "sourcery": { "skipped": false, "true_positives": [], "false_positives": [ { - "candidate": "FallbackLocaleList#ensure_loaded! calls I18n.ensure_loaded!(l), but ensure_loaded! is not defined as a class method on I18n (it exists on the translate accelerator backend/instance), so this will raise NoMethodError at runtime; it should call the correct receiver (e.g., I18n.backend.ensure_loaded!(l) or expose a proper I18n helper)." + "candidate": "grafanaInfraLogWrapper.FromContext type-asserts the underlying infra logger to *log.ConcreteLogger, tightly coupling the wrapper to a concrete implementation and breaking when FromContext returns a different type" + }, + { + "candidate": "grafanaInfraLogWrapper.FromContext falls back to New() when the underlying logger is not *log.ConcreteLogger, which silently drops context-derived fields/attributes" + }, + { + "candidate": "grafanaInfraLogWrapper should store the underlying logger as the infra logger interface type (e.g., log.Logger) rather than *log.ConcreteLogger to preserve contextual attributes and avoid implementation coupling" + }, + { + "candidate": "Constructors (e.g., New()) should accept/pass the infra logger interface into grafanaInfraLogWrapper instead of requiring a concrete logger type" + }, + { + "candidate": "grafanaInfraLogWrapper methods should rely only on interface methods exposed by the infra logger (log.Logger) rather than concrete-only methods" }, { - "candidate": "Locale source-of-truth mismatch: ApplicationController#set_locale preloads fallbacks based on I18n.locale via I18n.fallbacks.ensure_loaded!, but the translate accelerator's translate method lazily loads using config.locale, which can lead to confusion and potential locale mismatches." + "candidate": "In contextual_logger_middleware.go, the helper instrumentContext now only enriches the logger (not metrics), so its name and comment are misleading and should be renamed to something logger-specific to avoid confusion" } ], "false_negatives": [ { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" + "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", + "severity": "High" }, { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", + "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", "severity": "Low" } ], "errors": [], - "total_candidates": 2, + "total_candidates": 6, "total_golden": 2, "tp": 0, - "fp": 2, + "fp": 6, "fn": 2, "errors_count": 0, "precision": 0.0, "recall": 0.0, "tool": "sourcery", - "repo_name": "discourse__discourse-graphite__sourcery__PR9__20260308", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__sourcery__PR9__20260308/pull/1" + "repo_name": "grafana__grafana__sourcery__PR76186__20260308", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__sourcery__PR76186__20260308/pull/1" }, "cubic-dev": { "skipped": false, "true_positives": [ { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low", - "matched_candidate": "In ensure_loaded!, @loaded_locales lazy initialization and the check-and-load sequence are not protected by LOAD_MUTEX, creating a potential race with reload! in multi-threaded environments (identified by cubic)", - "confidence": 0.93, - "reasoning": "The golden issue is a thread-safety problem caused by lazy initialization of @loaded_locales. The candidate explicitly describes that @loaded_locales is lazily initialized without mutex protection and can race with reload! in multi-threaded use, which is the same underlying concern." + "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", + "severity": "High", + "matched_candidate": "ContextualLoggerMiddleware.QueryData can panic when req is nil because it unconditionally accesses req.PluginContext; it should guard against nil requests like LoggerMiddleware does", + "confidence": 0.78, + "reasoning": "The candidate flags the same core bug: a panic due to unguarded access to req.PluginContext when req is nil in ContextualLoggerMiddleware. However, it only mentions QueryData and not the other methods (CallResource, CheckHealth, CollectMetrics) or the regression aspect, so it\u2019s a partial but semantically matching identification of the underlying issue." }, { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", + "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", "severity": "Low", - "matched_candidate": "In ensure_loaded!, the locale argument is not normalized (e.g., to_sym), so passing \"en\" vs :en can bypass the include? guard and cause the same locale to be loaded twice (identified by cubic)", + "matched_candidate": "traceID is no longer included in the \"Plugin Request Completed\" log output because the explicit tracing.TraceIDFromContext usage was removed and ContextualLoggerMiddleware does not add traceID to contextual attributes, breaking trace-to-log correlation", "confidence": 0.94, - "reasoning": "Both comments flag the same underlying issue: the locale input isn\u2019t normalized (String vs Symbol), so the include?/guard check can miss and the same locale may be loaded twice. The candidate explicitly mentions to_sym and the double-load scenario, matching the golden concern." + "reasoning": "The candidate states that traceID is missing from plugin request completion logs due to removal of TraceIDFromContext usage and that ContextualLoggerMiddleware does not add traceID, which matches the golden issue about refactoring removing traceID extraction/logging and the new middleware not restoring it." + } + ], + "false_positives": [ + { + "candidate": "metrics_middleware_test.go has an incomplete rename: the test function is still named TestInstrumentationMiddleware even though the middleware was renamed to MetricsMiddleware/newMetricsMiddleware" + }, + { + "candidate": "The doc comment for Logger.FromContext is incorrect/copy-pasted (describes creating a new contextual logger with extra key/value pairs) and should instead describe retrieving a logger from a context.Context" + }, + { + "candidate": "TestLogger.FromContext returns a new disconnected TestLogger, causing logs written via the returned logger to be lost to the test; it should return the receiver so tests can assert on emitted logs" } ], - "false_positives": [], "false_negatives": [], "errors": [], - "total_candidates": 2, + "total_candidates": 5, "total_golden": 2, "tp": 2, - "fp": 0, + "fp": 3, "fn": 0, "errors_count": 0, - "precision": 1.0, + "precision": 0.4, "recall": 1.0, "tool": "cubic-dev", - "repo_name": "discourse__discourse-graphite__cubic-dev__PR9__20260306", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__cubic-dev__PR9__20260306/pull/1" + "repo_name": "grafana__grafana__cubic-dev__PR76186__20260306", + "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__cubic-dev__PR76186__20260306/pull/1" }, "mesa": { "skipped": false, - "true_positives": [ + "true_positives": [], + "false_positives": [ { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low", - "matched_candidate": "`@loaded_locales` is not thread-safe; concurrent requests can race when reading/modifying it, causing duplicate locale loads or inconsistent visibility", - "confidence": 0.96, - "reasoning": "The golden comment flags a thread-safety problem with lazy initialization/caching of @loaded_locales. The candidate explicitly states @loaded_locales is not thread-safe and describes race conditions during concurrent access leading to duplicate loads/inconsistent state, which is the same underlying issue." - } - ], - "false_positives": [ - { - "candidate": "`ensure_loaded!` calls `I18n.ensure_loaded!` without checking that the method exists on the I18n module, risking failures if the monkey patch is not loaded" - }, - { - "candidate": "`ensure_loaded!` implicitly depends on the monkey-patched `I18n.ensure_loaded!` from `translate_accelerator.rb`, but this dependency is not explicit/guarded" - }, - { - "candidate": "Custom `FallbackLocaleList` fully replaces Rails/i18n default fallback behavior, increasing maintenance burden and upgrade breakage risk" - }, - { - "candidate": "Insufficient test coverage for the custom fallback implementation (regional variants like `pt-BR -> pt`, plugin-provided locales, missing/invalid locales)" - }, - { - "candidate": "Fallback chain reads `SiteSetting.default_locale` on every lookup, which may be unavailable during early initialization and can raise errors before settings/DB are ready" - }, - { - "candidate": "Fallback loading is triggered in `set_locale`, so any `I18n.t` usage before `set_locale` (middleware/error handlers/early concerns) may run without fallbacks loaded" - } - ], - "false_negatives": [ - { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 7, - "total_golden": 2, - "tp": 1, - "fp": 6, - "fn": 1, - "errors_count": 0, - "precision": 0.14285714285714285, - "recall": 0.5, - "tool": "mesa", - "repo_name": "discourse__discourse-graphite__mesa__PR9__20260307", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__mesa__PR9__20260307/pull/1" - }, - "codeant": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "Initializer may access SiteSetting.default_locale before SiteSetting is available/initialized, causing errors or incorrect fallback values due to initializer load order not being enforced" - }, - { - "candidate": "FallbackLocaleList#[] calls SiteSetting.default_locale.to_sym without guarding against nil/undefined, which can raise exceptions" - }, - { - "candidate": "FallbackLocaleList#ensure_loaded! calls I18n.ensure_loaded!(locale) but I18n.ensure_loaded! may not exist, leading to NoMethodError" - }, - { - "candidate": "ensure_loaded!(locale) was added in translate_accelerator but may not be implemented on the actual I18n.backend instance that I18n uses, so preloading may not work" - }, - { - "candidate": "Per-request call to I18n.fallbacks.ensure_loaded! in ApplicationController#set_locale may add overhead if translation loading is heavy or not sufficiently idempotent" - }, - { - "candidate": "Fallback list hardcodes :en instead of using I18n.default_locale, risking inconsistency between configured default locale and fallback behavior" - }, - { - "candidate": "Custom fallback list only supports [user locale, site locale, en] and does not handle language-region fallbacks (e.g., zh_TW -> zh_CN) if needed" - }, - { - "candidate": "Removing config.i18n.fallbacks = true from environment files could break behavior if other parts of the app rely on that flag being set" - }, - { - "candidate": "Initializer comment about ordering after 02-freedom_patches.rb does not actually enforce load order; file naming/ordering should be enforced to avoid dependency issues" - }, - { - "candidate": "Missing tests to verify fallback order, preloading behavior, and handling when SiteSetting.default_locale is nil/unset, risking regressions and production issues" - } - ], - "false_negatives": [ - { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" - }, - { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 10, - "total_golden": 2, - "tp": 0, - "fp": 10, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "codeant", - "repo_name": "discourse__discourse-graphite__codeant__PR9__20260309", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__codeant__PR9__20260309/pull/1" - }, - "kodus": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "I18n fallback mechanism is incomplete because `I18n.fallbacks.ensure_loaded!` is not automatically invoked after changing `I18n.locale`, requiring manual calls to avoid translation errors" - }, - { - "candidate": "Critical execution paths (background jobs, rake tasks, and test setup) set `I18n.locale` without calling `I18n.fallbacks.ensure_loaded!`, which can cause translation/fallback bugs" - }, - { - "candidate": "`I18n.fallbacks.ensure_loaded!` is added in `ApplicationController` but missing in `app/jobs/base.rb` after setting `I18n.locale`, risking incorrect translations in background job processing" - }, - { - "candidate": "I18n initializer (`config/initializers/i18n.rb`) does not patch `I18n.locale=` to ensure `ensure_loaded!` runs whenever the locale changes, leading to inconsistent behavior across the codebase" - } - ], - "false_negatives": [ - { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" - }, - { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 4, - "total_golden": 2, - "tp": 0, - "fp": 4, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "kodus", - "repo_name": "discourse__discourse-graphite__kodus__PR9__20260309", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__kodus__PR9__20260309/pull/1" - }, - "claude-code": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low", - "matched_candidate": "New ensure_loaded! entry point overlaps with existing translate_accelerator locale loading logic, risking race conditions or double-loading", - "confidence": 0.84, - "reasoning": "Golden calls out a thread-safety problem in lazy loading of @loaded_locales. The candidate flags race conditions/double-loading due to overlapping locale-loading entry points, which is essentially the same thread-safety concern around lazy locale loading state." - } - ], - "false_positives": [ - { - "candidate": "Architectural i18n change across the app (replacing Rails built-in fallbacks with a custom system) requires careful human review due to broad impact" - }, - { - "candidate": "No tests were added/updated to cover the new custom i18n fallback behavior" - }, - { - "candidate": "FallbackLocaleList inherits from Hash but only implements [] and ensure_loaded!, so other Hash methods may behave unexpectedly if the i18n framework calls them" - }, - { - "candidate": "Calling I18n.fallbacks.ensure_loaded! on every request may introduce performance overhead" - }, - { - "candidate": "Monkey-patching I18n backend via send(:include) is fragile and may break on gem upgrades" - }, - { - "candidate": "Locale loading appears load-order sensitive (initializer ordering concerns) and could cause subtle boot/runtime issues" - } - ], - "false_negatives": [ - { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 7, - "total_golden": 2, - "tp": 1, - "fp": 6, - "fn": 1, - "errors_count": 0, - "precision": 0.14285714285714285, - "recall": 0.5, - "tool": "claude-code", - "repo_name": "discourse__discourse-graphite__claude-code__PR9__20260310", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__claude-code__PR9__20260310/pull/1" - }, - "vercel": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" - }, - { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 2, - "tp": 0, - "fp": 0, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "vercel", - "repo_name": "discourse__discourse-graphite__vercel__PR9__20260310", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__vercel__PR9__20260310/pull/1" - }, - "bito": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" - }, - { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 2, - "tp": 0, - "fp": 0, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "bito", - "repo_name": "discourse__discourse-graphite__bito__PR9__20260310", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__bito__PR9__20260310/pull/1" - }, - "sentry": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" - }, - { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 2, - "tp": 0, - "fp": 0, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "sentry", - "repo_name": "discourse__discourse-graphite__sentry__PR9__20260310", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__sentry__PR9__20260310/pull/1" - }, - "linearb": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" - }, - { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 2, - "tp": 0, - "fp": 0, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "linearb", - "repo_name": "discourse__discourse-graphite__linearb__PR9__20260309", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__linearb__PR9__20260309/pull/1" - }, - "kodus-v2": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" - }, - { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 2, - "tp": 0, - "fp": 0, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "kodus-v2", - "repo_name": "discourse__discourse-graphite__kodus-v2__PR9__20260312", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__kodus-v2__PR9__20260312/pull/1" - }, - "qodo-v2-2": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "Background jobs set I18n.locale without calling I18n.fallbacks.ensure_loaded!, so fallback locales (e.g., :en) may not be loaded in job/Sidekiq contexts" - }, - { - "candidate": "Because translate_accelerator enforces explicit per-locale YAML loading, missing fallback locale loading in jobs can cause fallback translation lookups to return missing translations even when the fallback locale contains the key" - } - ], - "false_negatives": [ - { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" - }, - { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 2, - "total_golden": 2, - "tp": 0, - "fp": 2, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "qodo-v2-2", - "repo_name": "discourse__discourse-graphite__qodo-v2-2__PR9__20260312", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-v2-2__PR9__20260312/pull/1" - }, - "qodo-v22": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", - "severity": "Low", - "matched_candidate": "Fallback locale list mixes String and Symbol locale representations (e.g., \"en\" vs :en), causing duplicate include?/load checks in the loaded-locales cache and redundant locale loading work.", - "confidence": 0.9, - "reasoning": "Both comments flag the same underlying problem: locales may be represented as Strings or Symbols, and without normalization this can cause duplicate cache/include checks and redundant loading (double-loading) for the same locale. The candidate frames it in the fallback list context but matches the golden concern about normalizing locale inputs to avoid double-loading across the code." - } - ], - "false_positives": [ - { - "candidate": "Fallback translations are only preloaded in ApplicationController#set_locale (I18n.fallbacks.ensure_loaded!), so background jobs/services translating under user locales may miss fallback locale data and produce missing-translation strings (e.g., in Jobs::Base/PostAlerter notifications/emails)." - }, - { - "candidate": "Locale loading builds a Regexp from an unescaped locale string, and user.locale can be set from raw request params; a crafted locale can trigger RegexpError (500s) or broaden regex matches to load unintended locale files, now invoked eagerly on each request via controller-side preload." - } - ], - "false_negatives": [ - { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 3, - "total_golden": 2, - "tp": 1, - "fp": 2, - "fn": 1, - "errors_count": 0, - "precision": 0.3333333333333333, - "recall": 0.5, - "tool": "qodo-v22", - "repo_name": "discourse__discourse-graphite__qodo-v22__PR9__20260312", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-v22__PR9__20260312/pull/1" - }, - "qodo-extended": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "Background jobs set I18n.locale but do not call I18n.fallbacks.ensure_loaded!, so fallback locales are not loaded in job context" - }, - { - "candidate": "After removing config.i18n.fallbacks = true, only web requests (ApplicationController#set_locale) explicitly load fallbacks; background jobs calling I18n.t may return missing-translation strings when keys exist only in fallback locales" - } - ], - "false_negatives": [ - { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" - }, - { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 2, - "total_golden": 2, - "tp": 0, - "fp": 2, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "qodo-extended", - "repo_name": "discourse__discourse-graphite__qodo-extended__PR9__20260312", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-extended__PR9__20260312/pull/1" - }, - "cubic-v2": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low", - "matched_candidate": "`@loaded_locales ||= []` is lazily initialized without `LOAD_MUTEX`, creating a race condition where multiple threads can initialize/overwrite the array concurrently (identified by cubic)", - "confidence": 0.96, - "reasoning": "The golden comment flags a thread-safety problem due to lazy initialization of `@loaded_locales`. The candidate explicitly describes `@loaded_locales ||= []` being lazily initialized without a mutex, causing a race condition in multi-threaded contexts. This is the same underlying issue." - }, - { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", - "severity": "Low", - "matched_candidate": "`locale` is not normalized (Symbol vs String) before checking `@loaded_locales.include?(locale)`, so `:en` and \"en\" mismatch can bypass the guard and cause redundant locale loads (identified by cubic)", - "confidence": 0.95, - "reasoning": "Both comments flag the same underlying issue: the locale value should be normalized (e.g., String vs Symbol) before checking/adding to loaded locales to prevent double-loading when the same locale is passed in different types." - } - ], - "false_positives": [ - { - "candidate": "Custom I18n fallback list does not preserve standard locale-ancestor fallbacks, so regional locales (e.g., `en-GB`) may not fall back to base language translations (e.g., `en`) (identified by cubic)" - } - ], - "false_negatives": [], - "errors": [], - "total_candidates": 3, - "total_golden": 2, - "tp": 2, - "fp": 1, - "fn": 0, - "errors_count": 0, - "precision": 0.6666666666666666, - "recall": 1.0, - "tool": "cubic-v2", - "repo_name": "discourse__discourse-graphite__cubic-v2__PR9__20260313", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__cubic-v2__PR9__20260313/pull/1" - }, - "qodo-extended-summary": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low", - "matched_candidate": "ensure_loaded! reads/writes @loaded_locales without LOAD_MUTEX synchronization, creating a race condition where concurrent threads can both load the same locale or race on @loaded_locales initialization", - "confidence": 0.95, - "reasoning": "The golden comment flags a thread-safety problem with lazy initialization/access of @loaded_locales. The candidate explicitly describes unsynchronized reads/writes to @loaded_locales in ensure_loaded! without using LOAD_MUTEX, leading to race conditions during initialization/loading. This is the same underlying issue." - }, - { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", - "severity": "Low", - "matched_candidate": "FallbackLocaleList#[] mixes string locales (from I18n.locale) with symbol locales in the fallback array, causing @loaded_locales include? checks (symbol vs string) to fail and trigger redundant locale reloads", - "confidence": 0.93, - "reasoning": "Both comments identify the same underlying issue: locales may be represented as Strings or Symbols, and without normalization this can cause include?/loaded checks to miss matches and lead to redundant/double loading. The candidate pinpoints the symbol-vs-string mismatch causing redundant reloads, which is exactly what the golden comment warns about." - } - ], - "false_positives": [ - { - "candidate": "Background jobs set I18n.locale but never call I18n.fallbacks.ensure_loaded!, so fallback locale translation files are not loaded during job execution and missing keys may return raw keys or raise MissingTranslation" - }, - { - "candidate": "Translation LRU cache key does not include SiteSetting.default_locale even though it is now part of the fallback chain, so changing default_locale can serve stale cached translations" - } - ], - "false_negatives": [], - "errors": [], - "total_candidates": 4, - "total_golden": 2, - "tp": 2, - "fp": 2, - "fn": 0, - "errors_count": 0, - "precision": 0.5, - "recall": 1.0, - "tool": "qodo-extended-summary", - "repo_name": "discourse__discourse-graphite__qodo-extended-summary__PR9__20260313", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-extended-summary__PR9__20260313/pull/1" - }, - "macroscope": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "Thread-safety issue with lazy @loaded_locales", - "severity": "Low" - }, - { - "golden_comment": "Consider normalizing the input locale (e.g., to a symbol) when checking/loading here to avoid double-loading if the same locale is passed as a String vs Symbol (also applies to other locations in the PR).", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 2, - "tp": 0, - "fp": 0, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "macroscope", - "repo_name": "discourse__discourse-graphite__macroscope__PR9__20260309", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__macroscope__PR9__20260309/pull/1" - } - }, - "https://github.com/grafana/grafana/pull/76186": { - "gemini": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "SubscribeStream is not instrumented with a contextual logger despite having access to PluginContext, leading to missing contextual log fields (e.g., pluginId)" - }, - { - "candidate": "RunStream is not instrumented with a contextual logger despite having access to PluginContext, leading to missing contextual log fields (e.g., pluginId)" - }, - { - "candidate": "A test function name is not aligned with the file rename and should be renamed for consistency" - } - ], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 3, - "total_golden": 2, - "tp": 0, - "fp": 3, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "gemini", - "repo_name": "grafana__grafana__gemini__PR76186__20260128", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__gemini__PR76186__20260128/pull/1" - }, - "claude": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 2, - "tp": 0, - "fp": 0, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "claude", - "repo_name": "grafana__grafana__claude__PR76186__20260127", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__claude__PR76186__20260127/pull/1" - }, - "graphite": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 2, - "tp": 0, - "fp": 0, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "graphite", - "repo_name": "grafana__grafana__graphite__PR76186__20260123", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__graphite__PR76186__20260123/pull/1" - }, - "qodo": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "TestLogger.FromContext returns a brand-new logger instance, potentially dropping previously accumulated logger state/context and masking production bugs; it should preserve existing context (e.g., return f or f.New(...))" - }, - { - "candidate": "grafanaInfraLogWrapper.FromContext depends on a type assertion to *log.ConcreteLogger; if the underlying logger is a different implementation it falls back to d.New() and may unexpectedly discard contextual attributes" - }, - { - "candidate": "Contextual logging enrichment is moved to a separate middleware while LoggerMiddleware consumes logger.FromContext(ctx), creating a fragile dependency on middleware ordering/inclusion; missing or misordered middleware can silently degrade log fields" - } - ], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 3, - "total_golden": 2, - "tp": 0, - "fp": 3, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "qodo", - "repo_name": "grafana__grafana__qodo__PR76186__20260123", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__qodo__PR76186__20260123/pull/1" - }, - "bugbot": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "`TestLogger.FromContext` creates a new `TestLogger` via `NewTestLogger()` instead of returning the receiver or sharing state, causing logs to be written to an orphan instance" - }, - { - "candidate": "Because `FromContext` returns a fresh logger, `LoggerMiddleware.logRequest` logging calls are not recorded on the original `TestLogger`, preventing tests from verifying that logging occurred" - } - ], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 2, - "total_golden": 2, - "tp": 0, - "fp": 2, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "bugbot", - "repo_name": "grafana__grafana__bugbot__PR76186__20260122", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__bugbot__PR76186__20260122/pull/1" - }, - "copilot": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 2, - "tp": 0, - "fp": 0, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "copilot", - "repo_name": "grafana__grafana__copilot__PR76186__20260122", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__copilot__PR76186__20260122/pull/1" - }, - "augment": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High", - "matched_candidate": "QueryData and other request methods dereference req without checking for nil, so a nil request propagating to this middleware would cause a panic", - "confidence": 0.9, - "reasoning": "The candidate states that QueryData and other request methods dereference req without a nil check, leading to a panic if a nil request reaches the middleware. This matches the golden issue about ContextualLoggerMiddleware methods panicking on nil requests due to accessing req.PluginContext without checking req for nil (regression detail omitted but same core bug)." - } - ], - "false_positives": [ - { - "candidate": "TestLogger.FromContext returns a new logger instance, so logs written via m.logger.FromContext(ctx).Info(...) are not recorded on the original TestLogger instance that tests assert against" - } - ], - "false_negatives": [ - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 2, - "total_golden": 2, - "tp": 1, - "fp": 1, - "fn": 1, - "errors_count": 0, - "precision": 0.5, - "recall": 0.5, - "tool": "augment", - "repo_name": "grafana__grafana__augment__PR76186__20260122", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__augment__PR76186__20260122/pull/1" - }, - "propel": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "/propel review" - } - ], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 1, - "total_golden": 2, - "tp": 0, - "fp": 1, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "propel", - "repo_name": "grafana__grafana__propel__PR76186__20260122", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__propel__PR76186__20260122/pull/1" - }, - "baz": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "Baz review" - } - ], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 1, - "total_golden": 2, - "tp": 0, - "fp": 1, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "baz", - "repo_name": "grafana__grafana__baz__PR76186__20260122", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__baz__PR76186__20260122/pull/1" - }, - "coderabbit": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High", - "matched_candidate": "ContextualLoggerMiddleware dereferences req.PluginContext unconditionally in QueryData, CallResource, CheckHealth, and CollectMetrics, which can panic when req is nil", - "confidence": 0.96, - "reasoning": "The candidate issue states that ContextualLoggerMiddleware unconditionally dereferences req.PluginContext in the listed methods, causing a panic when req is nil. This is the same core bug described in the golden comment (nil request not checked before accessing PluginContext)." - } - ], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 2, - "total_golden": 2, - "tp": 1, - "fp": 0, - "fn": 1, - "errors_count": 0, - "precision": 0.5, - "recall": 0.5, - "tool": "coderabbit", - "repo_name": "grafana__grafana__coderabbit__PR76186__20260122", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__coderabbit__PR76186__20260122/pull/1" - }, - "greptile": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 2, - "tp": 0, - "fp": 0, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "greptile", - "repo_name": "grafana__grafana__greptile__PR76186__20260122", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__greptile__PR76186__20260122/pull/1" - }, - "kg": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 2, - "tp": 0, - "fp": 0, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "kg", - "repo_name": "grafana__grafana__kg__PR76186__20260202", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__kg__PR76186__20260202/pull/1" - }, - "qodo-v2": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low", - "matched_candidate": "LoggerMiddleware now relies on logger.FromContext(ctx) for traceID enrichment, but the traceID contextual log provider is only registered when tracing.ProvideService is called, so in tests or non-standard setups plugin request logs can silently miss trace IDs", - "confidence": 0.84, - "reasoning": "Both point to missing traceID enrichment in plugin request logs after refactoring: the golden says traceID extraction/adding was removed and the new contextual middleware doesn\u2019t add it; the candidate says relying on logger.FromContext without the traceID provider being registered can cause logs to miss trace IDs. Same underlying concern: trace IDs are not reliably present in plugin request logging due to changed middleware/contextual logging setup." - } - ], - "false_positives": [ - { - "candidate": "TestLogger.FromContext returns a new TestLogger instance, dropping previously captured log state (InfoLogs/WarnLogs/etc.) and violating the Logger contract to preserve existing context plus the given context" - } - ], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 2, - "total_golden": 2, - "tp": 1, - "fp": 1, - "fn": 1, - "errors_count": 0, - "precision": 0.5, - "recall": 0.5, - "tool": "qodo-v2", - "repo_name": "grafana__grafana__qodo-v2__PR76186__20260227", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__qodo-v2__PR76186__20260227/pull/1" - }, - "devin": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "TestLogger.FromContext returns a new disconnected TestLogger instance (NewTestLogger()) instead of the receiver, causing logs to be recorded on a throwaway logger rather than the one referenced by tests" - }, - { - "candidate": "Because TestLogger.FromContext returns a new instance, log assertions in tests (e.g., checking InfoLogs.Calls/messages) can silently show zero calls even when logging occurred via logger.FromContext(ctx).Info(...), breaking the test double contract" - } - ], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 2, - "total_golden": 2, - "tp": 0, - "fp": 2, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "devin", - "repo_name": "grafana__grafana__devin__PR76186__20260302", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__devin__PR76186__20260302/pull/1" - }, - "mra-max": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High", - "matched_candidate": "ContextualLoggerMiddleware lacks nil checks and dereferences req.PluginContext in multiple methods, risking panics on nil requests", - "confidence": 0.93, - "reasoning": "The candidate notes that ContextualLoggerMiddleware is missing nil checks and dereferences req.PluginContext across multiple methods, which can panic on nil requests. This is the same core issue as the golden comment (panic due to accessing req.PluginContext without nil guard)." - } - ], - "false_positives": [ - { - "candidate": "Logger interface is a breaking public API change: variadic params changed to ctx ...any and new FromContext(context.Context) method added, requiring all external implementors to update" - }, - { - "candidate": "InstrumentationMiddleware was renamed/refactored to MetricsMiddleware (including constructor rename), causing large churn and requiring downstream updates" - }, - { - "candidate": "MetricsMiddleware uses unsafe type assertions to prometheus.ExemplarObserver/ExemplarAdder without ok-checks, which can panic if exemplars aren\u2019t supported by the underlying metric implementation" - }, - { - "candidate": "MetricsMiddleware.QueryData dereferences req without a nil check, so a nil *backend.QueryDataRequest will panic" - }, - { - "candidate": "Middleware ordering places ContextualLoggerMiddleware after MetricsMiddleware, so metrics/log instrumentation may miss contextual attributes; ContextualLoggerMiddleware should run before MetricsMiddleware" - }, - { - "candidate": "grafanaInfraLogWrapper.FromContext performs a potentially unsafe type assertion to *log.ConcreteLogger and fallback behavior may yield unexpected nil/default logger; should use ok-check and ensure a valid logger is returned" - }, - { - "candidate": "metrics_middleware_test.go keeps the test name TestInstrumentationMiddleware while constructing MetricsMiddleware, creating confusing/misaligned test naming" - }, - { - "candidate": "Branch contains a merge commit from origin/main, increasing risk of merge/conflict issues in the change set" - } - ], - "false_negatives": [ - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 9, - "total_golden": 2, - "tp": 1, - "fp": 8, - "fn": 1, - "errors_count": 0, - "precision": 0.1111111111111111, - "recall": 0.5, - "tool": "mra-max", - "repo_name": "grafana__grafana__mra-max__PR76186__20260306", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__mra-max__PR76186__20260306/pull/1" - }, - "greptile-v4": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High", - "matched_candidate": "ContextualLoggerMiddleware handlers dereference req.PluginContext without guarding against req == nil, so a nil request can panic before LoggerMiddleware\u2019s existing nil-request guard is reached (needs nil checks in QueryData/CallResource/CheckHealth/CollectMetrics)", - "confidence": 0.93, - "reasoning": "The candidate describes the same bug: ContextualLoggerMiddleware methods dereference req.PluginContext without checking if req is nil, causing a panic before the downstream LoggerMiddleware nil guard. It also names the affected handlers and suggests adding nil checks, matching the golden issue (including the regression context implicitly)." - } - ], - "false_positives": [ - { - "candidate": "TestLogger.FromContext returns a new detached TestLogger instance, so logs emitted via logger.FromContext(ctx).Info/Warn/Error are written to a throwaway logger and tests asserting on the original TestLogger\u2019s captured logs will see zero calls" - }, - { - "candidate": "metrics_middleware_test.go test function name was not updated after renaming InstrumentationMiddleware to MetricsMiddleware (still named TestInstrumentationMiddleware), causing inconsistency and reduced discoverability" - } - ], - "false_negatives": [ - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 3, - "total_golden": 2, - "tp": 1, - "fp": 2, - "fn": 1, - "errors_count": 0, - "precision": 0.3333333333333333, - "recall": 0.5, - "tool": "greptile-v4", - "repo_name": "grafana__grafana__greptile-v4__PR76186__20260306", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__greptile-v4__PR76186__20260306/pull/1" - }, - "mra-nano": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "Need to confirm NewContextualLoggerMiddleware is called/wired in pluginsintegration.go and covered by tests" - }, - { - "candidate": "Instrumentation logic should use logger.FromContext for contextual logging; verify it does so" - }, - { - "candidate": "TestLogger in pkg/plugins/log/fake.go mutates ErrorLogs fields (Calls/Message/Ctx) without synchronization, risking data races/flaky tests when logging concurrently" - } - ], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 3, - "total_golden": 2, - "tp": 0, - "fp": 3, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "mra-nano", - "repo_name": "grafana__grafana__mra-nano__PR76186__20260306", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__mra-nano__PR76186__20260306/pull/1" - }, - "mra-ultra": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "MetricsMiddleware accesses pluginRegistry (m.pluginRegistry.Plugin) without explicit synchronization, risking a data race if the registry implementation is not thread-safe under concurrent requests" - }, - { - "candidate": "TestLogger in pkg/plugins/log/fake.go mutates shared internal fields (e.g., InfoLogs/ErrorLogs, Calls, Message, Ctx) in Info/Error methods without synchronization, risking data races when used concurrently" - }, - { - "candidate": "WithContextualAttributes appends new log attributes to existing context-stored attributes, allowing unbounded accumulation across middleware layers and potentially increasing exposure of sensitive fields in logs" - } - ], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 3, - "total_golden": 2, - "tp": 0, - "fp": 3, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "mra-ultra", - "repo_name": "grafana__grafana__mra-ultra__PR76186__20260306", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__mra-ultra__PR76186__20260306/pull/1" - }, - "mra-b": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High", - "matched_candidate": "ContextualLoggerMiddleware dereferences request pointer fields without checking if req is nil, risking a nil-pointer panic", - "confidence": 0.9, - "reasoning": "The candidate states that ContextualLoggerMiddleware dereferences request pointer fields without a nil check, leading to a potential nil-pointer panic. This matches the golden issue about panics when req is nil due to accessing req.PluginContext without checking req first (even if it doesn\u2019t list the specific methods or mention regression)." - } - ], - "false_positives": [ - { - "candidate": "Adding FromContext(context.Context) to the exported Logger interface is a breaking API change that will cause compile errors for existing Logger implementations that are not updated" - }, - { - "candidate": "TestLogger.New and TestLogger.FromContext discard the provided context and return a fresh logger, violating the new Logger interface contract" - }, - { - "candidate": "MetricsMiddleware dereferences request pointer fields without checking if req is nil, risking a nil-pointer panic" - }, - { - "candidate": "grafanaInfraLogWrapper.FromContext type-asserts to *log.ConcreteLogger and, on failure, returns d.New(), which drops contextual attributes from ctx and can lose contextual logging data" - }, - { - "candidate": "LoggerMiddleware now relies on logger.FromContext(ctx) for request/plugin details, so if contextual attributes are not injected into ctx before it runs (e.g., due to middleware ordering changes), logs will miss expected context information" - } - ], - "false_negatives": [ - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 6, - "total_golden": 2, - "tp": 1, - "fp": 5, - "fn": 1, - "errors_count": 0, - "precision": 0.16666666666666666, - "recall": 0.5, - "tool": "mra-b", - "repo_name": "grafana__grafana__mra-b__PR76186__20260307", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__mra-b__PR76186__20260307/pull/1" - }, - "mra-a": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High", - "matched_candidate": "ContextualLoggerMiddleware methods (QueryData/CallResource/CheckHealth/CollectMetrics) dereference req (e.g., req.PluginContext) without nil checks, so a nil request can panic and cause DoS for plugin requests", - "confidence": 0.93, - "reasoning": "The candidate flags that ContextualLoggerMiddleware methods dereference req (including req.PluginContext) without nil checks, leading to a panic when req is nil. This is the same core issue described in the golden comment (nil request causes panic due to direct access of req.PluginContext)." - } - ], - "false_positives": [ - { - "candidate": "Adding FromContext(ctx context.Context) Logger to the public Logger interface is a source-level breaking change for any external implementations that haven\u2019t added the new method" - }, - { - "candidate": "Splitting context enrichment into a separate ContextualLoggerMiddleware introduces an implicit middleware ordering dependency; if it\u2019s absent or misordered, LoggerMiddleware will silently miss expected log fields (plugin/endpoint/user/datasource/traceID)" - }, - { - "candidate": "grafanaInfraLogWrapper.FromContext uses a concrete type assertion to *log.ConcreteLogger and falls back to d.New() on failure, which can drop context-enriched attributes when the returned logger isn\u2019t that concrete type" - }, - { - "candidate": "TestLogger.FromContext ignores the provided context and returns a fresh logger, reducing test fidelity by not propagating context-derived logging attributes" - } - ], - "false_negatives": [ - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 5, - "total_golden": 2, - "tp": 1, - "fp": 4, - "fn": 1, - "errors_count": 0, - "precision": 0.2, - "recall": 0.5, - "tool": "mra-a", - "repo_name": "grafana__grafana__mra-a__PR76186__20260307", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__mra-a__PR76186__20260307/pull/1" - }, - "entelligence": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "Adding `FromContext` to the `Logger` interface is a breaking change that will cause existing `Logger` implementations to fail to compile unless they are updated or a separate interface (e.g., `ContextLogger`) is introduced." - }, - { - "candidate": "`newMetricsMiddleware` duplicates `newInstrumentationMiddleware` code, creating redundant implementations instead of reusing or refactoring shared logic." - }, - { - "candidate": "Prometheus metric registration logic is duplicated between the old and new middleware implementations, which can panic at runtime if both register the same metrics against the same `prometheus.Registerer`." - }, - { - "candidate": "`NewMetricsMiddleware` duplicates `NewInstrumentationMiddleware`, leaving redundant constructors instead of completing the rename or reusing the existing function." - }, - { - "candidate": "`MetricsMiddleware.pluginTarget` duplicates `InstrumentationMiddleware.pluginTarget`, indicating shared logic should be extracted into a helper to avoid duplication." - }, - { - "candidate": "`MetricsMiddleware.instrumentPluginRequestSize` duplicates `InstrumentationMiddleware.instrumentPluginRequestSize`, indicating shared Prometheus observation logic should be consolidated or the old middleware removed." - } - ], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 6, - "total_golden": 2, - "tp": 0, - "fp": 6, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "entelligence", - "repo_name": "grafana__grafana__entelligence__PR76186__20260306", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__entelligence__PR76186__20260306/pull/1" - }, - "sourcery": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "grafanaInfraLogWrapper.FromContext type-asserts the underlying infra logger to *log.ConcreteLogger, tightly coupling the wrapper to a concrete implementation and breaking when FromContext returns a different type" - }, - { - "candidate": "grafanaInfraLogWrapper.FromContext falls back to New() when the underlying logger is not *log.ConcreteLogger, which silently drops context-derived fields/attributes" - }, - { - "candidate": "grafanaInfraLogWrapper should store the underlying logger as the infra logger interface type (e.g., log.Logger) rather than *log.ConcreteLogger to preserve contextual attributes and avoid implementation coupling" - }, - { - "candidate": "Constructors (e.g., New()) should accept/pass the infra logger interface into grafanaInfraLogWrapper instead of requiring a concrete logger type" - }, - { - "candidate": "grafanaInfraLogWrapper methods should rely only on interface methods exposed by the infra logger (log.Logger) rather than concrete-only methods" - }, - { - "candidate": "In contextual_logger_middleware.go, the helper instrumentContext now only enriches the logger (not metrics), so its name and comment are misleading and should be renamed to something logger-specific to avoid confusion" - } - ], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 6, - "total_golden": 2, - "tp": 0, - "fp": 6, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "sourcery", - "repo_name": "grafana__grafana__sourcery__PR76186__20260308", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__sourcery__PR76186__20260308/pull/1" - }, - "cubic-dev": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High", - "matched_candidate": "ContextualLoggerMiddleware.QueryData can panic when req is nil because it unconditionally accesses req.PluginContext; it should guard against nil requests like LoggerMiddleware does", - "confidence": 0.78, - "reasoning": "The candidate flags the same core bug: a panic due to unguarded access to req.PluginContext when req is nil in ContextualLoggerMiddleware. However, it only mentions QueryData and not the other methods (CallResource, CheckHealth, CollectMetrics) or the regression aspect, so it\u2019s a partial but semantically matching identification of the underlying issue." - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low", - "matched_candidate": "traceID is no longer included in the \"Plugin Request Completed\" log output because the explicit tracing.TraceIDFromContext usage was removed and ContextualLoggerMiddleware does not add traceID to contextual attributes, breaking trace-to-log correlation", - "confidence": 0.94, - "reasoning": "The candidate states that traceID is missing from plugin request completion logs due to removal of TraceIDFromContext usage and that ContextualLoggerMiddleware does not add traceID, which matches the golden issue about refactoring removing traceID extraction/logging and the new middleware not restoring it." - } - ], - "false_positives": [ - { - "candidate": "metrics_middleware_test.go has an incomplete rename: the test function is still named TestInstrumentationMiddleware even though the middleware was renamed to MetricsMiddleware/newMetricsMiddleware" - }, - { - "candidate": "The doc comment for Logger.FromContext is incorrect/copy-pasted (describes creating a new contextual logger with extra key/value pairs) and should instead describe retrieving a logger from a context.Context" - }, - { - "candidate": "TestLogger.FromContext returns a new disconnected TestLogger, causing logs written via the returned logger to be lost to the test; it should return the receiver so tests can assert on emitted logs" - } - ], - "false_negatives": [], - "errors": [], - "total_candidates": 5, - "total_golden": 2, - "tp": 2, - "fp": 3, - "fn": 0, - "errors_count": 0, - "precision": 0.4, - "recall": 1.0, - "tool": "cubic-dev", - "repo_name": "grafana__grafana__cubic-dev__PR76186__20260306", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__cubic-dev__PR76186__20260306/pull/1" - }, - "mesa": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "FromContext type-asserts to concrete *log.ConcreteLogger, creating tight coupling to infrastructure internals and brittle dependencies on a specific logger implementation" - }, - { - "candidate": "If the underlying logger implementation changes, the failed type assertion will silently fall back to d.New() (line 51), dropping contextual attributes without any error/visibility" - }, - { - "candidate": "Middleware ordering likely incorrect: LoggerMiddleware runs before ContextualLoggerMiddleware, so logs may miss expected contextual attributes (plugin ID, endpoint, datasource); ordering should be reversed so context enrichment happens before logging" - }, - { - "candidate": "TestLogger.FromContext returns a new TestLogger instance instead of the receiver (or a clone sharing the same sinks), breaking test observability because tests can\u2019t verify logged calls/messages via the original TestLogger\u2019s captured state" - } - ], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 4, - "total_golden": 2, - "tp": 0, - "fp": 4, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "mesa", - "repo_name": "grafana__grafana__mesa__PR76186__20260306", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__mesa__PR76186__20260306/pull/1" - }, - "codeant": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low", - "matched_candidate": "LoggerMiddleware no longer includes traceID in log fields; trace ID enrichment may be lost unless added elsewhere (tracing middleware/contextual logger) or re-added to LoggerMiddleware", - "confidence": 0.9, - "reasoning": "The candidate states that LoggerMiddleware no longer includes/enriches logs with traceID and that this enrichment may be lost unless added elsewhere or re-added. This matches the golden issue about traceID no longer being logged after refactoring and the new middleware not adding it, causing missing traceID in plugin request logs." - } - ], - "false_positives": [ - { - "candidate": "Middleware order in CreateMiddlewares may be incorrect at runtime/tests; ContextualLoggerMiddleware must run before LoggerMiddleware for logger.FromContext(ctx) to include contextual attributes" - }, - { - "candidate": "Need to validate whether MetricsMiddleware should be first in the chain to measure intended durations correctly after introducing ContextualLoggerMiddleware" - }, - { - "candidate": "grafanaInfraLogWrapper.FromContext relies on underlying infra logger exposing FromContext and returning *log.ConcreteLogger; concrete type assertion may fail if implementation changes, causing fallback to base logger" - }, - { - "candidate": "Fallback behavior in grafanaInfraLogWrapper.FromContext (returning d.New() when type assertion fails) is undocumented and may hide loss of contextual logging" - }, - { - "candidate": "ContextualLoggerMiddleware lacks unit tests verifying it attaches expected fields (pluginId, endpoint, dsName/dsUID, uname) and that LoggerMiddleware logs them via FromContext" - }, - { - "candidate": "Missing tests for ContextualLoggerMiddleware edge cases where DataSourceInstanceSettings is nil or User is nil to ensure no panics and correct behavior" - }, - { - "candidate": "MetricsMiddleware tests may no longer validate correct duration/count metrics after adding ContextualLoggerMiddleware; need to ensure metrics are still registered and updated" - }, - { - "candidate": "Potential mismatch if ContextualLoggerMiddleware does not actually add all fields previously logged by LoggerMiddleware (pluginId, endpoint, dsName, dsUID, uname), leading to missing log context" - }, - { - "candidate": "Logger name still uses \"plugin.instrumentation\" in CreateMiddlewares despite renaming to metrics; may need updating for consistency" - }, - { - "candidate": "Some comments/docstrings may still reference \"Instrumentation\" after rename; need to audit and update remaining references" - }, - { - "candidate": "Repository may have other Logger implementations not updated to satisfy new Logger interface with FromContext, causing build failures" - }, - { - "candidate": "Potential missing imports (e.g., context) in files updated for FromContext/any changes could still cause compile errors if any were missed" - } - ], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 13, - "total_golden": 2, - "tp": 1, - "fp": 12, - "fn": 1, - "errors_count": 0, - "precision": 0.07692307692307693, - "recall": 0.5, - "tool": "codeant", - "repo_name": "grafana__grafana__codeant__PR76186__20260309", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__codeant__PR76186__20260309/pull/1" - }, - "kodus": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "ContextualLoggerMiddleware does not instrument stream-related methods (SubscribeStream, RunStream) with instrumentContext, causing missing pluginId/dsUID/etc. in streaming logs" - }, - { - "candidate": "TestLogger.FromContext returns a new TestLogger instance (NewTestLogger()) instead of the receiver, so logs emitted via the returned logger are not captured by the original test logger and test assertions fail" - }, - { - "candidate": "LoggerMiddleware.logRequest refactor removed pluginCtx and relies on m.logger.FromContext(ctx) without callers enriching ctx, causing plugin request logs to lose critical context fields (pluginId, dsName, dsUID, uname) and creating an observability regression" - } - ], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 3, - "total_golden": 2, - "tp": 0, - "fp": 3, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "kodus", - "repo_name": "grafana__grafana__kodus__PR76186__20260309", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__kodus__PR76186__20260309/pull/1" - }, - "claude-code": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "`TestLogger.FromContext` incorrectly returns a new `TestLogger` via `NewTestLogger()` instead of returning the existing receiver, causing logs to go to a throwaway instance and preventing tests from observing `LoggerMiddleware` log calls" - }, - { - "candidate": "Test function name in `metrics_middleware_test.go` is outdated (`TestInstrumentationMiddleware`) after renaming `InstrumentationMiddleware` to `MetricsMiddleware`; it should be renamed to `TestMetricsMiddleware` for consistency" - } - ], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 2, - "total_golden": 2, - "tp": 0, - "fp": 2, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "claude-code", - "repo_name": "grafana__grafana__claude-code__PR76186__20260310", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__claude-code__PR76186__20260310/pull/1" - }, - "vercel": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 2, - "tp": 0, - "fp": 0, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "vercel", - "repo_name": "grafana__grafana__vercel__PR76186__20260310", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__vercel__PR76186__20260310/pull/1" - }, - "bito": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 2, - "tp": 0, - "fp": 0, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "bito", - "repo_name": "grafana__grafana__bito__PR76186__20260310", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__bito__PR76186__20260310/pull/1" - }, - "sentry": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 2, - "tp": 0, - "fp": 0, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "sentry", - "repo_name": "grafana__grafana__sentry__PR76186__20260310", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__sentry__PR76186__20260310/pull/1" - }, - "linearb": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "/gs review" - } - ], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 1, - "total_golden": 2, - "tp": 0, - "fp": 1, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "linearb", - "repo_name": "grafana__grafana__linearb__PR76186__20260309", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__linearb__PR76186__20260309/pull/1" - }, - "kodus-v2": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low", - "matched_candidate": "Refactor removed traceID extraction from plugin request logging context, causing \"Plugin Request Completed\" log entries to miss traceID and lose distributed-trace correlation", - "confidence": 0.93, - "reasoning": "The candidate states that a refactor removed traceID extraction from plugin request logging, leading to plugin request completion logs missing traceID and breaking trace correlation. This matches the golden issue about LoggerMiddleware refactor and ContextualLoggerMiddleware not adding traceID, resulting in missing traceID in plugin logs." - } - ], - "false_positives": [ - { - "candidate": "TestLogger.FromContext returns a new/disconnected TestLogger instance instead of the receiver, so logs written via logger.FromContext(ctx) are not captured by the original TestLogger used in test assertions" - } - ], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 2, - "total_golden": 2, - "tp": 1, - "fp": 1, - "fn": 1, - "errors_count": 0, - "precision": 0.5, - "recall": 0.5, - "tool": "kodus-v2", - "repo_name": "grafana__grafana__kodus-v2__PR76186__20260312", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__kodus-v2__PR76186__20260312/pull/1" - }, - "qodo-v2-2": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 2, - "tp": 0, - "fp": 0, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "qodo-v2-2", - "repo_name": "grafana__grafana__qodo-v2-2__PR76186__20260312", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__qodo-v2-2__PR76186__20260312/pull/1" - }, - "qodo-v22": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low", - "matched_candidate": "LoggerMiddleware now relies on logger.FromContext(ctx) for trace correlation, making traceID logging dependent on a registered contextual log provider; tracing.InitializeTracerForTest does not register that provider, so plugin request logs in tests/custom setups lose traceID even when spans exist in the context.", - "confidence": 0.86, - "reasoning": "Both describe the same underlying regression: traceID is no longer being added to plugin request logs after refactoring. The golden comment attributes it to removal of traceID extraction/parameter injection and the new ContextualLoggerMiddleware not adding it; the candidate explains the mechanism (now relying on logger.FromContext(ctx) and a contextual provider not being registered in some setups/tests), which results in missing traceID despite spans existing. Different framing, same missing traceID-in-logs issue." - } - ], - "false_positives": [ - { - "candidate": "TestLogger.FromContext returns a new TestLogger instance, so logs written via logger.FromContext(ctx) are recorded on a different logger than the one held by the test, breaking log assertions and potentially hiding logging regressions (e.g., in LoggerMiddleware)." - } - ], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 2, - "total_golden": 2, - "tp": 1, - "fp": 1, - "fn": 1, - "errors_count": 0, - "precision": 0.5, - "recall": 0.5, - "tool": "qodo-v22", - "repo_name": "grafana__grafana__qodo-v22__PR76186__20260312", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__qodo-v22__PR76186__20260312/pull/1" - }, - "qodo-extended": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "TestLogger.FromContext returns a new TestLogger via NewTestLogger() instead of the receiver, discarding the injected logger state" - }, - { - "candidate": "Because TestLogger.FromContext returns a disconnected instance, LoggerMiddleware.logRequest logs (m.logger.FromContext(ctx).Info(...)) go to an ephemeral logger and tests cannot assert on the original TestLogger's recorded logs (InfoLogs remains empty)" - } - ], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 2, - "total_golden": 2, - "tp": 0, - "fp": 2, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "qodo-extended", - "repo_name": "grafana__grafana__qodo-extended__PR76186__20260312", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__qodo-extended__PR76186__20260312/pull/1" - }, - "cubic-v2": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High", - "matched_candidate": "ContextualLoggerMiddleware.QueryData accesses req.PluginContext without checking if req is nil, which can panic on nil requests; it should guard like LoggerMiddleware does", - "confidence": 0.78, - "reasoning": "The golden issue is that multiple ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) can panic on nil requests due to accessing req.PluginContext without a nil check, representing a regression. The candidate identifies the same root cause (nil req leading to panic via req.PluginContext access) but only mentions QueryData, not the other methods or the regression aspect. It still matches the underlying bug, albeit partially scoped." - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low", - "matched_candidate": "instrumentContext/contextual log attributes do not include traceID, reducing trace correlation for plugin request logs; should add traceID from tracing.TraceIDFromContext", - "confidence": 0.93, - "reasoning": "The candidate states that contextual log attributes lack traceID for plugin request logs and suggests adding it from context (TraceIDFromContext). This matches the golden issue about traceID no longer being logged after refactor and the new ContextualLoggerMiddleware not adding it." - } - ], - "false_positives": [ - { - "candidate": "TestLogger.FromContext returns a new TestLogger instance, causing logs written via FromContext/contextual logging to be lost and making tests unable to observe those log writes; it should return the existing logger" - } - ], - "false_negatives": [], - "errors": [], - "total_candidates": 3, - "total_golden": 2, - "tp": 2, - "fp": 1, - "fn": 0, - "errors_count": 0, - "precision": 0.6666666666666666, - "recall": 1.0, - "tool": "cubic-v2", - "repo_name": "grafana__grafana__cubic-v2__PR76186__20260313", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__cubic-v2__PR76186__20260313/pull/1" - }, - "qodo-extended-summary": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low", - "matched_candidate": "traceID is no longer included in the 'Plugin Request Completed' log line because the refactor removed TraceIDFromContext extraction and the new instrumentContext does not add it, breaking trace-to-log correlation", - "confidence": 0.93, - "reasoning": "Both describe that after refactoring, traceID is no longer extracted from context and added to plugin request logs. The candidate notes the removal of TraceIDFromContext and that the new middleware/instrumentation doesn\u2019t add it, causing missing traceID and breaking trace-log correlation\u2014same underlying issue as the golden comment." - } - ], - "false_positives": [ - { - "candidate": "TestLogger.FromContext returns a new TestLogger instance, discarding accumulated log state so logs written via logger.FromContext(ctx).Info(...) are not captured by the original TestLogger referenced in tests (breaking assertions on InfoLogs.*)" - } - ], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 2, - "total_golden": 2, - "tp": 1, - "fp": 1, - "fn": 1, - "errors_count": 0, - "precision": 0.5, - "recall": 0.5, - "tool": "qodo-extended-summary", - "repo_name": "grafana__grafana__qodo-extended-summary__PR76186__20260313", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__qodo-extended-summary__PR76186__20260313/pull/1" - }, - "macroscope": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "TestLogger.FromContext returns a new *TestLogger instance, so logs written via the returned logger are captured on an ephemeral logger and never appear in the original logger's InfoLogs/DebugLogs/etc., preventing tests from verifying logs; it should return the existing receiver (f) instead." - } - ], - "false_negatives": [ - { - "golden_comment": "The ContextualLoggerMiddleware methods (QueryData, CallResource, CheckHealth, CollectMetrics) panic when a nil request is received. This occurs because they directly access req.PluginContext (via the instrumentContext function) without first checking if req is nil. This is a regression, as previous middleware layers gracefully handled nil requests.", - "severity": "High" - }, - { - "golden_comment": "The traceID is no longer logged for plugin requests. During a refactoring, the tracing import and the logic to extract and add traceID from the context to log parameters were removed from the LoggerMiddleware. The newly introduced ContextualLoggerMiddleware does not add this information, resulting in missing traceID in plugin request logs and impacting debugging and request tracing capabilities.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 1, - "total_golden": 2, - "tp": 0, - "fp": 1, - "fn": 2, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "macroscope", - "repo_name": "grafana__grafana__macroscope__PR76186__20260309", - "pr_url": "https://github.com/code-review-benchmark/grafana__grafana__macroscope__PR76186__20260309/pull/1" - } - }, - "https://github.com/ai-code-review-evaluation/discourse-graphite/pull/10": { - "gemini": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium", - "matched_candidate": "Using `where(id: params[:id]).first` to fetch a single record can return `nil` and cause `NoMethodError` when passed to `save_host(nil)`; use `find(params[:id])` to raise `ActiveRecord::RecordNotFound` and return a proper 404", - "confidence": 0.93, - "reasoning": "Both the golden comment and the candidate issue point out that fetching the record with `where(...).first` may return `nil` when the ID does not exist, and subsequent calls (e.g., `save_host`/`destroy`) will raise a `NoMethodError`. The candidate also suggests using `find` to raise `RecordNotFound` and produce a 404, which aligns with the expected fix direction." - } - ], - "false_positives": [ - { - "candidate": "Using `where(id: params[:id]).first` in the destroy path can return `nil` and cause `NoMethodError` on `host.destroy`; use `find(params[:id])` to raise `ActiveRecord::RecordNotFound` and return a proper 404" - }, - { - "candidate": "Directly assigning model attributes from `params` risks mass-assignment vulnerabilities; use strong parameters to whitelist permitted attributes" - }, - { - "candidate": "Migration code accessing `execute(...)[0]['id']` is unsafe when the query returns no rows, leading to `NoMethodError` and migration failure; handle empty results safely (e.g., `first&.[]('id')`)" - }, - { - "candidate": "SQL query in the migration uses string interpolation to insert values, creating SQL injection risk; use parameter binding or quoting (e.g., `connection.quote`)" - }, - { - "candidate": "Client code manually sets the category after saving because the server response only includes `category_id`; a more robust approach is for the server to embed the full category object in the response so the relationship updates automatically" - }, - { - "candidate": "`saveChanges` action appears to be dead code: it calls a server-side `update` action that is a no-op and there is no UI trigger; remove the unused client action and corresponding server route/action" - }, - { - "candidate": "Inline `style` attributes are used for table layout, reducing maintainability; replace with CSS classes and stylesheet rules" - }, - { - "candidate": "Host validation regex allows an optional path `(\\/.*)?` but a `before_validation` hook strips paths, making that regex portion redundant and confusing; remove the path-matching part" - }, - { - "candidate": "Serializer methods `id`, `host`, and `category_id` are redundant because `attributes :id, :host, :category_id` already defines them; remove the explicit method definitions" - }, - { - "candidate": "An explicit `id` method is redundant because `attributes :id` already defines it; remove the method definition" - } - ], - "false_negatives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical" - }, - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 11, - "total_golden": 4, - "tp": 1, - "fp": 10, - "fn": 3, - "errors_count": 0, - "precision": 0.09090909090909091, - "recall": 0.25, - "tool": "gemini", - "repo_name": "discourse__discourse-graphite__gemini__PR10__20260128", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__gemini__PR10__20260128/pull/1" - }, - "claude": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "spec/fabricators/category_fabricator.rb was overwritten with embeddable_host content, removing/destroying category fabricators like Fabricator(:category) and Fabricator(:diff_category)" - }, - { - "candidate": "Controller uses deprecated where().first pattern instead of the codebase-standard find_by()" - }, - { - "candidate": "Controller accesses params[:embeddable_host] directly without strong parameters (missing require/permit)" - } - ], - "false_negatives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical" - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium" - }, - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 3, - "total_golden": 4, - "tp": 0, - "fp": 3, - "fn": 4, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "claude", - "repo_name": "discourse__discourse-graphite__claude__PR10__20260127", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__claude__PR10__20260127/pull/1" - }, - "qodo": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical", - "matched_candidate": "Model `before_validation` callback calls `sub!` on `self.host` without guarding against `nil`/blank, which can raise exceptions when `host` is unset; should add presence validation and/or skip callback when blank", - "confidence": 0.93, - "reasoning": "The golden issue is a NoMethodError occurring in a `before_validation` callback in `EmbeddableHost`. The candidate describes that the `before_validation` callback calls `sub!` on `self.host` without handling `nil/blank`, which would cause a NoMethodError when `host` is unset. This is the same underlying problem and suggests appropriate guards/validations." - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium", - "matched_candidate": "`update` action fetches host with `where(...).first` and calls `save_host(host)` without handling the record-not-found case, risking `NoMethodError` and inconsistent responses; should return 404 when missing", - "confidence": 0.86, - "reasoning": "The candidate flags that the update action uses `where(...).first` and then calls `save_host(host)` without handling a missing record, which can lead to `NoMethodError` and should return 404. This matches the golden issue about not validating existence before calling methods on a potentially nil EmbeddableHost (at least for update; destroy is not mentioned but the core bug is the same)." - } - ], - "false_positives": [ - { - "candidate": "Migration `db/migrate/20150818190757_create_embeddable_hosts.rb` interpolates `h` directly into an INSERT SQL string, allowing SQL injection or broken migrations if the value contains quotes/malicious SQL; should use quoting/parameterized inserts" - }, - { - "candidate": "Controller `app/controllers/admin/embeddable_hosts_controller.rb` assigns `params[:embeddable_host][:host]` directly without explicit sanitization/normalization and assumes nested params exist, which can cause unsafe input handling or crashes when params are missing" - }, - { - "candidate": "`destroy` action fetches host with `where(...).first` and calls `host.destroy` without handling the record-not-found case, risking `NoMethodError` and inconsistent responses; should return 404 when missing" - }, - { - "candidate": "`save_host` assumes `params[:embeddable_host]` is present and dereferences it directly, which can raise exceptions if the nested params are absent" - } - ], - "false_negatives": [ - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 6, - "total_golden": 4, - "tp": 2, - "fp": 4, - "fn": 2, - "errors_count": 0, - "precision": 0.3333333333333333, - "recall": 0.5, - "tool": "qodo", - "repo_name": "discourse__discourse-graphite__qodo__PR10__20260125", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo__PR10__20260125/pull/1" - }, - "graphite": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical" - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium" - }, - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 4, - "tp": 0, - "fp": 0, - "fn": 4, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "graphite", - "repo_name": "discourse__discourse-graphite__graphite__PR10__20260123", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__graphite__PR10__20260123/pull/1" - }, - "bugbot": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical", - "matched_candidate": "EmbeddableHost before_validation callback calls host.sub! without guarding against nil, causing a crash when host is missing instead of producing a validation error", - "confidence": 0.93, - "reasoning": "Both describe a NoMethodError occurring in EmbeddableHost's before_validation callback. The candidate specifies the cause: calling `host.sub!` when `host` is nil, leading to a crash instead of a validation error, which aligns with the golden issue." - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium", - "matched_candidate": "Admin::EmbeddableHostsController update and destroy actions do not check whether the fetched EmbeddableHost exists before calling save_host/host.destroy, risking NoMethodError on nil", - "confidence": 0.96, - "reasoning": "Both the golden comment and the candidate issue point out that update/destroy fetch an EmbeddableHost by id without validating it exists, so calling save_host or destroy on a nil object can raise NoMethodError." - }, - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium", - "matched_candidate": "EmbeddableHost.record_for_host lowercases the database column but not the host parameter, so case-insensitive host matching can fail when the input host contains uppercase letters", - "confidence": 0.98, - "reasoning": "Both describe the same issue: the query lowercases the DB column but does not normalize/lowercase the input host parameter, causing intended case-insensitive matching to fail for mixed/uppercase hosts." - } - ], - "false_positives": [ - { - "candidate": "Contents of spec/fabricators/category_fabricator.rb and spec/fabricators/embeddable_host_fabricator.rb are swapped, placing the wrong fabricator definitions in each file" - }, - { - "candidate": "Migration 20150818190757_create_embeddable_hosts.rb assumes execute(...)[0]['id'] exists; if the query returns no rows (embed_category not configured or no matching category), it crashes with NoMethodError" - }, - { - "candidate": "embeddable-host.js delete action calls destroyRecord() without a catch handler, so request failures are silently swallowed and the user gets no feedback" - }, - { - "candidate": "store.js plural *_ids hydration branch deletes the original key unconditionally even if hydration fails, potentially losing the original IDs and replacing them with undefined values" - } - ], - "false_negatives": [ - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 7, - "total_golden": 4, - "tp": 3, - "fp": 4, - "fn": 1, - "errors_count": 0, - "precision": 0.42857142857142855, - "recall": 0.75, - "tool": "bugbot", - "repo_name": "discourse__discourse-graphite__bugbot__PR10__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__bugbot__PR10__20260122/pull/1" - }, - "copilot": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium", - "matched_candidate": "`update` and `destroy` use `EmbeddableHost.where(...).first` and immediately dereference the result; when no record exists this can raise `NoMethodError` instead of returning a 404-style error (should use `find` or handle `nil`)", - "confidence": 0.96, - "reasoning": "The candidate issue describes that `update` and `destroy` fetch an EmbeddableHost with `where(...).first` and then immediately call methods on it, which will raise `NoMethodError` when the record is missing, and suggests handling nil or using `find`. This is the same underlying problem as the golden comment." - } - ], - "false_positives": [ - { - "candidate": "Fabricators are in non-conventional files: `category`-related fabricators were moved into `embeddable_host_fabricator.rb` while `category_fabricator.rb` now defines `:embeddable_host`, reducing maintainability/discoverability" - }, - { - "candidate": "Test description string contains a typo: `'with an emeddable host'` should be `'with an embeddable host'`" - }, - { - "candidate": "Store test is inconsistent: it calls `store.find('fruit', 2)` but asserts colors for fruit id 1; the test passes only because the pretender route always returns `fruits[0]` regardless of requested id (fixture/route/requested id should be aligned)" - }, - { - "candidate": "Migration assumes SQL queries always return rows; `execute(...)[0]` can be `nil` if settings/rows are missing, causing `['id']`/`['value']` to raise (should check presence/`cmd_tuples` and handle missing data safely)" - } - ], - "false_negatives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical" - }, - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 5, - "total_golden": 4, - "tp": 1, - "fp": 4, - "fn": 3, - "errors_count": 0, - "precision": 0.2, - "recall": 0.25, - "tool": "copilot", - "repo_name": "discourse__discourse-graphite__copilot__PR10__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__copilot__PR10__20260122/pull/1" - }, - "propel": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical", - "matched_candidate": "before_validation mutates self.host with sub! even when host is nil/blank, causing NoMethodError and a 500 instead of allowing validation errors", - "confidence": 0.93, - "reasoning": "The golden issue is a NoMethodError occurring in a before_validation callback in EmbeddableHost. The candidate explicitly describes before_validation calling sub! on self.host when host is nil/blank, which would raise NoMethodError and lead to a 500. This is the same underlying problem." - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium", - "matched_candidate": "Admin embeddable hosts update action doesn\u2019t handle missing/invalid :id, so save_host can receive nil and raise NoMethodError, returning 500 instead of a proper 404/422", - "confidence": 0.9, - "reasoning": "The candidate flags that the update action does not handle a missing/invalid :id, leading to nil being passed/used and causing a NoMethodError (500). This is the same core issue as the golden comment: lack of nil-check/validation when fetching EmbeddableHost by id before calling methods on it. It doesn\u2019t explicitly mention destroy, but it matches the underlying bug pattern described." - }, - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium", - "matched_candidate": "EmbeddableHost.record_for_host compares lower(host) to an undowncased extracted host, so mixed-case referrer hosts never match allow-listed hosts", - "confidence": 0.95, - "reasoning": "Both describe the same bug: the query lower(host)=? is intended to be case-insensitive, but the input/extracted host parameter is not downcased/normalized, so mixed-case referrer hosts may not match allow-listed hosts." - } - ], - "false_positives": [ - { - "candidate": "Admin embeddable hosts destroy action doesn\u2019t handle missing/invalid :id, so calling destroy on nil raises NoMethodError, returning 500 instead of a proper 404/422" - } - ], - "false_negatives": [ - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 4, - "total_golden": 4, - "tp": 3, - "fp": 1, - "fn": 1, - "errors_count": 0, - "precision": 0.75, - "recall": 0.75, - "tool": "propel", - "repo_name": "discourse__discourse-graphite__propel__PR10__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__propel__PR10__20260122/pull/1" - }, - "augment": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical", - "matched_candidate": "Model host normalization calls sub! when host is nil/blank, which can raise before validations run and turn an invalid request into a 500 instead of a validation error", - "confidence": 0.9, - "reasoning": "The golden issue is a NoMethodError occurring in a before_validation hook in EmbeddableHost. The candidate describes host normalization calling `sub!` when `host` is nil/blank, which would raise (NoMethodError) before validations and cause a 500 instead of a validation error. This is the same underlying problem: a before_validation normalization method not handling nil leading to NoMethodError." - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium", - "matched_candidate": "Controller actions call save_host(host) / host.destroy without handling the case where params[:id] does not match a record, so host can be nil and raise a 500 instead of returning a 404/not-found response", - "confidence": 0.95, - "reasoning": "The candidate issue describes the same problem: update/destroy fetch a host by id that may be nil, then call save_host or destroy on it, causing an error (500/NoMethodError) instead of handling not-found." - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High", - "matched_candidate": "Migration inserts host values via string interpolation, which can break on quotes and is unsafe, and it bypasses model host normalization (scheme/path stripping), potentially creating records that will never match record_for_host", - "confidence": 0.9, - "reasoning": "Both point out that the migration uses raw SQL to insert host values, bypassing the EmbeddableHost model\u2019s normalization (scheme/path stripping). This can leave migrated hosts in a non-normalized form that won\u2019t match later lookups comparing bare hosts, breaking existing configurations. The candidate also mentions SQL interpolation safety, but it still includes the same core normalization/lookup mismatch issue as the golden comment." - } - ], - "false_positives": [ - { - "candidate": "record_for_host matches against uri.host (no port) while the model allows saving host values with :port, so stored entries like example.com:3000 will never match and will be treated as disallowed" - }, - { - "candidate": "Client sets host.category from the input categoryId instead of using the saved record\u2019s returned category_id, so when categoryId is blank/undefined the UI may show no category even though the server defaulted/persisted uncategorized" - } - ], - "false_negatives": [ - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - } - ], - "errors": [], - "total_candidates": 5, - "total_golden": 4, - "tp": 3, - "fp": 2, - "fn": 1, - "errors_count": 0, - "precision": 0.6, - "recall": 0.75, - "tool": "augment", - "repo_name": "discourse__discourse-graphite__augment__PR10__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__augment__PR10__20260122/pull/1" - }, - "baz": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical", - "matched_candidate": "`before_validation` calls `self.host.sub!` without guarding against `host` being nil/blank, causing `NoMethodError` on admin POST/PUT when `embeddable_host` params omit `host` before validation can reject it", - "confidence": 0.93, - "reasoning": "The golden issue is a NoMethodError occurring in a before_validation callback in EmbeddableHost. The candidate pinpoints that before_validation calls `self.host.sub!` without handling nil/blank `host`, leading to NoMethodError when params omit `host`. This is the same underlying bug." - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High", - "matched_candidate": "Migration copies raw `site_settings.embeddable_hosts` entries (including scheme/path) into `embeddable_hosts.host` without normalization, so `EmbeddableHost.record_for_host` (which compares `URI(host).host`) will not match migrated values and previously allowed hosts become disallowed after migration", - "confidence": 0.96, - "reasoning": "Both describe that the migration inserts embeddable host values via raw SQL without applying the model\u2019s normalization, so entries containing schemes/paths won\u2019t match the new lookup logic that compares only the bare host, potentially breaking existing allowed embedding configurations." - } - ], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium" - }, - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - } - ], - "errors": [], - "total_candidates": 2, - "total_golden": 4, - "tp": 2, - "fp": 0, - "fn": 2, - "errors_count": 0, - "precision": 1.0, - "recall": 0.5, - "tool": "baz", - "repo_name": "discourse__discourse-graphite__baz__PR10__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__baz__PR10__20260122/pull/1" - }, - "coderabbit": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical", - "matched_candidate": "EmbeddableHost before_validation callback calls sub! on host without guarding nil/blank, which can raise and 500 instead of returning validation errors", - "confidence": 0.9, - "reasoning": "Golden points to a NoMethodError occurring in EmbeddableHost's before_validation. Candidate describes the before_validation callback calling `sub!` on `host` without nil/blank guard, which would trigger a NoMethodError and cause a 500. Same underlying issue and location." - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium", - "matched_candidate": "Admin::EmbeddableHostsController update action can pass nil host into save_host when an invalid id is provided, leading to NoMethodError", - "confidence": 0.86, - "reasoning": "The candidate flags that in Admin::EmbeddableHostsController#update an invalid id can result in a nil host being passed to save_host, causing a NoMethodError. This matches the golden issue about not validating existence of the record before calling methods on it. However, it only mentions update and not destroy, so it\u2019s a partial match but the same underlying bug pattern." - } - ], - "false_positives": [ - { - "candidate": "Icon-only Save/Cancel/Edit/Delete d-button instances in embeddable-host.hbs lack accessible labels (no title/aria label) for screen readers" - }, - { - "candidate": "Proposed i18n keys for button titles (admin.embedding.save/cancel/edit/delete) are missing from locale files, so titles would not resolve" - }, - { - "candidate": "Store hydration logic for *_ids assumes obj[k] is an array and calls obj[k].map, which will throw if the API returns null or a non-array" - }, - { - "candidate": "Admin::EmbeddableHostsController destroy action can call destroy on nil when an invalid id is provided, leading to NoMethodError" - }, - { - "candidate": "EmbeddableHost host validation regex restricts TLD length to 5 characters, rejecting valid modern TLDs (e.g., .museum, .community)" - }, - { - "candidate": "Migration resolving category_id uses execute(...)[0] without checking for empty results, which can raise IndexError and abort the migration if rows are missing" - }, - { - "candidate": "Migration backfill interpolates host strings directly into SQL without quoting/escaping, breaking on quotes/special characters and posing SQL-injection risk" - }, - { - "candidate": "Migration backfill does not skip blank/whitespace host lines produced by splitting, potentially inserting invalid/empty hosts" - }, - { - "candidate": "Fabricator(:private_category) uses update! inside after_build, causing persistence even for Fabricate.build (unexpected side effect)" - }, - { - "candidate": "Fabricator(:private_category) assumes transients[:group] is present and calls transients[:group].id, which will raise if group is not provided" - }, - { - "candidate": "Spec files contain extra blank lines that violate RuboCop Layout/EmptyLinesAroundBlockBody and may fail CI linting" - }, - { - "candidate": "RSpec context description contains a typo: 'emeddable' should be 'embeddable'" - }, - { - "candidate": "Store test requests store.find('fruit', 2) but Pretender mock /fruits/:id always returns fruits[0] (id 1), making the test misleading/accidentally passing" - }, - { - "candidate": "spec/fabricators/category_fabricator.rb filename no longer matches its contents (contains Fabricator(:embeddable_host)), which is misleading and inconsistent with conventions" - }, - { - "candidate": "Admin embeddable-host component cancel action removes new host from array but does not delete/unload the unsaved record from the store, leaving orphaned store entries" - } - ], - "false_negatives": [ - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 17, - "total_golden": 4, - "tp": 2, - "fp": 15, - "fn": 2, - "errors_count": 0, - "precision": 0.11764705882352941, - "recall": 0.5, - "tool": "coderabbit", - "repo_name": "discourse__discourse-graphite__coderabbit__PR10__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__coderabbit__PR10__20260122/pull/1" - }, - "greptile": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical", - "matched_candidate": "`EmbeddableHost` model normalization can raise `NoMethodError` because `self.host`/`host` can be nil when calling string methods", - "confidence": 0.9, - "reasoning": "The golden issue is a NoMethodError occurring in a before_validation callback in EmbeddableHost. The candidate describes EmbeddableHost normalization calling string methods on a possibly nil host, which would raise NoMethodError during validation callbacks. This is the same underlying problem (nil host causing NoMethodError in pre-validation normalization)." - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium", - "matched_candidate": "Admin embeddable hosts controller update/save path can crash due to missing nil check when no `EmbeddableHost` is found for `params[:id]`", - "confidence": 0.9, - "reasoning": "The candidate issue flags that the Admin embeddable hosts controller update/save path can crash when no EmbeddableHost is found for params[:id] due to a missing nil check. This matches the golden issue about update/destroy calling methods on a nil record returned by where(...).first, causing NoMethodError." - } - ], - "false_positives": [ - { - "candidate": "SQL injection vulnerability in migration: `h` from `embeddable_hosts` site setting is interpolated into raw SQL without escaping/quoting" - }, - { - "candidate": "Migration can crash when no matching category is found because the category lookup query may return an empty result set and code assumes a row exists" - }, - { - "candidate": "Admin embeddable hosts controller destroy path can crash due to missing nil check when no `EmbeddableHost` is found for `params[:id]`" - } - ], - "false_negatives": [ - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 5, - "total_golden": 4, - "tp": 2, - "fp": 3, - "fn": 2, - "errors_count": 0, - "precision": 0.4, - "recall": 0.5, - "tool": "greptile", - "repo_name": "discourse__discourse-graphite__greptile__PR10__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__greptile__PR10__20260122/pull/1" - }, - "kg": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "spec/fabricators/category_fabricator.rb defines an embeddable_host fabricator instead of category fabricators; its content appears swapped with spec/fabricators/embeddable_host_fabricator.rb, breaking tests that rely on Fabricate(:category)" - }, - { - "candidate": "spec/fabricators/embeddable_host_fabricator.rb defines a category fabricator instead of an embeddable_host fabricator; its content appears swapped with spec/fabricators/category_fabricator.rb" - } - ], - "false_negatives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical" - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium" - }, - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 2, - "total_golden": 4, - "tp": 0, - "fp": 2, - "fn": 4, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "kg", - "repo_name": "discourse__discourse-graphite__kg__PR10__20260202", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__kg__PR10__20260202/pull/1" - }, - "qodo-v2": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical", - "matched_candidate": "EmbeddableHost before_validation callback calls sub! on self.host without nil/blank guarding, causing exceptions before validations can add errors", - "confidence": 0.93, - "reasoning": "The golden issue is a NoMethodError occurring in EmbeddableHost's before_validation. The candidate describes the before_validation callback calling `sub!` on `self.host` without guarding against nil/blank, which would raise a NoMethodError before validations run. This is the same underlying problem." - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium", - "matched_candidate": "Admin::EmbeddableHostsController update/destroy fetch records with where(...).first and then operate on a possibly nil record, causing 500s on missing ids", - "confidence": 0.93, - "reasoning": "The candidate notes that update/destroy use where(...).first and then act on a potentially nil record, leading to server errors when the ID is missing. This matches the golden issue about not validating existence and risking NoMethodError on nil." - } - ], - "false_positives": [ - { - "candidate": "Migration uses create_table(..., force: true), which can drop/recreate the table and cause data loss if the migration is re-run or partially applied" - }, - { - "candidate": "Migration interpolates unescaped host values into raw SQL INSERT statements, which can break on quotes and enables SQL injection via stored site_settings values" - }, - { - "candidate": "Migration indexes execute(...)[0] results without guarding for missing rows, which can raise errors when queries return no results" - }, - { - "candidate": "Admin::EmbeddableHostsController save_host assumes params[:embeddable_host] (and nested keys) exist, causing exceptions on malformed/missing request payloads" - }, - { - "candidate": "Admin destroy action does not handle destroy failures (always returns success), potentially hiding errors" - }, - { - "candidate": "Admin UI component overwrites host.category after save using local categoryId, which can be blank/null and clears the hydrated category in the UI until reload" - }, - { - "candidate": "Client store hydration treats any *_ids field as an array and blindly calls .map, which can throw if the value is not an array" - }, - { - "candidate": "Client store hydration deletes the original *_ids field even when hydration cannot succeed (missing embedded collection), silently dropping ids and producing undefined entries" - } - ], - "false_negatives": [ - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 10, - "total_golden": 4, - "tp": 2, - "fp": 8, - "fn": 2, - "errors_count": 0, - "precision": 0.2, - "recall": 0.5, - "tool": "qodo-v2", - "repo_name": "discourse__discourse-graphite__qodo-v2__PR10__20260227", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-v2__PR10__20260227/pull/1" - }, - "devin": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "Migration can crash with NoMethodError by unconditionally accessing result[0]['id'] when the embed_category setting is missing or no category matches (query returns zero rows)" - }, - { - "candidate": "Migration builds an INSERT SQL string by interpolating unescaped host values (h) from site_settings, which can break on quotes and enables SQL injection; should use quoting/parameterization" - } - ], - "false_negatives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical" - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium" - }, - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 2, - "total_golden": 4, - "tp": 0, - "fp": 2, - "fn": 4, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "devin", - "repo_name": "discourse__discourse-graphite__devin__PR10__20260302", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__devin__PR10__20260302/pull/1" - }, - "mra-max": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "Ember REST adapter for embedding uses path \"/admin/customize/embedding\" but server routes expose \"/customize/embedding\", likely causing 404s for embedding edits" - }, - { - "candidate": "Routes map \"customize/embedding\" to a non-namespaced EmbeddingController, but the implemented controller is Admin::EmbeddingController, likely causing misrouting/404s" - }, - { - "candidate": "Admin REST adapter changes are broad and touch many files, increasing risk of merge conflicts and admin UI regressions" - }, - { - "candidate": "New embeddable-host Ember component contains substantial UI/state/DOM logic, increasing risk of UI regressions and brittle event handling" - }, - { - "candidate": "Admin::EmbeddableHostsController introduces new create/update/destroy endpoints that may lack sufficient permission checks and strong parameter/data validation" - }, - { - "candidate": "Admin::EmbeddingController adds a sensitive admin endpoint (show/update) that must strictly enforce authentication/authorization to prevent unauthorized access" - }, - { - "candidate": "Embed controller now relies on EmbeddableHost.host_allowed?(request.referer); if EmbeddableHost data isn\u2019t seeded/migrations haven\u2019t run, legitimate embedding may be blocked" - }, - { - "candidate": "EmbeddableHost host normalization/validation and category association could diverge from expectations, risking data integrity or incorrect allowlist behavior" - }, - { - "candidate": "Topic#expandable_first_post? no longer requires embeddable hosts to be present, changing behavior and potentially enabling embedding UI/behavior when no hosts are configured" - }, - { - "candidate": "TopicEmbed.import now depends on EmbeddableHost.record_for_host to assign category_id; failures/incomplete host data can cause incorrect category assignment or import behavior" - }, - { - "candidate": "Topic embedding/retrieval logic now depends on DB-backed EmbeddableHost allowlist; if DB state is out of sync, host validation may behave incorrectly" - }, - { - "candidate": "Migration seeds embeddable_hosts using raw SQL string interpolation of host values, creating SQL injection/broken SQL risk if host contains quotes/special characters" - }, - { - "candidate": "TopicRetriever invalid_host? now uses EmbeddableHost.host_allowed?, adding runtime dependency on DB-backed allowlist that can fail if DB/config is not in sync" - }, - { - "candidate": "Admin controller specs for Admin::EmbeddableHostsController only test inheritance; missing tests for create/update/destroy behavior, validation errors, and JSON responses" - }, - { - "candidate": "Admin controller specs for Admin::EmbeddingController only test inheritance; missing tests for show/update/saveChanges behavior and serializer/persistence responses" - }, - { - "candidate": "Admin REST adapter model type normalization uses replace('_','-') (first underscore only); models with multiple underscores may not match ADMIN_MODELS and may route to wrong base path" - }, - { - "candidate": "Store hydration for keys ending in _ids may mishandle types/keys with multiple underscores due to first-underscore-only normalization, causing missing/incorrect relationship hydration" - }, - { - "candidate": "Removal of server locale key (embed_category) may break templates expecting that translation, causing missing i18n keys" - }, - { - "candidate": "Removing embeddable_hosts/embed_category from site_settings introduces a migration boundary; environments that don\u2019t run migrations may lose configuration or behave unexpectedly" - }, - { - "candidate": "Test coverage for the end-to-end editing/saving flow of embeddable hosts appears limited, risking undetected integration issues between Ember UI and Rails controllers" - }, - { - "candidate": "Tests previously relying on SiteSetting embeddable host behavior were removed/reworked; test environment must ensure migrations/DB-backed config are present to avoid false assumptions" - }, - { - "candidate": "TopicEmbed specs lack edge-case coverage (e.g., nil category_id, multiple hosts, different URLs), leaving potential bugs untested" - }, - { - "candidate": "No CHANGELOG entry documents the embedding feature/admin endpoints/migrations, reducing visibility of breaking changes" - }, - { - "candidate": "Admin embedding template condition uses truthiness of embeddable_hosts; empty array is truthy, so an empty table header may render when there are no hosts" - }, - { - "candidate": "New EmbeddableHost fabricator/specs must align with model validations/associations; mismatches could cause flaky tests" - } - ], - "false_negatives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical" - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium" - }, - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 25, - "total_golden": 4, - "tp": 0, - "fp": 25, - "fn": 4, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "mra-max", - "repo_name": "discourse__discourse-graphite__mra-max__PR10__20260306", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__mra-max__PR10__20260306/pull/1" - }, - "greptile-v4": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical", - "matched_candidate": "app/models/embeddable_host.rb before_validation calls self.host.sub!(...) without a nil check, causing NoMethodError when host is blank/nil", - "confidence": 0.93, - "reasoning": "The golden issue is a NoMethodError occurring in a before_validation callback in EmbeddableHost. The candidate pinpoints that before_validation calls `self.host.sub!(...)` without guarding against nil/blank, which would raise NoMethodError. This is the same underlying problem and location." - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium", - "matched_candidate": "app/controllers/admin/embeddable_hosts_controller.rb update action does not handle record-not-found (EmbeddableHost.where(...).first can be nil), leading to NoMethodError", - "confidence": 0.9, - "reasoning": "The candidate issue flags that the update action uses EmbeddableHost.where(...).first without handling a nil result, which can cause a NoMethodError\u2014this is the same core problem described in the golden comment (missing existence validation before calling methods). It does not explicitly mention destroy, but it matches the underlying issue for update." - } - ], - "false_positives": [ - { - "candidate": "spec/fabricators/category_fabricator.rb defines Fabricator(:embeddable_host) instead of category fabricators (file content does not match filename)" - }, - { - "candidate": "spec/fabricators/embeddable_host_fabricator.rb contains category fabricators (:category, :diff_category, :happy_category, :private_category) instead of the embeddable host fabricator (file content does not match filename)" - }, - { - "candidate": "db/migrate/20150818190757_create_embeddable_hosts.rb builds a raw INSERT with host values string-interpolated, creating a SQL injection/syntax error risk for hosts containing quotes" - }, - { - "candidate": "app/controllers/admin/embeddable_hosts_controller.rb destroy action does not handle record-not-found (EmbeddableHost.where(...).first can be nil), leading to NoMethodError" - }, - { - "candidate": "app/assets/javascripts/admin/components/embeddable-host.js.es6 delete action calls destroyRecord() without a .catch() handler, so deletion errors are silently swallowed" - }, - { - "candidate": "app/assets/javascripts/discourse/adapters/rest.js.es6 uses String.replace('_','-') which only replaces the first underscore, breaking normalization for model names with multiple underscores" - } - ], - "false_negatives": [ - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 8, - "total_golden": 4, - "tp": 2, - "fp": 6, - "fn": 2, - "errors_count": 0, - "precision": 0.25, - "recall": 0.5, - "tool": "greptile-v4", - "repo_name": "discourse__discourse-graphite__greptile-v4__PR10__20260306", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__greptile-v4__PR10__20260306/pull/1" - }, - "mra-nano": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "DB migration creates embeddable_hosts table, migrates data from site_settings, then deletes old site_settings keys, making the change one-way and risking missing embed data if migration fails or is skipped" - }, - { - "candidate": "Embedding feature changes span frontend adapter, backend controller, and routes, increasing risk of merge conflicts, permission gaps, and regressions" - }, - { - "candidate": "Admin embedding management update endpoint is a no-op (PUT /admin/customize/embedding does not persist changes), while the UI triggers an update on save" - }, - { - "candidate": "EmbedController#ensure_embeddable uses an unusual rescue placement after the method end, which may cause syntax/runtime issues or incorrect exception handling for invalid referer URLs" - }, - { - "candidate": "EmbeddableHost model couples host_allowed? validation with TopicEmbed.import categorization; missing host records can lead to nil category assignment or silent failures during import" - }, - { - "candidate": "Topic#expandable_first_post? removed the embeddable_hosts presence check, potentially expanding embed behavior beyond configured hosts and causing unexpected expandability conditions" - }, - { - "candidate": "New Ember embeddable-host component/template changes combined with adapter/serializer changes risk frontend/backend binding mismatches and UI regressions" - }, - { - "candidate": "REST adapter admin path detection relies on brittle type normalization (underscore-to-dash) and ADMIN_MODELS matching, which can misroute requests if Ember Data type naming differs" - }, - { - "candidate": "Store _hydrateEmbedded now handles *_ids and assigns hydrated arrays to pluralized keys, changing the in-memory association shape and potentially breaking callers expecting the old singular key/shape" - }, - { - "candidate": "Removing embeddable_hosts and embed_category from site_settings.yml risks new installs having neither defaults nor DB records if migrations are skipped or fail" - }, - { - "candidate": "Embedding endpoint sets X-Frame-Options to ALLOWALL, disabling clickjacking protection and enabling iframe embedding of sensitive UI flows" - }, - { - "candidate": "Topic retrieval/embedding validation now relies on EmbeddableHost.host_allowed? instead of SiteSetting checks; call sites and tests may not fully cover the new validation path" - }, - { - "candidate": "Admin::EmbeddableHostsController and Admin::EmbeddingController REST actions lack meaningful test coverage for authorization, params edge cases, and serialization behavior" - }, - { - "candidate": "Embed controller tests were rewritten to use EmbeddableHost; coverage may be missing for single-host vs multi-host scenarios and referer edge cases (invalid/missing referer)" - }, - { - "candidate": "Test suite introduces duplicate/fragile Fabricator(:category) definitions across files, risking conflicts or unintended fabricator usage" - }, - { - "candidate": "EmbeddableHost model specs do not cover invalid host format validations or belongs_to :category association edge cases (e.g., nil category_id behavior)" - } - ], - "false_negatives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical" - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium" - }, - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 16, - "total_golden": 4, - "tp": 0, - "fp": 16, - "fn": 4, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "mra-nano", - "repo_name": "discourse__discourse-graphite__mra-nano__PR10__20260306", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__mra-nano__PR10__20260306/pull/1" - }, - "mra-ultra": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical", - "matched_candidate": "EmbeddableHost before_validation calls self.host.sub! without guarding against nil, causing NoMethodError when host is not set", - "confidence": 0.93, - "reasoning": "The golden issue is a NoMethodError occurring in EmbeddableHost's before_validation. The candidate pinpoints that before_validation calls `self.host.sub!` without a nil guard, which would raise NoMethodError when `host` is nil. This is the same underlying problem and adds the likely cause." - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium", - "matched_candidate": "Admin::EmbeddableHostsController#update does not handle a missing record (host == nil) and can call save_host(nil), leading to NoMethodError on host.save", - "confidence": 0.86, - "reasoning": "Both point out that the controller\u2019s update action does not handle the case where the EmbeddableHost lookup by id returns nil, leading to a NoMethodError when methods are called on the missing record. The golden also mentions destroy, which the candidate omits, but the underlying issue (missing nil check after lookup) is the same." - } - ], - "false_positives": [ - { - "candidate": "Admin embedding REST adapter pathFor() always returns /admin/customize/embedding, causing requests meant for /admin/embeddable_hosts to be routed to the wrong endpoint" - }, - { - "candidate": "Fragile type.replace('_','-') mapping in the REST adapter can cause type mismatches that lead to silent 404s or incorrect updates" - }, - { - "candidate": "Ember component uses decorator syntax (@on/@observes/@computed) that may not be supported by the current Babel/Ember build configuration, risking build failures or runtime errors" - }, - { - "candidate": "Embed allowlist logic moved from SiteSetting to DB-backed EmbeddableHost.host_allowed?, so if the embeddable_hosts table is empty embedding is blocked even when previously allowed by site settings" - }, - { - "candidate": "Migration that creates embeddable_hosts and migrates/deletes old site setting keys is risky (potential data loss, category mis-mapping, and difficult rollback) and needs idempotence/rollback planning" - }, - { - "candidate": "TopicRetriever (and other call sites) now depend on EmbeddableHost.host_allowed? with no fallback, so a failed/skipped migration or empty table silently blocks all embedding for non-admins" - }, - { - "candidate": "Admin embedding UI saveChanges calls embedding.update({}) but the server-side update action only re-serializes without persisting, making the save flow a no-op/misleading" - }, - { - "candidate": "Serializer setup embeds only embeddable_host IDs and exposes only category_id, so the Ember UI lacks category data (host.category is nil) and triggers N+1 category fetches" - }, - { - "candidate": "TopicEmbed.import uses eh.try(:category_id); if no EmbeddableHost matches, category_id is nil and post creation may fail or select an unintended default category" - }, - { - "candidate": "Routes expose both /admin/customize/embedding and /admin/embeddable_hosts, creating overlapping/divergent API surfaces that can conflict with the Ember adapter and cause silent misrouting" - }, - { - "candidate": "Test suite removed SiteSetting allows_embeddable_host coverage and may leave gaps for edge cases (nil/malformed hosts) if not fully replaced by EmbeddableHost tests" - } - ], - "false_negatives": [ - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 13, - "total_golden": 4, - "tp": 2, - "fp": 11, - "fn": 2, - "errors_count": 0, - "precision": 0.15384615384615385, - "recall": 0.5, - "tool": "mra-ultra", - "repo_name": "discourse__discourse-graphite__mra-ultra__PR10__20260306", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__mra-ultra__PR10__20260306/pull/1" - }, - "mra-b": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical", - "matched_candidate": "EmbeddableHost before_validation callback calls sub! on self.host without nil guarding, so a nil host can raise NoMethodError and cause a 500 during validation", - "confidence": 0.93, - "reasoning": "Both describe the same underlying problem: a NoMethodError occurring in EmbeddableHost's before_validation callback due to calling a method (e.g., sub!) on a nil host without guarding, leading to an exception during validation." - } - ], - "false_positives": [ - { - "candidate": "Migration builds raw SQL INSERT statements by interpolating host strings without escaping/parameterization, enabling SQL injection if settings contain SQL metacharacters" - }, - { - "candidate": "Client store hydration change: _hydrateEmbedded now treats keys ending in _ids as arrays of IDs, maps them to records, assigns to a pluralized key, and deletes the original key\u2014this alters the payload contract and can silently break embedded relationship wiring across the app" - }, - { - "candidate": "Ember REST adapter payload root key (computed via Ember.String.underscore(type) from createRecord('embeddable-host')) may not match Rails controller\u2019s expected params root (embeddable_host), causing params[:embeddable_host] to be nil and leading to errors or failed saves" - }, - { - "candidate": "Admin model detection in rest adapter normalizes type with type.replace('_','-') (only first underscore) before checking ADMIN_MODELS, which can misclassify types with multiple underscores or naming variations and route requests to wrong endpoints" - }, - { - "candidate": "Embeddable host whitelist check moved from SiteSetting.allows_embeddable_host? to EmbeddableHost.host_allowed?, breaking plugins/other code that relied on the removed SiteSetting API" - }, - { - "candidate": "Switching embeddable host allowlist logic from a SiteSetting string to a DB-backed EmbeddableHost check can change behavior/availability (e.g., during early boot or migrations), causing compatibility/timing issues" - } - ], - "false_negatives": [ - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium" - }, - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 7, - "total_golden": 4, - "tp": 1, - "fp": 6, - "fn": 3, - "errors_count": 0, - "precision": 0.14285714285714285, - "recall": 0.25, - "tool": "mra-b", - "repo_name": "discourse__discourse-graphite__mra-b__PR10__20260307", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__mra-b__PR10__20260307/pull/1" - }, - "mra-a": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical", - "matched_candidate": "EmbeddableHost before_validation uses sub! on self.host without nil/type checks, causing exceptions when host is nil instead of clean validation errors", - "confidence": 0.93, - "reasoning": "The golden issue is a NoMethodError occurring in EmbeddableHost's before_validation. The candidate pinpoints that before_validation calls `sub!` on `self.host` without guarding against nil/type, which would raise NoMethodError when host is nil. This is the same underlying problem and adds detail about the cause." - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium", - "matched_candidate": "Admin::EmbeddableHostsController update/destroy actions don\u2019t handle missing records (nil host) before calling methods, risking NoMethodError and 500 responses for invalid ids", - "confidence": 0.96, - "reasoning": "The candidate issue states that the update/destroy actions don\u2019t handle missing (nil) records before calling methods, which matches the golden comment about EmbeddableHost.where(...).first returning nil and causing NoMethodError when calling save_host/destroy." - } - ], - "false_positives": [ - { - "candidate": "Core store hydration change for embedded _id/_ids keys is risky and may cause widespread regressions by altering how arrays are hydrated, forcing empty arrays, and deleting the original _ids key" - }, - { - "candidate": "Admin embedding UI saveChanges calls update() but the server-side Admin::EmbeddingController#update ignores params and does not persist changes, making Save Changes a no-op" - }, - { - "candidate": "Migration inserts host values via raw SQL string interpolation without escaping/parameterization, creating SQL injection/syntax-break risk if host contains quotes or special characters" - }, - { - "candidate": "REST adapter basePath logic now normalizes model types by replacing '_' with '-', which can misclassify underscore-named models as admin models and route requests to the wrong (/admin) endpoints" - }, - { - "candidate": "Topic embed creation removed fallback to SiteSetting.embed_category; if no EmbeddableHost matches, category becomes nil and PostCreator may fail or create topics in an unexpected category" - }, - { - "candidate": "EmbeddableHostSerializer only returns category_id while Ember UI expects host.category (Category object); missing association hydration can cause UI errors when accessing host.category.id or rendering category-badge" - } - ], - "false_negatives": [ - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 8, - "total_golden": 4, - "tp": 2, - "fp": 6, - "fn": 2, - "errors_count": 0, - "precision": 0.25, - "recall": 0.5, - "tool": "mra-a", - "repo_name": "discourse__discourse-graphite__mra-a__PR10__20260307", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__mra-a__PR10__20260307/pull/1" - }, - "entelligence": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical" - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium" - }, - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 4, - "tp": 0, - "fp": 0, - "fn": 4, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "entelligence", - "repo_name": "discourse__discourse-graphite__entelligence__PR10__20260306", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__entelligence__PR10__20260306/pull/1" - }, - "sourcery": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical", - "matched_candidate": "EmbeddableHost before_validation mutates self.host with sub!/sub without guarding against nil/blank, which can raise if the model is instantiated or saved without a host", - "confidence": 0.93, - "reasoning": "The golden issue is a NoMethodError occurring in EmbeddableHost's before_validation. The candidate pinpoints that before_validation calls sub!/sub on self.host without nil/blank guarding, which would raise a NoMethodError when host is nil. This is the same underlying problem and location." - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium", - "matched_candidate": "Admin::EmbeddableHostsController#update and #destroy do not handle the case where EmbeddableHost lookup by id returns nil, leading to exceptions when calling save_host/destroy; should return a 404/JSON error or use find with not-found handling", - "confidence": 0.96, - "reasoning": "The candidate issue flags the same problem: update/destroy fetch an EmbeddableHost by id that may be nil and then call methods on it (save_host/destroy), causing exceptions; it suggests proper not-found handling (404/JSON error or find)." - }, - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium", - "matched_candidate": "EmbeddableHost host normalization should downcase the host to align with case-insensitive lookup behavior and avoid mismatches when URLs contain uppercase characters", - "confidence": 0.94, - "reasoning": "Both point out that the host lookup is intended to be case-insensitive (using lower(host) in the query) but the input host parameter isn\u2019t normalized/downcased, so mixed/upper-case hosts can fail to match. Candidate explicitly recommends downcasing to avoid mismatches." - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High", - "matched_candidate": "Migration may import legacy embeddable_hosts values that still include schemes/paths; these should be normalized during migration so stored hosts match the new lookup expectations", - "confidence": 0.93, - "reasoning": "The candidate flags that the migration may bring over embeddable_hosts values containing schemes/paths and that they should be normalized during migration to match new lookup behavior. This is the same core concern as the golden comment about raw SQL bypassing model normalization and causing host-only comparisons to fail for migrated data." - } - ], - "false_positives": [ - { - "candidate": "Migration 20150818190757_create_embeddable_hosts assumes SELECT queries always return rows by indexing [0]; missing site_settings rows would raise during deployment and should be guarded with presence checks and fallbacks" - }, - { - "candidate": "Migration should fall back to uncategorized_category_id when embed_category is missing or resolves to 0, rather than raising or leaving category_id invalid" - }, - { - "candidate": "Migration inserts embeddable_hosts using raw SQL string interpolation of host values, which can break on malformed values (e.g., apostrophes) and should use quoting/parameterization" - }, - { - "candidate": "Ember component embeddable-host passes props into host.save(props), but Ember Data ignores positional args to save; category_id/other buffered changes may not persist and code is misleading\u2014should set properties on the model before saving" - }, - { - "candidate": "TopicEmbed.import lacks test coverage for the case where EmbeddableHost.record_for_host(url) returns nil; should add a spec asserting the imported topic\u2019s category behavior in that scenario" - }, - { - "candidate": "EmbeddableHost.record_for_host lacks tests for invalid URL input (e.g., non-URL strings) and should verify it returns false/handles errors appropriately" - }, - { - "candidate": "EmbeddableHost.record_for_host lacks tests for nil and empty string inputs" - }, - { - "candidate": "Admin::EmbeddingController spec only checks inheritance; missing controller tests for #show/#update JSON shape, ordering of embeddable hosts by host, and consistent response structure" - }, - { - "candidate": "Ember store plural embedded hydration assumes obj[k] is always an array and calls map without checking, which can throw on malformed payloads" - }, - { - "candidate": "Ember store plural embedded hydration does not filter out failed _lookupSubType results, leaving null/undefined entries and making singular/plural hydration inconsistent" - }, - { - "candidate": "rest.js.es6 uses type.replace('_', '-') which only replaces the first underscore; admin model type normalization can be incorrect for types with multiple underscores and should use a global replace or centralized mapping" - } - ], - "false_negatives": [], - "errors": [], - "total_candidates": 17, - "total_golden": 4, - "tp": 4, - "fp": 11, - "fn": 0, - "errors_count": 0, - "precision": 0.23529411764705882, - "recall": 1.0, - "tool": "sourcery", - "repo_name": "discourse__discourse-graphite__sourcery__PR10__20260308", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__sourcery__PR10__20260308/pull/1" - }, - "cubic-dev": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical" - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium" - }, - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 4, - "tp": 0, - "fp": 0, - "fn": 4, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "cubic-dev", - "repo_name": "discourse__discourse-graphite__cubic-dev__PR10__20260306", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__cubic-dev__PR10__20260306/pull/1" - }, - "mesa": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical", - "matched_candidate": "Calling `self.host.sub!` can crash with NoMethodError when `self.host` is nil; needs a nil/presence check before calling `sub!`", - "confidence": 0.78, - "reasoning": "The golden comment flags a NoMethodError occurring in a `before_validation` callback in `EmbeddableHost`. The candidate specifically identifies a NoMethodError risk from calling `self.host.sub!` when `self.host` is nil, which is a concrete instance of that same class of failure in a validation callback. This aligns on the underlying issue: a nil receiver causing NoMethodError during validation." - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium", - "matched_candidate": "Admin embeddable hosts update/save path can call `save_host(nil)` when the host record isn't found; add not-found handling before calling `save_host`", - "confidence": 0.9, - "reasoning": "Both describe missing handling for a non-existent EmbeddableHost record in the controller. The golden notes nil from `where(...).first` leading to NoMethodError when calling methods like `save_host`/`destroy`; the candidate specifically calls out `save_host(nil)` when the record isn't found and recommends not-found handling. Same underlying nil-record validation issue (at least for update/save path)." - } - ], - "false_positives": [ - { - "candidate": "Case-insensitive host matching without a database unique index on `LOWER(host)` allows duplicate hosts with different casing, creating ambiguous authorization/security risk; add a unique index on `LOWER(host)`" - }, - { - "candidate": "SQL injection vulnerability in migration: host value `h` is interpolated directly into SQL without escaping/parameterization; use parameterized/sanitized SQL" - }, - { - "candidate": "Migration category lookup can return an empty result set; accessing `[0]['id']` will raise if `embed_category` is unset or no category matches; add error handling for missing category" - }, - { - "candidate": "Admin embeddable hosts destroy action can call `destroy` on nil when no host exists for the given ID; add not-found handling before calling `destroy`" - }, - { - "candidate": "When no embeddable host is found for a URL, `eh.try(:category_id)` is nil and topics may be created without a category, which is a breaking behavior change; add fallback to a default category (e.g., `SiteSetting.embed_category` or `uncategorized_category_id`)" - } - ], - "false_negatives": [ - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 7, - "total_golden": 4, - "tp": 2, - "fp": 5, - "fn": 2, - "errors_count": 0, - "precision": 0.2857142857142857, - "recall": 0.5, - "tool": "mesa", - "repo_name": "discourse__discourse-graphite__mesa__PR10__20260307", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__mesa__PR10__20260307/pull/1" - }, - "codeant": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical", - "matched_candidate": "before_validation mutates host with sub!/sub calls without guarding against nil/blank host, which can raise exceptions", - "confidence": 0.9, - "reasoning": "The golden issue is a NoMethodError occurring in a before_validation callback in EmbeddableHost. The candidate describes before_validation calling sub!/sub on host without guarding against nil/blank, which would trigger a NoMethodError (calling methods on nil). This is the same underlying problem: unguarded mutation in before_validation leading to exceptions." - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium", - "matched_candidate": "Admin::EmbeddableHostsController#update and #destroy do not handle missing records; nil host can cause NoMethodError when calling save_host or destroy", - "confidence": 0.96, - "reasoning": "The candidate issue states that update and destroy in Admin::EmbeddableHostsController don\u2019t handle missing records and that a nil host can lead to NoMethodError when calling save_host or destroy. This matches the golden comment\u2019s concern about EmbeddableHost.where(...).first returning nil and subsequent method calls failing." - } - ], - "false_positives": [ - { - "candidate": "Migration inserts embeddable_hosts via raw SQL with interpolated host strings, allowing quotes to break SQL and creating SQL injection risk" - }, - { - "candidate": "Migration logic assumes specific site_settings rows/value array behavior, making it brittle across DB adapters/environments" - }, - { - "candidate": "Migration enforces category_id NOT NULL but model/controller may allow nil category_id, risking validation/DB errors" - }, - { - "candidate": "EmbeddableHost model has belongs_to :category but lacks presence validation for category_id despite DB NOT NULL constraint" - }, - { - "candidate": "save_host assumes params[:embeddable_host] exists and lacks guarding/strong parameter handling, risking errors on missing/invalid params" - }, - { - "candidate": "Controllers use deprecated before_filter instead of before_action (Rails compatibility concern)" - }, - { - "candidate": "EmbeddableHost host validation regex is overly strict (e.g., TLD length limited to 5, no IDN/IPv6), potentially rejecting valid hosts" - }, - { - "candidate": "Host validation allows optional path/port while before_validation strips paths, creating inconsistent validation/normalization behavior" - }, - { - "candidate": "TopicEmbed.import sets category to eh.try(:category_id); if no EmbeddableHost record is found, category can be nil and may need a default/fallback category" - }, - { - "candidate": "EmbeddableHost.record_for_host returns false for invalid URL while other code expects nil/record, creating inconsistent return semantics" - }, - { - "candidate": "Admin::EmbeddingController#update does not persist any changes and only renders @embedding, making the PUT endpoint confusing or potentially broken for the Ember client flow" - }, - { - "candidate": "Admin::EmbeddableHostsController#create/update lack strong params (permit host, category_id), risking mass-assignment or parameter handling issues depending on Rails version" - }, - { - "candidate": "Fabricator files appear to have swapped/moved definitions (category fabricator replaced/misplaced), which can confuse maintainers and potentially break fabricator loading order" - }, - { - "candidate": "Embed controller error messaging changed to generic 'invalid referer host' and may reduce debugging clarity if logs/messages are insufficient" - }, - { - "candidate": "Controller test coverage is missing for error cases (update/destroy with missing id, create with invalid params, embedding show/update flows)" - }, - { - "candidate": "Docs/changelog not updated to reflect migration and removal of old site_settings rows, which can surprise operators" - } - ], - "false_negatives": [ - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 18, - "total_golden": 4, - "tp": 2, - "fp": 16, - "fn": 2, - "errors_count": 0, - "precision": 0.1111111111111111, - "recall": 0.5, - "tool": "codeant", - "repo_name": "discourse__discourse-graphite__codeant__PR10__20260309", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__codeant__PR10__20260309/pull/1" - }, - "kodus": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical", - "matched_candidate": "EmbeddableHost before_validation callback calls sub! on host without checking presence, so nil host causes NoMethodError during save", - "confidence": 0.93, - "reasoning": "Golden issue is a NoMethodError occurring in a before_validation callback in EmbeddableHost. The candidate describes that the before_validation callback calls `sub!` on `host` without a presence check, leading to a NoMethodError when `host` is nil during save. This is the same underlying problem with more detail." - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium", - "matched_candidate": "Rails update action can crash when the record is not found (nil dereference) and also updates using raw params, enabling mass assignment; should return 404 and use strong params", - "confidence": 0.86, - "reasoning": "The golden issue is that update/destroy fetch the record with `where(...).first` and don\u2019t handle the nil case, leading to NoMethodError when calling methods on nil. The candidate explicitly notes the update action can crash when the record is not found (nil dereference) and suggests returning 404. It also mentions mass assignment, which is extra, but it still captures the same core nil-record problem (at least for update)." - }, - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium", - "matched_candidate": "EmbeddableHost.record_for_host uses lower(host) in SQL but does not downcase the input parameter, causing case-insensitive lookups to fail for mixed-case hosts", - "confidence": 0.98, - "reasoning": "Both describe the same issue: the query lower(host)=? is intended to be case-insensitive but the input parameter is not normalized (downcased), so mixed-case hosts may not match." - } - ], - "false_positives": [ - { - "candidate": "Cancel action rolls back buffered host properties but does not reset the component's categoryId, leaving inconsistent state after canceling an edit" - }, - { - "candidate": "Ember controller actions (saveChanges/addHost/deleteHost) can be invoked while embedding is null, causing a TypeError from dereferencing null" - }, - { - "candidate": "Dead code: saveChanges action and corresponding backend embedding#update (and PUT /admin/customize/embedding route) are implemented but not triggered by the UI" - }, - { - "candidate": "Route definition uses an absolute path for the embedding resource, breaking intended nesting under /customize and creating the route at /embedding instead of /customize/embedding" - }, - { - "candidate": "Edit and delete buttons are not disabled while host.isSaving is true, allowing actions during an in-flight save and creating a race condition" - }, - { - "candidate": "String replacement for model type uses a non-global underscore replacement, so types with multiple underscores are transformed incorrectly and can generate wrong API paths/404s" - }, - { - "candidate": "Store hydration calls .map on *_ids values without ensuring the value is an array, so null *_ids values can crash hydration; failed lookups can also leave undefined entries without filtering" - }, - { - "candidate": "Rails create action (via save_host) assigns attributes from raw params, bypassing strong parameters and enabling mass assignment" - }, - { - "candidate": "Rails destroy action can crash when the record is not found by calling destroy on nil; should return 404 when missing" - }, - { - "candidate": "Embedding controller fetch uses EmbeddableHost.all, loading all records into memory on each request; should paginate to avoid performance issues" - }, - { - "candidate": "Embed controller referer validation does not handle missing Referer header; passing nil into host_allowed? can raise TypeError and produce a 500 instead of access denied" - }, - { - "candidate": "Duplicate YAML key 'embedding' under the same scope causes one definition to overwrite the other, creating redundant/fragile localization configuration" - }, - { - "candidate": "Migration queries access result[0]['id'] / result[0]['value'] without checking for empty results, causing NoMethodError and migration failure when site settings are missing" - }, - { - "candidate": "Migration inserts host values using raw string interpolation, allowing SQL injection or syntax errors when host contains quotes; should quote/parameterize" - }, - { - "candidate": "Test fabricator files have swapped contents between category_fabricator.rb and embeddable_host_fabricator.rb, breaking expected test structure and lookups" - }, - { - "candidate": "private_category fabricator after_build unconditionally calls transients[:group].id, crashing when group transient is not provided" - }, - { - "candidate": "Pretender test endpoint /fruits/:id ignores the id parameter and always returns the first fruit, causing incorrect behavior and assertions" - }, - { - "candidate": "Brittle test assumes ordering of store.findAll results and asserts by index; should locate records by properties instead of position" - }, - { - "candidate": "save_host method accesses params[:embeddable_host] fields directly without strong-parameter whitelisting, enabling mass assignment" - }, - { - "candidate": "Admin embedding_controller update action is effectively dead/incorrect: it re-renders current state without persisting submitted changes, silently discarding updates while returning 200 OK" - } - ], - "false_negatives": [ - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 24, - "total_golden": 4, - "tp": 3, - "fp": 20, - "fn": 1, - "errors_count": 0, - "precision": 0.125, - "recall": 0.75, - "tool": "kodus", - "repo_name": "discourse__discourse-graphite__kodus__PR10__20260309", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__kodus__PR10__20260309/pull/1" - }, - "claude-code": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "Database migration uses unparameterized raw SQL with string interpolation (VALUES ('#{h}', ...)), creating potential SQL injection risk if data is tainted" - }, - { - "candidate": "Migration uses create_table :embeddable_hosts, force: true, which can drop an existing table and cause data loss if re-run" - }, - { - "candidate": "Fabricator files appear swapped: category_fabricator.rb contains the embeddable_host fabricator and embeddable_host_fabricator.rb contains category fabricators, making file contents mismatched to filenames" - }, - { - "candidate": "Admin embedding_controller.rb update action is a no-op (re-renders without persisting changes), which may be unintended" - }, - { - "candidate": "In rest.js.es6, type.replace('_', '-') only replaces the first underscore, which is fragile for types containing multiple underscores" - }, - { - "candidate": "Test coverage for the new controller is minimal (only checks inheritance; lacks integration tests for CRUD actions)" - } - ], - "false_negatives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical" - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium" - }, - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 6, - "total_golden": 4, - "tp": 0, - "fp": 6, - "fn": 4, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "claude-code", - "repo_name": "discourse__discourse-graphite__claude-code__PR10__20260310", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__claude-code__PR10__20260310/pull/1" - }, - "vercel": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical" - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium" - }, - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 4, - "tp": 0, - "fp": 0, - "fn": 4, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "vercel", - "repo_name": "discourse__discourse-graphite__vercel__PR10__20260310", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__vercel__PR10__20260310/pull/1" - }, - "sentry": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical" - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium" - }, - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 4, - "tp": 0, - "fp": 0, - "fn": 4, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "sentry", - "repo_name": "discourse__discourse-graphite__sentry__PR10__20260310", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__sentry__PR10__20260310/pull/1" - }, - "bito": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical" - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium" - }, - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 4, - "tp": 0, - "fp": 0, - "fn": 4, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "bito", - "repo_name": "discourse__discourse-graphite__bito__PR10__20260310", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__bito__PR10__20260310/pull/1" - }, - "linearb": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "/gs review" - } - ], - "false_negatives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical" - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium" - }, - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 1, - "total_golden": 4, - "tp": 0, - "fp": 1, - "fn": 4, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "linearb", - "repo_name": "discourse__discourse-graphite__linearb__PR10__20260309", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__linearb__PR10__20260309/pull/1" - }, - "kodus-v2": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium", - "matched_candidate": "Missing nil guard in Admin::EmbeddableHostsController update action: record lookup can return nil and passing it to save_host can raise errors", - "confidence": 0.86, - "reasoning": "The candidate flags a missing nil guard in Admin::EmbeddableHostsController#update where the record lookup may return nil and calling save_host would error. This matches the golden issue about not validating existence before calling methods on a potentially nil record (at least for update)." - }, - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium", - "matched_candidate": "Case-sensitivity bug in EmbeddableHost.record_for_host: query lower(host) = ? compares against a non-downcased Ruby host parameter, breaking host matching and potentially bypassing embed access control", - "confidence": 0.94, - "reasoning": "The candidate flags the same issue: the SQL uses lower(host) but the bound Ruby host parameter isn\u2019t downcased/normalized, causing mixed-case hosts to fail matching despite intended case-insensitive comparison." - } - ], - "false_positives": [ - { - "candidate": "Hydrating plural ID arrays can produce arrays containing undefined entries because _lookupSubType may return undefined and the code relies on a truthy .map() result (so the fallback never triggers), corrupting downstream consumers" - }, - { - "candidate": "Mass assignment vulnerability in save_host due to extracting/assigning attributes from raw params[:embeddable_host] instead of using Rails strong parameters (require/permit)" - }, - { - "candidate": "Missing nil guard in Admin::EmbeddableHostsController destroy action: record lookup can return nil and calling destroy on nil can raise NoMethodError" - }, - { - "candidate": "Ember component delete flow lacks error handling for destroyRecord() promise, so server-side deletion errors may be silently swallowed" - }, - { - "candidate": "Admin embedding_controller update action is a no-op: it re-serializes existing data without reading request params or persisting changes, causing PUT/PATCH to discard client-submitted updates" - }, - { - "candidate": "Migration can crash with IndexError/NoMethodError when embed_category site setting (or joined category) is missing because it accesses result[0]['id'] on an empty PG::Result" - }, - { - "candidate": "Migration fallback query for uncategorized_category_id can also crash by accessing fallback[0]['value'] on an empty PG::Result" - }, - { - "candidate": "SQL injection risk in migration due to direct string interpolation of host values into an INSERT statement without escaping (e.g., single quotes in host break/alter SQL)" - }, - { - "candidate": "Fabricator files have swapped contents: category_fabricator.rb contains embeddable_host fabricator and embeddable_host_fabricator.rb contains category fabricators, causing maintainability confusion" - }, - { - "candidate": "Duplicate fabricator definitions: embeddable_host_fabricator.rb defines category-related fabricators that already exist elsewhere, which can raise Fabrication::DuplicateFabricatorError and crash the test suite" - } - ], - "false_negatives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 12, - "total_golden": 4, - "tp": 2, - "fp": 10, - "fn": 2, - "errors_count": 0, - "precision": 0.16666666666666666, - "recall": 0.5, - "tool": "kodus-v2", - "repo_name": "discourse__discourse-graphite__kodus-v2__PR10__20260312", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__kodus-v2__PR10__20260312/pull/1" - }, - "qodo-v2-2": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical", - "matched_candidate": "EmbeddableHost before_validation callback calls sub! on self.host without guarding against nil, causing NoMethodError and 500s when embeddable_host[host] is missing", - "confidence": 0.93, - "reasoning": "The golden issue is a NoMethodError occurring in EmbeddableHost's before_validation. The candidate specifically describes the before_validation callback calling `sub!` on `self.host` without a nil guard, leading to NoMethodError when `host` is missing. This is the same underlying problem with more detail." - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium", - "matched_candidate": "Admin::EmbeddableHostsController update/destroy fetch records with where(...).first and then call methods without handling nil, causing 500s for invalid IDs or race-deleted records instead of returning 404", - "confidence": 0.94, - "reasoning": "The candidate notes that update/destroy use where(...).first and then call methods without checking for nil, leading to 500/NoMethodError for invalid or deleted IDs instead of a 404. This is the same underlying issue as the golden comment." - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High", - "matched_candidate": "CreateEmbeddableHosts migration inserts raw embeddable_hosts strings without escaping/normalization (e.g., scheme/path preserved), producing host records that will never match runtime host lookups based on URI(url).host", - "confidence": 0.93, - "reasoning": "Both describe the migration inserting embeddable_hosts via raw SQL without applying the model\u2019s normalization, so stored values may include scheme/path and won\u2019t match runtime lookups that compare only the bare host (URI(url).host). This can break existing embedding configurations after migration." - } - ], - "false_positives": [ - { - "candidate": "Admin update passes a nil EmbeddableHost into save_host, which then attempts to write attributes on nil and crashes" - }, - { - "candidate": "TopicEmbed.import sets category from EmbeddableHost.record_for_host(url) and can pass category: nil when no host record matches; with skip_validations: true this can create topics without category_id" - }, - { - "candidate": "Jobs::PollFeed calls TopicEmbed.import for feed entry URLs without ensuring a matching/allowed EmbeddableHost exists, making nil-category topic creation plausible" - }, - { - "candidate": "CreateEmbeddableHosts migration indexes empty SQL results with [0], which can crash when expected site_settings rows are missing and abort deploys" - } - ], - "false_negatives": [ - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - } - ], - "errors": [], - "total_candidates": 8, - "total_golden": 4, - "tp": 3, - "fp": 4, - "fn": 1, - "errors_count": 0, - "precision": 0.375, - "recall": 0.75, - "tool": "qodo-v2-2", - "repo_name": "discourse__discourse-graphite__qodo-v2-2__PR10__20260312", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-v2-2__PR10__20260312/pull/1" - }, - "qodo-v22": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical", - "matched_candidate": "EmbeddableHost `before_validation` callback calls `sub!` on `self.host` without guarding against nil, causing `NoMethodError` and 500s instead of validation errors for malformed requests", - "confidence": 0.93, - "reasoning": "The golden comment flags a NoMethodError occurring in EmbeddableHost's before_validation. The candidate explicitly describes the before_validation callback calling `sub!` on `self.host` without a nil guard, leading to a NoMethodError. This is the same underlying issue with more detail." - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium", - "matched_candidate": "Admin::EmbeddableHostsController update/destroy actions don\u2019t handle missing records (`where(...).first` can return nil) and then call methods on nil, returning 500 instead of 404 for invalid IDs", - "confidence": 0.96, - "reasoning": "The candidate issue flags that update/destroy use `where(...).first` which may return nil for missing IDs, and then methods are called on that nil object, causing a 500/NoMethodError instead of handling not-found. This is the same underlying problem as the golden comment." - } - ], - "false_positives": [ - { - "candidate": "Migration crashes when legacy `embed_category` site setting is missing because it indexes `[0]` on an empty SQL result set, aborting deploy/boot and making the uncategorized fallback unreachable" - }, - { - "candidate": "Migration interpolates host strings directly into an INSERT SQL statement without escaping/quoting, so hosts containing quotes can break the migration and pose an injection-style risk" - }, - { - "candidate": "Admin UI sets `host.category` after save using the pre-save `categoryId` rather than the server-persisted `category_id`, so when the server defaults blank category to uncategorized the UI can show no category until refresh" - }, - { - "candidate": "Embeddable-host component delete flow doesn\u2019t catch errors from `destroyRecord()`, leading to unhandled promise rejections and no user-visible error on failed deletes" - } - ], - "false_negatives": [ - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 6, - "total_golden": 4, - "tp": 2, - "fp": 4, - "fn": 2, - "errors_count": 0, - "precision": 0.3333333333333333, - "recall": 0.5, - "tool": "qodo-v22", - "repo_name": "discourse__discourse-graphite__qodo-v22__PR10__20260312", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-v22__PR10__20260312/pull/1" - }, - "qodo-extended": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical", - "matched_candidate": "EmbeddableHost before_validation callback calls sub! on self.host without guarding for nil, causing NoMethodError instead of a validation error when host is missing", - "confidence": 0.93, - "reasoning": "Both describe the same issue: a NoMethodError occurring in EmbeddableHost's before_validation callback due to calling a method on a nil host (missing guard), rather than producing a validation error." - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium", - "matched_candidate": "Admin::EmbeddableHostsController update action does not handle missing EmbeddableHost records (where(...).first can return nil) and passes nil into save_host, causing a 500 instead of a 404/JSON error", - "confidence": 0.86, - "reasoning": "The candidate flags that `where(...).first` in the update action can return nil and that the code doesn\u2019t handle the missing record, leading to a server error (NoMethodError/500) instead of a proper not-found response. This is the same core issue as the golden comment (lack of nil-check/validation for the retrieved EmbeddableHost). It doesn\u2019t mention destroy explicitly, but it matches the underlying bug for update which is central to the golden issue." - } - ], - "false_positives": [ - { - "candidate": "Admin::EmbeddableHostsController destroy action does not handle missing EmbeddableHost records (where(...).first can return nil) and calls destroy on nil, causing a 500 instead of a 404/JSON error" - }, - { - "candidate": "Admin::EmbeddableHostsController does not guard for missing params[:embeddable_host], leading to nil dereference errors when accessing expected keys (e.g., nil[:host])" - }, - { - "candidate": "Migration db/migrate/20150818190757_create_embeddable_hosts.rb interpolates host strings directly into SQL INSERT, allowing quotes/unexpected characters to break the migration and creating SQL injection risk" - }, - { - "candidate": "Migration db/migrate/20150818190757_create_embeddable_hosts.rb unconditionally indexes into the embed_category query result ([0]['id']), crashing with NoMethodError on fresh installs when the query returns zero rows" - }, - { - "candidate": "In _hydrateEmbedded, the plural _ids hydration path calls obj[k].map() without verifying the value is an array, causing TypeError when the _ids field is null/undefined" - }, - { - "candidate": "In rest adapter, type.replace('_','-') only replaces the first underscore, so model type names with multiple underscores may not match ADMIN_MODELS and will resolve to the wrong (non-admin) path" - } - ], - "false_negatives": [ - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 8, - "total_golden": 4, - "tp": 2, - "fp": 6, - "fn": 2, - "errors_count": 0, - "precision": 0.25, - "recall": 0.5, - "tool": "qodo-extended", - "repo_name": "discourse__discourse-graphite__qodo-extended__PR10__20260312", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-extended__PR10__20260312/pull/1" - }, - "cubic-v2": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical", - "matched_candidate": "In app/models/embeddable_host.rb before_validation callback, calling `self.host.sub!` will raise `NoMethodError` when `host` is nil; add a nil guard or use safe navigation so nil host results in a validation error instead of a crash (identified by cubic).", - "confidence": 0.93, - "reasoning": "The golden issue is a NoMethodError occurring in a before_validation callback in EmbeddableHost. The candidate specifically explains that `self.host.sub!` in the before_validation will raise NoMethodError when `host` is nil and suggests guarding against nil. This is the same underlying crash in the callback." - }, - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium", - "matched_candidate": "In app/models/embeddable_host.rb, the query uses `lower(host)` on the DB column but compares against an un-downcased parameter; downcase the input host to ensure consistent case-insensitive matching (identified by cubic).", - "confidence": 0.96, - "reasoning": "Both describe the same issue: the query lowercases the DB column (`lower(host)`) but does not lowercase/normalize the input parameter, so mixed-case hosts may not match despite intended case-insensitive comparison." - } - ], - "false_positives": [ - { - "candidate": "In app/assets/javascripts/admin/routes/admin-embedding.js.es6, `this.store.find('embedding')` is called without an id, returning a collection; fetch the singleton embedding record by id instead (identified by cubic)." - }, - { - "candidate": "In db/migrate/20150818190757_create_embeddable_hosts.rb, the `embed_category` query result is indexed with `[0]['id']` without checking for missing rows; guard the lookup so missing data doesn\u2019t raise before the uncategorized fallback can run (identified by cubic)." - } - ], - "false_negatives": [ - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 4, - "total_golden": 4, - "tp": 2, - "fp": 2, - "fn": 2, - "errors_count": 0, - "precision": 0.5, - "recall": 0.5, - "tool": "cubic-v2", - "repo_name": "discourse__discourse-graphite__cubic-v2__PR10__20260313", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__cubic-v2__PR10__20260313/pull/1" - }, - "qodo-extended-summary": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical", - "matched_candidate": "EmbeddableHost before_validation callback calls sub! on self.host without checking for nil; if host param is omitted and host is nil, it raises NoMethodError and returns 500 instead of a validation error (422)", - "confidence": 0.93, - "reasoning": "The golden issue is a NoMethodError occurring in EmbeddableHost's before_validation. The candidate describes that the before_validation callback calls `sub!` on `self.host` without a nil check, causing a NoMethodError when `host` is nil. This is the same underlying problem." - }, - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium", - "matched_candidate": "Admin::EmbeddableHostsController update action fetches host with .where(...).first and then calls save_host without guarding against nil, leading to NoMethodError and a 500 instead of a 404 when the record is missing", - "confidence": 0.86, - "reasoning": "The candidate flags that update fetches the host via where(...).first and then calls save_host without checking for nil, which would raise NoMethodError when the record is missing\u2014this is the core issue described in the golden comment (at least for update)." - } - ], - "false_positives": [ - { - "candidate": "Fabricator load order bug: :embeddable_host fabricator is defined in category_fabricator.rb but references the :category fabricator before it is loaded due to spec_helper requiring fabricators in filename order, causing undefined fabricator errors during spec boot" - }, - { - "candidate": "Admin::EmbeddableHostsController destroy action fetches host with .where(...).first and then calls destroy without guarding against nil, leading to NoMethodError and a 500 instead of a 404 when the record is missing" - }, - { - "candidate": "Migration 20150818190757_create_embeddable_hosts assumes embed_category exists in site_settings; execute(...)[0] can be nil when the setting is at default (no DB row), causing NoMethodError and crashing db:migrate" - }, - { - "candidate": "Migration 20150818190757_create_embeddable_hosts similarly assumes uncategorized_category_id exists in site_settings; missing row can make execute(...)[0] nil and crash the migration" - }, - { - "candidate": "Migration 20150818190757_create_embeddable_hosts interpolates host strings directly into an INSERT statement (VALUES ('#{h}', ...)), which can break on quotes and is unsafe (SQL injection risk)" - }, - { - "candidate": "In store.js _hydrateEmbedded, the plural _ids hydration branch unconditionally deletes the original ID array and replaces it with mapped results even when lookups return undefined, silently losing valid IDs and producing arrays with undefined entries" - }, - { - "candidate": "In rest.js basePath, type.replace('_','-') only replaces the first underscore, so admin model types with multiple underscores won't match hyphenated ADMIN_MODELS entries" - } - ], - "false_negatives": [ - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 9, - "total_golden": 4, - "tp": 2, - "fp": 7, - "fn": 2, - "errors_count": 0, - "precision": 0.2222222222222222, - "recall": 0.5, - "tool": "qodo-extended-summary", - "repo_name": "discourse__discourse-graphite__qodo-extended-summary__PR10__20260313", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-extended-summary__PR10__20260313/pull/1" - }, - "macroscope": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "The update and destroy methods in Admin::EmbeddableHostsController do not validate the existence of the EmbeddableHost record retrieved by ID. If EmbeddableHost.where(id: params[:id]).first returns nil (i.e., the host does not exist), attempting to call methods on the nil object (e.g., save_host or destroy) will result in a NoMethodError.", - "severity": "Medium", - "matched_candidate": "admin/embeddable_hosts_controller.rb update: EmbeddableHost lookup can return nil for missing id, nil is passed to save_host and then host.host= is called, causing NoMethodError; should return 404 when not found", - "confidence": 0.93, - "reasoning": "The candidate flags that the EmbeddableHost lookup in update can return nil for a missing id, leading to NoMethodError when save_host/host.host= is called, and suggests returning 404. This is the same core issue as the golden comment (missing nil check for record existence in update/destroy)." - }, - { - "golden_comment": "record_for_host compares lower(host) = ? but does not normalize the parameter\u2019s case, so mixed\u2011case referer hosts may fail to match even though comparison intends to be case\u2011insensitive.", - "severity": "Medium", - "matched_candidate": "app/models/embeddable_host.rb record_for_host: query compares lower(host) to an un-downcased parameter, so lookups can fail when uri.host contains uppercase letters; should downcase the parameter", - "confidence": 0.95, - "reasoning": "Both describe the same issue: the SQL uses lower(host) for case-insensitive comparison but the bound parameter (host/uri.host) is not downcased/normalized, so mixed-case hosts may not match." - } - ], - "false_positives": [ - { - "candidate": "db/migrate/20150818190757_create_embeddable_hosts.rb: execute(...)[0] can return nil when the uncategorized_category_id site setting is missing, and accessing ['value'] on nil raises NoMethodError" - }, - { - "candidate": "spec/fabricators/embeddable_host_fabricator.rb: transients[:group] has no default and can be nil, so transients[:group].id raises NoMethodError when fabricating :private_category without providing :group" - }, - { - "candidate": "app/models/embeddable_host.rb record_for_host: bare hostnames like \"example.com\" fail because URI(host).host is nil without a scheme, causing the method to return false; should handle inputs without scheme (e.g., by prepending https://)" - }, - { - "candidate": "admin/embedding_controller.rb update: action ignores params and performs no update, yet returns the same 200 response as show, misleading callers; should implement update logic or return an error/not supported" - }, - { - "candidate": "db/migrate/20150818190757_create_embeddable_hosts.rb: host value h is interpolated into SQL without escaping, so single quotes in host cause SQL syntax errors (and potential injection); should use quote(h)" - } - ], - "false_negatives": [ - { - "golden_comment": "NoMethodError before_validation in EmbeddableHost", - "severity": "Critical" - }, - { - "golden_comment": "Because this migration inserts embeddable_hosts rows with raw SQL, any existing embeddable_hosts values that include http:// or /https:// or path segments won\u2019t go through the EmbeddableHost model\u2019s normalization, so the new host lookup (which compares only the bare host) may fail for migrated data. Consider ensuring that migrated hosts are normalized to the same format as newly created EmbeddableHost records so existing embedding configurations keep working.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 7, - "total_golden": 4, - "tp": 2, - "fp": 5, - "fn": 2, - "errors_count": 0, - "precision": 0.2857142857142857, - "recall": 0.5, - "tool": "macroscope", - "repo_name": "discourse__discourse-graphite__macroscope__PR10__20260309", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__macroscope__PR10__20260309/pull/1" - } - }, - "https://github.com/ai-code-review-evaluation/discourse-graphite/pull/7": { - "gemini": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low", - "matched_candidate": "Light theme lightness changed from 30% to 70%, making the color too light on a light background and causing poor contrast/readability", - "confidence": 0.93, - "reasoning": "The candidate explicitly notes that the light theme lightness value changed from 30% to 70%, which is the core issue in the golden comment (a dramatic inversion in the light theme color choice). It also adds the consequence (poor contrast), which is consistent with the concern." - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low", - "matched_candidate": "Light theme color for .group-member-info .name changed from 30% to 50%, introducing an unintended visual/contrast change (also present in mobile/user.scss)", - "confidence": 0.9, - "reasoning": "Both point out that in the light theme the primary/lightness value for the .name styling was changed from 30% to 50%, likely unintentionally, and that the same issue exists in the mobile variant. The golden frames it as $primary in desktop/user.css and mentions broader context, but the core issue matches." - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low", - "matched_candidate": "Inconsistent .custom-message-length color between desktop and mobile stylesheets (desktop uses light theme lightness 70% while mobile uses 30%)", - "confidence": 0.78, - "reasoning": "Golden issue is that in topic-post.css the light theme lightness was changed from 70% to 30%, making text too dark. The candidate also flags a 70% vs 30% lightness discrepancy for the light theme, though it frames it as desktop vs mobile inconsistency. This still identifies the same underlying problem: an unintended 30% lightness value in the light theme compared to the original 70%." - } - ], - "false_positives": [ - { - "candidate": "Dark theme uses scale-color($secondary, $lightness: 30%), producing a too-dark color on a dark background and causing poor contrast (should use a lighter value like 70%)" - }, - { - "candidate": "h3 light theme lightness changed from 20% to 50%, reducing contrast compared to desktop/topic-post.scss which uses 20%; should be consistent and keep 20% for readability" - } - ], - "false_negatives": [], - "errors": [], - "total_candidates": 6, - "total_golden": 3, - "tp": 3, - "fp": 2, - "fn": 0, - "errors_count": 0, - "precision": 0.5, - "recall": 1.0, - "tool": "gemini", - "repo_name": "discourse__discourse-graphite__gemini__PR7__20260128", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__gemini__PR7__20260128/pull/1" - }, - "claude": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low", - "matched_candidate": "app/assets/stylesheets/desktop/topic-post.scss line 291: dark-light-choose conversion inverted the lightness values (used primary 70% / secondary 30%) instead of preserving the original 30% for primary and using 70% for secondary, causing incorrect link color hierarchy", - "confidence": 0.93, - "reasoning": "Both describe the same issue: in .topic-meta-data h5 a the change to dark-light-choose altered the light-theme primary color lightness from the original 30% to 70%, effectively inverting the intended lightness values and impacting link color hierarchy." - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low", - "matched_candidate": "app/assets/stylesheets/desktop/user.scss ~518-522: `scale-color($primary, $lightness: 30%)` was transformed to 50%/50% instead of 30%/70%, creating inconsistent styling", - "confidence": 0.86, - "reasoning": "Both point out that in desktop user stylesheet the light theme change altered the $primary lightness scaling from 30% to 50%, causing inconsistency versus the intended 30%/70% split and other changes that keep $primary and move complement to $secondary. Candidate doesn\u2019t mention the mobile/.name parallel, but the core issue matches." - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low", - "matched_candidate": "app/assets/stylesheets/desktop/topic-post.scss line 291: dark-light-choose conversion inverted the lightness values (used primary 70% / secondary 30%) instead of preserving the original 30% for primary and using 70% for secondary, causing incorrect link color hierarchy", - "confidence": 0.78, - "reasoning": "Both comments flag that during conversion the light theme lightness values were inverted/changed from the original, resulting in the light theme using 30% where 70% was intended (making colors/text darker). The candidate frames it as an inversion affecting link color hierarchy, but it\u2019s the same underlying lightness swap problem." - } - ], - "false_positives": [ - { - "candidate": "app/assets/stylesheets/common/base/_topic-list.scss line 115: missed wrapping `scale-color($primary, $lightness: 50%)` with `dark-light-choose(...)`, leaving one instance without dark theme support" - }, - { - "candidate": "app/assets/stylesheets/mobile/topic-post.scss ~178-182: `scale-color($primary, $lightness: 20%)` was transformed to 50%/50% instead of 20%/80%, breaking the established conversion pattern and contrast" - }, - { - "candidate": "app/assets/stylesheets/desktop/topic-post.scss ~892: transformation unintentionally changed the CSS property from `border: 1px solid scale-color(...)` to `background-color: dark-light-choose(...)`, altering the visual effect from border to background" - }, - { - "candidate": "app/assets/stylesheets/mobile/user.scss ~493-497: `scale-color($primary, $lightness: 30%)` was transformed to 50%/50% instead of 30%/70%, creating inconsistent styling" - }, - { - "candidate": "app/assets/stylesheets/mobile/modal.scss ~98-102: `scale-color($primary, $lightness: 70%)` was transformed to 30%/70% instead of 70%/30%, inverting the intended light/dark theme mapping" - } - ], - "false_negatives": [], - "errors": [], - "total_candidates": 7, - "total_golden": 3, - "tp": 3, - "fp": 5, - "fn": 0, - "errors_count": 0, - "precision": 0.42857142857142855, - "recall": 1.0, - "tool": "claude", - "repo_name": "discourse__discourse-graphite__claude__PR7__20260127", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__claude__PR7__20260127/pull/1" - }, - "graphite": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low" - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low" - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 3, - "tp": 0, - "fp": 0, - "fn": 3, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "graphite", - "repo_name": "discourse__discourse-graphite__graphite__PR7__20260123", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__graphite__PR7__20260123/pull/1" - }, - "qodo": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "Mixed and inconsistent $lightness values in new dark-light-choose(...) usages may cause insufficient contrast/readability between light and dark themes (notably for borders and helper text); needs validation against the design system" - }, - { - "candidate": "Repeated dark-light-choose(scale-color(...)) patterns for muted/subtle text are duplicated across many elements, risking divergence and making future theme tweaks harder; should be centralized via a shared variable/mixin/token" - }, - { - "candidate": "Disabled button hover styling changes text color via dark-light-choose(...), which may make disabled buttons appear enabled or reduce legibility, especially in dark theme; needs confirmation/testing" - } - ], - "false_negatives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low" - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low" - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 3, - "total_golden": 3, - "tp": 0, - "fp": 3, - "fn": 3, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "qodo", - "repo_name": "discourse__discourse-graphite__qodo__PR7__20260123", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo__PR7__20260123/pull/1" - }, - "bugbot": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low", - "matched_candidate": "Light theme link color for `.topic-meta-data h5 a` in embedded posts was unintentionally changed: original `scale-color($primary, $lightness: 30%)` but `dark-light-choose` now uses `70%`, making it much lighter than before", - "confidence": 0.94, - "reasoning": "The candidate flags the same change in `.topic-meta-data h5 a`: the light theme color went from `scale-color($primary, $lightness: 30%)` to using `dark-light-choose(...70%...)`, making the light theme much lighter/inverted compared to before. This matches the golden issue about the dramatic lightness shift (30% -> 70%)." - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low", - "matched_candidate": "Light theme color for `.group-member-info .name` was unintentionally changed: original `scale-color($primary, $lightness: 30%)` but `dark-light-choose` now uses `50%`, affecting both desktop and mobile stylesheets", - "confidence": 0.9, - "reasoning": "Both point out that the light theme\u2019s $primary lightness adjustment was changed from 30% to 50% (via dark-light-choose), and that this affects the .name styling and also applies in the mobile variant. This is the same underlying concern as the golden comment about an unintended $primary change in the light theme while other changes preserve $primary and shift complement to $secondary for dark." - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low", - "matched_candidate": "Light theme color for `.custom-message-length` was unintentionally changed: original `scale-color($primary, $lightness: 70%)` but `dark-light-choose` now uses `30%`, making it much darker than before", - "confidence": 0.96, - "reasoning": "The candidate flags the same change in topic-post.css: light theme color computation went from scale-color with $lightness: 70% to a dark-light-choose value of 30%, resulting in much darker text than intended." - } - ], - "false_positives": [ - { - "candidate": "Light theme color for `.topic-map h3` heading was unintentionally changed: original `scale-color($primary, $lightness: 20%)` but `dark-light-choose` now uses `50%`, making the heading significantly lighter than before" - } - ], - "false_negatives": [], - "errors": [], - "total_candidates": 4, - "total_golden": 3, - "tp": 3, - "fp": 1, - "fn": 0, - "errors_count": 0, - "precision": 0.75, - "recall": 1.0, - "tool": "bugbot", - "repo_name": "discourse__discourse-graphite__bugbot__PR7__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__bugbot__PR7__20260122/pull/1" - }, - "copilot": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low", - "matched_candidate": "A SCSS color conversion changed the light theme lightness from the original `scale-color($primary, $lightness: 30%)` to `70%`, altering appearance and making it inconsistent with the dark theme value of `30%`; should use `dark-light-choose(scale-color($primary, $lightness: 30%), scale-color($secondary, $lightness: 70%))` instead", - "confidence": 0.9, - "reasoning": "Both point to the same underlying issue: in `.topic-meta-data h5 a` the light-theme color lightness was changed from 30% to 70% when introducing `dark-light-choose`, causing a major visual inversion. The candidate also suggests a fix (though it swaps dark values), but it correctly identifies the problematic 30%\u219270% change for the light theme." - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low", - "matched_candidate": "Several SCSS color conversions changed the light theme lightness from the original `scale-color($primary, $lightness: 30%)` to `50%`, unintentionally altering light theme appearance; should use `dark-light-choose(scale-color($primary, $lightness: 30%), scale-color($secondary, $lightness: 70%))` instead", - "confidence": 0.9, - "reasoning": "Both point out that the light theme\u2019s $primary lightness was changed from 30% to 50%, altering appearance, and suggest keeping the original 30% for light while using the complement/secondary for dark via dark-light-choose. The candidate doesn\u2019t mention the mobile/.name parallel explicitly, but the core issue matches." - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low", - "matched_candidate": "A SCSS color conversion changed the light theme lightness from the original `scale-color($primary, $lightness: 70%)` to `30%`, unintentionally altering light theme appearance; should use `dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%))` instead", - "confidence": 0.93, - "reasoning": "The candidate explicitly notes that the light theme lightness was changed from 70% to 30%, which would darken the light theme appearance, matching the golden issue about unintended darker text due to the lightness change in topic-post.css." - } - ], - "false_positives": [ - { - "candidate": "A SCSS color conversion changed the light theme lightness from the original `scale-color($primary, $lightness: 20%)` to `50%`, unintentionally altering light theme appearance; should use `dark-light-choose(scale-color($primary, $lightness: 20%), scale-color($secondary, $lightness: 80%))` instead" - } - ], - "false_negatives": [], - "errors": [], - "total_candidates": 4, - "total_golden": 3, - "tp": 3, - "fp": 1, - "fn": 0, - "errors_count": 0, - "precision": 0.75, - "recall": 1.0, - "tool": "copilot", - "repo_name": "discourse__discourse-graphite__copilot__PR7__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__copilot__PR7__20260122/pull/1" - }, - "augment": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low", - "matched_candidate": "`dark-light-choose` is being used in a way that unintentionally changes the light-theme color (e.g., from `scale-color($primary, $lightness: 30%)` to `scale-color($primary, $lightness: 70%)`), causing a large and possibly unintended contrast shift", - "confidence": 0.93, - "reasoning": "The candidate flags that using `dark-light-choose` changed the light-theme color from `scale-color($primary, $lightness: 30%)` to `scale-color($primary, $lightness: 70%)`, causing a large contrast shift. This is the same underlying issue as the golden comment (dramatic inversion due to lightness change in the light theme)." - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low", - "matched_candidate": "In `.group-member-info .name`, using `dark-light-choose` changes the light-mode color from `scale-color($primary, $lightness: 30%)` to `scale-color($primary, $lightness: 50%)`, which may be unintended and inconsistent with the pattern of keeping light theme unchanged while adjusting only dark theme", - "confidence": 0.9, - "reasoning": "Both point out that the light-theme value for the `.name` color changes from `scale-color($primary, $lightness: 30%)` to `...50%` due to `dark-light-choose`, which breaks the intended pattern of keeping light theme unchanged while only adjusting dark theme (moving complement to $secondary). The candidate covers the same underlying issue, specifically the `.name` change mentioned in the golden comment." - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low", - "matched_candidate": "`dark-light-choose` is being used in a way that unintentionally changes the light-theme color (e.g., from `scale-color($primary, $lightness: 30%)` to `scale-color($primary, $lightness: 70%)`), causing a large and possibly unintended contrast shift", - "confidence": 0.86, - "reasoning": "Golden says the replacement uses $lightness: 30% instead of the original 70% for the light theme, making text darker. The candidate flags an unintended light-theme color change involving dark-light-choose and a swap between 30% and 70%, causing a contrast shift. This is the same underlying issue (light theme lightness value changed, affecting contrast), even though the direction is described as an example." - } - ], - "false_positives": [ - { - "candidate": "In `mobile/modal.scss`, the `dark-light-choose` argument order appears to swap the intended light and dark values (light changes from `scale-color($primary, $lightness: 70%)` to `scale-color($primary, $lightness: 30%)`, and vice-versa for dark)" - }, - { - "candidate": "In mobile `topic-post.scss`, the light-theme `h3` color is changed from `scale-color($primary, $lightness: 20%)` to `scale-color($primary, $lightness: 50%)` due to `dark-light-choose` returning its first argument in light themes; this may be unintended" - } - ], - "false_negatives": [], - "errors": [], - "total_candidates": 4, - "total_golden": 3, - "tp": 3, - "fp": 2, - "fn": 0, - "errors_count": 0, - "precision": 0.75, - "recall": 1.0, - "tool": "augment", - "repo_name": "discourse__discourse-graphite__augment__PR7__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__augment__PR7__20260122/pull/1" - }, - "propel": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low", - "matched_candidate": "The `dark-light-choose` usage should preserve the original 70% light-theme lightening and only add a dark-theme fallback, rather than altering the light-theme value", - "confidence": 0.74, - "reasoning": "Both comments flag that the light-theme value was unintentionally changed when introducing dark/light handling, and that the original light-theme value should be preserved while adding a dark-theme counterpart. The golden is specific about $primary changing from 30% to 50% (and mentions mobile/.name too), while the candidate generally states the same preservation concern (though with different percentages)." - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low", - "matched_candidate": "Wrapping the color in `dark-light-choose` changed the light-theme value from `scale-color($primary, $lightness: 70%)` to `scale-color($primary, $lightness: 30%)`, making the custom message length hint much darker and reducing readability", - "confidence": 0.96, - "reasoning": "The candidate explicitly notes that the light-theme value changed from $lightness: 70% to $lightness: 30% after wrapping in dark-light-choose, resulting in a much darker hint text and reduced readability. This is the same underlying issue as the golden comment." - } - ], - "false_positives": [ - { - "candidate": "The mobile modal\u2019s custom message length hint color is now inconsistent with the desktop modal, which still uses 70% lightening for the light theme" - } - ], - "false_negatives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 3, - "total_golden": 3, - "tp": 2, - "fp": 1, - "fn": 1, - "errors_count": 0, - "precision": 0.6666666666666666, - "recall": 0.6666666666666666, - "tool": "propel", - "repo_name": "discourse__discourse-graphite__propel__PR7__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__propel__PR7__20260122/pull/1" - }, - "baz": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low", - "matched_candidate": "In the light theme, the topic-meta link color was changed to scale-color($primary, $lightness: 70%) due to wrapping in dark-light-choose, making links nearly white and reducing contrast; the light branch should preserve the previous 30% lightness.", - "confidence": 0.94, - "reasoning": "Both describe the same change in `.topic-meta-data h5 a`: the light-theme color went from `scale-color($primary, $lightness: 30%)` to `scale-color($primary, $lightness: 70%)` via `dark-light-choose`, causing an overly light/near-white link and effectively inverting the intended lightness. Candidate also suggests preserving 30%, aligning with the golden issue." - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low", - "matched_candidate": "For .custom-message-length, the light-theme color was changed to scale-color($primary, $lightness: 30%) instead of the previous 70%, making helper text unexpectedly darker; the light branch should keep 70% while adding a dark branch.", - "confidence": 0.96, - "reasoning": "Both point out that in topic-post.css the light theme\u2019s scale-color lightness was changed from 70% to 30%, resulting in text being much darker than intended, and suggest keeping 70% for the light branch." - } - ], - "false_positives": [ - { - "candidate": "Light-theme h3 color was changed to scale-color($primary, $lightness: 50%) instead of the previous 20% when moving to dark-light-choose, brightening header text and regressing contrast; the light branch should keep 20% while still providing a dark branch." - } - ], - "false_negatives": [ - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 3, - "total_golden": 3, - "tp": 2, - "fp": 1, - "fn": 1, - "errors_count": 0, - "precision": 0.6666666666666666, - "recall": 0.6666666666666666, - "tool": "baz", - "repo_name": "discourse__discourse-graphite__baz__PR7__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__baz__PR7__20260122/pull/1" - }, - "coderabbit": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "In app/assets/stylesheets/mobile/compose.scss (#file-uploading, around lines 30-37), the text color still uses scale-color($secondary, $lightness: 50%) instead of the new dark-light-choose pattern used by the sibling #draft-status, causing inconsistent theme handling and potential poor visibility in dark themes" - }, - { - "candidate": "In app/assets/stylesheets/mobile/compose.scss (.wmd-input, around lines 174-176), the text color uses darken($primary, 40%), which may reduce readability when $primary is already dark; consider switching to dark-light-choose for consistent theme support" - } - ], - "false_negatives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low" - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low" - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 2, - "total_golden": 3, - "tp": 0, - "fp": 2, - "fn": 3, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "coderabbit", - "repo_name": "discourse__discourse-graphite__coderabbit__PR7__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__coderabbit__PR7__20260122/pull/1" - }, - "greptile": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low", - "matched_candidate": "In app/assets/stylesheets/desktop/topic-post.scss line 291, the dark-light-choose lightness values are flipped: light theme should keep scale-color($primary, $lightness: 30%) and dark theme should use scale-color($secondary, $lightness: 70%) (inverted), not the opposite", - "confidence": 0.9, - "reasoning": "Both point to the same underlying problem: the change in .topic-meta-data h5 a replaced the original scale-color($primary, $lightness: 30%) with a dark-light-choose where the light-theme lightness is effectively inverted/too high (70% instead of 30%). The candidate explicitly calls out the flipped lightness values between light/dark themes, matching the golden concern about the dramatic inversion in the light theme." - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low", - "matched_candidate": "In app/assets/stylesheets/desktop/user.scss line 522, an original scale-color($primary, $lightness: 30%) was incorrectly changed to 50% instead of using 30% for light and 70% (inverted) for dark with $secondary", - "confidence": 0.9, - "reasoning": "Both point out that in desktop/user styles the light theme scale-color($primary, $lightness: 30%) was changed to 50%, which is inconsistent with the pattern of keeping $primary at 30% and moving the complementary/inverted value to $secondary for dark. The candidate is more specific (mentions 70% inverted for dark) but it\u2019s the same underlying issue the golden comment highlights." - } - ], - "false_positives": [ - { - "candidate": "In app/assets/stylesheets/desktop/user.scss line 527, an original scale-color($primary, $lightness: 30%) was incorrectly changed to 50% instead of using 30% for light and 70% (inverted) for dark with $secondary" - }, - { - "candidate": "In app/assets/stylesheets/mobile/user.scss line 497, an original scale-color($primary, $lightness: 30%) was incorrectly changed to 50% instead of using 30% for light and 70% (inverted) for dark with $secondary" - }, - { - "candidate": "In app/assets/stylesheets/mobile/user.scss line 502, an original scale-color($primary, $lightness: 30%) was incorrectly changed to 50% instead of using 30% for light and 70% (inverted) for dark with $secondary" - }, - { - "candidate": "In app/assets/stylesheets/mobile/modal.scss line 102, the .custom-message-length color inversion is inconsistent with desktop: desktop uses (70%, 30%) while mobile uses (30%, 70%); they should match" - } - ], - "false_negatives": [ - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 6, - "total_golden": 3, - "tp": 2, - "fp": 4, - "fn": 1, - "errors_count": 0, - "precision": 0.3333333333333333, - "recall": 0.6666666666666666, - "tool": "greptile", - "repo_name": "discourse__discourse-graphite__greptile__PR7__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__greptile__PR7__20260122/pull/1" - }, - "kg": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low" - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low" - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 3, - "tp": 0, - "fp": 0, - "fn": 3, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "kg", - "repo_name": "discourse__discourse-graphite__kg__PR7__20260202", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__kg__PR7__20260202/pull/1" - }, - "qodo-v2": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low", - "matched_candidate": "In app/assets/stylesheets/desktop/topic-post.scss, .topic-meta-data h5 a uses dark-light-choose with a too-light light-theme value (primary 70% instead of the intended darker ~30%), reducing contrast and becoming inconsistent with base author link styling", - "confidence": 0.94, - "reasoning": "Both point to the same change in .topic-meta-data h5 a: the light-theme color in dark-light-choose was set to scale-color($primary, $lightness: 70%) instead of the prior ~30%, making it much lighter and effectively inverting the intended contrast." - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low", - "matched_candidate": "In app/assets/stylesheets/desktop/user.scss (and similarly mobile/user.scss), .group-member-info .name uses a weaker 50%/50% lightness pair instead of the base name styling\u2019s stronger 30%/70%, making group member names less prominent and inconsistent", - "confidence": 0.78, - "reasoning": "Golden notes that in desktop/user.css the light theme $primary was changed from 30% to 50% (and mentions a similar .name change in mobile), potentially unintentionally, while other changes keep $primary and move the complement to $secondary for dark. The candidate specifically calls out that .group-member-info .name in desktop and mobile uses a 50%/50% lightness pair instead of the original 30%/70%, making names less prominent/inconsistent. This aligns with the golden\u2019s concern about the light-theme primary being shifted to 50% and the related .name change in mobile, though it focuses on the .name selector rather than the variable name." - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low", - "matched_candidate": "In app/assets/stylesheets/mobile/modal.scss, .custom-message-length has light/dark lightness values inverted (light theme uses 30% and dark theme uses 70%), likely an accidental swap during dark-light-choose conversion and a contrast regression vs desktop", - "confidence": 0.74, - "reasoning": "Both comments flag the same underlying problem: the light theme is using a much lower lightness value (30%) than intended (70%), making text darker and suggesting an accidental inversion/swap with the dark theme value. Although the candidate cites a different file/selector, the core issue (light/dark lightness values swapped leading to darker light theme text) matches the golden concern." - } - ], - "false_positives": [ - { - "candidate": "In app/assets/stylesheets/mobile/topic-post.scss, .topic-map h3 uses a 50%/50% lightness pair instead of matching desktop\u2019s stronger 20%/80%, reducing emphasis/contrast and creating inconsistency between mobile and desktop" - } - ], - "false_negatives": [], - "errors": [], - "total_candidates": 4, - "total_golden": 3, - "tp": 3, - "fp": 1, - "fn": 0, - "errors_count": 0, - "precision": 0.75, - "recall": 1.0, - "tool": "qodo-v2", - "repo_name": "discourse__discourse-graphite__qodo-v2__PR7__20260227", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-v2__PR7__20260227/pull/1" - }, - "devin": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low", - "matched_candidate": "In desktop/topic-post.scss for `.embedded-posts .topic-meta-data h5 a`, the `dark-light-choose` light/dark values are swapped, changing light-theme link color from `scale-color($primary, $lightness: 30%)` to `70%` and making embedded post meta-data links/author names hard to read on light themes", - "confidence": 0.9, - "reasoning": "Both point to the same change in `.topic-meta-data h5 a`: replacing `scale-color($primary, $lightness: 30%)` with a `dark-light-choose(...)` where the light-theme value becomes `scale-color($primary, $lightness: 70%)`, i.e., a major lightness inversion affecting readability. Candidate additionally mentions swapped values, but still identifies the core issue of 30% -> 70% for light theme." - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low", - "matched_candidate": "In desktop/user.scss for `.group-member-info .name`, the `dark-light-choose` transformation changes the light-theme lightness from `30%` to `50%`, making group member names lighter and less readable on light themes", - "confidence": 0.86, - "reasoning": "Both point out that in the desktop variant, the light-theme lightness/primary value was changed from 30% to 50% (not preserving the original), specifically mentioning the `.name` change. The golden also notes broader context (primary vs secondary and mobile too), but the core issue matches." - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low", - "matched_candidate": "In desktop/topic-post.scss for `.embedded-posts .topic-meta-data h5 a`, the `dark-light-choose` light/dark values are swapped, changing light-theme link color from `scale-color($primary, $lightness: 30%)` to `70%` and making embedded post meta-data links/author names hard to read on light themes", - "confidence": 0.86, - "reasoning": "Golden issue: in topic-post.css the light theme lightness was changed from 70% to 30%, making text darker than intended. Candidate talks about desktop/topic-post.scss where light/dark values are swapped, resulting in light theme using 30% instead of 70% (and dark theme vice versa), causing poor readability. This includes the same core problem (light theme incorrectly using 30% rather than 70%), though it adds the swap detail and a specific selector/file." - } - ], - "false_positives": [ - { - "candidate": "In mobile/modal.scss for `.custom-message-length`, the `dark-light-choose` light/dark values are swapped, changing light-theme color from `scale-color($primary, $lightness: 70%)` to `30%` and inverting intended contrast on both light and dark themes" - }, - { - "candidate": "In mobile/topic-post.scss for `.topic-map h3`, the `dark-light-choose` transformation changes the light-theme lightness from `20%` to `50%` (and uses `50%` for both branches), making headings less prominent and inconsistent with the desktop equivalent" - }, - { - "candidate": "In mobile/user.scss for `.group-member-info .name`, the `dark-light-choose` transformation changes the light-theme lightness from `30%` to `50%`, making group member names lighter on light themes" - } - ], - "false_negatives": [], - "errors": [], - "total_candidates": 5, - "total_golden": 3, - "tp": 3, - "fp": 3, - "fn": 0, - "errors_count": 0, - "precision": 0.6, - "recall": 1.0, - "tool": "devin", - "repo_name": "discourse__discourse-graphite__devin__PR7__20260302", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__devin__PR7__20260302/pull/1" - }, - "mra-max": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "Sweeping migration of color functions in admin_base.scss (scale-color to dark-light-choose(scale-color(...), ...)) creates high churn and risk of visual regressions, merge conflicts, and potential SCSS compile issues if theme variables are missing" - }, - { - "candidate": "Widespread replacement of scale-color($primary, ...) with dark-light-choose(scale-color($primary,...), scale-color($secondary,...)) across core/global SCSS files may shift global styling behavior and requires thorough regression testing due to large ripple potential" - }, - { - "candidate": "In admin_base.scss, .setting.overridden h3 uses scale-color($highlight, $lightness: -50%) instead of the dark-light-choose pattern, risking inconsistent theming/contrast in dark mode" - }, - { - "candidate": "admin_base.scss contains multiple scale-color(...) usages not wrapped in dark-light-choose (e.g., tertiary/danger backgrounds), creating inconsistent theming and potential dark-mode breakage" - }, - { - "candidate": "Possible SCSS syntax/typo in _topic-list.scss where a rule appears truncated as 'color: dark-ligh', which would cause a compile-time error if present" - }, - { - "candidate": "_topic-list.scss has a color rule using scale-color($primary, $lightness: 50%) without dark-light-choose, inconsistent with surrounding theming approach" - }, - { - "candidate": "Header/navigation SCSS underwent large-scale dark-light-choose color changes, increasing risk of visual regressions and theme drift" - }, - { - "candidate": "In header.scss, some dark-light-choose calls use a $primary-derived value as an option such that dark mode may still resolve to a $primary-derived color instead of the intended $secondary-derived color (e.g., around line 301)" - }, - { - "candidate": "In header.scss, another similar dark-light-choose usage may cause dark mode to derive from $primary rather than $secondary (e.g., around line 332)" - }, - { - "candidate": "Centralizing theming decisions in the dark-light-choose Sass function (based on brightness($primary)/brightness($secondary)) tightly couples UI colors to theme palette/brightness logic, so changes to tokens or brightness logic can cascade broadly" - }, - { - "candidate": "Desktop topic-list SCSS mass refactor to dark-light-choose may cause inconsistent theming across breakpoints if $secondary is not consistently defined across themes" - }, - { - "candidate": "Mobile topic-list SCSS mass refactor to dark-light-choose increases churn and risk of mobile rendering regressions" - }, - { - "candidate": "Lack of explicit automated visual regression and/or color-contrast testing for the new dark/light color logic increases risk of undetected UI regressions" - }, - { - "candidate": "Overall patch scope is very broad across desktop/mobile/admin styles, increasing regression risk and warranting comprehensive testing (visual/UI/accessibility)" - }, - { - "candidate": "dark-light-choose depends on theme_variables imports; if the import graph/build context lacks theme_variables, builds may fail or colors may compute unexpectedly (build fragility)" - }, - { - "candidate": "PR description is insufficient to explain rationale, scope, affected files, and testing performed, making review/maintenance harder" - }, - { - "candidate": "Repository lacks a CHANGELOG entry/file despite docs referencing changelog practices; theming/token changes should be documented for consumers" - }, - { - "candidate": "Partial adoption of the new theming scheme leaves some rules using direct tokens (e.g., color: $primary) alongside dark-light-choose, creating inconsistent color strategy within the same file/theme" - }, - { - "candidate": "Static uses of $header_primary in topic-post.scss are not wrapped in dark-light-choose, potentially leading to inconsistent dark-theme behavior if theme variables change" - }, - { - "candidate": "No tests were found that explicitly cover dark-theme behavior or validate the new dark-light-choose-based CSS outputs, leaving changes unguarded" - } - ], - "false_negatives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low" - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low" - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 20, - "total_golden": 3, - "tp": 0, - "fp": 20, - "fn": 3, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "mra-max", - "repo_name": "discourse__discourse-graphite__mra-max__PR7__20260306", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__mra-max__PR7__20260306/pull/1" - }, - "greptile-v4": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low", - "matched_candidate": "desktop/topic-post.scss: In `.topic-meta-data h5 a`, the light-theme color lightness was accidentally changed from `scale-color($primary, $lightness: 30%)` to `scale-color($primary, $lightness: 70%)` in the first argument of `dark-light-choose`, causing an unintended light-mode link color regression", - "confidence": 0.96, - "reasoning": "The candidate flags the same change in `.topic-meta-data h5 a`: the light-theme color went from `scale-color($primary, $lightness: 30%)` to `scale-color($primary, $lightness: 70%)` as part of `dark-light-choose`, implying an unintended inversion/regression in light mode. This matches the golden issue." - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low", - "matched_candidate": "desktop/user.scss: In `.name`, the light-theme lightness was changed from 30% to 50% in the first argument of `dark-light-choose`, unintentionally lightening the text in light mode", - "confidence": 0.86, - "reasoning": "Both point out that in the desktop user stylesheet, the `.name` light-theme value was changed from 30% to 50%, altering/lightening the light theme primary/lightness unexpectedly. The golden comment also mentions broader context ($primary vs $secondary and mobile variant), but the core issue about the 30%\u219250% change in `.name` matches." - } - ], - "false_positives": [ - { - "candidate": "mobile/modal.scss: In `.custom-message-length`, the `dark-light-choose(light, dark)` arguments are swapped (light uses 30% and dark uses 70%), diverging from the desktop rule and producing incorrect colors in both light and dark modes on mobile" - }, - { - "candidate": "mobile/topic-post.scss: In `.media-body h3`, the light-theme lightness was changed from 20% to 50% in the first argument of `dark-light-choose`, making the heading too light and inconsistent with the correct desktop implementation" - } - ], - "false_negatives": [ - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 5, - "total_golden": 3, - "tp": 2, - "fp": 2, - "fn": 1, - "errors_count": 0, - "precision": 0.4, - "recall": 0.6666666666666666, - "tool": "greptile-v4", - "repo_name": "discourse__discourse-graphite__greptile-v4__PR7__20260306", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__greptile-v4__PR7__20260306/pull/1" - }, - "mra-nano": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low" - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low" - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 3, - "tp": 0, - "fp": 0, - "fn": 3, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "mra-nano", - "repo_name": "discourse__discourse-graphite__mra-nano__PR7__20260306", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__mra-nano__PR7__20260306/pull/1" - }, - "mra-ultra": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "dark-light-choose decides output based on global $primary/$secondary brightness, so overriding those variables in nested scopes or different color schemes can yield unexpected or incorrect results instead of using explicit argument values" - }, - { - "candidate": "Inconsistent lightness pair choices (e.g., 80/20 vs 75/25 vs 50/50 vs 60/40) when replacing colors with dark-light-choose may cause uneven contrast/readability across components" - }, - { - "candidate": "Topic list category badge-notification color still uses legacy scale-color($primary, $lightness: 50%) and has not been migrated to the dark-light-choose pattern used elsewhere" - }, - { - "candidate": "No Sass unit tests or visual regression tests were added to validate dark-light-choose outputs across light vs dark themes, risking undetected color regressions" - }, - { - "candidate": "Inline documentation in discourse.scss about default table cell color still references scale-color($primary, $lightness: 50%) but the code now uses dark-light-choose with different lightness values, making the comment misleading/outdated" - } - ], - "false_negatives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low" - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low" - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 5, - "total_golden": 3, - "tp": 0, - "fp": 5, - "fn": 3, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "mra-ultra", - "repo_name": "discourse__discourse-graphite__mra-ultra__PR7__20260306", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__mra-ultra__PR7__20260306/pull/1" - }, - "mra-b": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low", - "matched_candidate": "In mobile/modal.scss, the replacement for .custom-message-length changes the primary lightness from the original 70% to 30% (and uses secondary 70%), which looks reversed/inconsistent with similar changes and may be an error or needs justification", - "confidence": 0.74, - "reasoning": "Both comments flag the same underlying problem: a change in the light theme\u2019s lightness value from 70% to 30%, making the light theme text/colors darker than intended. The candidate cites a different file/selector (mobile/modal.scss vs topic-post.css), but the core issue (70% -> 30% for the light theme) matches the golden concern." - } - ], - "false_positives": [ - { - "candidate": "Need to verify dark-light-choose and dark-light-diff function signatures/parameter order (light-then-dark vs dark-then-light) because many call sites were updated and could now be passing arguments in the wrong order, causing widespread incorrect colors" - }, - { - "candidate": "Cannot find the @function definition of dark-light-choose in the repository, preventing verification of its behavior; if it comes from an external import/gem it should be documented" - }, - { - "candidate": "dark-light-choose appears to have inverted branching logic: it returns the light-theme result when $primary is darker than $secondary (brightness($primary) < brightness($secondary)), which may select incorrect colors for dark themes" - }, - { - "candidate": "Large mechanical replacement across many frequently edited stylesheet files increases merge-conflict risk and the chance of visual regressions" - }, - { - "candidate": "Replacing heatmap/coldmap (coldmap-*) classes to use dark-light-choose with $secondary under some conditions may change the intended quantitative color semantics of those numeric highlight classes" - } - ], - "false_negatives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low" - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 6, - "total_golden": 3, - "tp": 1, - "fp": 5, - "fn": 2, - "errors_count": 0, - "precision": 0.16666666666666666, - "recall": 0.3333333333333333, - "tool": "mra-b", - "repo_name": "discourse__discourse-graphite__mra-b__PR7__20260307", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__mra-b__PR7__20260307/pull/1" - }, - "mra-a": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low", - "matched_candidate": "Automated replacement changed primary-branch lightness values in multiple selectors (e.g., 30%\u219470%) compared to the original, likely introducing unintended visual regressions", - "confidence": 0.84, - "reasoning": "The golden issue is a specific change in `.topic-meta-data h5 a` where the light-theme lightness for `$primary` was altered from 30% to 70% via `dark-light-choose`, causing a major visual inversion. The candidate notes that an automated replacement flipped lightness values (30%\u219470%) in selectors and may cause visual regressions. This captures the same underlying problem (lightness inversion from 30 to 70) though it is less specific about the exact selector and secondary color branch." - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low", - "matched_candidate": "Automated replacement changed primary-branch lightness values in multiple selectors (e.g., 30%\u219470%) compared to the original, likely introducing unintended visual regressions", - "confidence": 0.86, - "reasoning": "The golden issue is specifically that in topic-post.css the light theme $lightness was changed from 70% to 30%, making text darker than intended. The candidate notes an automated replacement swapping lightness values (30%\u219470%) across selectors, implying unintended visual regressions. This captures the same underlying problem of incorrect lightness change leading to visual issues, though it is less specific about the exact file/theme." - } - ], - "false_positives": [ - { - "candidate": "dark-light-choose returns the light-theme result under the dark-theme brightness condition (brightness($primary) < brightness($secondary)), meaning its dark/light selection logic is inverted" - }, - { - "candidate": "Bulk refactor wrapping scale-color($primary, ...) and scale-color($secondary, ...) into dark-light-choose causes primary-derived colors to be used in dark themes (opposite of intended) due to the inverted dark-light-choose logic" - }, - { - "candidate": "Desktop and mobile counterparts for the same element (e.g., .custom-message-length) use inverted lightness pairs, creating inconsistent styling across breakpoints" - }, - { - "candidate": "Some existing call sites intentionally pass dark-light-choose arguments in reversed order (secondary, primary) to compensate for current semantics, but the PR\u2019s bulk replacements don\u2019t preserve this ordering, leading to inconsistent behavior across stylesheets" - }, - { - "candidate": "search-highlight was changed from a single scale-color($primary, $lightness: 25%) to an asymmetric dark-light-choose(25% primary, 75% secondary) that will produce highly visible wrong colors if dark-light-choose logic is inverted" - }, - { - "candidate": "Lack of tests that assert resolved colors for asymmetric dark/light cases (like 25%/75%) means an inverted dark-light-choose implementation could slip through undetected" - } - ], - "false_negatives": [ - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 7, - "total_golden": 3, - "tp": 2, - "fp": 6, - "fn": 1, - "errors_count": 0, - "precision": 0.2857142857142857, - "recall": 0.6666666666666666, - "tool": "mra-a", - "repo_name": "discourse__discourse-graphite__mra-a__PR7__20260307", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__mra-a__PR7__20260307/pull/1" - }, - "entelligence": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low" - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low" - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 3, - "tp": 0, - "fp": 0, - "fn": 3, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "entelligence", - "repo_name": "discourse__discourse-graphite__entelligence__PR7__20260306", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__entelligence__PR7__20260306/pull/1" - }, - "sourcery": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low", - "matched_candidate": "Mobile `.custom-message-length` link color uses inverted lightness values compared to desktop/previous implementation (switches to primary 30% and secondary 70%), potentially reversing intended contrast hierarchy; should align with desktop `dark-light-choose(primary 70%, secondary 30%)` if unintentional", - "confidence": 0.78, - "reasoning": "Golden issue is specifically that in topic-post.css the light theme lightness was changed from 70% to 30%, making text too dark. The candidate flags an inversion of lightness values (primary 30% vs 70%) compared to desktop/previous behavior, implying the same underlying problem of swapped/incorrect lightness leading to unintended contrast (darker than intended). While it mentions mobile `.custom-message-length` and contrast hierarchy, it still identifies the 70% vs 30% inversion at the core." - } - ], - "false_positives": [ - { - "candidate": "Several other theme color rules appear to have inconsistent lightness ratios between light and dark themes (e.g., swapped 20%/80% or 30%/70%), which may be unintentional and should be reviewed for visual balance" - }, - { - "candidate": "Repeated use of `dark-light-choose(scale-color($primary, ...), scale-color($secondary, ...))` suggests missing a Sass helper/mixin to reduce duplication and ease future theme tuning" - } - ], - "false_negatives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low" - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 3, - "total_golden": 3, - "tp": 1, - "fp": 2, - "fn": 2, - "errors_count": 0, - "precision": 0.3333333333333333, - "recall": 0.3333333333333333, - "tool": "sourcery", - "repo_name": "discourse__discourse-graphite__sourcery__PR7__20260308", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__sourcery__PR7__20260308/pull/1" - }, - "cubic-dev": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low", - "matched_candidate": "In app/assets/stylesheets/desktop/topic-post.scss:291, the light-theme $lightness value appears accidentally swapped when converting to dark-light-choose (light changed from 30% to 70%), making embedded-post author link text too light/hard to read; the light/dark arguments should likely be swapped (light 30%, dark 70%) (identified by cubic).", - "confidence": 0.93, - "reasoning": "Both describe the same underlying regression: when changing the link color to use dark-light-choose, the light-theme scale-color($primary) lightness was changed from 30% to 70%, effectively inverting/making it much lighter than intended. The candidate explicitly notes the 30%->70% swap and suggests swapping arguments, matching the golden issue." - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low", - "matched_candidate": "In app/assets/stylesheets/desktop/user.scss:59, converting to dark-light-choose changed the light-theme $lightness from the original 30% to 50%; the first (light-theme) argument should preserve 30% to keep light-theme appearance unchanged (identified by cubic).", - "confidence": 0.86, - "reasoning": "Both point out that during conversion to dark-light-choose, the light theme value (lightness/$primary) was unintentionally changed from 30% to 50% and should be preserved to keep the light theme unchanged. The candidate doesn\u2019t mention the mobile/.name parallel, but it matches the core issue in the desktop file." - } - ], - "false_positives": [ - { - "candidate": "In app/assets/stylesheets/mobile/user.scss:115, converting to dark-light-choose changed the light-theme $lightness for .group-member-info .name from 30% to 50%, making it visually identical to .title; the light-theme argument should remain 30% (identified by cubic)." - }, - { - "candidate": "In app/assets/stylesheets/mobile/topic-post.scss:49, converting to dark-light-choose changed the light-theme $lightness for .topic-map h3 from 20% to 50%, creating a lighter heading in light theme and inconsistency with desktop; light should be 20% and dark should be 80% (identified by cubic)." - }, - { - "candidate": "In app/assets/stylesheets/desktop/topic-list.scss:40, .fa-thumb-tack.unpinned has the same color as .fa-thumb-tack, making the unpinned rule redundant; either give it a distinct color or remove the rule (identified by cubic)." - } - ], - "false_negatives": [ - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 5, - "total_golden": 3, - "tp": 2, - "fp": 3, - "fn": 1, - "errors_count": 0, - "precision": 0.4, - "recall": 0.6666666666666666, - "tool": "cubic-dev", - "repo_name": "discourse__discourse-graphite__cubic-dev__PR7__20260306", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__cubic-dev__PR7__20260306/pull/1" - }, - "mesa": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low", - "matched_candidate": "Desktop topic-post.scss uses lightness values (70% for light, 30% for dark) that appear inverted relative to the original 30% value, causing a significant unintended color shift; may be an accidental parameter swap", - "confidence": 0.93, - "reasoning": "The golden issue is that in `.topic-meta-data h5 a` the light theme lightness changed from 30% to 70% when switching to `dark-light-choose(...)`, effectively inverting/dramatically shifting the intended color. The candidate flags the same inversion (70% for light vs 30% for dark) relative to the original 30% and notes an unintended color shift/parameter swap. Same underlying problem." - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low", - "matched_candidate": "Desktop topic-post.scss uses lightness values (70% for light, 30% for dark) that appear inverted relative to the original 30% value, causing a significant unintended color shift; may be an accidental parameter swap", - "confidence": 0.86, - "reasoning": "Golden says the light theme lightness was changed from 70% to 30%, making text darker than intended. The candidate notes lightness values 70% (light) and 30% (dark) appear inverted/accidentally swapped and cause an unintended color shift. This captures the same underlying problem: incorrect lightness value for the light theme leading to darker text." - } - ], - "false_positives": [ - { - "candidate": "Mobile topic-post.scss changes light theme $lightness from 20% to 50%, which is inconsistent with the similar desktop h3 styling that uses 70%, potentially causing mobile/desktop visual inconsistency" - }, - { - "candidate": "Mobile modal.scss appears to invert intended lightness values by using $lightness: 30% for the light theme where the original used 70%, making text darker in light mode and inconsistent with desktop/modal.scss which keeps 70% for light" - }, - { - "candidate": "Hardcoded lightness percentage pairs are repeated across many files, creating a maintenance burden and making theme adjustments require scattered updates instead of using semantic color tokens" - }, - { - "candidate": "Inconsistent conversion patterns exist: most use dark-light-choose(scale-color($primary, ...), scale-color($secondary, ...)) but at least one uses dark-light-choose($secondary, $primary), increasing cognitive load and error risk" - }, - { - "candidate": "Residual unconverted scale-color($primary) calls likely remain in other stylesheets, risking incomplete dark mode support and inconsistent theme behavior across the app" - } - ], - "false_negatives": [ - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 6, - "total_golden": 3, - "tp": 2, - "fp": 5, - "fn": 1, - "errors_count": 0, - "precision": 0.3333333333333333, - "recall": 0.6666666666666666, - "tool": "mesa", - "repo_name": "discourse__discourse-graphite__mesa__PR7__20260307", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__mesa__PR7__20260307/pull/1" - }, - "codeant": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low", - "matched_candidate": "Lightness mapping between primary and secondary is inconsistent across files and may not have been intentionally chosen or visually validated", - "confidence": 0.74, - "reasoning": "The golden comment flags a specific unintended change: in desktop/user.css the light theme $primary lightness is changed from 30% to 50%, while other edits keep $primary and shift the complement to $secondary for dark; it also notes a similar .name change in mobile. The candidate issue broadly notes inconsistent primary/secondary lightness mapping across files and questions whether it was intentional/validated. This captures the same underlying concern (inconsistent/possibly unintended remapping of primary vs secondary lightness), though it is less specific about the exact 30%->50% change and the mobile .name mention." - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low", - "matched_candidate": "In mobile/modal.scss (.custom-message-length), the new dark-light-choose mapping uses primary lightness 30% where it previously used 70%, which may be an accidental inversion and should be confirmed", - "confidence": 0.74, - "reasoning": "Both comments flag a change in the light theme\u2019s lightness value from 70% to 30%, implying the resulting colors/text will be darker than intended. Although the candidate cites a different file/selector, it describes the same underlying issue: an apparent inversion/mistake in lightness for the light theme." - } - ], - "false_positives": [ - { - "candidate": "Repeated use of the long expression dark-light-choose(scale-color(...), scale-color(...)) across many files increases duplication and risk of typos; should be refactored into a helper function/mixin or semantic variables" - }, - { - "candidate": "Theme-based color changes may reduce text/UI contrast and harm accessibility; contrast ratios should be validated in both light and dark themes" - }, - { - "candidate": "Components using !important or background overlays may render incorrectly after the color changes and require visual testing" - }, - { - "candidate": "Some intended scale-color($primary, ...) instances may have been missed; a repo-wide search should confirm all required replacements were made" - }, - { - "candidate": "Adding many Sass function calls may slightly increase Sass compilation time and should be monitored" - }, - { - "candidate": "dark-light-choose (and related dark-light-diff) must be present and correctly implemented/documented; otherwise Sass compilation or theme selection behavior could be wrong" - } - ], - "false_negatives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 8, - "total_golden": 3, - "tp": 2, - "fp": 6, - "fn": 1, - "errors_count": 0, - "precision": 0.25, - "recall": 0.6666666666666666, - "tool": "codeant", - "repo_name": "discourse__discourse-graphite__codeant__PR7__20260309", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__codeant__PR7__20260309/pull/1" - }, - "kodus": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "Text color choices in theme implementations have incorrect contrast, making text unreadable or too low-contrast in light or dark themes" - }, - { - "candidate": "Incorrect color variables are being used for text/background combinations (e.g., using $primary-derived text color against a $secondary background) leading to poor readability" - }, - { - "candidate": "dark-light-choose arguments are misapplied/inverted; the first argument should be the dark-theme color and the second the light-theme color, otherwise themes get the wrong colors" - }, - { - "candidate": "scale-color lightness adjustments are inappropriate (e.g., lightening in light theme), producing insufficient contrast between text and background" - }, - { - "candidate": "The provided code uses identical dark and light theme values in dark-light-choose, preventing proper theme-specific contrast tuning" - } - ], - "false_negatives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low" - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low" - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 5, - "total_golden": 3, - "tp": 0, - "fp": 5, - "fn": 3, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "kodus", - "repo_name": "discourse__discourse-graphite__kodus__PR7__20260309", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__kodus__PR7__20260309/pull/1" - }, - "claude-code": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low", - "matched_candidate": "desktop/topic-post.scss: `.topic-meta-data h5 a` light-theme lightness changed from 30% to 70%, likely causing a light-theme visual regression", - "confidence": 0.94, - "reasoning": "The candidate flags the same selector (.topic-meta-data h5 a) and the same change in the light theme lightness value from 30% to 70%, noting it could cause a visual regression. This matches the golden issue about the dramatic inversion in light-theme lightness." - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low", - "matched_candidate": "desktop/user.scss and mobile/user.scss: `.group-member-info .name` light-theme lightness changed from 30% to 50%, likely an unintended change from the mechanical transformation", - "confidence": 0.86, - "reasoning": "Both point out an unintended light-theme lightness change from 30% to 50% and note it applies in both desktop and mobile variants (the .name selector). The golden also mentions $primary specifically and the broader pattern of moving complement to $secondary for dark, but the core issue (30%->50% in light theme, likely unintended, including mobile .name) is the same." - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low", - "matched_candidate": "desktop/topic-post.scss: `.topic-meta-data h5 a` light-theme lightness changed from 30% to 70%, likely causing a light-theme visual regression", - "confidence": 0.72, - "reasoning": "Both comments refer to a light-theme lightness value being changed between 70% and 30% in the topic-post styling, causing an unintended visual change. Although the candidate describes the direction as 30% -> 70% (opposite of the golden\u2019s 70% -> 30%), it is still flagging the same underlying issue: the light-theme lightness value is incorrect/regressed due to swapping 70% and 30%." - } - ], - "false_positives": [ - { - "candidate": "mobile/modal.scss: `.custom-message-length` light-theme lightness changed from 70% to 30% (desktop equivalent keeps 70%), creating inconsistent styling and potential regression" - }, - { - "candidate": "mobile/topic-post.scss: `h3` light-theme lightness changed from 20% to 50% while desktop keeps 20%, causing inconsistent appearance across platforms" - }, - { - "candidate": "PR description is uninformative ('Test 7'), making it difficult to assess intent behind the styling changes" - }, - { - "candidate": "No automated tests/coverage for these CSS changes, increasing risk of undetected visual regressions and warranting manual visual spot-checking" - } - ], - "false_negatives": [], - "errors": [], - "total_candidates": 6, - "total_golden": 3, - "tp": 3, - "fp": 4, - "fn": 0, - "errors_count": 0, - "precision": 0.5, - "recall": 1.0, - "tool": "claude-code", - "repo_name": "discourse__discourse-graphite__claude-code__PR7__20260310", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__claude-code__PR7__20260310/pull/1" - }, - "vercel": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low" - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low" - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 3, - "tp": 0, - "fp": 0, - "fn": 3, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "vercel", - "repo_name": "discourse__discourse-graphite__vercel__PR7__20260310", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__vercel__PR7__20260310/pull/1" - }, - "bito": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low" - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low" - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 3, - "tp": 0, - "fp": 0, - "fn": 3, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "bito", - "repo_name": "discourse__discourse-graphite__bito__PR7__20260310", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__bito__PR7__20260310/pull/1" - }, - "sentry": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low" - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low" - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 3, - "tp": 0, - "fp": 0, - "fn": 3, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "sentry", - "repo_name": "discourse__discourse-graphite__sentry__PR7__20260310", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__sentry__PR7__20260310/pull/1" - }, - "linearb": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "/gs review" - } - ], - "false_negatives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low" - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low" - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 1, - "total_golden": 3, - "tp": 0, - "fp": 1, - "fn": 3, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "linearb", - "repo_name": "discourse__discourse-graphite__linearb__PR7__20260309", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__linearb__PR7__20260309/pull/1" - }, - "kodus-v2": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low", - "matched_candidate": "During the dark mode migration, the `dark-light-choose` conversion appears to have swapped the intended lightness values (primary should remain 30% and secondary should be 70% per convention), resulting in incorrect `dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%))` on line 291.", - "confidence": 0.93, - "reasoning": "Both point to the same underlying issue in `.topic-meta-data h5 a`: the migration to `dark-light-choose(...)` changed the light-theme lightness from 30% to 70% (and effectively inverted/swapped the intended values), leading to an incorrect color choice." - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low", - "matched_candidate": "During the dark mode migration, the `dark-light-choose` conversion appears to have swapped the intended lightness values (primary should remain 30% and secondary should be 70% per convention), resulting in incorrect `dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%))` on line 291.", - "confidence": 0.74, - "reasoning": "Both comments flag that the migration altered the intended light-theme primary lightness: golden says $primary was changed from 30% to 50% (and should likely stay 30%) while other changes keep $primary and move the complement to $secondary for dark; the candidate similarly says the conversion swapped/incorrectly set lightness values and that primary should remain 30% (with secondary being the complement). Candidate is more specific about a 30/70 swap and doesn\u2019t mention the 50% or the mobile .name, but it targets the same underlying problem: primary lightness got changed incorrectly during dark-mode conversion." - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low", - "matched_candidate": "During the dark mode migration, the `dark-light-choose` conversion appears to have swapped the intended lightness values (primary should remain 30% and secondary should be 70% per convention), resulting in incorrect `dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%))` on line 291.", - "confidence": 0.86, - "reasoning": "Golden issue: in topic-post.css the light theme lightness was changed from 70% to 30%, making text too dark. Candidate issue also flags that the lightness values were swapped in a dark-light-choose conversion, specifically pointing out 70% vs 30% being reversed, which is the same underlying problem (incorrect light-theme lightness leading to wrong brightness)." - } - ], - "false_positives": [], - "false_negatives": [], - "errors": [], - "total_candidates": 2, - "total_golden": 3, - "tp": 3, - "fp": 0, - "fn": 0, - "errors_count": 0, - "precision": 1.5, - "recall": 1.0, - "tool": "kodus-v2", - "repo_name": "discourse__discourse-graphite__kodus-v2__PR7__20260312", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__kodus-v2__PR7__20260312/pull/1" - }, - "qodo-v2-2": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low", - "matched_candidate": "In desktop/topic-post.scss, .embedded-posts .topic-meta-data h5 a uses an inverted dark-light-choose() mapping (70%/30% instead of the established 30%/70%), making embedded-post metadata links inconsistent with common link styling", - "confidence": 0.9, - "reasoning": "Both point to the same underlying change in .topic-meta-data h5 a: the switch from a single scale-color($primary, $lightness: 30%) to a dark-light-choose() where the light-theme lightness became 70% (with 30% on the other branch), effectively inverting/altering the intended lightness and causing inconsistent styling." - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low", - "matched_candidate": "In desktop/topic-post.scss, .embedded-posts .topic-meta-data h5 a uses an inverted dark-light-choose() mapping (70%/30% instead of the established 30%/70%), making embedded-post metadata links inconsistent with common link styling", - "confidence": 0.78, - "reasoning": "Both comments point to the same underlying problem: the light theme lightness values were swapped (using 30% where 70% was intended), resulting in darker-than-expected text/link color. The candidate describes it as an inverted dark-light mapping (70/30 vs 30/70) for embedded-post metadata links, which is the same lightness inversion issue highlighted by the golden comment." - } - ], - "false_positives": [ - { - "candidate": "In mobile/modal.scss, .custom-message-length calls dark-light-choose() with swapped arguments, causing incorrect colors in both light and dark themes and diverging from desktop/modal.scss behavior" - }, - { - "candidate": "In mobile/topic-post.scss, .topic-map h3 was changed to the same 50%/50% dark-light-choose mapping as .topic-map h4, flattening heading hierarchy and diverging from desktop where h3 and h4 use distinct mappings" - } - ], - "false_negatives": [ - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 3, - "total_golden": 3, - "tp": 2, - "fp": 2, - "fn": 1, - "errors_count": 0, - "precision": 0.6666666666666666, - "recall": 0.6666666666666666, - "tool": "qodo-v2-2", - "repo_name": "discourse__discourse-graphite__qodo-v2-2__PR7__20260312", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-v2-2__PR7__20260312/pull/1" - }, - "qodo-v22": { - "skipped": false, - "true_positives": [], - "false_positives": [ - { - "candidate": "Inconsistent `.custom-message-length` hint text color between `mobile/modal.scss` and `desktop/modal.scss` due to inverted `dark-light-choose(...)` light/dark argument mapping, causing different contrast for the same theme across platforms" - }, - { - "candidate": "Mobile `.topic-map h3` in `mobile/topic-post.scss` uses the same `dark-light-choose(...)` 50%/50% color mapping as `.topic-map h4`, collapsing header hierarchy and diverging from desktop where `h3` uses a distinct 20%/80% mapping" - } - ], - "false_negatives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low" - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low" - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 2, - "total_golden": 3, - "tp": 0, - "fp": 2, - "fn": 3, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "qodo-v22", - "repo_name": "discourse__discourse-graphite__qodo-v22__PR7__20260312", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-v22__PR7__20260312/pull/1" - }, - "qodo-extended": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low", - "matched_candidate": "In desktop/topic-post.scss, .topic-meta-data h5 a was changed to use a much lighter $primary (70% lightness) for the light theme via dark-light-choose(), likely a typo that reduces link contrast and unintentionally changes the default (light) appearance", - "confidence": 0.93, - "reasoning": "Both describe the same change in .topic-meta-data h5 a: the light-theme color went from scale-color($primary, $lightness: 30%) to using dark-light-choose with scale-color($primary, $lightness: 70%), which is a large/lightening shift likely unintended and affecting contrast/appearance." - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low", - "matched_candidate": "In mobile/modal.scss, .custom-message-length now uses a much darker $primary (30% lightness) for the light theme via dark-light-choose(), unintentionally altering light-theme hint text prominence/contrast instead of only adding a dark-theme alternative", - "confidence": 0.74, - "reasoning": "Both comments flag the same underlying problem: a change in the light theme\u2019s lightness value from 70% to 30%, making text/hint colors much darker than intended. The candidate cites a different file/selector (mobile/modal.scss, .custom-message-length) than the golden (topic-post.css), but the core issue\u2014incorrect 30% lightness applied to the light theme\u2014is the same." - } - ], - "false_positives": [ - { - "candidate": "In mobile/topic-post.scss, .topic-map h3 now uses 50%/50% lightness for light/dark themes via dark-light-choose(), changing heading emphasis and creating an inconsistency with desktop topic-map heading styling (20%/80%), likely accidental" - } - ], - "false_negatives": [ - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 3, - "total_golden": 3, - "tp": 2, - "fp": 1, - "fn": 1, - "errors_count": 0, - "precision": 0.6666666666666666, - "recall": 0.6666666666666666, - "tool": "qodo-extended", - "repo_name": "discourse__discourse-graphite__qodo-extended__PR7__20260312", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-extended__PR7__20260312/pull/1" - }, - "cubic-v2": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low", - "matched_candidate": "In app/assets/stylesheets/mobile/modal.scss (.custom-message-length), the dark-light-choose conversion reverses the intended light/dark scale-color lightness percentages; it should preserve 70% for the light theme and use 30% for the dark theme (using $secondary) to match desktop styling (identified by cubic).", - "confidence": 0.78, - "reasoning": "Both point to the same underlying problem: the light theme\u2019s scale-color lightness value was changed/reversed from the original 70% to 30%, making the light-theme text darker than intended. The candidate adds file/context details but describes the same lightness-percentage inversion issue." - } - ], - "false_positives": [ - { - "candidate": "In app/assets/stylesheets/desktop/topic-post.scss (nav.post-controls a), the dark-light-choose conversion swaps the light and dark lightness values, regressing the existing light-theme color instead of preserving it (identified by cubic)." - }, - { - "candidate": "In app/assets/stylesheets/mobile/topic-post.scss (.topic-map h3), the light-theme lightness was changed from 20% to 50%, altering the light-theme appearance; it should keep 20% for light and use 80% for dark to match the desktop selector (identified by cubic)." - } - ], - "false_negatives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low" - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 3, - "total_golden": 3, - "tp": 1, - "fp": 2, - "fn": 2, - "errors_count": 0, - "precision": 0.3333333333333333, - "recall": 0.3333333333333333, - "tool": "cubic-v2", - "repo_name": "discourse__discourse-graphite__cubic-v2__PR7__20260313", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__cubic-v2__PR7__20260313/pull/1" - }, - "qodo-extended-summary": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low", - "matched_candidate": "In `desktop/topic-post.scss` for `.embedded-posts` link text, the light-theme lightness value in `dark-light-choose` was accidentally changed from 30% to 70%, causing a light-theme visual regression", - "confidence": 0.74, - "reasoning": "Both describe the same underlying regression: a link color in SCSS was changed to use `dark-light-choose(...)` and the light-theme `scale-color($primary, $lightness: ...)` value was altered from 30% to 70%, inverting/lightening the light theme dramatically. The selector/file differ (`.topic-meta-data h5 a` vs `.embedded-posts`), but the specific mistake (30% -> 70% for light theme in `dark-light-choose`) matches the golden issue." - }, - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low", - "matched_candidate": "In `desktop/user.scss` and `mobile/user.scss`, `.group-member-info .name` was changed to use the title color scheme (50%/50%) instead of the intended name scheme (30%/70%), reducing name prominence and collapsing the visual hierarchy", - "confidence": 0.78, - "reasoning": "The golden comment flags an unintended change in the light theme where $primary shifts from 30% to 50% (and notes a similar .name change in mobile). The candidate focuses specifically on the .name selector in both desktop and mobile being changed from a 30/70 scheme to 50/50, which is the same underlying concern about the primary/title color balance being altered and reducing hierarchy. It does not mention $primary explicitly, but it targets the same problematic change the golden highlights (at least the .name aspect)." - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low", - "matched_candidate": "In `mobile/modal.scss` for `.custom-message-length`, the `dark-light-choose` light/dark arguments are swapped (light uses 30% and dark uses 70% instead of the intended 70%/30%), making text too dark in light theme on mobile", - "confidence": 0.74, - "reasoning": "Both point to the same underlying problem: the light theme is using 30% lightness instead of the intended 70%, resulting in text that is too dark. The candidate describes it as swapped light/dark arguments in a different file/selector, but the core issue (incorrect lightness value for light theme) matches the golden comment." - } - ], - "false_positives": [ - { - "candidate": "Mobile `.topic-map h3` color contrast/emphasis regressed by changing from the intended 20%/80% light/dark scheme to 50%/50%, reducing heading emphasis and contrast on mobile" - } - ], - "false_negatives": [], - "errors": [], - "total_candidates": 4, - "total_golden": 3, - "tp": 3, - "fp": 1, - "fn": 0, - "errors_count": 0, - "precision": 0.75, - "recall": 1.0, - "tool": "qodo-extended-summary", - "repo_name": "discourse__discourse-graphite__qodo-extended-summary__PR7__20260313", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-extended-summary__PR7__20260313/pull/1" - }, - "macroscope": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In .topic-meta-data h5 a, the original code had color: scale-color($primary, $lightness: 30%) but was changed to dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)). The lightness for the light theme changed from 30% to 70%, which is a dramatic inversion", - "severity": "Low", - "matched_candidate": "In desktop/topic-post.scss, the dark-light-choose call for .topic-meta-data h5 a uses lightness: 70% for the light theme instead of the intended/original 30%, making embedded post links too light and less visible in light themes", - "confidence": 0.93, - "reasoning": "The candidate flags the same change in .topic-meta-data h5 a: the light-theme color lightness was altered from 30% to 70% within dark-light-choose, causing links to become too light in light mode. This matches the golden issue about the dramatic inversion in lightness for the light theme." - } - ], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": "This change for desktop/user.css changes $primary from 30% to 50% for the light theme; most other changes preserve the original $primary value and move the complement to $secondary for dark. Consider reviewing this (also applies to a similar .name change in the mobile variant).", - "severity": "Low" - }, - { - "golden_comment": "In topic-post.css the original code used $lightness: 70% but the replacement uses $lightness: 30% for the light theme. This makes the text significantly darker than intended.", - "severity": "Low" - } - ], - "errors": [], - "total_candidates": 1, - "total_golden": 3, - "tp": 1, - "fp": 0, - "fn": 2, - "errors_count": 0, - "precision": 1.0, - "recall": 0.3333333333333333, - "tool": "macroscope", - "repo_name": "discourse__discourse-graphite__macroscope__PR7__20260309", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__macroscope__PR7__20260309/pull/1" - } - }, - "https://github.com/ai-code-review-evaluation/discourse-graphite/pull/8": { - "gemini": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "HTTP method mismatch in .remove_member - test uses PUT but remove_member action expects DELETE", - "severity": "Medium", - "matched_candidate": "Test uses PUT to hit remove_member but the route is DELETE; test should use xhr :delete", - "confidence": 0.97, - "reasoning": "Both the golden comment and the candidate issue describe the same problem: the test is using PUT to call remove_member, but the route/action expects DELETE, so the test should use a DELETE request." - } - ], - "false_positives": [ - { - "candidate": "Backend controller reads group ID from params[:group_id] but the route provides it in params[:id], causing the action to fail; should use :id" - }, - { - "candidate": "Frontend pagination totalPages calculation is wrong when user_count is an exact multiple of limit (e.g., 50/50 yields 2 instead of 1); should use Math.ceil(user_count/limit)" - }, - { - "candidate": "addMembers action leaves a TODO to clear the input; usernames field should be cleared after adding members for better UX" - }, - { - "candidate": "CSS uses !important, which hurts maintainability/debugging; should avoid it by adjusting selector specificity or structure" - }, - { - "candidate": "add_members backend action performs one DB query per username (N+1); should fetch users in a single query (e.g., User.where(username: ...))" - } - ], - "false_negatives": [ - { - "golden_comment": " The findMembers() call is now asynchronous and unhandled. The controller may not have member data immediately available, creating a race condition.", - "severity": "High" - }, - { - "golden_comment": "In the next action, capping the next offset at user_count can produce an empty page (e.g., total equal to limit results in offset == total, showing 2/2 with no members). This can cause confusing UX on the last page.", - "severity": "Medium" - } - ], - "errors": [], - "total_candidates": 6, - "total_golden": 3, - "tp": 1, - "fp": 5, - "fn": 2, - "errors_count": 0, - "precision": 0.16666666666666666, - "recall": 0.3333333333333333, - "tool": "gemini", - "repo_name": "discourse__discourse-graphite__gemini__PR8__20260128", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__gemini__PR8__20260128/pull/1" - }, - "claude": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In the next action, capping the next offset at user_count can produce an empty page (e.g., total equal to limit results in offset == total, showing 2/2 with no members). This can cause confusing UX on the last page.", - "severity": "Medium", - "matched_candidate": "Pagination calculation error in admin-group.js totalPages formula can return an extra empty page", - "confidence": 0.78, - "reasoning": "Golden issue is about capping the next offset at user_count leading to an empty last page when offset == total (confusing UX). The candidate mentions a pagination calculation error where totalPages can return an extra empty page. Both describe the same underlying symptom: pagination logic producing an extra/empty page at the end due to boundary/rounding issues." - }, - { - "golden_comment": "HTTP method mismatch in .remove_member - test uses PUT but remove_member action expects DELETE", - "severity": "Medium", - "matched_candidate": "Test spec uses the wrong HTTP method for remove_member (uses PUT/xhr :put but the route expects DELETE/xhr :delete), so the test may not exercise the intended behavior", - "confidence": 0.98, - "reasoning": "Both the golden comment and the candidate issue point out the same problem: the test uses PUT for the remove_member action, but the route/action expects DELETE, meaning the test isn't hitting the intended behavior." - } - ], - "false_positives": [ - { - "candidate": "Removed TODO comment about fixing the root cause of group member truncation without clearly addressing whether the underlying bug was actually fixed" - }, - { - "candidate": "Introduced a new TODO in admin-group.js to clear the input after adding members, but the functionality is not implemented" - }, - { - "candidate": "Regression: groups_controller.rb now paginates (limits) members for non-automatic groups, reintroducing truncation that a recent commit explicitly prevented" - }, - { - "candidate": "group.js function returns undefined instead of a promise when name is empty, causing inconsistent return type and breaking promise chaining" - }, - { - "candidate": "Test suite change removed a pending pagination test, meaning the truncation regression would not be caught by tests" - } - ], - "false_negatives": [ - { - "golden_comment": " The findMembers() call is now asynchronous and unhandled. The controller may not have member data immediately available, creating a race condition.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 7, - "total_golden": 3, - "tp": 2, - "fp": 5, - "fn": 1, - "errors_count": 0, - "precision": 0.2857142857142857, - "recall": 0.6666666666666666, - "tool": "claude", - "repo_name": "discourse__discourse-graphite__claude__PR8__20260127", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__claude__PR8__20260127/pull/1" - }, - "graphite": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": " The findMembers() call is now asynchronous and unhandled. The controller may not have member data immediately available, creating a race condition.", - "severity": "High" - }, - { - "golden_comment": "In the next action, capping the next offset at user_count can produce an empty page (e.g., total equal to limit results in offset == total, showing 2/2 with no members). This can cause confusing UX on the last page.", - "severity": "Medium" - }, - { - "golden_comment": "HTTP method mismatch in .remove_member - test uses PUT but remove_member action expects DELETE", - "severity": "Medium" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 3, - "tp": 0, - "fp": 0, - "fn": 3, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "graphite", - "repo_name": "discourse__discourse-graphite__graphite__PR8__20260123", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__graphite__PR8__20260123/pull/1" - }, - "qodo": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In the next action, capping the next offset at user_count can produce an empty page (e.g., total equal to limit results in offset == total, showing 2/2 with no members). This can cause confusing UX on the last page.", - "severity": "Medium", - "matched_candidate": "`next` pagination action clamps `offset` to `user_count`, which can produce an empty page; last valid offset should typically be `user_count - limit` (or last page start)", - "confidence": 0.95, - "reasoning": "Both describe the same pagination bug: clamping the next offset to user_count can make offset==total, yielding an empty last page and confusing UX; candidate also suggests correct last-page offset logic." - } - ], - "false_positives": [ - { - "candidate": "Client-controlled `limit` param in `GroupsController#members` is not capped, enabling potentially very large queries/responses (DoS risk)" - }, - { - "candidate": "`members` action uses `offset = params[:offset].to_i` without a sane default/validation, which can lead to unexpected pagination behavior" - }, - { - "candidate": "`add_members` splits comma-separated usernames without trimming whitespace, leading to failed lookups or surprising behavior for inputs like `\" bob,alice \"`" - }, - { - "candidate": "`add_members` does not deduplicate usernames, causing unnecessary repeated lookups/add attempts" - }, - { - "candidate": "`add_members` does not handle empty tokens from the split (e.g., trailing commas), causing unnecessary lookups" - }, - { - "candidate": "`remove_member` deletes membership via `group.users.delete(user_id)` directly; may bypass intended group add/remove APIs (callbacks/validations) and should be confirmed as intended" - } - ], - "false_negatives": [ - { - "golden_comment": " The findMembers() call is now asynchronous and unhandled. The controller may not have member data immediately available, creating a race condition.", - "severity": "High" - }, - { - "golden_comment": "HTTP method mismatch in .remove_member - test uses PUT but remove_member action expects DELETE", - "severity": "Medium" - } - ], - "errors": [], - "total_candidates": 8, - "total_golden": 3, - "tp": 1, - "fp": 6, - "fn": 2, - "errors_count": 0, - "precision": 0.125, - "recall": 0.3333333333333333, - "tool": "qodo", - "repo_name": "discourse__discourse-graphite__qodo__PR8__20260123", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo__PR8__20260123/pull/1" - }, - "bugbot": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In the next action, capping the next offset at user_count can produce an empty page (e.g., total equal to limit results in offset == total, showing 2/2 with no members). This can cause confusing UX on the last page.", - "severity": "Medium", - "matched_candidate": "`totalPages` uses `Math.floor(user_count / limit) + 1`, causing an off-by-one extra page when `user_count` is a multiple of `limit`; should use `Math.ceil(user_count / limit)`", - "confidence": 0.86, - "reasoning": "Both point to the same underlying pagination edge case when total/user_count is an exact multiple of limit: the logic creates an extra/empty last page (offset == total). The candidate frames it as an off-by-one in totalPages calculation (floor+1 vs ceil), which is the same root cause as the golden comment\u2019s empty page/UX issue." - } - ], - "false_positives": [ - { - "candidate": "`remove_member` calls `group.users.delete(user_id)` with an integer ID, but the association `delete` expects a User object (or should use the Group model's `remove(user)`), so the user may not be removed from the group" - } - ], - "false_negatives": [ - { - "golden_comment": " The findMembers() call is now asynchronous and unhandled. The controller may not have member data immediately available, creating a race condition.", - "severity": "High" - }, - { - "golden_comment": "HTTP method mismatch in .remove_member - test uses PUT but remove_member action expects DELETE", - "severity": "Medium" - } - ], - "errors": [], - "total_candidates": 3, - "total_golden": 3, - "tp": 1, - "fp": 1, - "fn": 2, - "errors_count": 0, - "precision": 0.3333333333333333, - "recall": 0.3333333333333333, - "tool": "bugbot", - "repo_name": "discourse__discourse-graphite__bugbot__PR8__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__bugbot__PR8__20260122/pull/1" - }, - "copilot": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In the next action, capping the next offset at user_count can produce an empty page (e.g., total equal to limit results in offset == total, showing 2/2 with no members). This can cause confusing UX on the last page.", - "severity": "Medium", - "matched_candidate": "Pagination next action clamps offset to user_count, allowing offset past the last valid page start and producing empty result sets; should clamp to lastPageStart = max(user_count - limit, 0)", - "confidence": 0.95, - "reasoning": "Both describe the same pagination bug: clamping the next offset to user_count can yield offset==total (or beyond the last valid page start), resulting in an empty last page and confusing UX. The candidate also suggests the correct fix (clamp to lastPageStart)." - } - ], - "false_positives": [ - { - "candidate": "create action forces group.visible to false when the visible param is omitted, overriding the model default (visible true) and unintentionally creating invisible groups" - }, - { - "candidate": "create action ignores alias_level even though the client sends it, so alias level is not persisted" - }, - { - "candidate": "update action unconditionally sets group.visible from a string comparison, so omitting visible in a partial update resets it to false; should only update when the param key is present/normalize booleans" - }, - { - "candidate": "add_members uses group.add(user) (persisting join rows immediately) and then relies on group.save for success, so it can return an error even though memberships were already added; should avoid group.save and make join changes atomic (e.g., transaction)" - }, - { - "candidate": "remove_member uses group.users.delete(user_id) on a has_many :through, which can bypass GroupUser destroy callbacks and break the user_count counter_cache; should destroy join rows via group.group_users...destroy_all (and handle primary_group cleanup)" - }, - { - "candidate": "findMembers returns undefined when name is empty, but callers treat it like a promise; should consistently return a resolved promise (or a consistent null contract)" - }, - { - "candidate": "A database query is executed inside a loop and should be hoisted to a single query outside the loop (e.g., fetch users by username once and index them)" - } - ], - "false_negatives": [ - { - "golden_comment": " The findMembers() call is now asynchronous and unhandled. The controller may not have member data immediately available, creating a race condition.", - "severity": "High" - }, - { - "golden_comment": "HTTP method mismatch in .remove_member - test uses PUT but remove_member action expects DELETE", - "severity": "Medium" - } - ], - "errors": [], - "total_candidates": 10, - "total_golden": 3, - "tp": 1, - "fp": 7, - "fn": 2, - "errors_count": 0, - "precision": 0.1, - "recall": 0.3333333333333333, - "tool": "copilot", - "repo_name": "discourse__discourse-graphite__copilot__PR8__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__copilot__PR8__20260122/pull/1" - }, - "augment": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In the next action, capping the next offset at user_count can produce an empty page (e.g., total equal to limit results in offset == total, showing 2/2 with no members). This can cause confusing UX on the last page.", - "severity": "Medium", - "matched_candidate": "Over-counted `totalPages` allows `next` to set `offset` to `user_count`, producing an empty last page and incorrect `currentPage/totalPages` display", - "confidence": 0.9, - "reasoning": "Both describe the same underlying pagination bug: the next action can set offset to user_count (e.g., when total==limit), resulting in an empty last page and confusing/incorrect page display. Candidate adds detail about totalPages being over-counted but still targets the same empty-page issue." - }, - { - "golden_comment": "HTTP method mismatch in .remove_member - test uses PUT but remove_member action expects DELETE", - "severity": "Medium", - "matched_candidate": "Controller spec uses `xhr :put` for `remove_member` even though the route is `DELETE /admin/groups/:group_id/members`, so the spec may pass while the real route/verb behaves differently", - "confidence": 0.95, - "reasoning": "Both point out the same underlying problem: the test/spec is issuing a PUT request for `remove_member` while the actual route/action expects a DELETE, creating an HTTP verb mismatch." - } - ], - "false_positives": [ - { - "candidate": "`automatic` may not be in scope in the group member item view template; if it resolves against the member context it can be `undefined`, causing the remove control to appear for automatic groups" - }, - { - "candidate": "If the remove control is shown for automatic groups, clicking remove may trigger a failing request (likely 422) with no UI error handling" - }, - { - "candidate": "`offset = params[:offset].to_i` can yield negative offsets and `limit` can be <= 0, leading to invalid/raising ActiveRecord `.limit(...).offset(...)` queries; inputs should be clamped/sanitized" - } - ], - "false_negatives": [ - { - "golden_comment": " The findMembers() call is now asynchronous and unhandled. The controller may not have member data immediately available, creating a race condition.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 6, - "total_golden": 3, - "tp": 2, - "fp": 3, - "fn": 1, - "errors_count": 0, - "precision": 0.3333333333333333, - "recall": 0.6666666666666666, - "tool": "augment", - "repo_name": "discourse__discourse-graphite__augment__PR8__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__augment__PR8__20260122/pull/1" - }, - "propel": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In the next action, capping the next offset at user_count can produce an empty page (e.g., total equal to limit results in offset == total, showing 2/2 with no members). This can cause confusing UX on the last page.", - "severity": "Medium", - "matched_candidate": "Pagination total pages calculation uses `Math.floor(user_count / limit) + 1`, which overcounts when `user_count` is an exact multiple of `limit`, leaving the \u201cnext\u201d button enabled and allowing navigation to an empty page", - "confidence": 0.93, - "reasoning": "Both describe the same pagination edge case: when total users is an exact multiple of the page size, the logic allows advancing to an offset equal to the total, resulting in an empty last page and confusing UX (e.g., next enabled / overcounted pages)." - } - ], - "false_positives": [ - { - "candidate": "Add/remove member routes are defined outside a `member` block, generating `/admin/groups/members` without a group id, while the Ember client calls `/admin/groups/${id}/members.json`, causing 404s" - }, - { - "candidate": "In `add_members`, the controller requires `params[:group_id]` but the client only provides the group id in the URL (`params[:id]`), triggering `ActionController::ParameterMissing` and returning 400" - }, - { - "candidate": "In `remove_member`, the controller requires `params[:group_id]` but the client only provides the group id in the URL (`params[:id]`), triggering `ActionController::ParameterMissing` and returning 400" - }, - { - "candidate": "Server-side username parsing for adding members does not trim whitespace from comma-separated usernames (e.g. \"bob, alice\"), causing `User.find_by_username` to miss users and silently skip adding them" - } - ], - "false_negatives": [ - { - "golden_comment": " The findMembers() call is now asynchronous and unhandled. The controller may not have member data immediately available, creating a race condition.", - "severity": "High" - }, - { - "golden_comment": "HTTP method mismatch in .remove_member - test uses PUT but remove_member action expects DELETE", - "severity": "Medium" - } - ], - "errors": [], - "total_candidates": 5, - "total_golden": 3, - "tp": 1, - "fp": 4, - "fn": 2, - "errors_count": 0, - "precision": 0.2, - "recall": 0.3333333333333333, - "tool": "propel", - "repo_name": "discourse__discourse-graphite__propel__PR8__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__propel__PR8__20260122/pull/1" - }, - "baz": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In the next action, capping the next offset at user_count can produce an empty page (e.g., total equal to limit results in offset == total, showing 2/2 with no members). This can cause confusing UX on the last page.", - "severity": "Medium", - "matched_candidate": "next pagination clamps offset to user_count, allowing offset==user_count on the last page and causing the backend to return zero members (blank page)", - "confidence": 0.95, - "reasoning": "The candidate describes the same pagination bug: clamping the next offset to user_count can result in offset == user_count on the last page, yielding an empty/blank page (zero members), matching the golden comment\u2019s empty page/UX concern." - } - ], - "false_positives": [ - { - "candidate": "Group create action never assigns alias_level from params before saving, so non-default alias_level set in the UI is silently dropped on create" - }, - { - "candidate": "members endpoint always paginates with limit/offset but the public group/members template has no pagination controls, making members beyond the first page unreachable for non-automatic groups" - } - ], - "false_negatives": [ - { - "golden_comment": " The findMembers() call is now asynchronous and unhandled. The controller may not have member data immediately available, creating a race condition.", - "severity": "High" - }, - { - "golden_comment": "HTTP method mismatch in .remove_member - test uses PUT but remove_member action expects DELETE", - "severity": "Medium" - } - ], - "errors": [], - "total_candidates": 4, - "total_golden": 3, - "tp": 1, - "fp": 2, - "fn": 2, - "errors_count": 0, - "precision": 0.25, - "recall": 0.3333333333333333, - "tool": "baz", - "repo_name": "discourse__discourse-graphite__baz__PR8__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__baz__PR8__20260122/pull/1" - }, - "coderabbit": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In the next action, capping the next offset at user_count can produce an empty page (e.g., total equal to limit results in offset == total, showing 2/2 with no members). This can cause confusing UX on the last page.", - "severity": "Medium", - "matched_candidate": "next() pagination clamps offset to user_count, allowing offset == user_count and resulting in navigation to an empty members page; should clamp to the last valid offset", - "confidence": 0.94, - "reasoning": "Both describe the same pagination bug: clamping the next offset to user_count can make offset == total/user_count, which yields an empty last page and confusing UX. The candidate also suggests clamping to the last valid offset, aligning with the golden issue." - }, - { - "golden_comment": "HTTP method mismatch in .remove_member - test uses PUT but remove_member action expects DELETE", - "severity": "Medium", - "matched_candidate": "admin/groups_controller_spec remove_member test uses PUT instead of the DELETE verb required by the route, so it doesn't validate correct HTTP method usage", - "confidence": 0.98, - "reasoning": "The candidate issue states that the remove_member spec uses PUT instead of the DELETE verb required by the route/action, which is the same HTTP method mismatch described in the golden comment." - } - ], - "false_positives": [ - { - "candidate": "totalPages computed property has an off-by-one error when user_count is an exact multiple of limit due to using Math.floor(...)+1 instead of Math.ceil(...)" - }, - { - "candidate": "add_members splits usernames by comma without trimming whitespace or rejecting blank entries, causing valid usernames with spaces (or empty items) to be skipped during lookup" - }, - { - "candidate": "groups_controller pagination uses limit and offset directly from params without bounds checking, allowing negative or excessively large values that can cause heavy/unbounded or odd queries" - }, - { - "candidate": "group/members.hbs template guard checks model but iterates over members, so headers can render without rows when members is undefined/empty; guard should check members" - }, - { - "candidate": "group/members.hbs contains a stray closing tag without a matching opening tag, producing invalid HTML and potential rendering issues" - }, - { - "candidate": "routes define add/remove member endpoints as member routes passing :id, but controller actions require :group_id, causing missing parameter errors at runtime" - }, - { - "candidate": "admin_base.scss hardcodes colors (#333, #aaa) instead of using SCSS variables, reducing theme consistency and potentially impacting accessibility across color schemes" - }, - { - "candidate": "admin-group.js addMembers action does not clear the usernames input after adding, increasing risk of accidental repeated submissions" - } - ], - "false_negatives": [ - { - "golden_comment": " The findMembers() call is now asynchronous and unhandled. The controller may not have member data immediately available, creating a race condition.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 10, - "total_golden": 3, - "tp": 2, - "fp": 8, - "fn": 1, - "errors_count": 0, - "precision": 0.2, - "recall": 0.6666666666666666, - "tool": "coderabbit", - "repo_name": "discourse__discourse-graphite__coderabbit__PR8__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__coderabbit__PR8__20260122/pull/1" - }, - "greptile": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "HTTP method mismatch in .remove_member - test uses PUT but remove_member action expects DELETE", - "severity": "Medium", - "matched_candidate": "Controller spec uses the wrong HTTP method (PUT) for the remove_member route/action; it should send DELETE to match the route and controller action", - "confidence": 0.98, - "reasoning": "The candidate issue states that the controller spec uses PUT for remove_member but should use DELETE to match the route/action, which is the same HTTP method mismatch described in the golden comment." - } - ], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": " The findMembers() call is now asynchronous and unhandled. The controller may not have member data immediately available, creating a race condition.", - "severity": "High" - }, - { - "golden_comment": "In the next action, capping the next offset at user_count can produce an empty page (e.g., total equal to limit results in offset == total, showing 2/2 with no members). This can cause confusing UX on the last page.", - "severity": "Medium" - } - ], - "errors": [], - "total_candidates": 1, - "total_golden": 3, - "tp": 1, - "fp": 0, - "fn": 2, - "errors_count": 0, - "precision": 1.0, - "recall": 0.3333333333333333, - "tool": "greptile", - "repo_name": "discourse__discourse-graphite__greptile__PR8__20260122", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__greptile__PR8__20260122/pull/1" - }, - "kg": { - "skipped": false, - "true_positives": [], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": " The findMembers() call is now asynchronous and unhandled. The controller may not have member data immediately available, creating a race condition.", - "severity": "High" - }, - { - "golden_comment": "In the next action, capping the next offset at user_count can produce an empty page (e.g., total equal to limit results in offset == total, showing 2/2 with no members). This can cause confusing UX on the last page.", - "severity": "Medium" - }, - { - "golden_comment": "HTTP method mismatch in .remove_member - test uses PUT but remove_member action expects DELETE", - "severity": "Medium" - } - ], - "errors": [], - "total_candidates": 0, - "total_golden": 3, - "tp": 0, - "fp": 0, - "fn": 3, - "errors_count": 0, - "precision": 0.0, - "recall": 0.0, - "tool": "kg", - "repo_name": "discourse__discourse-graphite__kg__PR8__20260202", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__kg__PR8__20260202/pull/1" - }, - "qodo-v2": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In the next action, capping the next offset at user_count can produce an empty page (e.g., total equal to limit results in offset == total, showing 2/2 with no members). This can cause confusing UX on the last page.", - "severity": "Medium", - "matched_candidate": "Pagination can navigate to offset == user_count, resulting in an empty members list and incorrect/confusing next/prev state", - "confidence": 0.94, - "reasoning": "Both describe the same pagination edge case: the next offset is capped/allowed to reach user_count, which yields an empty page (offset == total) and leads to confusing/incorrect pagination UX/state on the last page." - } - ], - "false_positives": [ - { - "candidate": "Admin::GroupsController#remove_member removes membership via group.users.delete(user_id) and group.save, bypassing the existing GroupUser destroy/callback/counter-cache semantics used elsewhere" - }, - { - "candidate": "remove_member does not trigger the primary_group_id cleanup path (destroy_deletions/@deletions), leaving User.primary_group_id pointing to a group the user is no longer a member of" - } - ], - "false_negatives": [ - { - "golden_comment": " The findMembers() call is now asynchronous and unhandled. The controller may not have member data immediately available, creating a race condition.", - "severity": "High" - }, - { - "golden_comment": "HTTP method mismatch in .remove_member - test uses PUT but remove_member action expects DELETE", - "severity": "Medium" - } - ], - "errors": [], - "total_candidates": 4, - "total_golden": 3, - "tp": 1, - "fp": 2, - "fn": 2, - "errors_count": 0, - "precision": 0.25, - "recall": 0.3333333333333333, - "tool": "qodo-v2", - "repo_name": "discourse__discourse-graphite__qodo-v2__PR8__20260227", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__qodo-v2__PR8__20260227/pull/1" - }, - "devin": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In the next action, capping the next offset at user_count can produce an empty page (e.g., total equal to limit results in offset == total, showing 2/2 with no members). This can cause confusing UX on the last page.", - "severity": "Medium", - "matched_candidate": "Inflated totalPages prevents showingLast (currentPage === totalPages) from being true on the real last page, leaving the Next button enabled and allowing navigation to a phantom empty final page", - "confidence": 0.86, - "reasoning": "Both describe the same underlying pagination bug: the logic allows navigating to an extra/phantom last page that is empty (offset reaches total/user_count), leading to confusing UX and an enabled Next button when it should be disabled." - } - ], - "false_positives": [], - "false_negatives": [ - { - "golden_comment": " The findMembers() call is now asynchronous and unhandled. The controller may not have member data immediately available, creating a race condition.", - "severity": "High" - }, - { - "golden_comment": "HTTP method mismatch in .remove_member - test uses PUT but remove_member action expects DELETE", - "severity": "Medium" - } - ], - "errors": [], - "total_candidates": 2, - "total_golden": 3, - "tp": 1, - "fp": 0, - "fn": 2, - "errors_count": 0, - "precision": 0.5, - "recall": 0.3333333333333333, - "tool": "devin", - "repo_name": "discourse__discourse-graphite__devin__PR8__20260302", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__devin__PR8__20260302/pull/1" - }, - "mra-max": { - "skipped": false, - "true_positives": [ - { - "golden_comment": " The findMembers() call is now asynchronous and unhandled. The controller may not have member data immediately available, creating a race condition.", - "severity": "High", - "matched_candidate": "admin_group_route.js removed afterModel and calls model.findMembers() in setupController without awaiting, causing a potential race where UI renders before members load", - "confidence": 0.94, - "reasoning": "Both describe that findMembers() became asynchronous and is invoked in setupController without being awaited/handled, leading to a race condition where member data may not be available when the UI/controller expects it." - } - ], - "false_positives": [ - { - "candidate": "admin-group.js.es6 refactor introduces risk of UI state desynchronization with server data due to new pagination helpers and separate usernames input" - }, - { - "candidate": "admin-group.js.es6 save/create path changes may introduce edge cases or incorrect persistence behavior" - }, - { - "candidate": "admin/group.hbs template rewrite may break dependent UI/components relying on previous markup/IDs/wiring" - }, - { - "candidate": "Group.findMembers returns undefined when group name is blank, breaking callers that expect a Promise" - }, - { - "candidate": "Group.findMembers switched to /members.json with limit/offset, potentially breaking callers expecting the previous endpoint/contract and requiring backend support for the new payload" - }, - { - "candidate": "admin-group-selector.hbs template appears removed/empty, which can cause runtime errors or missing UI if still referenced" - }, - { - "candidate": "Admin::GroupsController server-side API refactor changes create/update semantics and adds add_members/remove_member, risking backward compatibility with existing clients/integrations" - }, - { - "candidate": "New Admin::GroupsController membership actions require careful authorization checks; missing/incorrect checks would be a security/permission bug" - }, - { - "candidate": "GroupsController changes member listing/pagination: posts now returns meta (total/limit/offset) and default limit changed to 50, potentially breaking client expectations" - }, - { - "candidate": "Routes reorganize member endpoints and add collection actions (e.g., refresh_automatic_groups), requiring client updates and potentially breaking existing route consumers" - }, - { - "candidate": "Non-admin GroupsController spec still targets :posts while implementation uses a members endpoint, so tests may fail or not cover the intended API/payload" - }, - { - "candidate": "group_member.hbs uses '#unless automatic' but 'automatic' may be on the parent group, so context scoping could incorrectly show/hide the remove button" - }, - { - "candidate": "Admin groups specs were reorganized around new endpoints/behaviors; risk that edge cases are not covered or tests may not match actual behavior" - }, - { - "candidate": "CSS changes for Groups UI could cause layout regressions across admin screens" - }, - { - "candidate": "Translation placeholder text change ('add users' to 'enter username') may break UI tests or expectations due to string mismatch" - }, - { - "candidate": "New i18n keys may be missing in other locales/clients relying on older keys, leading to missing translations" - } - ], - "false_negatives": [ - { - "golden_comment": "In the next action, capping the next offset at user_count can produce an empty page (e.g., total equal to limit results in offset == total, showing 2/2 with no members). This can cause confusing UX on the last page.", - "severity": "Medium" - }, - { - "golden_comment": "HTTP method mismatch in .remove_member - test uses PUT but remove_member action expects DELETE", - "severity": "Medium" - } - ], - "errors": [], - "total_candidates": 17, - "total_golden": 3, - "tp": 1, - "fp": 16, - "fn": 2, - "errors_count": 0, - "precision": 0.058823529411764705, - "recall": 0.3333333333333333, - "tool": "mra-max", - "repo_name": "discourse__discourse-graphite__mra-max__PR8__20260306", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__mra-max__PR8__20260306/pull/1" - }, - "greptile-v4": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In the next action, capping the next offset at user_count can produce an empty page (e.g., total equal to limit results in offset == total, showing 2/2 with no members). This can cause confusing UX on the last page.", - "severity": "Medium", - "matched_candidate": "Pagination `totalPages` calculation uses `Math.floor(user_count / limit) + 1`, which overcounts pages when `user_count` is an exact multiple of `limit` (off-by-one)", - "confidence": 0.86, - "reasoning": "Both point to the same edge case: when user_count is an exact multiple of limit, the pagination logic creates an extra/empty last page (golden describes offset capped to total yielding an empty page; candidate describes totalPages off-by-one causing an extra page). Different locus, same underlying bug/UX issue." - }, - { - "golden_comment": "HTTP method mismatch in .remove_member - test uses PUT but remove_member action expects DELETE", - "severity": "Medium", - "matched_candidate": "Spec for `remove_member` uses `xhr :put` even though the route is `DELETE /admin/groups/:id/members`, so the test uses the wrong HTTP verb and can\u2019t catch routing/method issues", - "confidence": 0.96, - "reasoning": "Both point out the same issue: the remove_member spec uses PUT while the route/action expects DELETE, causing an HTTP verb mismatch in the test." - } - ], - "false_positives": [ - { - "candidate": "Controller actions `add_members` and `remove_member` require `:group_id`, but the member routes provide the group key as `params[:id]`, causing `ActionController::ParameterMissing` on real requests" - }, - { - "candidate": "Controller specs for `add_members`/`remove_member` inject params directly and bypass routing, masking the `group_id` vs `id` route/controller mismatch that will fail in production" - }, - { - "candidate": "`alias_level` parameter is not applied in the `create` action, so the user-selected alias level is silently dropped and defaults to 0" - } - ], - "false_negatives": [ - { - "golden_comment": " The findMembers() call is now asynchronous and unhandled. The controller may not have member data immediately available, creating a race condition.", - "severity": "High" - } - ], - "errors": [], - "total_candidates": 6, - "total_golden": 3, - "tp": 2, - "fp": 3, - "fn": 1, - "errors_count": 0, - "precision": 0.3333333333333333, - "recall": 0.6666666666666666, - "tool": "greptile-v4", - "repo_name": "discourse__discourse-graphite__greptile-v4__PR8__20260306", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__greptile-v4__PR8__20260306/pull/1" - }, - "mra-nano": { - "skipped": false, - "true_positives": [ - { - "golden_comment": " The findMembers() call is now asynchronous and unhandled. The controller may not have member data immediately available, creating a race condition.", - "severity": "High", - "matched_candidate": "Removing afterModel and moving member loading to setupController via model.findMembers() may cause race conditions or incomplete UI state if members load after render", - "confidence": 0.93, - "reasoning": "Both comments flag that moving member loading to setupController and calling findMembers() asynchronously without proper handling can lead to a race condition where member data isn't available when the controller/UI expects it." - } - ], - "false_positives": [ - { - "candidate": "Potential mismatch between rewritten admin-group.js.es6 controller logic and group.hbs template conditional rendering (e.g., member management gated by 'automatic' flag)" - }, - { - "candidate": "Asynchronous member fetching from a new endpoint may fail, leaving the view in an incomplete state without proper error handling" - }, - { - "candidate": "New pagination fields (limit, offset, user_count) in Group model may not align with frontend expectations, risking incorrect pagination behavior" - }, - { - "candidate": "Mismatch between newly added admin group routes and controller actions could lead to 404/422 errors in group management flows" - }, - { - "candidate": "GroupsController#members API response changed from a plain array to an object with members and pagination metadata, risking client incompatibility if not updated accordingly" - }, - { - "candidate": "Test suite reorganization/new endpoint coverage may not match actual implementation, potentially masking integration regressions" - }, - { - "candidate": "Updated client.en UI strings (e.g., selector_placeholder and new group management labels) may not align with the new UI behavior, causing confusing or incorrect messaging" - } - ], - "false_negatives": [ - { - "golden_comment": "In the next action, capping the next offset at user_count can produce an empty page (e.g., total equal to limit results in offset == total, showing 2/2 with no members). This can cause confusing UX on the last page.", - "severity": "Medium" - }, - { - "golden_comment": "HTTP method mismatch in .remove_member - test uses PUT but remove_member action expects DELETE", - "severity": "Medium" - } - ], - "errors": [], - "total_candidates": 8, - "total_golden": 3, - "tp": 1, - "fp": 7, - "fn": 2, - "errors_count": 0, - "precision": 0.125, - "recall": 0.3333333333333333, - "tool": "mra-nano", - "repo_name": "discourse__discourse-graphite__mra-nano__PR8__20260306", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__mra-nano__PR8__20260306/pull/1" - }, - "mra-ultra": { - "skipped": false, - "true_positives": [ - { - "golden_comment": " The findMembers() call is now asynchronous and unhandled. The controller may not have member data immediately available, creating a race condition.", - "severity": "High", - "matched_candidate": "Removing the route afterModel hook and calling model.findMembers() in setupController without awaiting leaves controller.members unset when the template renders", - "confidence": 0.94, - "reasoning": "Both describe the same underlying problem: findMembers() is asynchronous and is being called without awaiting/handling its promise, so member data may not be available when the controller/template needs it, causing a race condition/unset members." - }, - { - "golden_comment": "In the next action, capping the next offset at user_count can produce an empty page (e.g., total equal to limit results in offset == total, showing 2/2 with no members). This can cause confusing UX on the last page.", - "severity": "Medium", - "matched_candidate": "Pagination totalPages calculation uses floor(user_count / limit) + 1, causing an off-by-one extra page when user_count is an exact multiple of limit; should use Math.ceil(user_count / limit)", - "confidence": 0.86, - "reasoning": "Both point to the same underlying pagination edge case: when user_count is an exact multiple of limit, the logic creates an extra/empty last page (offset == total). The candidate frames it as an off-by-one in totalPages calculation (floor+1 vs ceil), which is the same root cause as the golden comment\u2019s empty page/UX issue." - }, - { - "golden_comment": "HTTP method mismatch in .remove_member - test uses PUT but remove_member action expects DELETE", - "severity": "Medium", - "matched_candidate": "Admin groups controller spec calls remove_member with HTTP PUT while the route and client use DELETE, so the spec tests the wrong verb and may not hit the real endpoint", - "confidence": 0.96, - "reasoning": "Both describe the same issue: the spec uses HTTP PUT for remove_member while the actual route/action expects DELETE, causing a verb mismatch and incorrect test coverage." - } - ], - "false_positives": [ - { - "candidate": "group.findMembers returns undefined when group name is empty, but callers expect a Promise, breaking async flows and .then chains" - }, - { - "candidate": "Admin::GroupsController was heavily rewritten with new/altered REST-like actions and endpoints, creating high risk of authorization/data integrity/client compatibility regressions" - }, - { - "candidate": "Public GroupsController member listing endpoint now returns a paginated payload with meta (total/limit/offset) instead of the previous plain list, breaking clients/tests relying on the old response shape" - }, - { - "candidate": "Routes change removes prior admin GET route for listing group members (e.g., /admin/groups/:id/users) and replaces it with member modification endpoints, potentially breaking admin UI/client code that relied on the old member-reading route" - } - ], - "false_negatives": [], - "errors": [], - "total_candidates": 9, - "total_golden": 3, - "tp": 3, - "fp": 4, - "fn": 0, - "errors_count": 0, - "precision": 0.3333333333333333, - "recall": 1.0, - "tool": "mra-ultra", - "repo_name": "discourse__discourse-graphite__mra-ultra__PR8__20260306", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__mra-ultra__PR8__20260306/pull/1" - }, - "mra-b": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In the next action, capping the next offset at user_count can produce an empty page (e.g., total equal to limit results in offset == total, showing 2/2 with no members). This can cause confusing UX on the last page.", - "severity": "Medium", - "matched_candidate": "totalPages calculation uses Math.floor(user_count/limit)+1, causing an off-by-one extra page when user_count is an exact multiple of limit (should use Math.ceil or equivalent)", - "confidence": 0.86, - "reasoning": "Both comments describe an off-by-one pagination bug that creates an extra/empty last page when the total count is an exact multiple of the page size. The golden frames it as capping next offset at user_count leading to offset==total and an empty page; the candidate frames it as totalPages computed with floor+1 instead of ceil, also yielding an extra page. Same underlying issue: incorrect last-page handling producing an empty page/extra page." - } - ], - "false_positives": [ - { - "candidate": "findMembers early-returns undefined when group name is empty instead of returning a Promise (e.g., Ember.RSVP.resolve([])), breaking callers that chain .then()" - }, - { - "candidate": "Admin group create/update API parameter shapes changed from nested group[...] to top-level keys, breaking existing clients expecting the old payload format" - }, - { - "candidate": "Admin group membership modification was removed from PUT /admin/groups/:id and moved to new member endpoints (PUT/DELETE /admin/groups/:group_id/members) with different params, breaking clients that updated membership via the old update endpoint" - }, - { - "candidate": "Public GET /groups/:group_id/members response shape changed from a top-level array to an object containing {members, meta}, breaking consumers expecting an array response" - }, - { - "candidate": "HTTP status for attempting to modify automatic groups changed from 403 Forbidden to 422 Unprocessable Entity, potentially breaking clients that branch on status code" - } - ], - "false_negatives": [ - { - "golden_comment": " The findMembers() call is now asynchronous and unhandled. The controller may not have member data immediately available, creating a race condition.", - "severity": "High" - }, - { - "golden_comment": "HTTP method mismatch in .remove_member - test uses PUT but remove_member action expects DELETE", - "severity": "Medium" - } - ], - "errors": [], - "total_candidates": 6, - "total_golden": 3, - "tp": 1, - "fp": 5, - "fn": 2, - "errors_count": 0, - "precision": 0.16666666666666666, - "recall": 0.3333333333333333, - "tool": "mra-b", - "repo_name": "discourse__discourse-graphite__mra-b__PR8__20260307", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__mra-b__PR8__20260307/pull/1" - }, - "mra-a": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In the next action, capping the next offset at user_count can produce an empty page (e.g., total equal to limit results in offset == total, showing 2/2 with no members). This can cause confusing UX on the last page.", - "severity": "Medium", - "matched_candidate": "totalPages calculation is off-by-one when user_count is exactly divisible by limit due to using Math.floor(user_count/limit)+1 instead of Math.ceil(user_count/limit), creating an extra empty page", - "confidence": 0.86, - "reasoning": "Both describe the same underlying problem: when the total count is an exact multiple of the page size, the pagination logic creates an extra/empty last page (offset equals total), leading to confusing UX. The candidate frames it as an off-by-one in totalPages calculation; the golden frames it as capping next offset at user_count causing an empty page, but the effect and condition match." - } - ], - "false_positives": [ - { - "candidate": "findMembers returns undefined (not a Promise/thenable) when the group name is empty, causing callers that expect a Promise to throw TypeErrors at runtime" - }, - { - "candidate": "Group.create no longer supports passing initial usernames during group creation (removal of createWithUsernames behavior), preventing atomic creation with initial members and changing the client REST contract" - }, - { - "candidate": "group/members.hbs template changed iteration from model to members, but members may not exist in template scope, potentially rendering an empty members list" - }, - { - "candidate": "Admin::GroupsController#create now expects top-level params (e.g., params[:name], params[:visible]) instead of nested params[:group][...], breaking the admin create API contract for existing clients and integrations" - } - ], - "false_negatives": [ - { - "golden_comment": " The findMembers() call is now asynchronous and unhandled. The controller may not have member data immediately available, creating a race condition.", - "severity": "High" - }, - { - "golden_comment": "HTTP method mismatch in .remove_member - test uses PUT but remove_member action expects DELETE", - "severity": "Medium" - } - ], - "errors": [], - "total_candidates": 5, - "total_golden": 3, - "tp": 1, - "fp": 4, - "fn": 2, - "errors_count": 0, - "precision": 0.2, - "recall": 0.3333333333333333, - "tool": "mra-a", - "repo_name": "discourse__discourse-graphite__mra-a__PR8__20260307", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__mra-a__PR8__20260307/pull/1" - }, - "entelligence": { - "skipped": false, - "true_positives": [ - { - "golden_comment": " The findMembers() call is now asynchronous and unhandled. The controller may not have member data immediately available, creating a race condition.", - "severity": "High", - "matched_candidate": "Removing afterModel and not assigning the result of model.findMembers() means the controller never receives loaded members; the promise result is ignored and templates/controllers expecting members get undefined (admin_group_route.js and group-members route)", - "confidence": 0.9, - "reasoning": "Both describe that findMembers() is asynchronous and its returned promise/result is not handled/assigned, so member data may be unavailable when the controller/template expects it, leading to undefined/race condition behavior." - }, - { - "golden_comment": "In the next action, capping the next offset at user_count can produce an empty page (e.g., total equal to limit results in offset == total, showing 2/2 with no members). This can cause confusing UX on the last page.", - "severity": "Medium", - "matched_candidate": "Pagination totalPages calculation uses Math.floor(...)+1, which overcounts when user_count is an exact multiple of limit, causing showingLast to be false on the real last page and allowing navigation to an empty page; should use Math.ceil", - "confidence": 0.9, - "reasoning": "Both describe the same pagination edge case: when total/user_count is an exact multiple of limit, the logic allows navigating to an offset equal to total, yielding an empty last page. The candidate frames it as an incorrect totalPages calculation (floor+1 vs ceil) leading to showingLast being wrong and enabling the empty page, which matches the golden UX/empty-page concern." - } - ], - "false_positives": [ - { - "candidate": "Replacing saveWithUsernames/createWithUsernames with group.save()/group.create() stops sending entered usernames during save/create, so group members are not added when saving/creating a group" - }, - { - "candidate": "Group.findMembers returns undefined when group name is empty, breaking the prior contract of always returning a promise and causing callers chaining .then() to crash; should return a resolved promise (e.g., empty array)" - }, - { - "candidate": "Admin GroupsController create/update now read attributes from top-level params, but previous update_put expected params[:group]; clients sending nested { group: { ... } } will have input silently ignored unless both payload shapes are supported" - }, - { - "candidate": "GroupsController now paginates all groups with a default limit of 50, regressing prior behavior where non-automatic groups returned the full member list; this is a breaking change for clients expecting unpaged results" - } - ], - "false_negatives": [ - { - "golden_comment": "HTTP method mismatch in .remove_member - test uses PUT but remove_member action expects DELETE", - "severity": "Medium" - } - ], - "errors": [], - "total_candidates": 6, - "total_golden": 3, - "tp": 2, - "fp": 4, - "fn": 1, - "errors_count": 0, - "precision": 0.3333333333333333, - "recall": 0.6666666666666666, - "tool": "entelligence", - "repo_name": "discourse__discourse-graphite__entelligence__PR8__20260306", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__entelligence__PR8__20260306/pull/1" - }, - "sourcery": { - "skipped": false, - "true_positives": [ - { - "golden_comment": "In the next action, capping the next offset at user_count can produce an empty page (e.g., total equal to limit results in offset == total, showing 2/2 with no members). This can cause confusing UX on the last page.", - "severity": "Medium", - "matched_candidate": "Pagination totalPages calculation uses Math.floor(user_count/limit)+1, which is off by one when user_count is an exact multiple of limit and can expose an empty extra page; should use Math.ceil(user_count/limit)", - "confidence": 0.9, - "reasoning": "Both describe the same underlying pagination edge case: when total/user_count is an exact multiple of limit, the logic creates an extra/last page with offset == total, yielding an empty page. The candidate frames it as an off-by-one in totalPages (Math.floor+1 vs Math.ceil), which leads to the same empty extra page/UX issue noted in the golden comment." - } - ], - "false_positives": [ - { - "candidate": "Admin::GroupsController#add_members incorrectly requires params[:group_id] for a member route that supplies the group id as params[:id], causing ActionController::ParameterMissing on valid requests" - }, - { - "candidate": "Admin::GroupsController#remove_member incorrectly requires params[:group_id] for a member route that supplies the group id as params[:id], causing ActionController::ParameterMissing and breaking DELETE /admin/groups/:id/members" - }, - { - "candidate": "group-member template references `member` (e.g., {{avatar member ...}}) even though the itemView context is already the member, leading to an undefined binding; should reference `this`/view.content instead" - }, - { - "candidate": "Group#findMembers returns undefined when name is empty instead of a resolved promise, which can break callers that expect a promise and chain .then" - } - ], - "false_negatives": [ - { - "golden_comment": " The findMembers() call is now asynchronous and unhandled. The controller may not have member data immediately available, creating a race condition.", - "severity": "High" - }, - { - "golden_comment": "HTTP method mismatch in .remove_member - test uses PUT but remove_member action expects DELETE", - "severity": "Medium" - } - ], - "errors": [], - "total_candidates": 5, - "total_golden": 3, - "tp": 1, - "fp": 4, - "fn": 2, - "errors_count": 0, - "precision": 0.2, - "recall": 0.3333333333333333, - "tool": "sourcery", - "repo_name": "discourse__discourse-graphite__sourcery__PR8__20260308", - "pr_url": "https://github.com/code-review-benchmark/discourse__discourse-graphite__sourcery__PR8__20260308/pull/1" - }, - "cubic-dev": { - "skipped": false, - "true_positives": [ - { - "golden_comment": " The findMembers() call is now asynchronous and unhandled. The controller may not have member data immediately available, creating a race condition.", - "severity": "High", - "matched_candidate": "Admin group route calls model.findMembers() without returning/awaiting the promise, creating a race condition (renders before members load) and risking stale writes if navigation occurs before the AJAX completes", - "confidence": 0.93, - "reasoning": "Both describe the same underlying problem: findMembers() is asynchronous and its promise is not awaited/handled, so the controller/route may proceed (e.g., render) before member data is available, causing a race condition." - }, - { - "golden_comment": "In the next action, capping the next offset at user_count can produce an empty page (e.g., total equal to limit results in offset == total, showing 2/2 with no members). This can cause confusing UX on the last page.", - "severity": "Medium", - "matched_candidate": "findMembers() offset clamping uses Math.min(user_count, ...), allowing offset==user_count and producing an empty last page; should clamp to last valid page start (user_count - limit)", - "confidence": 0.95, - "reasoning": "Both describe the same pagination bug: clamping the next offset to user_count can yield offset==total, resulting in an empty last page (confusing UX). The candidate also suggests the correct fix (clamp to last valid page start)." - }, - { - "golden_comment": "HTTP method mismatch in .remove_member - test uses PUT but remove_member action expects DELETE", - "severity": "Medium", - "matched_candidate": "Test for remove_member uses the wrong HTTP verb (:put instead of :delete), likely hitting the wrong route and not actually testing automatic-group protection", - "confidence": 0.96, - "reasoning": "Both the golden comment and the candidate issue point out that the remove_member test is using PUT when the controller/action expects DELETE, meaning the test may be exercising the wrong route/action." - } - ], - "false_positives": [ - { - "candidate": "Client-side locale string uses Ruby i18n interpolation syntax (%{variable}) instead of the expected Handlebars-style syntax ({{variable}}), causing variables to render literally in the UI" - }, - { - "candidate": "totalPages calculation uses Math.floor(...)+1, causing an off-by-one error; it should use ceiling division (Math.ceil(user_count/limit))" - }, - { - "candidate": "Removing a user via group.users.delete(user_id) passes a raw integer and bypasses Group#remove callbacks (e.g., resetting primary_group_id); should remove via the model method with a User object" - }, - { - "candidate": "findMembers() returns undefined when group name is empty, breaking the method\u2019s promise contract and causing callers chaining .then() to fail" - }, - { - "candidate": "Remove-member UI control uses an without href, making it not keyboard-focusable/inaccessible; should be a