Skip to content

fix: resolve issue #299 - bert-e: pr status should be displayed at all time - #301

Open
matthiasL-scality wants to merge 7 commits into
mainfrom
fix/issue-299
Open

matthiasL-scality wants to merge 7 commits into
mainfrom
fix/issue-299

Conversation

@matthiasL-scality

Copy link
Copy Markdown
Contributor

Automated Fix for Issue #299

Issue: bert-e: pr status should be displayed at all time

Description:
TL;DR: authors and reviewers find the status of a pull request fast. This decreases the questions about the status.

Context: Bert-E writes a new comment at each step. These comments mix with the comments of humans and other bots. The status must be asked for and sometines the link to integration branch are hard to find or just not mentionned.

What: the user cannot find the current status of the pull request. Old error comments stay visible after the fix. The user cannot see what is the current status of Bert-e.

How: show the status in a prominent place, as close as possible to the description. Do not change the description. Update the status with the latest state, for example Conflict. Show the name of each integration pull request and branches. If possible, show the build status next to it. For example dependabot changes its message when rebasing.

Done:

the status shows near the description of the pull request

the description of the pull request does not change

the status shows the latest state, for example Conflict, and the name of each integration pull request

optional: the status shows the build status next to the state


Changes Made

This PR was automatically generated by Claude Code based on the issue analysis.

AI Evaluation

  • Relevance: true
  • Confidence: 70%
  • Reason: Feature request bien spécifiée, avec contexte, objectif et critères d'acceptation clairs. Il s'agit d'afficher un statut unique de la PR, mis à jour en continu près de la description (par exemple via un commentaire épinglé ou édité), sans modifier la description. Ce statut liste l'état courant (Conflict, etc.) et les PR/branches d'intégration, avec en option le statut de build. Le changement est réalisable mais conséquent : il touche le workflow gitwaterflow, l'abstraction git_host (édition de commentaires sur GitHub et Bitbucket) et les templates. La part de conception non triviale réduit la confiance pour une correction automatique.

Files Modified

[
"bert_e/workflow/gitwaterflow/init.py",
"bert_e/workflow/pr_utils.py",
"bert_e/workflow/base.py",
"bert_e/git_host/base.py",
"bert_e/git_host/github/init.py",
"bert_e/git_host/bitbucket/init.py",
"bert_e/git_host/mock.py",
"bert_e/exceptions.py",
"bert_e/templates/status.md",
"bert_e/settings.py"
]

Tests Created

[
"bert_e/tests/unit/test_pr_status_comment.py",
"bert_e/tests/unit/test_status_template.py"
]


Generated by n8n automation workflow

Maintain a single status comment per pull request, edited in place, showing
the latest state, the integration branches / pull requests and their build
status.
@matthiasL-scality
matthiasL-scality requested a review from a team as a code owner October 2, 2026 15:57
except messages.TemplateException as err:
# The status comment goes first: the message must stay the most
# recent comment of the pull request.
publish_status(job, err)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

If publish_status raises anything outside EXPECTED_ERRORS (Jinja error, KeyError, the assert in render_status, an error from _check_approvals_status), that error replaces err. notify_user is then skipped and the user never gets the real message. Wrap this call (and the one on line 69) so a failed status update can't hide the original exception, for example by catching Exception and logging it inside publish_status, which matches its "never fatal" docstring.

— Claude Code

msg = render('pr_status.md', icon=state.icon, label=state.label,
code=state.code, integration=integration, status=report,
active_options=job.active_options)
assert msg.startswith(STATUS_COMMENT_HEADER)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Production code shouldn't depend on assert: it is stripped under -O, and when it fails it raises AssertionError, which publish_status doesn't catch (see the comment on handle_pull_request). Log and return instead, or drop the check since a test already covers the template header.

— Claude Code

pr = prs.get(branch.name)
build = None
if key:
build = job.project_repo.get_build_status(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This calls get_build_status once per integration branch, then _build_status_report → _check_builds_status calls it again for the same commits. It also re-clones and runs build_branch_cascade and the approvals checks. All of this happens on every run, including BuildInProgress, which fires on each build webhook. On GitHub, find_status_comment also re-lists all comments each time. That is a lot of extra API calls against rate limits. Fetch the build status once and share it between the table and the checklist, or skip the integration/report refresh for BuildInProgress/BuildNotStarted.

— Claude Code

@claude

claude Bot commented Oct 2, 2026

Copy link
Copy Markdown
  • An exception from publish_status that isn't in EXPECTED_ERRORS replaces the original TemplateException in handle_pull_request, so notify_user is skipped
    - Catch every exception in publish_status (log it with exc_info) so it is really never fatal
    - Replace the assert in render_status with a log-and-return
    - The status refresh adds many git host API calls on every run, including BuildInProgress
    - Fetch the build status of each integration branch once and share it between the table and _build_status_report
    - Skip the integration/report refresh for silent build-waiting states, or when nothing changed

    Review by Claude Code

@upsun-dispatch

upsun-dispatch Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

📋 PR Summary

The PR adds one persistent status comment to each pull request. Bert-E edits this comment in place to show the current state (for example Conflict or Waiting) along with the integration branches and pull requests, and the PR description stays unchanged. To support this, comment editing is added to the git_host layer for GitHub, Bitbucket and the mock host, and the PR also adds a template, settings, documentation and tests. The latest push changes how silent states (exceptions that don't produce their own comment) are matched: the lookup now uses isinstance, so subclasses such as WaitOptionSet also replace the previous state. A test covers this case.

Changes
Layer / File(s) Summary
Status comment workflow
bert_e/workflow/gitwaterflow/status_comment.py Defines the status comment model. The silent-state lookup now matches by isinstance instead of an exact type match.
Tests
bert_e/tests/unit/test_pr_status_comment.py Adds a test checking that WaitOptionSet replaces an earlier Conflict state in the single status comment.

@upsun-dispatch upsun-dispatch Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Warning

Changes suggested — 🟡 3 warnings · 🔵 1 minor point

🔍 Full review · 19 files reviewed

Verification
  • find_comment and the command scan in handle_comments skip the robot's status comment, so the dont_repeat_if_in_history check and command detection still see the real last message.
  • Bitbucket add_comment appends to the non-empty _comments cache, and edit updates data['content']['raw'], so find_status_comment does not create a duplicate within one run.
  • GitHub comments re-fetches on every access, so a status comment created earlier in the run is found again without needing a cache.
  • render_status skips cloning, integration rows and the checklist for final states (Merged, Partially merged, Declined).

New unit tests (test_pr_status_comment.py, test_status_template.py, test_github_bitbucket_edit_comment.py), an end-to-end test_status_comment in test_bert_e.py and test_pull_request_edit_comment in test_git_host.py cover the new code, and the tox jobs in .github/workflows/main.yaml run them. No test covers command-answer exceptions (e.g. UnknownCommand) overwriting the state, or an unexpected error inside publish_status.

Review details
  • Commit: 38a739a
  • Model: claude-opus-5-5

Review 1 of 10 for this pull request · View the full run

Comment thread bert_e/exceptions.py
Comment thread bert_e/workflow/gitwaterflow/status_comment.py
Comment thread bert_e/workflow/gitwaterflow/status_comment.py
Comment thread bert_e/workflow/gitwaterflow/__init__.py
@codecov

codecov Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.79725% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.82%. Comparing base (cfe0bc2) to head (246c2d2).

Files with missing lines Patch % Lines
bert_e/server/webhook.py 25.00% 6 Missing ⚠️
bert_e/workflow/gitwaterflow/status_comment.py 98.94% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #301      +/-   ##
==========================================
+ Coverage   90.21%   90.82%   +0.61%     
==========================================
  Files          82       86       +4     
  Lines       11293    11851     +558     
==========================================
+ Hits        10188    10764     +576     
+ Misses       1105     1087      -18     
Flag Coverage Δ
integration 87.78% <91.21%> (+0.10%) ⬆️
tests 87.75% <91.21%> (+0.10%) ⬆️
tests-BuildFailedTest 25.84% <28.45%> (+0.13%) ⬆️
tests-QuickTest 32.97% <28.45%> (-0.05%) ⬇️
tests-RepositoryTests 25.53% <28.45%> (+0.13%) ⬆️
tests-TaskQueueTests 51.15% <63.17%> (+1.54%) ⬆️
tests-TestBertE 67.54% <88.70%> (+0.56%) ⬆️
tests-TestQueueing 52.92% <64.01%> (+1.23%) ⬆️
tests-api-mock 14.07% <6.18%> (-0.38%) ⬇️
tests-noqueue 78.67% <88.70%> (+0.29%) ⬆️
tests-noqueue-BuildFailedTest 25.84% <28.45%> (+0.13%) ⬆️
tests-noqueue-QuickTest 32.97% <28.45%> (-0.05%) ⬇️
tests-noqueue-RepositoryTests 25.53% <28.45%> (+0.13%) ⬆️
tests-noqueue-TaskQueueTests 51.15% <63.17%> (+1.54%) ⬆️
tests-noqueue-TestBertE 64.17% <87.44%> (+0.62%) ⬆️
tests-noqueue-TestQueueing 25.55% <28.45%> (+0.13%) ⬆️
tests-server 26.01% <11.51%> (-0.71%) ⬇️
unittests 46.01% <85.73%> (+2.60%) ⬆️
utests 32.50% <83.67%> (+3.40%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@upsun-dispatch upsun-dispatch Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Warning

Changes suggested — 🟡 1 warning · 1 still open

🔁 Incremental · 3 files reviewed

Outstanding from earlier reviews:

  • 🔵 #4167470304 — bert_e/workflow/gitwaterflow/status_comment.py:148: Doubles API/git work per run and increases rate-limit pressure on busy repos. — The cache only removes the duplicate get_build_status calls. The rest of the per-run API and git work remains on every build webhook.
Verification
  • UnknownCommand, NotEnoughCredentials, IncorrectCommandSyntax, IncorrectPullRequestNumber, FlakyGitHost and NotAuthor now set reports_state = False, so State.from_exception returns None for them.
  • The except TemplateException path in handle_pull_request can no longer lose notify_user: publish_status now catches Exception around rendering and upsert.
  • render_status restores get_build_status in a finally with del. This is safe because the host Repository classes define it as a class method, not an instance attribute.
  • render_status now logs a missing header instead of asserting.

This push adds test_publish_never_raises_on_unexpected_errors, test_publish_ignores_command_answers and test_publish_does_not_comment_old_declined_pull_requests in bert_e/tests/unit/test_pr_status_comment.py. No test covers the build-status cache sharing, or a failure inside the new declined guard's find_status_comment call.

Review details

Review 2 of 10 for this pull request · View the full run

Comment thread bert_e/workflow/gitwaterflow/status_comment.py Outdated

early_checks(job)
send_greetings(job)
ensure_status_comment(job)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The status comment is created here as Being analyzed, but NothingToDo (raised below when dst.includes_commit(src) or the source branch was deleted) is not handled in handle_pull_request. The comment then stays Being analyzed forever, for example on a PR that was already merged when this feature is rolled out.
Map NothingToDo to a state in SILENT_STATES (only when a status comment already exists, as done for PullRequestDeclined), or create the comment only once a real state is known.

— Claude Code

pull_request.add_comment(msg)
elif comment.text != msg:
LOG.debug('UPDATING STATUS COMMENT %s', msg)
comment.edit(msg)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

On GitHub, each status comment create/edit sends an issue_comment webhook, and handle_github_issue_comment turns it into a new PullRequestJob without checking the author or the action. Most runs now end with an edit, so each one starts another full run (clone, cascade, API calls). It stops only when the text no longer changes, and build statuses keep changing it.
In bert_e/server/webhook.py, ignore issue_comment events written by the robot (at least edited ones).

— Claude Code

repo.get_build_status = cached_build_status
try:
integration = _integration_rows(job)
report = _build_status_report(job)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This runs on every TemplateException path, so each failing run now does a second clone check and cascade build (_build_status_report calls build_branch_cascade again) plus one PR list, approvals and one build status call per integration branch. On large repos this adds a lot of git host API usage, which already hits rate limits.
Reuse job.git.cascade when it is already built, and skip the checklist for states where it adds nothing (for example Conflict).

— Claude Code

@claude

claude Bot commented Oct 2, 2026

Copy link
Copy Markdown
  • Status comment can stay stuck on Being analyzed: NothingToDo raised after ensure_status_comment (PR already merged, source branch deleted) never updates it
      - Map NothingToDo to a state in SILENT_STATES when a status comment exists
      - Or create the comment only once a real state is known
    - On GitHub, every status comment create/edit fires an issue_comment webhook that starts another full PullRequestJob
      - Ignore issue_comment events written by the robot (at least edited ones) in bert_e/server/webhook.py
    - render_status rebuilds the cascade and makes many git host API calls on every failing run
      - Reuse job.git.cascade when it is already built
      - Skip the checklist for states where it adds nothing

    Review by Claude Code

@upsun-dispatch upsun-dispatch Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Warning

Changes suggested — 🟡 2 warnings · 🔵 1 minor point

🔁 Incremental · 4 files reviewed

Verification
  • publish_status now runs the UPDATE_ONLY find_status_comment lookup inside the try, so host errors there are logged instead of escaping.
  • render_status wraps repo.get_build_status with a per-call cache and restores the original in finally, so the table and checklist share build fetches.
  • settings.robot is a UserDict whose __eq__ matches a string against username or account_id, so the webhook login comparison works.
  • Command-answer exceptions and FlakyGitHost set reports_state = False, so they no longer overwrite the status comment.

Unit tests in bert_e/tests/unit/test_pr_status_comment.py were updated: they now treat NothingToDo as a state and check that handle_pull_request publishes it. No test covers the queued-PR NothingToDo path, the wait path, or the new webhook robot filter.

Review details

Review 3 of 10 for this pull request · View the full run

Comment thread bert_e/workflow/gitwaterflow/__init__.py
Comment thread bert_e/workflow/gitwaterflow/status_comment.py Outdated
Comment thread bert_e/server/webhook.py
notify_user(job.settings, job.pull_request, err)
raise
except (messages.BuildInProgress, messages.BuildNotStarted,
messages.PullRequestDeclined, messages.NothingToDo) as err:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Publishing NothingToDo overwrites final states. After a queue merge sets the status to "Merged", the next run on the PR (for example the close webhook) hits early_checks, which raises NothingToDo("The pull request is 'MERGED'"), or hits dst.includes_commit(src). Both replace "Merged" with "Nothing to do". The same thing happens to "Declined" on the run after cleanup, and to any state when the wait option is set. Don't publish NothingToDo, or skip the update when the existing comment already shows a final state.

Suggested change
messages.PullRequestDeclined, messages.NothingToDo) as err:
messages.PullRequestDeclined) as err:



— Claude Code

@claude

claude Bot commented Oct 2, 2026

Copy link
Copy Markdown
  • Final states "Merged" and "Declined" get overwritten by "Nothing to do" on the next run (early_checks / includes_commit / cleanup raise NothingToDo, which is published as an update)
    - Stop publishing NothingToDo in handle_pull_request
    - Or never replace a final state (Merged, Partially merged, Declined) with "Nothing to do", and add a test for a run on an already merged PR

    Review by Claude Code

@upsun-dispatch upsun-dispatch Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Warning

Changes suggested — 🟡 1 warning

🔁 Incremental · 3 files reviewed

Verification
  • publish_status now catches any Exception after EXPECTED_ERRORS, so a failed status update can no longer replace the original exception or skip notify_user.
  • NothingToDo replaces only a status comment that still reads State.initial().label, so it no longer overwrites "Queued", "Merged" or "Declined".
  • BuildInProgress and BuildNotStarted are no longer in FINAL_STATES, so the integration table and build statuses render while builds run.
  • The Bitbucket handler now skips comment* events whose author nickname, username or account_id equals settings.robot, matching the GitHub filter.

This push adds settings.robot to the MockBertE fixture in bert_e/tests/test_server.py. I found no test in the diff for the new Bitbucket robot-comment filter or for the NothingToDo rule that only replaces the initial state. The pytest suite (.venv/bin/pytest) is the repository check that covers this code.

Review details

Review 4 of 10 for this pull request · View the full run

Comment thread bert_e/workflow/gitwaterflow/status_comment.py
@upsun-dispatch
upsun-dispatch Bot dismissed stale reviews from themself October 2, 2026 16:56

Superseded: the latest Upsun Dispatch review no longer requests changes.

raise
except (messages.BuildInProgress, messages.BuildNotStarted,
messages.PullRequestDeclined, messages.NothingToDo) as err:
publish_status(job, err)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Every run that ends here (BuildInProgress fires on each build status webhook) now calls render_status with git on: a second clone_git_repo, a second build_branch_cascade via _build_status_report, get_pull_requests, one get_build_status per integration branch, and the approval checks. That roughly doubles the git host API calls per job, so rate limits get hit sooner on busy repos.
Reuse what the main flow already computed (job.git.cascade, integration branches), or use with_git=False for silent states.

— Claude Code

return cache[(revision, key)]

# share the build statuses between the table and the checklist
repo.get_build_status = cached_build_status

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

job.project_repo is the shared bert_e.project_repo, so this monkey-patch changes the repository object for every job. If any other thread calls get_build_status during this window, it reads the per-call cache. And if get_build_status was already an instance attribute (a wrapper or a test mock), the del in finally deletes it instead of restoring it.
Pass a caching wrapper in explicitly, or save the original and put it back with repo.get_build_status = original.

— Claude Code

Comment thread bert_e/workflow/pr_utils.py Outdated
if settings.no_comment or settings.interactive:
LOG.debug('Not sending the status comment.')
return
comment = find_status_comment(pull_request, settings.robot)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

GitHub pull_request.comments is not cached, so each find_status_comment call fetches every comment page again. ensure_status_comment and publish_status (for UPDATE_ONLY) look up the comment first, then this call looks it up again, so each run lists the comments 2 or 3 extra times.
Pass the comment already found into upsert_status_comment instead of searching again.

— Claude Code

@claude

claude Bot commented Oct 2, 2026

Copy link
Copy Markdown
  • Each status refresh redoes the clone, branch cascade and build/PR lookups in the exception handler, including on every BuildInProgress run. That roughly doubles git host API calls per job.
    - Reuse job.git.cascade and the integration branches already computed by the main flow
    - Or use with_git=False for silent states
    - render_status monkey-patches get_build_status on the shared project_repo, and del deletes any instance attribute that was already there
    - Pass a caching wrapper in explicitly, or put the saved original back
    - find_status_comment runs several times per run, and on GitHub each run fetches all comments again
    - Pass the comment already found into upsert_status_comment

    Review by Claude Code

job.git.src_branch and job.git.dst_branch):
clone_git_repo(job)
repo = job.project_repo
had_own = 'get_build_status' in vars(repo)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This swaps out get_build_status on the shared project_repo object for a while, then puts it back. That is fragile: an exception in the cleanup path, or any other code using the repo object during that window, sees the patched method.
Pass a cache dict to _integration_rows instead. On GitHub, cache.BUILD_STATUS_CACHE already covers repeated lookups.

— Claude Code

prs = {}
if names:
prs = {pr.src_branch: pr for pr in
job.project_repo.get_pull_requests(src_branch=names)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Every run that ends on a state now makes extra API calls. On GitHub that is one PR listing per integration branch (get_pull_requests sends one request per src_branch), plus one get_build_status per branch, plus the approvals and cascade checks in _build_status_report. GitHub also does not cache pr.comments, so ensure_status_comment and upsert_status_comment each list all comments again.
This can hit API rate limits on busy repos. Reuse the integration PRs already known in job, and pass the comment found by ensure_status_comment along instead of searching again.

— Claude Code

if state is None:
return
existing = None
waiting = (isinstance(outcome, exceptions.NothingToDo) and

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Matching on 'wait option' in str(outcome) ties this code to the exception message text in check_wait. If that message is reworded, a PR with the wait option silently shows the wrong state.
Raise a dedicated NothingToDo subclass (or set an attribute) for the wait case and check with isinstance.

— Claude Code

@claude

claude Bot commented Oct 2, 2026

Copy link
Copy Markdown
  • render_status temporarily swaps get_build_status on the shared project_repo object
    - Pass an explicit cache dict to _integration_rows instead
    - The status comment adds many API calls per run (one PR listing and one build status per integration branch, plus repeated full comment listings on GitHub)
    - Reuse the integration PR data already in job
    - Pass the comment found by ensure_status_comment along instead of searching again
    - The wait-option state is detected by matching the exception message text
    - Use a dedicated NothingToDo subclass or attribute

    Review by Claude Code

@upsun-dispatch upsun-dispatch Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Warning

Changes suggested — 🟡 1 warning

🔁 Incremental · 3 files reviewed

Verification
  • The per-call monkey-patch of project_repo.get_build_status is gone, so the shared repo object is no longer modified while a job runs.
  • check_dependencies is the only place that raises WaitOptionSet, and handle_pull_request still catches it because it subclasses NothingToDo.
  • Both the GitHub and Bitbucket get_build_status serve from BUILD_STATUS_CACHE only when the cached state is SUCCESSFUL; any other state triggers a new API call.

This push adds and changes no tests. test_pr_status_comment.py has no case for the wait option, so the broken WaitOptionSet path is not covered.

Review details

Review 6 of 10 for this pull request · View the full run

Comment thread bert_e/workflow/gitwaterflow/status_comment.py
r'(?<=[a-z])(?=[A-Z])', ' ', type(exc).__name__).capitalize()
return cls(label, exc.state_status or exc.status, exc.code,
final=isinstance(exc, FINAL_STATES))
label = SILENT_STATES.get(type(exc))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

type(exc) only matches exact classes, so WaitOptionSet (a subclass of NothingToDo) gets None here. publish_status then returns at if state is None before it reaches the waiting branch, so the 'Waiting (wait option is set)' state never shows. Match on subclasses instead, and add a test with /wait.

Suggested change
label = SILENT_STATES.get(type(exc))
label = next((v for k, v in SILENT_STATES.items()
if isinstance(exc, k)), None)



— Claude Code

@claude

claude Bot commented Oct 2, 2026

Copy link
Copy Markdown
  • The wait-option state is never shown in the status comment: State.from_exception looks up SILENT_STATES by exact type, so WaitOptionSet returns None and publish_status exits before its waiting branch (status_comment.py:105, :174).
    - Match with isinstance over SILENT_STATES, or handle WaitOptionSet before calling from_exception.
    - Add a test that sets /wait and checks the status comment says 'Waiting (wait option is set)'.

    Review by Claude Code

@upsun-dispatch
upsun-dispatch Bot dismissed their stale review October 2, 2026 17:34

Superseded: the latest Upsun Dispatch review no longer requests changes.

Comment thread bert_e/server/webhook.py
def handle_bitbucket_pr_event(bert_e, event, json_data):
"""Handle a Bitbucket webhook sent on a pull request event."""
commenter = (json_data.get('comment') or {}).get('user') or {}
if event.startswith('comment') and bert_e.settings.robot in (

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

settings.robot is a UserDict, and its __eq__ raises ValueError when compared with None. Bitbucket no longer sends username in user objects (and nickname/account_id can be missing), so robot in (..., None, ...) raises on every comment written by a human. The webhook then fails and the command is never handled. The tests miss this because test_server.py sets robot = 'robot' (a plain str). Drop the empty values before comparing, and add a test with a real UserDict.

— Claude Code

clone_git_repo(job)
# the git hosts cache build statuses, which are thus shared between
# the table and the checklist
integration = _integration_rows(job)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This runs on almost every job, including BuildInProgress/BuildNotStarted, which are the most common outcomes. Each run now adds a clone, one integration PR listing, one build-status call per integration branch and a full _build_status_report (approvals, builds, Jira fix versions, history). On GitHub that roughly doubles API usage per webhook and makes rate limits more likely. Consider skipping the refresh when the state did not change, or using only the data the main flow already fetched.

— Claude Code

@claude

claude Bot commented Oct 2, 2026

Copy link
Copy Markdown
  • handle_bitbucket_pr_event raises ValueError on human comments: UserDict.__eq__ cannot compare with the None payload fields (username is no longer sent by Bitbucket).
    - Filter out empty values before the in check (same issue if login is ever missing in the GitHub handler).
    - Use a real UserDict for settings.robot in test_server.py and add a test for a comment written by a human.
    - Status comment refresh adds a lot of git host / Jira calls on every job (BuildInProgress included).
    - Skip the refresh when the state did not change, or reuse the data the main flow already fetched.

    Review by Claude Code

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.

2 participants