diff --git a/.github/scripts/pull-request-dashboard/pr_status_comment.py b/.github/scripts/pull-request-dashboard/pr_status_comment.py
index 62b1dd6ff96..2bd152679e6 100644
--- a/.github/scripts/pull-request-dashboard/pr_status_comment.py
+++ b/.github/scripts/pull-request-dashboard/pr_status_comment.py
@@ -12,8 +12,8 @@
gh_api,
run_gh,
)
-from dashboard_override import PRE_REVIEW_ROUTES, author_override_guidance
-from route_presentation import route_status_summary
+from dashboard_override import PRE_REVIEW_ROUTES
+from route_presentation import route_status_summary, status_headline
from state import (
load_dashboard_state_cache,
load_status_comment_rollout_state,
@@ -32,7 +32,7 @@
)
# Increment whenever render_status_comment changes in a way existing comments
# need to adopt. Hourly runs durably roll the revision out to all open PRs.
-STATUS_COMMENT_REVISION = 13
+STATUS_COMMENT_REVISION = 14
STATUS_COMMENT_ROLLOUT_BATCH_SIZE = 50
AUTHOR_ACTION_FEEDBACK_LINK_LIMIT = 20
NON_BLOCKING_CHECK_FAILURE_LIMIT = 20
@@ -43,10 +43,7 @@
STATUS_REPORT_TRUNCATION_NOTICE = (
"[Status comment truncated to keep this report link usable.]"
)
-AUTHOR_GUIDANCE = (
- "For each item, reply to move the discussion forward, e.g. link to the commit "
- "that addresses it, explain why no change is needed, or ask a follow-up question."
-)
+RESPONSE_EXAMPLES = "(e.g. link a commit, explain why not, ask a follow-up)"
DASHBOARD_APP_SLUG = "opentelemetry-pr-dashboard"
# Remove after migrating open PRs as described by the post-rollout
# compatibility cleanup in WEBHOOK_SETUP.md.
@@ -97,44 +94,131 @@ def status_report_url(pr: dict[str, Any], status_comment: str) -> str:
return f"{STATUS_REPORT_ISSUE_URL}?{query}"
-def accuracy_note(
+def _bounded_report_url(pr: dict[str, Any], status_comment: str) -> str:
+ report_url = status_report_url(pr, status_comment)
+ if len(report_url) <= STATUS_REPORT_URL_MAX_CHARS:
+ return report_url
+ lower_bound = 0
+ upper_bound = len(status_comment)
+ while lower_bound <= upper_bound:
+ midpoint = (lower_bound + upper_bound) // 2
+ truncated_status_comment = (
+ f"{status_comment[:midpoint]}\n{STATUS_REPORT_TRUNCATION_NOTICE}"
+ )
+ candidate_url = status_report_url(pr, truncated_status_comment)
+ if len(candidate_url) <= STATUS_REPORT_URL_MAX_CHARS:
+ report_url = candidate_url
+ lower_bound = midpoint + 1
+ else:
+ upper_bound = midpoint - 1
+ return report_url
+
+
+def status_footer(
pr: dict[str, Any],
status_comment: str,
+ *,
override_route: str = "",
-) -> str:
- report_url = status_report_url(pr, status_comment)
- if len(report_url) > STATUS_REPORT_URL_MAX_CHARS:
- lower_bound = 0
- upper_bound = len(status_comment)
- while lower_bound <= upper_bound:
- midpoint = (lower_bound + upper_bound) // 2
- truncated_status_comment = (
- f"{status_comment[:midpoint]}\n{STATUS_REPORT_TRUNCATION_NOTICE}"
- )
- candidate_url = status_report_url(pr, truncated_status_comment)
- if len(candidate_url) <= STATUS_REPORT_URL_MAX_CHARS:
- report_url = candidate_url
- lower_bound = midpoint + 1
- else:
- upper_bound = midpoint - 1
- note = (
- "This automated status or its linked feedback items may be incorrect. "
- f"If something looks wrong, please [report it]({report_url}) with the result you expected."
- )
+ terminal: bool = False,
+) -> list[str]:
+ report_url = _bounded_report_url(pr, status_comment)
+ lines = [
+ "",
+ "Doesn't look right?
",
+ "",
+ "
",
+ "",
+ ]
+ if not terminal:
+ lines.append(
+ "- **Just replied or pushed?** Anything around or after the refresh "
+ "time above may not be picked up yet \u2014 give it a few minutes."
+ )
if override_route:
- note += " " + author_override_guidance(
- "If the last refreshed time above predates your latest reply or "
- "push, the dashboard hasn't processed it yet.",
- route=override_route,
+ lines.append(
+ "- **Should this be with reviewers?** Comment "
+ "`/dashboard route:reviewers` to route it to them."
+ )
+ report_lead = (
+ "Anything wrong \u2014 including the routing?"
+ if override_route
+ else "Anything look wrong?"
+ )
+ lines.append(
+ f"- **{report_lead}** [Report it]({report_url}) with what you expected; "
+ "it helps us improve the dashboard."
+ )
+ lines.extend(["", " "])
+ return lines
+
+
+def non_blocking_failure_summary(non_blocking_check_failures: list[str]) -> str:
+ if not non_blocking_check_failures:
+ return ""
+ displayed_failures = non_blocking_check_failures[:NON_BLOCKING_CHECK_FAILURE_LIMIT]
+ names = format_list([
+ markdown_escape(truncate(name, NON_BLOCKING_CHECK_FAILURE_NAME_LIMIT))
+ for name in displayed_failures
+ ])
+ if len(non_blocking_check_failures) == 1:
+ note = f"{names} is failing but is not a required check."
+ else:
+ note = f"{names} are failing but are not required checks."
+ omitted_count = len(non_blocking_check_failures) - len(displayed_failures)
+ if omitted_count:
+ noun = "failure" if omitted_count == 1 else "failures"
+ omitted_verb = "is" if omitted_count == 1 else "are"
+ note += (
+ f" {omitted_count} additional non-blocking check {noun} "
+ f"{omitted_verb} not shown."
)
- return f"_{note}_"
+ return note
+
+
+def author_body(
+ *,
+ feedback_count: int,
+ failing_count: int,
+ non_blocking_failure_note: str,
+ review_thread_urls: list[str],
+ top_level_feedback_urls: list[str],
+) -> list[str]:
+ noun = "item" if feedback_count == 1 else "items"
+ if failing_count and feedback_count:
+ checks_bullet = "- **Required checks are failing** \u2014 investigate the failures."
+ if non_blocking_failure_note:
+ checks_bullet += f" Note: {non_blocking_failure_note}"
+ body = [
+ "Two things need attention:",
+ checks_bullet,
+ f"- **{feedback_count} review {noun}** — respond to each {RESPONSE_EXAMPLES}:",
+ ]
+ body.extend(
+ feedback_breakdown_lines(
+ review_thread_urls, top_level_feedback_urls, indent=" "
+ )
+ )
+ return body
+ if feedback_count:
+ body = [f"Respond to {feedback_count} review {noun} {RESPONSE_EXAMPLES}:"]
+ body.extend(
+ feedback_breakdown_lines(review_thread_urls, top_level_feedback_urls)
+ )
+ return body
+ if failing_count:
+ sentence = "Investigate required status check failures."
+ if non_blocking_failure_note:
+ sentence += f" Note: {non_blocking_failure_note}"
+ return [sentence]
+ _, fallback_next_step = route_status_summary("author")
+ return [fallback_next_step]
def render_status_comment(
pr: dict[str, Any],
result: dict[str, Any] | None,
) -> str:
- last_updated = utc_now().strftime("%Y-%m-%d %H:%M:%S UTC")
+ last_updated = utc_now().strftime("%Y-%m-%d %H:%M UTC")
state = (pr.get("state") or "").lower()
facts = (result or {}).get("facts") or {}
review_thread_urls = facts.get("author_action_review_thread_urls") or []
@@ -142,132 +226,80 @@ def render_status_comment(
feedback_count = len(review_thread_urls) + len(top_level_feedback_urls)
failing_count = facts.get("ci_failing_count", 0)
non_blocking_check_failures = facts.get("non_blocking_check_failures") or []
- non_blocking_failure_note = ""
- if non_blocking_check_failures:
- displayed_failures = non_blocking_check_failures[
- :NON_BLOCKING_CHECK_FAILURE_LIMIT
- ]
- names = format_list([
- markdown_escape(truncate(name, NON_BLOCKING_CHECK_FAILURE_NAME_LIMIT))
- for name in displayed_failures
- ])
- if len(non_blocking_check_failures) == 1:
- non_blocking_failure_note = (
- f"{names} is failing but is not a required check."
- )
- else:
- non_blocking_failure_note = (
- f"{names} are failing but are not required checks."
- )
- omitted_count = len(non_blocking_check_failures) - len(displayed_failures)
- if omitted_count:
- noun = "failure" if omitted_count == 1 else "failures"
- omitted_verb = "is" if omitted_count == 1 else "are"
- non_blocking_failure_note += (
- f" {omitted_count} additional non-blocking check {noun} "
- f"{omitted_verb} not shown."
- )
+ non_blocking_failure_note = non_blocking_failure_summary(non_blocking_check_failures)
- feedback_indent: str | None = None
override_route = ""
+ terminal = False
+ body: list[str] = []
if pr.get("merged"):
- summary = ["- **Status:** Merged."]
+ headline = "Merged"
+ terminal = True
elif state == "closed":
- summary = ["- **Status:** Closed."]
+ headline = "Closed"
+ terminal = True
elif pr.get("draft"):
- summary = [
- "- **Waiting on:** Author",
- "- **Next step:** Move out of draft to request review.",
- ]
+ headline = status_headline("author")
+ body = ["Move out of draft to request review."]
elif result is None:
- summary = [
- "- **Waiting on:** Pull request dashboard",
- "- **Next step:** Finish refreshing this pull request.",
- ]
+ headline = "Waiting on the pull request dashboard"
+ body = ["Finish refreshing this pull request."]
else:
route = result.get("route") or "unknown"
if route in PRE_REVIEW_ROUTES:
override_route = route
+ headline = status_headline(route)
if route == "author":
- waiting_on, fallback_next_step = route_status_summary(route)
- check_action = None
- if failing_count:
- # One required aggregate check can represent multiple failing jobs.
- check_action = "Investigate required status check failures."
- if non_blocking_failure_note:
- check_action += f" Note: {non_blocking_failure_note}"
- noun = "item" if feedback_count == 1 else "items"
- feedback_action = f"Address or respond to {feedback_count} review feedback {noun}:"
- if check_action and feedback_count:
- summary = [
- f"- **Waiting on:** {waiting_on}",
- "- **Next steps:**",
- f" - {check_action}",
- f" - {feedback_action}",
- ]
- feedback_indent = " "
- elif feedback_count:
- summary = [
- f"- **Waiting on:** {waiting_on}",
- f"- **Next step:** {feedback_action}",
- ]
- feedback_indent = " "
- elif check_action:
- summary = [
- f"- **Waiting on:** {waiting_on}",
- f"- **Next step:** {check_action}",
- ]
- else:
- summary = [
- f"- **Waiting on:** {waiting_on}",
- f"- **Next step:** {fallback_next_step}",
- ]
+ body = author_body(
+ feedback_count=feedback_count,
+ failing_count=failing_count,
+ non_blocking_failure_note=non_blocking_failure_note,
+ review_thread_urls=review_thread_urls,
+ top_level_feedback_urls=top_level_feedback_urls,
+ )
else:
- waiting_on, next_step = route_status_summary(route)
- summary = [
- f"- **Waiting on:** {waiting_on}",
- f"- **Next step:** {next_step}",
- ]
+ _, next_step = route_status_summary(route)
+ body = [next_step]
if failing_count:
check_summary = (
"1 required status check is failing."
if failing_count == 1
else f"{failing_count} required status checks are failing."
)
- summary.append(f"- **Also blocked by:** {check_summary}")
+ body.extend(["", f"**Also blocked by:** {check_summary}"])
if non_blocking_failure_note:
label = (
"Non-blocking check failure"
if len(non_blocking_check_failures) == 1
else "Non-blocking check failures"
)
- summary.append(f"- **{label}:** {non_blocking_failure_note}")
+ body.extend(["", f"**{label}:** {non_blocking_failure_note}"])
lines = [
STATUS_MARKER,
f"",
"## Pull request dashboard status",
"",
- f"_Status last refreshed: {last_updated}._",
- "",
- *summary,
+ f"**{headline}** \u00b7 refreshed {last_updated}",
]
episode_id = str(facts.get("author_nudge_episode_id") or "")
if (result or {}).get("route") == "author" and episode_id:
lines.insert(2, author_nudge_episode_marker(episode_id))
- if feedback_indent is not None and feedback_count:
- lines.extend(
- feedback_breakdown_lines(
- review_thread_urls,
- top_level_feedback_urls,
- feedback_indent,
- )
- )
+ if body:
+ lines.append("")
+ lines.extend(body)
+
status_comment = "\n".join(lines)
lines.append("")
- lines.append(accuracy_note(pr, status_comment, override_route))
+ lines.extend(
+ status_footer(
+ pr,
+ status_comment,
+ override_route=override_route,
+ terminal=terminal,
+ )
+ )
lines.append("")
return "\n".join(lines)
@@ -283,12 +315,12 @@ def format_list(values: list[str]) -> str:
def feedback_breakdown_lines(
review_thread_urls: list[str],
top_level_feedback_urls: list[str],
- indent: str,
+ indent: str = "",
) -> list[str]:
feedback_count = len(review_thread_urls) + len(top_level_feedback_urls)
sections = (
("Inline threads", review_thread_urls),
- ("Top-level feedback", top_level_feedback_urls),
+ ("Top-level threads", top_level_feedback_urls),
)
lines: list[str] = []
remaining_limit = AUTHOR_ACTION_FEEDBACK_LINK_LIMIT
@@ -309,7 +341,6 @@ def feedback_breakdown_lines(
f"{indent}- _Showing {shown} of {feedback_count} feedback links; "
"resolve the remaining items from the pull request's conversation._"
)
- lines.append(f"{indent}- _{AUTHOR_GUIDANCE}_")
return lines
diff --git a/.github/scripts/pull-request-dashboard/route_presentation.py b/.github/scripts/pull-request-dashboard/route_presentation.py
index 20b204aa06d..e3068c3830c 100644
--- a/.github/scripts/pull-request-dashboard/route_presentation.py
+++ b/.github/scripts/pull-request-dashboard/route_presentation.py
@@ -4,36 +4,43 @@
ROUTE_PRESENTATION = {
"maintainer": {
"dashboard_label": "Waiting on maintainers",
+ "status_headline": "Waiting on maintainers",
"status_waiting_on": "Maintainers",
"status_next_step": "Merge when ready.",
},
"copilot": {
"dashboard_label": "Waiting on Copilot",
+ "status_headline": "Waiting on Copilot",
"status_waiting_on": "Copilot",
"status_next_step": "Wait for the pending review to complete.",
},
"approver": {
"dashboard_label": "Waiting on reviewers",
+ "status_headline": "Waiting on reviewers",
"status_waiting_on": "Reviewers",
"status_next_step": "Review the latest changes.",
},
"external": {
"dashboard_label": "Waiting on external",
+ "status_headline": "Waiting on an external dependency or decision",
"status_waiting_on": "An external dependency or decision",
"status_next_step": "Resolve it before work can continue.",
},
"author": {
"dashboard_label": "Waiting on authors",
+ "status_headline": "Waiting on the author",
"status_waiting_on": "Author",
"status_next_step": "Address or respond to review feedback.",
},
"transient-failure": {
"dashboard_label": "Transient GitHub failure retrieving PR data",
+ "status_headline": "Waiting on the pull request dashboard maintainers",
"status_waiting_on": "Pull request dashboard maintainers",
"status_next_step": "Determine the next action.",
},
"unknown": {
"dashboard_label": "Unknown",
+ "status_headline": "Waiting on the pull request dashboard maintainers",
"status_waiting_on": "Pull request dashboard maintainers",
"status_next_step": "Determine the next action.",
},
@@ -51,3 +58,7 @@ def route_status_summary(route: str) -> tuple[str, str]:
presentation["status_waiting_on"],
presentation["status_next_step"],
)
+
+
+def status_headline(route: str) -> str:
+ return ROUTE_PRESENTATION.get(route, ROUTE_PRESENTATION["unknown"])["status_headline"]
diff --git a/.github/scripts/pull-request-dashboard/test_pr_status_comment.py b/.github/scripts/pull-request-dashboard/test_pr_status_comment.py
index ca1771c555d..93e84799617 100644
--- a/.github/scripts/pull-request-dashboard/test_pr_status_comment.py
+++ b/.github/scripts/pull-request-dashboard/test_pr_status_comment.py
@@ -38,11 +38,8 @@ def test_waiting_on_author_splits_review_feedback_links(self) -> None:
},
)
- self.assertIn(
- "- **Waiting on:** Author",
- body,
- )
- self.assertIn("- **Next step:** Address or respond to 2 review feedback items:", body)
+ self.assertIn("**Waiting on the author** · refreshed ", body)
+ self.assertIn(f"Respond to 2 review items {pr_status_comment.RESPONSE_EXAMPLES}:", body)
self.assertIn(
f"",
body,
@@ -52,15 +49,11 @@ def test_waiting_on_author_splits_review_feedback_links(self) -> None:
body,
)
self.assertNotIn("### Review feedback", body)
- self.assertIn(" - **Inline threads:** [1]", body)
- self.assertIn(" - **Top-level feedback:** [2]", body)
- self.assertIn(f" - _{pr_status_comment.AUTHOR_GUIDANCE}_", body)
+ self.assertIn("- **Inline threads:** [1]", body)
+ self.assertIn("- **Top-level threads:** [2]", body)
self.assertIn(
- "If you believe this pull request is incorrectly routed as waiting "
- "on the author, comment `/dashboard route:reviewers` to route it from "
- "waiting on the author to waiting on reviewers. If the last refreshed "
- "time above predates your latest reply or push, the dashboard hasn't "
- "processed it yet.",
+ "- **Should this be with reviewers?** Comment "
+ "`/dashboard route:reviewers` to route it to them.",
body,
)
@@ -128,8 +121,8 @@ def test_status_last_refreshed_changes_for_identical_status(self, _utc_now: Mock
first_body = pr_status_comment.render_status_comment(self.pr(), {"route": "approver"})
second_body = pr_status_comment.render_status_comment(self.pr(), {"route": "approver"})
- self.assertIn("_Status last refreshed: 2026-07-18 12:34:56 UTC.", first_body)
- self.assertIn("_Status last refreshed: 2026-07-18 12:35:01 UTC.", second_body)
+ self.assertIn("**Waiting on reviewers** · refreshed 2026-07-18 12:34 UTC", first_body)
+ self.assertIn("**Waiting on reviewers** · refreshed 2026-07-18 12:35 UTC", second_body)
self.assertNotEqual(first_body, second_body)
def test_accuracy_note_prefills_central_issue_for_every_status(self) -> None:
@@ -145,11 +138,8 @@ def test_accuracy_note_prefills_central_issue_for_every_status(self) -> None:
with self.subTest(pr=pr, result=result):
body = pr_status_comment.render_status_comment(pr, result)
- self.assertIn(
- "This automated status or its linked feedback items may be incorrect",
- body,
- )
- self.assertIn("with the result you expected", body)
+ self.assertIn("[Report it](", body)
+ self.assertIn("with what you expected", body)
self.assertIn(
"https://github.com/open-telemetry/shared-workflows/issues/new?",
body,
@@ -168,11 +158,8 @@ def test_accuracy_note_prefills_quoted_live_status_comment(self) -> None:
{"route": "approver", "facts": {}},
)
- status_comment, accuracy_note = body.split(
- "\n\n_This automated status or its linked feedback items may be incorrect.",
- maxsplit=1,
- )
- report_url = accuracy_note.split("[report it](", maxsplit=1)[1].split(
+ status_comment, footer = body.split("\n\n", maxsplit=1)
+ report_url = footer.split("[Report it](", maxsplit=1)[1].split(
")", maxsplit=1
)[0]
issue_body = parse_qs(urlparse(report_url).query)["body"][0]
@@ -211,7 +198,7 @@ def test_accuracy_note_bounds_report_url_for_large_status(self) -> None:
},
)
- report_url = body.split("[report it](", maxsplit=1)[1].split(
+ report_url = body.split("[Report it](", maxsplit=1)[1].split(
")", maxsplit=1
)[0]
issue_body = parse_qs(urlparse(report_url).query)["body"][0]
@@ -234,13 +221,10 @@ def test_waiting_on_author_names_required_ci_failure(self) -> None:
},
)
- self.assertIn(
- "- **Waiting on:** Author",
- body,
- )
- self.assertIn("- **Next step:** Investigate required status check failures.", body)
+ self.assertIn("**Waiting on the author** · refreshed ", body)
+ self.assertIn("Investigate required status check failures.", body)
self.assertNotIn("### Review feedback", body)
- self.assertNotIn(pr_status_comment.AUTHOR_GUIDANCE, body)
+ self.assertNotIn(pr_status_comment.RESPONSE_EXAMPLES, body)
def test_waiting_on_author_combines_ci_and_review_feedback_reasons(self) -> None:
body = pr_status_comment.render_status_comment(
@@ -257,10 +241,10 @@ def test_waiting_on_author_combines_ci_and_review_feedback_reasons(self) -> None
},
)
- self.assertIn("- **Next steps:**", body)
- self.assertIn(" - Investigate required status check failures.", body)
- self.assertIn(" - Address or respond to 1 review feedback item:", body)
- self.assertIn(" - **Inline threads:** [1]", body)
+ self.assertIn("Two things need attention:", body)
+ self.assertIn("- **Required checks are failing** — investigate the failures.", body)
+ self.assertIn("- **1 review item** — respond to each {}:".format(pr_status_comment.RESPONSE_EXAMPLES), body)
+ self.assertIn(" - **Inline threads:** [1]", body)
def test_required_ci_action_notes_configured_non_blocking_failures(self) -> None:
body = pr_status_comment.render_status_comment(
@@ -278,7 +262,7 @@ def test_required_ci_action_notes_configured_non_blocking_failures(self) -> None
)
self.assertIn(
- "- **Next step:** Investigate required status check failures. "
+ "Investigate required status check failures. "
"Note: CodeQL and workflow-notification are failing but are not required checks.",
body,
)
@@ -345,9 +329,9 @@ def test_non_author_route_names_non_blocking_failure(self) -> None:
},
)
- self.assertIn("- **Waiting on:** Reviewers", body)
+ self.assertIn("**Waiting on reviewers** · refreshed ", body)
self.assertIn(
- "- **Non-blocking check failure:** codecov/patch is failing but is not a required check.",
+ "**Non-blocking check failure:** codecov/patch is failing but is not a required check.",
body,
)
@@ -356,18 +340,18 @@ def test_non_author_routes_also_name_required_ci_failures(self) -> None:
(
"maintainer",
1,
- "Maintainers",
+ "Waiting on maintainers",
["CodeQL"],
"1 required status check is failing.",
- "- **Non-blocking check failure:** CodeQL is failing but is not a required check.",
+ "**Non-blocking check failure:** CodeQL is failing but is not a required check.",
),
(
"approver",
2,
- "Reviewers",
+ "Waiting on reviewers",
["CodeQL", "workflow-notification"],
"2 required status checks are failing.",
- "- **Non-blocking check failures:** CodeQL and workflow-notification are failing but are not required checks.",
+ "**Non-blocking check failures:** CodeQL and workflow-notification are failing but are not required checks.",
),
)
for (
@@ -390,8 +374,8 @@ def test_non_author_routes_also_name_required_ci_failures(self) -> None:
},
)
- self.assertIn(f"- **Waiting on:** {waiting_on}", body)
- self.assertIn(f"- **Also blocked by:** {blocked_by}", body)
+ self.assertIn(f"**{waiting_on}** · refreshed ", body)
+ self.assertIn(f"**Also blocked by:** {blocked_by}", body)
self.assertIn(non_blocking_line, body)
def test_waiting_on_author_caps_feedback_links_across_sections(self) -> None:
@@ -416,10 +400,10 @@ def test_waiting_on_author_caps_feedback_links_across_sections(self) -> None:
},
)
- self.assertIn(" - **Inline threads:**", body)
- self.assertIn(" - **Top-level feedback:** [20]", body)
+ self.assertIn("- **Inline threads:**", body)
+ self.assertIn("- **Top-level threads:** [20]", body)
self.assertIn(
- " - _Showing 20 of 22 feedback links; "
+ "- _Showing 20 of 22 feedback links; "
"resolve the remaining items from the pull request's conversation._",
body,
)
@@ -444,9 +428,9 @@ def test_feedback_group_with_no_remaining_link_slots_still_reads_cleanly(self) -
},
)
- self.assertNotIn("Top-level feedback", body)
+ self.assertNotIn("Top-level threads", body)
self.assertIn(
- " - _Showing 20 of 21 feedback links; "
+ "- _Showing 20 of 21 feedback links; "
"resolve the remaining items from the pull request's conversation._",
body,
)
@@ -454,11 +438,8 @@ def test_feedback_group_with_no_remaining_link_slots_still_reads_cleanly(self) -
def test_draft_waits_on_author(self) -> None:
body = pr_status_comment.render_status_comment(self.pr(draft=True), None)
- self.assertIn("- **Waiting on:** Author", body)
- self.assertIn(
- "- **Next step:** Move out of draft to request review.",
- body,
- )
+ self.assertIn("**Waiting on the author** · refreshed ", body)
+ self.assertIn("Move out of draft to request review.", body)
def test_merged_pr_has_no_author_guidance(self) -> None:
body = pr_status_comment.render_status_comment(
@@ -466,8 +447,8 @@ def test_merged_pr_has_no_author_guidance(self) -> None:
None,
)
- self.assertIn("**Status:** Merged.", body)
- self.assertNotIn(pr_status_comment.AUTHOR_GUIDANCE, body)
+ self.assertIn("**Merged** · refreshed ", body)
+ self.assertNotIn(pr_status_comment.RESPONSE_EXAMPLES, body)
def test_terminal_pr_has_no_author_feedback_links(self) -> None:
result = {
@@ -489,7 +470,7 @@ def test_terminal_pr_has_no_author_feedback_links(self) -> None:
self.assertNotIn("### Review feedback", body)
self.assertNotIn("- **Inline threads", body)
- self.assertNotIn("- **Top-level feedback", body)
+ self.assertNotIn("- **Top-level threads", body)
def test_author_login_is_not_mentioned(self) -> None:
body = pr_status_comment.render_status_comment(
@@ -497,7 +478,7 @@ def test_author_login_is_not_mentioned(self) -> None:
{"route": "author", "facts": {"author": "alice"}},
)
- self.assertIn("- **Waiting on:** Author", body)
+ self.assertIn("**Waiting on the author** · refreshed ", body)
self.assertNotIn("@alice", body)
def test_external_route_advertises_reviewer_override(self) -> None:
@@ -506,34 +487,34 @@ def test_external_route_advertises_reviewer_override(self) -> None:
{"route": "external", "facts": {}},
)
+ self.assertIn("**Waiting on an external dependency or decision** · refreshed ", body)
self.assertIn(
- "waiting on an external dependency or decision, comment "
- "`/dashboard route:reviewers` to route it from waiting on an "
- "external dependency or decision to waiting on reviewers",
+ "- **Should this be with reviewers?** Comment "
+ "`/dashboard route:reviewers` to route it to them.",
body,
)
def test_routes_render_one_status_sentence(self) -> None:
expected_summaries = {
- "approver": ("Reviewers", "Review the latest changes."),
- "maintainer": ("Maintainers", "Merge when ready."),
- "copilot": ("Copilot", "Wait for the pending review to complete."),
- "external": ("An external dependency or decision", "Resolve it before work can continue."),
- "transient-failure": ("Pull request dashboard maintainers", "Determine the next action."),
- "unknown": ("Pull request dashboard maintainers", "Determine the next action."),
+ "approver": ("Waiting on reviewers", "Review the latest changes."),
+ "maintainer": ("Waiting on maintainers", "Merge when ready."),
+ "copilot": ("Waiting on Copilot", "Wait for the pending review to complete."),
+ "external": ("Waiting on an external dependency or decision", "Resolve it before work can continue."),
+ "transient-failure": ("Waiting on the pull request dashboard maintainers", "Determine the next action."),
+ "unknown": ("Waiting on the pull request dashboard maintainers", "Determine the next action."),
}
- for route, (waiting_on, next_step) in expected_summaries.items():
+ for route, (headline, next_step) in expected_summaries.items():
with self.subTest(route=route):
body = pr_status_comment.render_status_comment(
self.pr(),
{"route": route, "facts": {}},
)
- self.assertIn(f"- **Waiting on:** {waiting_on}", body)
- self.assertIn(f"- **Next step:** {next_step}", body)
+ self.assertIn(f"**{headline}** · refreshed ", body)
+ self.assertIn(next_step, body)
self.assertNotIn("**Status:**", body)
- self.assertNotIn(pr_status_comment.AUTHOR_GUIDANCE, body)
+ self.assertNotIn(pr_status_comment.RESPONSE_EXAMPLES, body)
class UpsertStatusCommentTest(unittest.TestCase):