Skip to content

Add the Dispatcher pull-request updater task - #24822

Open
HadhemiDD wants to merge 8 commits into
masterfrom
hs/dispatcher-pull-request-updater
Open

Add the Dispatcher pull-request updater task#24822
HadhemiDD wants to merge 8 commits into
masterfrom
hs/dispatcher-pull-request-updater

Conversation

@HadhemiDD

@HadhemiDD HadhemiDD commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Gives Dispatcher a voice on the pull request. Until now it ran the tests, gathered every result, and told nobody — the progress snapshots it produces had no consumer.

Dispatcher now maintains exactly one comment per pull request, posted as soon as the run starts and then edited in place, over and over, as each batch finishes. It never posts a second comment — not on later updates, and not on a later run against the same pull request, because it finds its previous comment by a hidden marker and edits that one. Within a run it never goes backwards: an update that arrives late is discarded rather than allowed to overwrite fresher results. (One active Dispatcher execution per PR is a design precondition, enforced by workflow concurrency.)

The comment answers, at a glance:

  • Is this still running, or is it the final word?
  • How many jobs have passed, failed, been skipped, or not reported yet?
  • What is each batch doing, and where is its workflow run?
  • Which jobs failed, which tests failed inside them, and where is the failing job?
  • Did anything have to be retried, and did it pass in the end?
  • Did anything fail to produce a result at all — a timeout, or missing artifacts?

Successful detail is deliberately thin. A run where everything passes is a few lines; the space goes to what a reader has to act on.

What it looks like

One preview comment on this PR shows the real rendering at three points in a run — starting, mid-run with a batch retrying, and finished with failures. It is a single comment edited three times, which is exactly how the real thing behaves.

The starting state, in full:

🔄 Dispatcher tests · in progress

Tests are still running. 3 of 3 batches have not finished yet. 12 of 12 jobs have not reported.
This comment updates automatically as each batch finishes.

░░░░░░░░░░░░░░░░░░░░░░░░ 0/12 jobs
✅ 0 passed · ❌ 0 failed · ⏳ 12 pending

Batches

Batch State Jobs Workflow
batch-01 ⏳ queued 0/4 link available after dispatch
batch-02 ⏳ queued 0/4 link available after dispatch
batch-03 ⏳ queued 0/4 link available after dispatch

and once it has finished, with something to report:

❌ Dispatcher tests · failed

Dispatcher tests failed. See the failures below.

████████████████████████ 12/12 jobs
✅ 9 passed · ❌ 2 failed · ⏭️ 1 skipped

Batches

Batch State Jobs Workflow
batch-01 ✅ passed 4/4 run 121
batch-02 ✅ passed 4/4 run 122
batch-03 ❌ failed 4/4 run 123

❌ Failures

vault / py3.12 / linux — view job

2 failed tests
  • tests.test_check::test_connection
  • tests.test_check::test_timeout

🔁 Retried jobs

  • postgres / py3.12 / linux — ✅ passed after 1 retry
  • redis / py3.12 / linux — ❌ failed after 1 retry

Notes for reviewers

An in-progress comment can never be mistaken for a final one. That is stated four ways — the heading, the callout, a bar that refuses to fill, and the footer — because a run that reads as finished when it is not is the one failure mode that actively misleads. A test asserts all four agree with each other.

A batch can fail even when every one of its jobs passed. That is a workflow-level failure — a setup, upload or cleanup step — and the comment reports it as a failure rather than quietly summing up the jobs and calling it green.

Runs with no pull request still work. Pushes to master, the nightly schedule and merge-queue runs have nothing to comment on, so the summary goes to the logs and no API call is made. The same set-up runs in every context.

If GitHub rejects the comment as too long, it is re-posted without the failure detail rather than dropped. A short comment beats no comment.

The marker identifies the comment but does not prove we own it. Quoting the bot's comment copies the marker, so an edit GitHub refuses (403/404) is treated as "not our comment": Dispatcher forgets it and writes one it does own, rather than retrying an edit that can never succeed. A rejected token, by contrast, propagates immediately with its own message instead of being retried.

Retry reporting is written but not yet reachable — it lights up when the retry work lands. Wiring Dispatcher to actually create this updater is the entry-point task (AI-6474 / AI-6484) and is not part of this PR.

Motivation

AI-6480. Dispatcher has been producing complete result snapshots since #24774 with nothing consuming them, so it currently gives no feedback to the person whose PR it is testing.

Review checklist (to be filled by reviewers)

  • Feature or bugfix MUST have appropriate tests (unit, integration, e2e)
  • Add qa/required if this PR needs QA validation, or qa/skip-qa if it does not. Exactly one of the two is required.
  • If you need to backport this PR to another branch, you can add the backport/<branch-name> label to the PR and it will automatically open a backport PR once this one is merged

Consumes the UpdatePRComment snapshots the gatherer already emits and
projects them onto one pull-request comment that never regresses.

- pr_comment.py renders DispatcherProgress as HTML-rich markdown, modelled
  on the Datadog CI Visibility comment. An unfinished run is signalled four
  redundant ways so it cannot be mistaken for a final one.
- task_pull_request_updater.py finds or creates the marked comment, holds
  its lock across revision validation and the write, and only advances the
  rendered revision once the write lands.
- Two GitHub client endpoints the updater needs: update_issue_comment and
  list_issue_comments.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@HadhemiDD HadhemiDD added the qa/skip-qa Automatically skip this PR for the next QA label Aug 10, 2026
@dd-octo-sts dd-octo-sts Bot added the ddev label Aug 10, 2026
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@HadhemiDD

HadhemiDD commented Aug 10, 2026

Copy link
Copy Markdown
Contributor Author

Important

Preview only. This is one comment, edited three times, to show what Dispatcher renders as a
run progresses. In a real run there is exactly one such comment per pull request and it is edited
in place — once when the run starts, then again as each batch finishes. The hidden marker
Dispatcher uses to find its own comment is stripped below, so a real run cannot hijack this preview.


When the run starts

🔄 Dispatcher tests · in progress

Note

Tests are still running. 3 of 3 batches have not finished yet. 12 of 12 jobs have not reported.
This comment updates automatically as each batch finishes.

░░░░░░░░░░░░░░░░░░░░░░░░ 0/12 jobs
✅ 0 passed · ❌ 0 failed · ⏳ 12 pending

Batches

BatchStateJobsWorkflow
batch-01⏳ queued0/4link available after dispatch
batch-02⏳ queued0/4link available after dispatch
batch-03⏳ queued0/4link available after dispatch
⏳ This comment updates automatically as each batch finishes.

Part way through, with a batch retrying

🔄 Dispatcher tests · in progress

Note

Tests are still running. 1 of 3 batches has not finished yet.
This comment updates automatically as each batch finishes.

███████████████████████░ 12/12 jobs
✅ 10 passed · ❌ 2 failed

Batches

BatchStateJobsWorkflow
batch-01✅ passed4/4run 121
batch-02✅ passed4/4run 122
batch-03🔄 in progress4/4run 123

❌ Failures

postgres / py3.12 / linux   view job

1 failed test
  • tests.test_check::test_connection

redis / py3.12 / linux   view job

1 failed step
  • Run E2E tests
⏳ This comment updates automatically as each batch finishes.

Finished, with failures

❌ Dispatcher tests · failed

Caution

Dispatcher tests failed. See the failures below.

████████████████████████ 12/12 jobs
✅ 9 passed · ❌ 2 failed · ⏭️ 1 skipped

Batches

BatchStateJobsWorkflow
batch-01✅ passed4/4run 121
batch-02✅ passed4/4run 122
batch-03❌ failed4/4run 123

❌ Failures

redis / py3.12 / linux   view job
No test-level failure was reported for this job.

vault / py3.12 / linux   view job

2 failed tests
  • tests.test_check::test_connection
  • tests.test_check::test_timeout

🔁 Retried jobs

  • postgres / py3.12 / linux — ✅ passed after 1 retry
  • redis / py3.12 / linux — ❌ failed after 1 retry
✅ Final result — Dispatcher has finished.

@datadog-prod-us1-4

datadog-prod-us1-4 Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Tests  Code Coverage

🎉 All green!

🧪 All tests passed
❄️ No new flaky tests detected

🎯 Code Coverage (details)
Patch Coverage: 96.84%
Overall Coverage: 88.60% (+0.14%)

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 62f5c70 | Docs | Datadog PR Page | Give us feedback!

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds Dispatcher progress reporting through a single continuously updated pull-request comment.

Changes:

  • Adds comment rendering, truncation, failure details, and progress summaries.
  • Adds a PR updater with revision ordering, retries, and comment reuse.
  • Extends the async GitHub client and test infrastructure for issue comments.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
ddev/src/ddev/cli/ci/tests/pr_comment.py Renders Dispatcher progress comments.
ddev/src/ddev/cli/ci/tests/task_pull_request_updater.py Creates and updates the PR comment.
ddev/src/ddev/utils/github_async/client.py Adds list and update comment endpoints.
ddev/tests/cli/ci/tests/test_pr_comment.py Tests comment rendering behavior.
ddev/tests/cli/ci/tests/test_task_pull_request_updater.py Tests updater ordering and failures.
ddev/tests/cli/ci/tests/test_task_test_gatherer.py Tests gatherer-to-updater integration.
ddev/tests/cli/ci/tests/preview_pr_comment.py Generates manual rendering previews.
ddev/tests/helpers/github_async.py Extends the fake GitHub client.
ddev/tests/test_fake_github_async.py Tests fake comment operations.
ddev/tests/utils/github_async/test_endpoints.py Tests new GitHub endpoints.
ddev/changelog.d/24822.added Records the new updater feature.
Suppressed comments (3)

ddev/src/ddev/cli/ci/tests/task_pull_request_updater.py:148

  • A PR author can place this public marker in their own comment. The next run will select that comment, but GitHub does not allow the Dispatcher token to edit another user's comment, so every update fails instead of finding or creating the bot-owned comment. Matching the marker does not prove authorship; also verify the expected bot/app author (and preferably require the marker at the start) before caching the ID.
        async for page in self._client.list_issue_comments(self._options.owner, self._options.repo, pr_number):
            for comment in page.data:
                # Only Dispatcher writes the marker, so matching it is enough to prove authorship.
                if COMMENT_MARKER in comment.body:
                    self._comment_id = comment.id

ddev/src/ddev/cli/ci/tests/task_pull_request_updater.py:98

  • This catches the client's actionable GitHubAuthenticationError (a subclass of HTTPStatusError) and retries it as though it were transient. On the final snapshot it is then replaced by the generic “Could not write” error, hiding the token/permission guidance; on intermediate snapshots it is swallowed entirely. Authentication failures should stop immediately while preserving their actionable message.
            except httpx.HTTPError as error:
                if _is_body_too_long(error):
                    # Not transient: the same body would be rejected again.
                    return await self._write_minimal(pr_number, message, log_extra)

ddev/src/ddev/utils/github_async/client.py:515

  • This new public endpoint is absent from ENDPOINT_CALLS, so it misses the suite's shared HTTP-error, authentication-error, and response-header coverage. ddev/tests/utils/github_async/AGENTS.md:31-36 requires every public endpoint to be registered; add a first-page list-comments case to that registry.
    async def list_issue_comments(
        self,
        owner: str,
        repo: str,
        issue_number: int,

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread ddev/src/ddev/cli/ci/tests/task_pull_request_updater.py
Comment thread ddev/tests/helpers/github_async.py Outdated
Comment thread ddev/tests/cli/ci/tests/preview_pr_comment.py Outdated
Comment thread ddev/src/ddev/utils/github_async/client.py
Comment thread ddev/src/ddev/cli/ci/tests/pr_comment.py Outdated
Comment content:
- Stop rendering the message revision: internal ordering metadata the PR
  author cannot act on.
- Render a retrying batch as plain "in progress"; the retry is reported
  per job, where it is actionable.
- Put failing test and step names in <code> rather than Markdown code
  spans. html.escape leaves backticks alone, and a pytest parametrised
  id can contain one, which closed the span early.

Updater robustness:
- Match the marker only at the start of a body, and treat a 403/404 on
  edit as "not our comment": forget it, remember it as unusable so the
  lookup cannot re-adopt it, and create one we own. Quoting the bot's
  comment copies the marker, which previously wedged the run entirely.
- Let GitHubAuthenticationError propagate instead of retrying it and
  replacing its actionable message with a generic write failure.

Test infrastructure:
- Register update_issue_comment and list_issue_comments in
  ENDPOINT_CALLS, as tests/utils/github_async/AGENTS.md requires, so
  they get the shared error and header coverage.
- Fake client: an empty comment list is one empty page, matching the
  real client, not zero pages.
- Fix the preview script's documented invocation, which could not
  resolve its imports as written.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@HadhemiDD

This comment was marked as outdated.

Measured the encoding margin rather than assuming it: a full 240-job
comment is 59,892 characters and 59,976 bytes — 1.0014x — because emoji
appear once per batch row and per heading, so their share falls as the
comment fills with ASCII test names and URLs.

The remaining ~5.5k of headroom is for GitHub's undocumented accounting,
not for UTF-8. Two tests hold it: the worst-case body must encode to
under 65,536 bytes, and the budget must keep a flat 5,000-character
reserve below that limit.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@HadhemiDD

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 408bd27667

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +369 to +370
return progress.failed > 0 or any(
batch.status is Status.FAILURE or batch.error is not None for batch in progress.batches

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Treat job-level unavailable results as failures

When a workflow job succeeds but its artifacts are missing, TaskTestGatherer records attempt.error = NO_ARTIFACTS while leaving the job and batch status as SUCCESS. _unavailable() will still render the warning for that job, but this predicate returns false, so the final comment heading/footer can say the Dispatcher tests passed even though the result was explicitly unavailable. Include latest job-attempt errors in this failure check so unavailable results cannot be presented as a passing run.

Useful? React with 👍 / 👎.

@cit-pr-commenter-54b7da

Copy link
Copy Markdown

evalya-impact-summary

evalya impact analysis
Impact analysis: 0 selected, 0 skipped (of 0 test tasks)
Publish tasks:   0 (always emitted)
Diff (12 files):
  ddev/changelog.d/24822.added
  ddev/src/ddev/cli/ci/tests/pr_comment.py
  ddev/src/ddev/cli/ci/tests/task_pull_request_updater.py
  ddev/src/ddev/utils/github_async/client.py
  ddev/tests/cli/ci/tests/preview_pr_comment.py
  ddev/tests/cli/ci/tests/test_pr_comment.py
  ddev/tests/cli/ci/tests/test_task_pull_request_updater.py
  ddev/tests/cli/ci/tests/test_task_test_gatherer.py
  ddev/tests/helpers/github_async.py
  ddev/tests/test_fake_github_async.py
  ddev/tests/utils/github_async/helpers.py
  ddev/tests/utils/github_async/test_endpoints.py

Debug a specific task: evalya plan impact --path <path> --task <task>

Learn more about CI impact filtering

HadhemiDD and others added 2 commits August 11, 2026 17:33
The client maps every non-rate-limit 403 to GitHubAuthenticationError, and
that class subclasses httpx.HTTPStatusError. Because _write checked the
authentication clause first, a 403 meaning "this comment is not yours to
edit" was re-raised as a credentials failure and never reached
_forget_unusable_comment, so only a 404 exercised the recovery that
UNUSABLE_COMMENT_STATUSES advertises for both.

The authentication clause now offers the error to that recovery before
treating it as a token problem. A 401, or a 403 with no comment to forget,
still propagates immediately with the client's own message.

_write_minimal gains the same recovery, bounded to two passes: the second
write targets a comment we just created, so it cannot be refused for not
being ours twice. Losing the fallback is the worst place to give up, since
it is the run's last chance to report anything. Its give-up policy moves to
a module-level helper rather than being repeated at each exit.

The tests that should have caught this were injecting a bare
HTTPStatusError for a 403, a shape the real client never produces, so they
passed against the broken path. They now build the error the client
actually raises, and cover the minimal-retry recovery plus two guards that
a genuinely rejected token is not swallowed into a silent comment rewrite.

Also replaces an unguarded re.search(...).group(1) in the gatherer suite
with the guarded helper the updater suite already uses.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A job can conclude success while its artifacts never arrive: the gatherer
derives the job's status from the workflow conclusion and sets NO_ARTIFACTS
from the missing artifact directory, two independent sources with nothing
coupling them. _has_failure only honoured batch-level errors, so such a run
rendered "✅ Dispatcher tests · passed" and an "⚠️ Unavailable results"
section at the same time, contradicting _unavailable's own contract that
batches and jobs whose result could not be established are never rendered
as success.

Counting attempt errors as failures would have swapped one contradiction
for another: the failures section only lists jobs whose status is FAILURE,
so the comment would have said "failed" with a CAUTION pointing at a
section that was never rendered, next to "❌ 0 failed" in the totals.

So a run whose results could not be established now reads as neither passed
nor failed but as a third state, with the heading, the alert and the footer
all agreeing. A failure still outranks it, since it is the more actionable
of the two.

Batch errors move out of _has_failure for the same reason. That also fixes
the case where a workflow concluded successfully but reported no jobs: it
used to render as a failure with an empty failures section. A timeout is
unaffected and still reads as a failure, because it fails every job in the
batch.

The count in the alert and the bullets in the section now come from one
list, so they cannot disagree with each other.

Counters are untouched: a missing-artifacts job still counts as passed,
which progress.py establishes deliberately — the error qualifies the
execution, it does not erase it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@HadhemiDD
HadhemiDD marked this pull request as ready for review August 12, 2026 08:21
@HadhemiDD
HadhemiDD requested a review from a team as a code owner August 12, 2026 08:21
@dd-octo-sts

dd-octo-sts Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Validation Report

All 21 validations passed.

Show details
Validation Description Status
agent-reqs Verify check versions match the Agent requirements file
ci Validate CI configuration and code coverage settings
codeowners Validate every integration has a CODEOWNERS entry
config Validate default configuration files against spec.yaml
dep Verify dependency pins are consistent and Agent-compatible
http Validate integrations use the HTTP wrapper correctly
imports Validate check imports do not use deprecated modules
integration-style Validate check code style conventions
jmx-metrics Validate JMX metrics definition files and config
labeler Validate PR labeler config matches integration directories
legacy-signature Validate no integration uses the legacy Agent check signature
license-headers Validate Python files have proper license headers
licenses Validate third-party license attribution list
metadata Validate metadata.csv metric definitions
models Validate configuration data models match spec.yaml
openmetrics Validate OpenMetrics integrations disable the metric limit
package Validate Python package metadata and naming
qa-label Validate the pull request declares whether it needs QA for the next Agent release
readmes Validate README files have required sections
saved-views Validate saved view JSON file structure and fields
version Validate version consistency between package and changelog

View full run

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 62f5c70109

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".


def _is_body_too_long(error: httpx.HTTPError) -> bool:
"""Whether *error* is GitHub rejecting the comment body for exceeding its length limit."""
return isinstance(error, httpx.HTTPStatusError) and error.response.status_code == BODY_TOO_LONG_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.

P2 Badge Check 422 content before dropping detail

When GitHub returns 422 for a comment validation/spam response instead of the specific over-long-body validation (GitHub documents 422 for these endpoints as validation failed or spammed), this predicate classifies it as BODY_TOO_LONG_STATUS, so _write() immediately resubmits a minimal comment and can hide the real failure instead of surfacing or retrying it correctly. Gate the fallback on the response body/error code for the “body is too long” validation, not just the status code.

Useful? React with 👍 / 👎.

@HadhemiDD

Copy link
Copy Markdown
Contributor Author

Preview: the all-passing final comment

The preview above covers starting, mid-run with a batch retrying, and finished with failures. This is the remaining case — a run where everything passed — which is what "successful detail is deliberately thin" means in practice: no Failures section, no Unavailable results, no Retried jobs, because there is nothing a reader has to act on.

Produced by render_comment from a three-batch, twelve-job passing snapshot. It is verbatim but for one deliberate change: the hidden <!-- ddev-dispatcher-tests --> marker has been stripped. Left in as the first line, a real Dispatcher run on this pull request would find this comment by the marker and edit it — the ownership hazard the 403/404 recovery exists for. It is an HTML comment, so removing it changes nothing you can see.


✅ Dispatcher tests · passed

████████████████████████ 12/12 jobs
✅ 12 passed · ❌ 0 failed

Batches

BatchStateJobsWorkflow
batch-01✅ passed4/4run 121
batch-02✅ passed4/4run 122
batch-03✅ passed4/4run 123
✅ Final result — Dispatcher has finished.

@HadhemiDD

Copy link
Copy Markdown
Contributor Author

Preview: a run whose results could not be established

Same twelve jobs as the passing preview above, with one difference: vault concluded success in its
workflow, but its artifacts never arrived. This is the state added in 58c0cd1 to address
Codex's finding.

Two things are worth reading carefully, because they look like a contradiction and are not:

  • The counters still say ✅ 12 passed · ❌ 0 failed, and every batch chip still says ✅ passed. That is deliberate. The chip is the workflow's own conclusion, taken verbatim, and the counters follow progress.py, where a missing-artifacts execution still counts — the error qualifies the execution, it does not erase it. What changed is only the run's overall verdict.
  • It is not reported as a failure either. Nothing failed, so there is no Failures section, and a [!CAUTION] See the failures below would point at something that was never rendered. Hence [!WARNING], and a heading that says neither passed nor failed.

Before the fix this exact snapshot rendered ## ✅ Dispatcher tests · passed with the
⚠️ Unavailable results section sitting directly underneath it. A batch-level error — a workflow that
concluded successfully but reported no jobs — now renders the same way; that one used to render as a
failure with an empty failures section.

The marker is stripped here for the same reason as the previous preview.


⚠️ Dispatcher tests · results incomplete

Warning

1 result could not be established. Nothing failed, but this is not a clean pass.
See the unavailable results below.

████████████████████████ 12/12 jobs
✅ 12 passed · ❌ 0 failed

Batches

BatchStateJobsWorkflow
batch-01✅ passed4/4run 121
batch-02✅ passed4/4run 122
batch-03✅ passed4/4run 123

⚠️ Unavailable results

  • vault / py3.12 / linux — no artifacts were downloaded for this job
⚠️ Final result — Dispatcher has finished with unavailable results.

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

Labels

ddev qa/skip-qa Automatically skip this PR for the next QA team/agent-integrations

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants