Skip to content

feat(gitlab): implement forge_resolve_outdated_review_threads via discussions api - #2002

Open
jefferyb wants to merge 5 commits into
fullsend-ai:mainfrom
jefferyb:feat/gitlab-resolve-outdated-review-threads
Open

jefferyb wants to merge 5 commits into
fullsend-ai:mainfrom
jefferyb:feat/gitlab-resolve-outdated-review-threads

Conversation

@jefferyb

Copy link
Copy Markdown

Summary

Implement forge_resolve_outdated_review_threads for GitLab using the GitLab Discussions REST API, bringing GitLab review thread lifecycle parity with GitHub.

Problem

When the review agent runs across iterative commits on a pull/merge request, previously opened inline review comments should be automatically resolved if they pertain to older commits that have since been superseded by new push events.

While GitHub implements this via GraphQL thread resolution, the GitLab implementation in scripts/lib/gitlab-review-ops.lib.sh was previously an empty stub:

forge_resolve_outdated_review_threads() {
    return 0
}

As a result, outdated review comments from previous bot review cycles remained open indefinitely on GitLab merge requests, cluttering the MR diff view and requiring human reviewers to manually resolve stale bot threads.

Solution

  1. Implement forge_resolve_outdated_review_threads in scripts/lib/gitlab-review-ops.lib.sh:
    • Paginate and query GitLab merge request discussions via GET /projects/:id/merge_requests/:mr_iid/discussions.
    • Filter for resolvable, unresolved discussion threads where all non-system notes are authored by the review bot (BOT_USERNAME / BOT_NAME).
    • Compare note position.head_sha against the current MR HEAD SHA (FORGE_HEAD_SHA).
    • Resolve candidate stale threads via PUT /projects/:id/merge_requests/:mr_iid/discussions/:discussion_id?resolved=true.
    • Human review comments and mixed threads are strictly preserved and never mutated.
    • Operations are best-effort: failures log warnings and do not abort the review pipeline.
  2. Wire forge_resolve_outdated_review_threads into scripts/post-review.src.sh after review comments are submitted.
  3. Add comprehensive automated tests in scripts/post-review-test.sh:
    • Resolving outdated bot discussions.
    • Preserving threads on current HEAD SHA.
    • Preserving human comments and mixed bot/human threads.
    • Handling GitLab system notes without false rejections.
    • Resiliency to API error responses (warn and continue).
  4. Rebuild all bundled scripts (make script-build) and verify bundle parity (make check-bundle).

Verification

  • Ran scripts/post-review-test.sh — all 300+ assertions passed, including all new GitLab discussion resolution test cases.
  • Ran make check-bundle — verified 0 diff and clean bundle parity across all bundled targets (pre-review.sh, post-review.sh).
  • Verified DCO sign-off on all commits.

…cussions api

Resolve still-open review threads on GitLab merge requests whose only
comments are outdated inline comments authored by the review agent.

On GitLab, when a review agent runs on subsequent commits of an MR, earlier
inline review comments created by the review bot remained unresolved, even
after newer commits were pushed, because forge_resolve_outdated_review_threads
was stubbed as a no-op for GitLab.

Query GitLab discussions via GET /projects/:id/merge_requests/:mr_iid/discussions,
identifying resolvable, unresolved threads where all notes were authored by
the review bot and whose position.head_sha does not match the current MR HEAD SHA.
Resolve each candidate thread via PUT .../discussions/:disc_id?resolved=true.
Operations remain best-effort and log warnings on failure without aborting
the post-review pipeline.

Signed-off-by: Jeffery Bagirimvano <jefferyb@redhat.com>
@jefferyb
jefferyb requested a review from a team as a code owner November 13, 2026 17:20
@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Resolve outdated bot review threads on GitLab merge requests

✨ Enhancement 🧪 Tests 🕐 20-40 Minutes

Grey Divider

AI Description

• Resolve stale, bot-only GitLab discussions before posting a new review.
• Preserve current, resolved, and human-authored threads; keep resolution failures nonfatal.
• Add GitLab discussion tests and regenerate the pre-review and post-review bundles.
Diagram

graph TD
  A["Post-review"] --> B["MR and actor"] --> C["GitLab discussions"] --> D{"Eligible thread?"} -->|Yes| E["Resolve discussion"] --> F["Report outcome"]
  D -->|No| G["Leave untouched"]
Loading
High-Level Assessment

Using GitLab’s Discussions API behind the existing forge operation is the most direct way to add GitLab parity without changing the post-review interface. Unlike GitHub’s outdated-thread signal, GitLab requires comparing the note’s position SHA with the MR HEAD; preserving the existing best-effort call is preferable to making review posting depend on cleanup.

Files changed (5) +372 / -15

Enhancement (2) +162 / -7
gitlab-review-ops.lib.shResolve eligible stale GitLab discussions +79/-2

Resolve eligible stale GitLab discussions

• Replaces the no-op with paginated discussion retrieval, bot-only and outdated-SHA filtering, and resolution through the GitLab REST API. Resolution failures produce warnings without failing post-review.

scripts/lib/gitlab-review-ops.lib.sh

post-review.shBundle GitLab cleanup into post-review +83/-5

Bundle GitLab cleanup into post-review

• Includes the generated GitLab resolver and updates the shared call-site comment to describe GitLab behavior. The existing cleanup call runs after the open-MR check and before the new review is posted.

scripts/post-review.sh

Tests (1) +127 / -3
post-review-test.shTest GitLab discussion cleanup +127/-3

Test GitLab discussion cleanup

• Extends the GitLab API mock and adds integration cases for stale and current threads, system notes, human-authored threads, already-resolved threads, and nonfatal resolution failures.

scripts/post-review-test.sh

Documentation (1) +4 / -3
post-review.src.shDocument GitLab behavior at the cleanup call +4/-3

Document GitLab behavior at the cleanup call

• Updates the call-site comment to explain that GitLab uses the MR HEAD SHA to identify outdated threads. The existing forge-neutral invocation remains in place.

scripts/post-review.src.sh

Other (1) +79 / -2
pre-review.shRegenerate the pre-review bundle +79/-2

Regenerate the pre-review bundle

• Includes the updated shared GitLab forge library in the generated pre-review script; its pre-review call flow is unchanged.

scripts/pre-review.sh

@qodo-code-review

qodo-code-review Bot commented Nov 13, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Quoted markers can close human threads ✓ Resolved

Resolving commit: 8a8b736

Resolution reason
The username-based fallback was removed and the primary ID check was tightened to ID-only. The
body-marker fallback remains. Tests were added for failure cases.
Description
When /user supplies no ID, the fallback authorship check accepts a note if its username looks
bot-like or its body contains a Fullsend marker. A human can put that marker in their own comment,
allowing an otherwise eligible human-only or mixed discussion to reach the resolving PUT when
identity lookup fails.

2. Active review threads are resolved ✗ Dismissed
Description
forge_resolve_outdated_review_threads treats a mismatch between the first note's
position.head_sha and the MR head as proof that the discussion is outdated. An unrelated push
changes the head SHA even when the commented line remains valid, so the resolver can close an
unresolved, actionable discussion.

3. Bot closures can count as human dismissals ✗ Dismissed
Description
The new resolving PUT makes the review bot the resolver of its own stamped discussion, but
forge_get_human_dismissals does not exclude that bot from eligible resolvers. If the bot has a
verified write-or-higher role and a later result marks a low or medium finding dismissed_by_human,
the ledger can accept the bot-closed thread as verification and close the finding without human
action.

View high (1)
4. Human review threads can be auto-closed ✗ Dismissed
Description
forge_resolve_outdated_review_threads treats the identity returned by /user as the review bot
and accepts every non-system note authored by that identity. When REVIEW_TOKEN is a supported
personal access token, the resolver cannot distinguish that person's manually written threads from
agent threads and can resolve them after a head change.


Remediation recommended

5. Large merge requests retain stale threads ✗ Dismissed
Description
The discussion pagination loop stops after page 10 even when that page is full. On an MR with more
than 1,000 discussions, qualifying threads on later pages are never inspected, while the resolver
still returns success.

6. Discussion fetch failures leave no warning ✓ Resolved

Resolving commit: 0ec8635

Resolution reason
Failed discussion-page GETs in forge_resolve_outdated_review_threads now emit a sanitized warning
naming the MR number before breaking non-fatally, and tests cover the discussion-fetch failure path
with a warning assertion and no-mutation check.
Description
forge_resolve_outdated_review_threads suppresses errors from the MR and discussion GET requests,
then returns or breaks without reporting them. A token-scope, rate-limit, or network failure can
therefore skip cleanup without the warning promised by the function's best-effort contract; only
failed PUTs produce warnings.

7. Rotated bots leave threads open ✗ Dismissed
Description
forge_resolve_outdated_review_threads accepts notes only when their author matches the current
/user identity. In fullsend deployments that fall back to project access tokens, rotating the
review credential changes its bot identity, so discussions posted before rotation are skipped even
when their positions are outdated.

Grey Divider

Context sources
✅ Compliance rules (platform): 55 rules
✅ Skills: 4 invoked
  code-review
  code-implementation
  pr-review
  docs-review
✅ Web pages:
  +15 more
✅ Cross-repo context — repo relationships
  Explored: repo: fullsend-ai/fullsend (sha: dda46236) — View relationship
✅ Context preparation
  Past issues: summarized; full text retained for this review
Review mode: Auto: ⚖️ Balanced: API mutation logic has meaningful lifecycle and filtering risks, but remains localized.

Grey Divider

Tip of the day
💡 Did you know, you can start a comment with 'qodo' or '@qodo' to chat about any finding

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread scripts/lib/gitlab-review-ops.lib.sh Resolved
Comment thread scripts/lib/gitlab-review-ops.lib.sh Resolved
Comment thread scripts/lib/gitlab-review-ops.lib.sh Resolved
Comment thread scripts/lib/gitlab-review-ops.lib.sh Resolved
Comment thread scripts/lib/gitlab-review-ops.lib.sh Resolved
Comment thread scripts/lib/gitlab-review-ops.lib.sh Resolved
Comment thread scripts/lib/gitlab-review-ops.lib.sh Resolved

@waynesun09 waynesun09 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Review-only comments. One finding is inline. The other is on a file this PR does not change, so it is here.

MEDIUM: docs/review.md still says only GitHub resolves outdated review-agent threads (docs/review.md:368)

docs/review.md:368 still says "On GitHub, it also resolves still-open review threads whose only comments are outdated inline comments authored by the review agent". This PR makes post-review resolve GitLab discussions too. On GitLab, "outdated" means the first note's position.head_sha differs from the MR head, which is not GitHub's isOutdated test. The page that documents this step is now out of date and does not say how GitLab decides a thread is outdated.

Suggestion: update step 4 in docs/review.md to cover GitLab. Say that a thread counts as outdated when the first note's position.head_sha differs from the current MR diff_refs.head_sha, and that only threads where every note is by the review identity are resolved.

GITLAB_OUTDATED_DISC='[{"id":"disc_outdated","notes":[{"id":10,"author":{"id":100,"username":"review-bot"},"resolvable":true,"resolved":false,"position":{"head_sha":"older_sha"}}]}]'
GITLAB_SYSTEM_NOTE_DISC='[{"id":"disc_sys","notes":[{"id":14,"author":{"id":100,"username":"review-bot"},"resolvable":true,"resolved":false,"position":{"head_sha":"older_sha"}},{"id":15,"system":true,"author":{"id":1,"username":"system"},"body":"added 1 commit"}]}]'
GITLAB_RESOLVED_DISC='[{"id":"disc_resolved","notes":[{"id":11,"author":{"id":100,"username":"review-bot"},"resolvable":true,"resolved":true,"position":{"head_sha":"older_sha"}}]}]'
GITLAB_HUMAN_DISC='[{"id":"disc_human","notes":[{"id":12,"author":{"id":200,"username":"alice"},"resolvable":true,"resolved":false,"position":{"head_sha":"older_sha"}}]}]'

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

MEDIUM: Tests skip the mixed bot/human authorship guard and the discussions-fetch failure path that the PR body says are covered

The PR body says tests cover "Preserving human comments and mixed bot/human threads" and "Resiliency to API error responses". At head c640ccb, the only GitLab non-bot fixture is GITLAB_HUMAN_DISC (this line), which holds a single human note. No discussion mixes bot and human notes, so the all(...) authorship check in forge_resolve_outdated_review_threads has no test for the mixed case. That check is the line that keeps human threads open. The GitHub side does test it (select-skips-mixed-authors, lines 4474 and 4503).

The mock adds MOCK_GITLAB_DISCUSSIONS_FAIL at line 700, but no test sets it, so a failed discussions GET is never run. Nothing tests the /user-missing fallback either. Pagination cannot be tested, because the mock returns the same body for every page.

Suggestion: add a GitLab discussion with one bot note and one human note, and assert that no PUT .../discussions call is made. Add a case with MOCK_GITLAB_DISCUSSIONS_FAIL=1 that expects exit 0 and no PUT. Add a case where /user returns no id. If these are not added, correct the PR body.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in 0ec8635: added tests covering mixed bot+human threads (gitlab-mixed-bot-human-no-mutation), MOCK_GITLAB_DISCUSSIONS_FAIL=1 non-fatal warning and no-PUT verification (gitlab-discussions-fail-warning, gitlab-discussions-fail-no-mutation), and /user-missing fallback handling (gitlab-user-fail-marker-fallback, gitlab-user-fail-human-no-mutation).

…error, and expand tests

Signed-off-by: Jeffery Bagirimvano <jefferyb@redhat.com>
@jefferyb

Copy link
Copy Markdown
Author

Addressed @waynesun09's review feedback on docs/review.md:368 in 0ec86357b110d9c729123effd10ababa3e3c28f4: updated step 4 in docs/review.md to cover GitLab outdated thread resolution, explicitly stating that a thread counts as outdated on GitLab when the first non-system note's position.head_sha differs from the MR's current diff_refs.head_sha and that only threads where every non-system note is authored by the review identity are resolved.

@waynesun09 waynesun09 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Review-only comments: two inline findings on the authorship filter in forge_resolve_outdated_review_threads.

| select(.notes | type == "array" and length > 0)
| select(.notes | any(.resolvable == true and .resolved == false))
| select(
if ($bot_id != "") then

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

MEDIUM: Username check is OR'd with the id check even when the id is known

When GET /user returns an id, the authorship filter is still (.author.id|tostring) == $bot_id or (.author.username // "" | ascii_downcase) == ($bot_user | ascii_downcase). The author id is immutable and is enough to identify the review account. GitLab usernames can change, and a freed username can be claimed by another account. If that happens, notes from the account that now holds the bot's old username pass the "authored by the review identity" check, so their outdated threads get resolved.

A second edge case: if /user returns an id but no username, $bot_user is empty, and any note whose author.username is missing matches "" == "".

The username comparison adds nothing once the id is known. docs/review.md describes the rule as "authored by the review identity", which the id alone already meets. (This is separate from the earlier thread on line 225 about a human PAT owner being treated as the bot.)

Suggestion: in the $bot_id != "" branch, compare on (.author.id | tostring) == $bot_id only and drop the username comparison. Optionally add a test where a note has the bot's username but a different id, and check that it is not resolved.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in 8a8b736 (8a8b736): removed bot_username and --arg bot_user from forge_resolve_outdated_review_threads in scripts/lib/gitlab-review-ops.lib.sh so when GET /user returns an ID ($bot_id != ""), authorship compares (.author.id | tostring) == $bot_id only. Added gitlab-different-id-same-username-no-mutation coverage in scripts/post-review-test.sh and rebuilt scripts/post-review.sh.

| select(
if ($bot_id != "") then
.notes | all((.author.id | tostring) == $bot_id or (.author.username // "" | ascii_downcase) == ($bot_user | ascii_downcase))
else

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

MEDIUM: The fallback username regex matches other bots, so their threads get resolved when GET /user fails

If GET /user returns no id, the fallback accepts any note whose author.username matches (^|[-_])bot([-_]|$)|\[bot\]$|fullsend. That matches unrelated automation accounts, for example renovate-bot, and every GitLab project or group access token bot (usernames like project_<id>_bot_<hash> or group_<id>_bot_<hash>, which contain _bot_). Every note in such a thread passes all(...), so fullsend resolves outdated diff threads opened by SAST, lint or dependency bots it does not own. That contradicts docs/review.md, which says only threads "authored by the review identity" are resolved. The GitHub path uses only viewerDidAuthor and has no heuristic.

The earlier thread on line 228 was about humans adding body markers, and was declined because all(...) keeps mixed human and bot threads open. That reasoning does not cover threads written entirely by third-party bots, which this regex makes eligible. The gitlab-user-fail-marker-fallback test pins the fallback, and no test checks that another bot's thread is left alone.

Suggestion: fail closed. When GET /user returns no id, emit a ::warning:: and return 0. This matches the GitHub path and the _gitlab_bot_username helpers in gitlab-prioritize/triage. If you want to keep a fallback, at least drop the generic bot username pattern, and add a test where a project_1_bot_abc or renovate-bot thread is not resolved.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in 8a8b736 (8a8b736): removed the generic bot username regex from the $bot_id == "" fallback in scripts/lib/gitlab-review-ops.lib.sh so fallback resolution requires the Fullsend body signature (<!-- fullsend / 🤖 Fullsend) on every non-system note. Added gitlab-user-fail-other-bot-no-mutation coverage (renovate-bot / project_1_bot_abc) in scripts/post-review-test.sh and rebuilt scripts/post-review.sh.

Signed-off-by: Jeffery Bagirimvano <jefferyb@redhat.com>

@waynesun09 waynesun09 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Review-only comment: one inline finding. The committed scripts/pre-review.sh bundle was not rebuilt after the authorship fix.

Comment thread scripts/pre-review.sh Outdated
return 0
fi

local current_user bot_user_id bot_username

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

HIGH: The committed scripts/pre-review.sh bundle is stale, so make check-bundle will fail

Commit 8a8b736 ("tighten authorship") updated scripts/lib/gitlab-review-ops.lib.sh and scripts/post-review.sh, but scripts/pre-review.sh was not rebuilt. It still bundles the old forge_resolve_outdated_review_threads. Lines 738-771 still have:

  • bot_username and --arg bot_user
  • the .author.username match OR'd with the id check
  • the fallback regex (^|[-_])bot([-_]|$)|\[bot\]$|fullsend

Running scripts/bundle-sh.sh on scripts/pre-review.src.sh and diffing the output against the committed file shows these differences. The Makefile check-bundle target runs the same diff and exits 1 with Bundled script stale: scripts/pre-review.sh. The Script tests workflow has not run on this PR (only the dispatch and DCO checks show), so CI has not caught this yet. The PR body says check-bundle showed 0 diff, which is not true at this head.

Pre-review does not call this function today, so this is dead code. It still ships the looser authorship check that 8a8b736 was meant to remove.

Suggestion: Run make script-build, commit the regenerated scripts/pre-review.sh, and confirm make check-bundle passes locally. Get the Script tests workflow to run on the PR before merge.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Regenerated scripts/pre-review.sh in 87204a22 (87204a22902468487a7ac42df71ff2ac46135bd4).

Verified locally with make -B script-build, make check-bundle (0 diff across all bundled scripts), shellcheck -S warning scripts/lib/gitlab-review-ops.lib.sh scripts/post-review.sh scripts/pre-review.sh scripts/post-review-test.sh, and scripts/post-review-test.sh in both source and bundled modes (all tests passed).

Note: fork PR workflows beyond dispatch and DCO (such as Script tests) appear to require maintainer approval / ok-to-test on fullsend-ai/agents.

Signed-off-by: Jeffery Bagirimvano <jefferyb@redhat.com>
Comment thread scripts/post-review.src.sh Outdated
fi

# Resolve still-open GitHub review threads whose only comments are
# Resolve still-open GitHub/GitLab review threads whose only comments are

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

if the agent produces no review this can still close the old GitLab threads. I wouldd move the cleanup to after the new review is posted. @waynesun09

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Good catch — fixed in 1f8995de (1f8995de21c664308619bc46b1400796095ce668):

  • Moved forge_resolve_outdated_review_threads in scripts/post-review.src.sh to run immediately after forge_post_review completes and after the ACTION = "failure" check, right before outcome labels are applied. Missing agent output, stale-head re-dispatches, failed posts, and explicit action: "failure" runs now exit without closing existing unresolved GitHub/GitLab threads.
  • Updated the forge_resolve_outdated_review_threads comment in scripts/lib/gitlab-review-ops.lib.sh and regenerated scripts/post-review.sh / scripts/pre-review.sh.
  • Added regression tests in scripts/post-review-test.sh (github-missing-result-no-resolve, github-action-failure-no-resolve, gitlab-missing-result-no-resolve, gitlab-action-failure-no-resolve).

Signed-off-by: Jeffery Bagirimvano <jefferyb@redhat.com>

@waynesun09 waynesun09 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Review-only comments: two inline findings on forge_resolve_outdated_review_threads.


local mr_info current_sha
mr_info=$(_gitlab_api GET "/projects/${REPO_ENCODED}/merge_requests/${PR_NUMBER}" 2>/dev/null) || mr_info="{}"
current_sha=$(echo "${mr_info}" | jq -r '.diff_refs.head_sha // .sha // empty' 2>/dev/null) || current_sha=""

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

MEDIUM: Outdated check reads the live MR head, so a push after posting resolves the comments this run just posted

forge_resolve_outdated_review_threads reads current_sha from a fresh GET of diff_refs.head_sha. It then resolves any bot thread whose notes[0].position.head_sha differs from it. In scripts/post-review.src.sh the call now runs after forge_post_review (where the stale-head exit 10 is checked) and after the label removals. The fullsend Go post-review places new discussions on the reviewed head.

  • If a push lands between the stale-head check and this GET, every inline comment this run just posted carries the old head_sha and is resolved at once. The review summary still asks for changes.
  • GitLab rewrites the positions of still-current discussions in its async ReloadDiffsService only after it has changed diff_refs. A GET inside that window treats still-current bot threads as outdated.

This is not the declined thread about using a SHA mismatch as the test. That thread said the definition is wrong. This one is a timing race that appears even with the current definition of outdated. Every test fixture sets the mock MR head equal to the reviewed head, so no test covers it.

Suggestion: Compare positions against the head this run reviewed (the head_sha already passed to fullsend post-review), not a fresh diff_refs read. Read the live diff_refs.head_sha only as a guard: if it differs from the reviewed head, log that the head moved and return 0 without resolving anything, because the push will trigger a new review. Add a test where MOCK_MR_HEAD_SHA differs from the result head_sha and assert that no PUT is made.

if ($bot_id != "") then
.notes | all((.author.id | tostring) == $bot_id)
else
.notes | all((.body // "" | test("<!--[ ]*fullsend|🤖 Fullsend"; "i")))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

MEDIUM: The /user-failure fallback is silent, never matches real review comments, and its test uses a made-up marker

When GET /user fails, the function prints no warning and decides authorship by matching each note body against <!--[ ]*fullsend|🤖 Fullsend.

  • Real inline review comments never match. formatFindingComment in fullsend internal/cli/postreview.go writes <!-- finding:<id> --> followed by **[sev]** category.... No code emits 🤖 Fullsend.
  • The fixture behind gitlab-user-fail-marker-fallback in scripts/post-review-test.sh uses <!-- fullsend:review:id=abc -->, a format nothing produces. The test passes only on invented data. The earlier decline of the quoted-marker thread rested on that test and on consistency with the review scripts.
  • So the fallback resolves none of the bot's real threads. It does resolve any thread whose notes all quote a fullsend HTML marker, such as <!-- fullsend:review-agent -->, whoever wrote them. That is the forgeable path raised earlier.
  • Other GitLab identity lookups in this repo fail closed with a warning (_gitlab_bot_username in gitlab-triage-ops.lib.sh).

Suggestion: Drop the body-marker branch. If /user fails or returns no .id, print ::warning::Could not determine review identity — skipping outdated-thread resolution and return 0. Change gitlab-user-fail-marker-fallback to assert that no PUT is made. If a fallback must stay, match the real <!-- finding:f_... --> stamp and copy the fixture from a real comment body.

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants