From c33d48e3fb060d7242043fc6d2984f91e174b4e1 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Wed, 2 Sep 2026 12:14:56 -0700 Subject: [PATCH 01/30] Fix reviewer feedback ownership classification Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../pull-request-dashboard/RATIONALE.md | 18 ++-- .../classification_policy.py | 31 ++++++- .../eval/reviewer_feedback_cases.json | 66 ++++++++++++-- .../test_classification_policy.py | 86 ++++++++++++++++++- 4 files changed, 184 insertions(+), 17 deletions(-) diff --git a/.github/scripts/pull-request-dashboard/RATIONALE.md b/.github/scripts/pull-request-dashboard/RATIONALE.md index c8f61f96ef6..b257c5439d8 100644 --- a/.github/scripts/pull-request-dashboard/RATIONALE.md +++ b/.github/scripts/pull-request-dashboard/RATIONALE.md @@ -213,6 +213,9 @@ the implementation understandable and operationally cheap. - LLM classification cache is stored with `actions/cache`. - Unchanged review threads and top-level feedback items reuse cached classifications and avoid new Copilot calls. +- Each discussion cache key includes the model, full prompt template, and prompt + input. Changing one classifier's policy invalidates its verdicts without + clearing unrelated classifier or dashboard state. - Cache keys are scoped by target repository and by either PR number or backfill. - Targeted PR runs restore their PR-specific cache first, then fall back to the @@ -597,11 +600,16 @@ the implementation understandable and operationally cheap. the same second cannot be confused. - "Unclear" remains classifier vocabulary but is not a route. It collapses onto the author when a pending action is built: when the classifier cannot tell - what a discussion needs, the author is the one who can clarify it. There is no - separate label for feedback blocked on a dependency, decision, or event - outside this repository, because the author still has to drive it. A route for - that case would name nobody, could not be nudged, and would outrank approvals, - leaving blocked PRs unowned. + what a discussion needs, the author is the one who can clarify it. Feedback + that leaves the author or their PR blocked on a dependency, decision, or event + outside this repository also stays with the author because they still have to + drive it. A prerequisite explicitly owned by reviewers, maintainers, or the + project needs nothing from the author unless the same feedback also asks them + to act. +- A concrete reviewer report that tests still fail, CI still reproduces the + defect, or the proposed fix remains broken is an author action even without + request wording. It is evidence that the change is not ready, not a status + summary. - Lifecycle transitions are deterministic after feedback and author-reply classification. An ordinary new item waits on the author with 📌 visible. Once the author gives a completed reply, the item is diff --git a/.github/scripts/pull-request-dashboard/classification_policy.py b/.github/scripts/pull-request-dashboard/classification_policy.py index ce4c3da1f6c..67b93302ca1 100644 --- a/.github/scripts/pull-request-dashboard/classification_policy.py +++ b/.github/scripts/pull-request-dashboard/classification_policy.py @@ -133,12 +133,14 @@ class _PromptTooLongError(ValueError): - author_action: anything the author would answer or act on, including questions, requests, objections, remarks that reject the pull request's premise or necessity without asking for anything, an answer to a question - the author asked, and a statement that this pull request is blocked on - another pull request, release, or decision + the author asked, a concrete report that this pull request's tests still + fail or its proposed fix remains broken, and a statement that the author + must resolve or wait on another pull request, release, or decision - no_author_action: the item needs nothing from the PR author, such as pure approval, thanks, a status summary, a preamble that only describes the - review it introduces, or a repository automation command (for example - "/workflow-approve", "/rerun", or "/easycla") + review it introduces, a prerequisite owned by reviewers or maintainers, or + a repository automation command (for example "/workflow-approve", + "/rerun", or "/easycla") Read the whole item before deciding. Approval is no_author_action however it is phrased ("LGTM", "I'm fine with the API changes", "looks good to me, feel free @@ -163,6 +165,27 @@ class _PromptTooLongError(ValueError): below, take them or leave them"). An invitation to push back on those comments is not a request. +Assign action to the actor who owns it. A reviewer statement that the reviewers, +maintainers, or project must review, decide, land a prerequisite, or otherwise +act is no_author_action when it asks the author for nothing. This includes +first-person reviewer statements such as "we need to land the main pull request +first, but we'll get to it." Do not assign that prerequisite to the author +merely because it blocks this pull request or the comment mentions the author. +Reviewer ownership requires a concrete action those reviewers, maintainers, or +the project will perform. Collaborative wording such as "we should", "we need +to align", or "our concern" does not make a question, objection, proposal, or +change request reviewer-owned. If the same item asks the author to answer, +change, investigate, or otherwise act, it is author_action. A statement that +the author says their own pull request remains blocked on an external dependency +is also author_action. Questions, objections, proposals, reviewer preferences, +change requests, and dependency blockers with no explicit reviewer-owned action +remain author_action under the rules above. + +A concrete report that tests still fail, CI still reproduces the defect, or the +proposed fix remains broken is author_action even when phrased only as a +statement, without polite request wording. This is unresolved defect evidence, +not a status summary. + Compare `addressed_to`, and every other login and team named in `body`, against `pr_author`. An item asking a different person or team to review, decide, or weigh in is no_author_action even when it describes a concern with this pull diff --git a/.github/scripts/pull-request-dashboard/eval/reviewer_feedback_cases.json b/.github/scripts/pull-request-dashboard/eval/reviewer_feedback_cases.json index da28d7a1e2a..2f6639865cd 100644 --- a/.github/scripts/pull-request-dashboard/eval/reviewer_feedback_cases.json +++ b/.github/scripts/pull-request-dashboard/eval/reviewer_feedback_cases.json @@ -1,6 +1,6 @@ { "note": "`recorded_label` is how the shipped classifier behaved when this file was generated, not an adjudicated correct answer. A run matching it has not regressed; it has not been shown to be right. Set `adjudicated_label` on a case once a human has decided it.", - "generated_at": "2026-08-01", + "generated_at": "2026-09-02", "baseline_configuration": { "model": "gpt-5.4-mini", "prompt": "REVIEWER_FEEDBACK_PROMPT_TEMPLATE", @@ -26,12 +26,12 @@ "root_timestamp": "the timestamp the dashboard orders a pull request's items by; cases are stored and measured in that order, so batches match the ones it sends" }, "counts": { - "cases": 616, - "scored": 613, + "cases": 618, + "scored": 615, "context": 3, - "stable": 529, + "stable": 531, "flaky": 84, - "adjudicated": 5 + "adjudicated": 7 }, "cases": [ { @@ -10114,6 +10114,34 @@ "author_action" ] }, + { + "id": "pr-issue-comment-5499477518", + "repo": "opentelemetry-python-contrib", + "pull_request": 5018, + "requester": "aabmass", + "pr_author": "Shriprasad-P", + "review_state": null, + "root_timestamp": "2026-09-01T19:45:40Z", + "body": "@Shriprasad-P the test is unfortunately still failing here", + "role": "scored", + "stability": "stable", + "recorded_label": "author_action", + "adjudicated_label": "author_action", + "run_actions": [ + "author_action", + "author_action", + "author_action", + "author_action", + "author_action" + ], + "run_labels": [ + "author_action", + "author_action", + "author_action", + "author_action", + "author_action" + ] + }, { "id": "pr-issue-comment-4906053988", "repo": "opentelemetry-python-genai", @@ -13892,6 +13920,34 @@ "no_author_action" ] }, + { + "id": "pr-review-4881272095", + "repo": "semantic-conventions", + "pull_request": 3671, + "requester": "chalin", + "pr_author": "thompson-tomo", + "review_state": "CHANGES_REQUESTED", + "root_timestamp": "2026-08-07T08:38:43Z", + "body": "@joaopgrassi @thompson-tomo - We need to land the main PR for the 1821 work first. With the latest security otel.io incident, #1821 just got bumped back, but we'll get to it.", + "role": "scored", + "stability": "stable", + "recorded_label": "author_action", + "adjudicated_label": "no_author_action", + "run_actions": [ + "author_action", + "author_action", + "author_action", + "author_action", + "author_action" + ], + "run_labels": [ + "author_action", + "author_action", + "author_action", + "author_action", + "author_action" + ] + }, { "id": "pr-review-4262370828", "repo": "semantic-conventions", diff --git a/.github/scripts/pull-request-dashboard/test_classification_policy.py b/.github/scripts/pull-request-dashboard/test_classification_policy.py index 830a4001dcf..f510b2152f1 100644 --- a/.github/scripts/pull-request-dashboard/test_classification_policy.py +++ b/.github/scripts/pull-request-dashboard/test_classification_policy.py @@ -123,8 +123,8 @@ def test_prompt_bytes_match_the_pre_extraction_prompts(self) -> None: }, { "review": ( - 5359, - "ea4f5a86153173cf98f1d3d21a097e18dd513ce52c8c119f563b016559376653", + 6912, + "d35ebd00b3bb136ddbca0c2eaf5de6aca088071abcf059a8f92ebb121264d260", ), "author": ( 3372, @@ -144,7 +144,7 @@ def test_cache_keys_match_the_pre_extraction_keys(self) -> None: "gpt-test", verdict_contract=VerdictContract.REVIEWER_FEEDBACK, ), - "c78a48290b695bf839eed8ba25a1481a95a7565309c86fd57bd15ce8395ec58b", + "e6f20cae76395000ec22f2244a206b060581eec47903f146afa9c4590b256835", ) self.assertEqual( discussion_cache_key( @@ -163,6 +163,86 @@ def test_cache_keys_match_the_pre_extraction_keys(self) -> None: "76e534a013fc212856acbebd3c1897aa2c27daa6a69c6c8bee02e2d11b7bb2fd", ) + def test_reviewer_feedback_prompt_assigns_reviewer_owned_work_to_reviewers( + self, + ) -> None: + item = discussion( + "reviewer-prerequisite", + DiscussionKind.TOP_LEVEL_FEEDBACK, + "We need to land the prerequisite first, but we'll get to it.", + requester="reviewer", + pr_author="author", + ) + + prompt = render_verdict_prompt( + [item], + VerdictContract.REVIEWER_FEEDBACK, + ) + + self.assertIn( + "A reviewer statement that the reviewers,\n" + "maintainers, or project must review, decide, land a prerequisite", + prompt, + ) + self.assertIn('"body": "We need to land the prerequisite first,', prompt) + + def test_reviewer_feedback_prompt_assigns_unresolved_defects_to_author( + self, + ) -> None: + item = discussion( + "still-failing", + DiscussionKind.TOP_LEVEL_FEEDBACK, + "The test is unfortunately still failing here.", + requester="reviewer", + pr_author="author", + ) + + prompt = render_verdict_prompt( + [item], + VerdictContract.REVIEWER_FEEDBACK, + ) + + self.assertIn( + "A concrete report that tests still fail, CI still reproduces the defect", + prompt, + ) + self.assertIn( + '"body": "The test is unfortunately still failing here."', + prompt, + ) + + def test_reviewer_prompt_change_invalidates_only_its_cached_verdicts( + self, + ) -> None: + review_key = discussion_cache_key( + self.review, + "gpt-test", + verdict_contract=VerdictContract.REVIEWER_FEEDBACK, + ) + praise_key = discussion_cache_key( + self.thread, + "gpt-test", + verdict_contract=VerdictContract.PRAISE, + ) + + with patch( + "classification_policy.REVIEWER_FEEDBACK_PROMPT_TEMPLATE", + "changed reviewer policy", + ): + changed_review_key = discussion_cache_key( + self.review, + "gpt-test", + verdict_contract=VerdictContract.REVIEWER_FEEDBACK, + ) + unchanged_praise_key = discussion_cache_key( + self.thread, + "gpt-test", + verdict_contract=VerdictContract.PRAISE, + ) + + self.assertNotEqual(review_key, changed_review_key) + self.assertEqual(praise_key, unchanged_praise_key) + def test_author_comment_prompt_supports_empty_input(self) -> None: prompt = make_author_comment_request( [], From 03d2414c924d814517c810405ae67544305d7e75 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Wed, 2 Sep 2026 12:46:43 -0700 Subject: [PATCH 02/30] Record appended eval measurement dates Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: b4fb04e6-510c-4053-8380-1207e1dad14a --- .../eval/reviewer_feedback_cases.json | 7 +++++-- .../pull-request-dashboard/test_eval_fixture.py | 14 ++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/.github/scripts/pull-request-dashboard/eval/reviewer_feedback_cases.json b/.github/scripts/pull-request-dashboard/eval/reviewer_feedback_cases.json index 2f6639865cd..2c328f041c4 100644 --- a/.github/scripts/pull-request-dashboard/eval/reviewer_feedback_cases.json +++ b/.github/scripts/pull-request-dashboard/eval/reviewer_feedback_cases.json @@ -1,6 +1,6 @@ { - "note": "`recorded_label` is how the shipped classifier behaved when this file was generated, not an adjudicated correct answer. A run matching it has not regressed; it has not been shown to be right. Set `adjudicated_label` on a case once a human has decided it.", - "generated_at": "2026-09-02", + "note": "`recorded_label` is how the shipped classifier behaved when the case's `run_actions` were measured, not an adjudicated correct answer. A run matching it has not regressed; it has not been shown to be right. Set `adjudicated_label` on a case once a human has decided it.", + "generated_at": "2026-08-01", "baseline_configuration": { "model": "gpt-5.4-mini", "prompt": "REVIEWER_FEEDBACK_PROMPT_TEMPLATE", @@ -23,6 +23,7 @@ "run_labels": "the answers it did give, mapped through action_labels", "recorded_label": "the label the baseline settled on, null when it did not settle", "adjudicated_label": "the label a human decided, null until one has", + "measurement_date": "optional date for cases measured after generated_at; cases without it were measured in the baseline generated_at run", "root_timestamp": "the timestamp the dashboard orders a pull request's items by; cases are stored and measured in that order, so batches match the ones it sends" }, "counts": { @@ -10123,6 +10124,7 @@ "review_state": null, "root_timestamp": "2026-09-01T19:45:40Z", "body": "@Shriprasad-P the test is unfortunately still failing here", + "measurement_date": "2026-09-02", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -13929,6 +13931,7 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-08-07T08:38:43Z", "body": "@joaopgrassi @thompson-tomo - We need to land the main PR for the 1821 work first. With the latest security otel.io incident, #1821 just got bumped back, but we'll get to it.", + "measurement_date": "2026-09-02", "role": "scored", "stability": "stable", "recorded_label": "author_action", diff --git a/.github/scripts/pull-request-dashboard/test_eval_fixture.py b/.github/scripts/pull-request-dashboard/test_eval_fixture.py index ff5d953e988..8a82a25a52d 100644 --- a/.github/scripts/pull-request-dashboard/test_eval_fixture.py +++ b/.github/scripts/pull-request-dashboard/test_eval_fixture.py @@ -1,6 +1,7 @@ import json import re import unittest +from datetime import date from itertools import groupby from pathlib import Path @@ -80,6 +81,19 @@ def test_the_note_only_names_fields_a_case_really_has(self) -> None: for name in re.findall(r"`([^`]+)`", self.data["note"]): self.assertIn(name, self.cases[0], f"the note tells a human to use `{name}`") + def test_appended_measurements_record_their_date(self) -> None: + generated_at = date.fromisoformat(self.data["generated_at"]) + for case in self.cases: + with self.subTest(case=case["id"]): + if date.fromisoformat(case["root_timestamp"][:10]) > generated_at: + self.assertIn("measurement_date", case) + if "measurement_date" not in case: + continue + self.assertGreater( + date.fromisoformat(case["measurement_date"]), + generated_at, + ) + def test_labels_are_known(self) -> None: for case in self.cases: with self.subTest(case=case["id"]): From daeb5cd923ce086db7605ed3f46dc7aae25d7c5f Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Wed, 2 Sep 2026 12:56:12 -0700 Subject: [PATCH 03/30] Keep eval baseline provenance scoped Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: b4fb04e6-510c-4053-8380-1207e1dad14a --- .../eval/reviewer_feedback_cases.json | 3 --- .../pull-request-dashboard/test_eval_fixture.py | 14 -------------- 2 files changed, 17 deletions(-) diff --git a/.github/scripts/pull-request-dashboard/eval/reviewer_feedback_cases.json b/.github/scripts/pull-request-dashboard/eval/reviewer_feedback_cases.json index 2c328f041c4..dda342e13af 100644 --- a/.github/scripts/pull-request-dashboard/eval/reviewer_feedback_cases.json +++ b/.github/scripts/pull-request-dashboard/eval/reviewer_feedback_cases.json @@ -23,7 +23,6 @@ "run_labels": "the answers it did give, mapped through action_labels", "recorded_label": "the label the baseline settled on, null when it did not settle", "adjudicated_label": "the label a human decided, null until one has", - "measurement_date": "optional date for cases measured after generated_at; cases without it were measured in the baseline generated_at run", "root_timestamp": "the timestamp the dashboard orders a pull request's items by; cases are stored and measured in that order, so batches match the ones it sends" }, "counts": { @@ -10124,7 +10123,6 @@ "review_state": null, "root_timestamp": "2026-09-01T19:45:40Z", "body": "@Shriprasad-P the test is unfortunately still failing here", - "measurement_date": "2026-09-02", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -13931,7 +13929,6 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-08-07T08:38:43Z", "body": "@joaopgrassi @thompson-tomo - We need to land the main PR for the 1821 work first. With the latest security otel.io incident, #1821 just got bumped back, but we'll get to it.", - "measurement_date": "2026-09-02", "role": "scored", "stability": "stable", "recorded_label": "author_action", diff --git a/.github/scripts/pull-request-dashboard/test_eval_fixture.py b/.github/scripts/pull-request-dashboard/test_eval_fixture.py index 8a82a25a52d..ff5d953e988 100644 --- a/.github/scripts/pull-request-dashboard/test_eval_fixture.py +++ b/.github/scripts/pull-request-dashboard/test_eval_fixture.py @@ -1,7 +1,6 @@ import json import re import unittest -from datetime import date from itertools import groupby from pathlib import Path @@ -81,19 +80,6 @@ def test_the_note_only_names_fields_a_case_really_has(self) -> None: for name in re.findall(r"`([^`]+)`", self.data["note"]): self.assertIn(name, self.cases[0], f"the note tells a human to use `{name}`") - def test_appended_measurements_record_their_date(self) -> None: - generated_at = date.fromisoformat(self.data["generated_at"]) - for case in self.cases: - with self.subTest(case=case["id"]): - if date.fromisoformat(case["root_timestamp"][:10]) > generated_at: - self.assertIn("measurement_date", case) - if "measurement_date" not in case: - continue - self.assertGreater( - date.fromisoformat(case["measurement_date"]), - generated_at, - ) - def test_labels_are_known(self) -> None: for case in self.cases: with self.subTest(case=case["id"]): From a5e21b966226ee436b0a52a28b2722bbff5da315 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Wed, 2 Sep 2026 13:07:32 -0700 Subject: [PATCH 04/30] Clarify eval corpus measurement dates Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: b4fb04e6-510c-4053-8380-1207e1dad14a --- .../pull-request-dashboard/eval/regenerate_baseline.py | 4 +++- .../eval/reviewer_feedback_cases.json | 3 ++- .../eval/score_reviewer_feedback.py | 3 ++- .../scripts/pull-request-dashboard/test_eval_baseline.py | 5 +++++ .../scripts/pull-request-dashboard/test_eval_fixture.py | 8 ++++++++ 5 files changed, 20 insertions(+), 3 deletions(-) diff --git a/.github/scripts/pull-request-dashboard/eval/regenerate_baseline.py b/.github/scripts/pull-request-dashboard/eval/regenerate_baseline.py index f66506c57e6..3a20d6e2502 100644 --- a/.github/scripts/pull-request-dashboard/eval/regenerate_baseline.py +++ b/.github/scripts/pull-request-dashboard/eval/regenerate_baseline.py @@ -209,9 +209,11 @@ def rebuild(payload: dict, trials: list[dict[str, str]], model: str) -> dict: cases.sort(key=lambda c: (c["repo"], c["pull_request"], c["root_timestamp"])) roles = Counter(c["role"] for c in cases) stabilities = Counter(c["stability"] for c in cases) + measurement_date = datetime.now(UTC).strftime("%Y-%m-%d") return { **payload, - "generated_at": datetime.now(UTC).strftime("%Y-%m-%d"), + "baseline_generated_at": measurement_date, + "measurements_updated_at": measurement_date, "baseline_configuration": { **payload["baseline_configuration"], "model": model, diff --git a/.github/scripts/pull-request-dashboard/eval/reviewer_feedback_cases.json b/.github/scripts/pull-request-dashboard/eval/reviewer_feedback_cases.json index dda342e13af..ef6162ce133 100644 --- a/.github/scripts/pull-request-dashboard/eval/reviewer_feedback_cases.json +++ b/.github/scripts/pull-request-dashboard/eval/reviewer_feedback_cases.json @@ -1,6 +1,7 @@ { "note": "`recorded_label` is how the shipped classifier behaved when the case's `run_actions` were measured, not an adjudicated correct answer. A run matching it has not regressed; it has not been shown to be right. Set `adjudicated_label` on a case once a human has decided it.", - "generated_at": "2026-08-01", + "baseline_generated_at": "2026-08-01", + "measurements_updated_at": "2026-09-02", "baseline_configuration": { "model": "gpt-5.4-mini", "prompt": "REVIEWER_FEEDBACK_PROMPT_TEMPLATE", diff --git a/.github/scripts/pull-request-dashboard/eval/score_reviewer_feedback.py b/.github/scripts/pull-request-dashboard/eval/score_reviewer_feedback.py index 467b4f8c346..87e9d86775a 100644 --- a/.github/scripts/pull-request-dashboard/eval/score_reviewer_feedback.py +++ b/.github/scripts/pull-request-dashboard/eval/score_reviewer_feedback.py @@ -295,7 +295,8 @@ def main() -> None: data = json.loads(CASES.read_text(encoding="utf-8")) print( f"{args.classifier} / {args.model} x{args.trials} " - f"baseline generated {data['generated_at']}\n" + f"baseline generated {data['baseline_generated_at']}; " + f"measurements updated {data['measurements_updated_at']}\n" ) trials = [ classify(data["cases"], template, fields, mapping, args.model) diff --git a/.github/scripts/pull-request-dashboard/test_eval_baseline.py b/.github/scripts/pull-request-dashboard/test_eval_baseline.py index 13c5a2381a4..2eb8712f3da 100644 --- a/.github/scripts/pull-request-dashboard/test_eval_baseline.py +++ b/.github/scripts/pull-request-dashboard/test_eval_baseline.py @@ -153,6 +153,11 @@ def test_counts_and_configuration_describe_the_new_measurement(self) -> None: self.assertEqual( regenerate_baseline.PROMPT, rebuilt["baseline_configuration"]["prompt"] ) + self.assertEqual( + rebuilt["baseline_generated_at"], + rebuilt["measurements_updated_at"], + ) + self.assertNotIn("generated_at", rebuilt) class RunBatchCachingTest(unittest.TestCase): diff --git a/.github/scripts/pull-request-dashboard/test_eval_fixture.py b/.github/scripts/pull-request-dashboard/test_eval_fixture.py index ff5d953e988..de5be41311a 100644 --- a/.github/scripts/pull-request-dashboard/test_eval_fixture.py +++ b/.github/scripts/pull-request-dashboard/test_eval_fixture.py @@ -1,6 +1,7 @@ import json import re import unittest +from datetime import date from itertools import groupby from pathlib import Path @@ -38,6 +39,13 @@ def test_counts_match_the_cases(self) -> None: sum(1 for case in self.cases if case["adjudicated_label"]), ) + def test_measurement_dates_describe_the_mixed_vintage_corpus(self) -> None: + baseline = date.fromisoformat(self.data["baseline_generated_at"]) + updated = date.fromisoformat(self.data["measurements_updated_at"]) + + self.assertLessEqual(baseline, updated) + self.assertNotIn("generated_at", self.data) + def test_case_ids_are_unique(self) -> None: ids = [case["id"] for case in self.cases] self.assertEqual(len(ids), len(set(ids))) From 8c2c563e34a927d73ab30fe4aef22194f4b7e41e Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Wed, 2 Sep 2026 14:07:25 -0700 Subject: [PATCH 05/30] Narrow broken-fix feedback classification Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: b4fb04e6-510c-4053-8380-1207e1dad14a --- .../pull-request-dashboard/RATIONALE.md | 11 +++--- .../classification_policy.py | 25 +++---------- .../eval/reviewer_feedback_cases.json | 36 +++---------------- .../test_classification_policy.py | 29 ++------------- 4 files changed, 16 insertions(+), 85 deletions(-) diff --git a/.github/scripts/pull-request-dashboard/RATIONALE.md b/.github/scripts/pull-request-dashboard/RATIONALE.md index b257c5439d8..7fcfbfbe69b 100644 --- a/.github/scripts/pull-request-dashboard/RATIONALE.md +++ b/.github/scripts/pull-request-dashboard/RATIONALE.md @@ -600,12 +600,11 @@ the implementation understandable and operationally cheap. the same second cannot be confused. - "Unclear" remains classifier vocabulary but is not a route. It collapses onto the author when a pending action is built: when the classifier cannot tell - what a discussion needs, the author is the one who can clarify it. Feedback - that leaves the author or their PR blocked on a dependency, decision, or event - outside this repository also stays with the author because they still have to - drive it. A prerequisite explicitly owned by reviewers, maintainers, or the - project needs nothing from the author unless the same feedback also asks them - to act. + what a discussion needs, the author is the one who can clarify it. There is no + separate label for feedback blocked on a dependency, decision, or event + outside this repository, because the author still has to drive it. A route for + that case would name nobody, could not be nudged, and would outrank approvals, + leaving blocked PRs unowned. - A concrete reviewer report that tests still fail, CI still reproduces the defect, or the proposed fix remains broken is an author action even without request wording. It is evidence that the change is not ready, not a status diff --git a/.github/scripts/pull-request-dashboard/classification_policy.py b/.github/scripts/pull-request-dashboard/classification_policy.py index 67b93302ca1..29ee9862291 100644 --- a/.github/scripts/pull-request-dashboard/classification_policy.py +++ b/.github/scripts/pull-request-dashboard/classification_policy.py @@ -134,13 +134,12 @@ class _PromptTooLongError(ValueError): questions, requests, objections, remarks that reject the pull request's premise or necessity without asking for anything, an answer to a question the author asked, a concrete report that this pull request's tests still - fail or its proposed fix remains broken, and a statement that the author - must resolve or wait on another pull request, release, or decision + fail or its proposed fix remains broken, and a statement that this pull + request is blocked on another pull request, release, or decision - no_author_action: the item needs nothing from the PR author, such as pure approval, thanks, a status summary, a preamble that only describes the - review it introduces, a prerequisite owned by reviewers or maintainers, or - a repository automation command (for example "/workflow-approve", - "/rerun", or "/easycla") + review it introduces, or a repository automation command (for example + "/workflow-approve", "/rerun", or "/easycla") Read the whole item before deciding. Approval is no_author_action however it is phrased ("LGTM", "I'm fine with the API changes", "looks good to me, feel free @@ -165,22 +164,6 @@ class _PromptTooLongError(ValueError): below, take them or leave them"). An invitation to push back on those comments is not a request. -Assign action to the actor who owns it. A reviewer statement that the reviewers, -maintainers, or project must review, decide, land a prerequisite, or otherwise -act is no_author_action when it asks the author for nothing. This includes -first-person reviewer statements such as "we need to land the main pull request -first, but we'll get to it." Do not assign that prerequisite to the author -merely because it blocks this pull request or the comment mentions the author. -Reviewer ownership requires a concrete action those reviewers, maintainers, or -the project will perform. Collaborative wording such as "we should", "we need -to align", or "our concern" does not make a question, objection, proposal, or -change request reviewer-owned. If the same item asks the author to answer, -change, investigate, or otherwise act, it is author_action. A statement that -the author says their own pull request remains blocked on an external dependency -is also author_action. Questions, objections, proposals, reviewer preferences, -change requests, and dependency blockers with no explicit reviewer-owned action -remain author_action under the rules above. - A concrete report that tests still fail, CI still reproduces the defect, or the proposed fix remains broken is author_action even when phrased only as a statement, without polite request wording. This is unresolved defect evidence, diff --git a/.github/scripts/pull-request-dashboard/eval/reviewer_feedback_cases.json b/.github/scripts/pull-request-dashboard/eval/reviewer_feedback_cases.json index ef6162ce133..da1e29c9141 100644 --- a/.github/scripts/pull-request-dashboard/eval/reviewer_feedback_cases.json +++ b/.github/scripts/pull-request-dashboard/eval/reviewer_feedback_cases.json @@ -27,12 +27,12 @@ "root_timestamp": "the timestamp the dashboard orders a pull request's items by; cases are stored and measured in that order, so batches match the ones it sends" }, "counts": { - "cases": 618, - "scored": 615, + "cases": 617, + "scored": 614, "context": 3, - "stable": 531, + "stable": 530, "flaky": 84, - "adjudicated": 7 + "adjudicated": 6 }, "cases": [ { @@ -13921,34 +13921,6 @@ "no_author_action" ] }, - { - "id": "pr-review-4881272095", - "repo": "semantic-conventions", - "pull_request": 3671, - "requester": "chalin", - "pr_author": "thompson-tomo", - "review_state": "CHANGES_REQUESTED", - "root_timestamp": "2026-08-07T08:38:43Z", - "body": "@joaopgrassi @thompson-tomo - We need to land the main PR for the 1821 work first. With the latest security otel.io incident, #1821 just got bumped back, but we'll get to it.", - "role": "scored", - "stability": "stable", - "recorded_label": "author_action", - "adjudicated_label": "no_author_action", - "run_actions": [ - "author_action", - "author_action", - "author_action", - "author_action", - "author_action" - ], - "run_labels": [ - "author_action", - "author_action", - "author_action", - "author_action", - "author_action" - ] - }, { "id": "pr-review-4262370828", "repo": "semantic-conventions", diff --git a/.github/scripts/pull-request-dashboard/test_classification_policy.py b/.github/scripts/pull-request-dashboard/test_classification_policy.py index f510b2152f1..cd226947f1f 100644 --- a/.github/scripts/pull-request-dashboard/test_classification_policy.py +++ b/.github/scripts/pull-request-dashboard/test_classification_policy.py @@ -123,8 +123,8 @@ def test_prompt_bytes_match_the_pre_extraction_prompts(self) -> None: }, { "review": ( - 6912, - "d35ebd00b3bb136ddbca0c2eaf5de6aca088071abcf059a8f92ebb121264d260", + 5714, + "d5960f93e1b5963a6355061281f4e418e807eaa17b8a0a101dfc2b4cc4277a67", ), "author": ( 3372, @@ -144,7 +144,7 @@ def test_cache_keys_match_the_pre_extraction_keys(self) -> None: "gpt-test", verdict_contract=VerdictContract.REVIEWER_FEEDBACK, ), - "e6f20cae76395000ec22f2244a206b060581eec47903f146afa9c4590b256835", + "c0bae7b1e460f5d7edd0b23e6193f5413a4dd2aee05cffe9d9c8ebc40ca0c8fb", ) self.assertEqual( discussion_cache_key( @@ -163,29 +163,6 @@ def test_cache_keys_match_the_pre_extraction_keys(self) -> None: "76e534a013fc212856acbebd3c1897aa2c27daa6a69c6c8bee02e2d11b7bb2fd", ) - def test_reviewer_feedback_prompt_assigns_reviewer_owned_work_to_reviewers( - self, - ) -> None: - item = discussion( - "reviewer-prerequisite", - DiscussionKind.TOP_LEVEL_FEEDBACK, - "We need to land the prerequisite first, but we'll get to it.", - requester="reviewer", - pr_author="author", - ) - - prompt = render_verdict_prompt( - [item], - VerdictContract.REVIEWER_FEEDBACK, - ) - - self.assertIn( - "A reviewer statement that the reviewers,\n" - "maintainers, or project must review, decide, land a prerequisite", - prompt, - ) - self.assertIn('"body": "We need to land the prerequisite first,', prompt) - def test_reviewer_feedback_prompt_assigns_unresolved_defects_to_author( self, ) -> None: From 585c4a19984089b8f31c9ea7e5d23c4c810ae8e8 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Wed, 2 Sep 2026 15:15:50 -0700 Subject: [PATCH 06/30] Address Copilot review comments: record case measurement dates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Copilot comment: Advancing `generated_at` makes the scorer report that the baseline was generated on September 2, but this change only appends two measurements; the existing 616 cases retain their August run data. That leaves the fixture with a mixed-vintage baseline presented as a fresh full baseline, which can hide model drift over that month. Keep the original baseline date unless the complete baseline is regenerated, or represent the appended cases' measurement date separately. Copilot comment: This uses the discussion's creation date as a proxy for when the case was measured, so a historical case appended after the baseline (or one appended later on `generated_at`) can omit `measurement_date` and still pass. The fixture would then falsely attribute that case to the baseline run. Track measurement provenance directly—for example, require a measurement date on every case and backfill `generated_at` for the original corpus—then validate that field instead. Analysis: Every case now records its measurement date directly. The original 616 cases use the August 1 baseline date, while the appended case uses September 2. Full baseline regeneration stamps every case and both corpus-level dates with the new run date. Upsides: The fixture no longer infers measurement provenance from discussion timestamps. Tests reject missing or out-of-range case dates and require the corpus dates to match the oldest and newest measurements. Downsides: The fixture repeats one date field on every case, which adds a large but mechanical JSON diff. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../eval/regenerate_baseline.py | 3 +- .../eval/reviewer_feedback_cases.json | 618 ++++++++++++++++++ .../test_eval_baseline.py | 4 + .../test_eval_fixture.py | 9 + 4 files changed, 633 insertions(+), 1 deletion(-) diff --git a/.github/scripts/pull-request-dashboard/eval/regenerate_baseline.py b/.github/scripts/pull-request-dashboard/eval/regenerate_baseline.py index 3a20d6e2502..0b2ed1bfbe5 100644 --- a/.github/scripts/pull-request-dashboard/eval/regenerate_baseline.py +++ b/.github/scripts/pull-request-dashboard/eval/regenerate_baseline.py @@ -181,6 +181,7 @@ def work(task: tuple[list[dict], str]) -> tuple[str, dict[str, str]]: def rebuild(payload: dict, trials: list[dict[str, str]], model: str) -> dict: + measurement_date = datetime.now(UTC).strftime("%Y-%m-%d") cases = [] for case in payload["cases"]: raw = [trial.get(case["id"]) for trial in trials] @@ -197,6 +198,7 @@ def rebuild(payload: dict, trials: list[dict[str, str]], model: str) -> dict: **{k: case[k] for k in ("id", "repo", "pull_request", "requester", "pr_author", "review_state", "root_timestamp", "body")}, + "measurement_date": measurement_date, "role": role, "stability": stability, "recorded_label": recorded, @@ -209,7 +211,6 @@ def rebuild(payload: dict, trials: list[dict[str, str]], model: str) -> dict: cases.sort(key=lambda c: (c["repo"], c["pull_request"], c["root_timestamp"])) roles = Counter(c["role"] for c in cases) stabilities = Counter(c["stability"] for c in cases) - measurement_date = datetime.now(UTC).strftime("%Y-%m-%d") return { **payload, "baseline_generated_at": measurement_date, diff --git a/.github/scripts/pull-request-dashboard/eval/reviewer_feedback_cases.json b/.github/scripts/pull-request-dashboard/eval/reviewer_feedback_cases.json index da1e29c9141..711c43c0c10 100644 --- a/.github/scripts/pull-request-dashboard/eval/reviewer_feedback_cases.json +++ b/.github/scripts/pull-request-dashboard/eval/reviewer_feedback_cases.json @@ -24,6 +24,7 @@ "run_labels": "the answers it did give, mapped through action_labels", "recorded_label": "the label the baseline settled on, null when it did not settle", "adjudicated_label": "the label a human decided, null until one has", + "measurement_date": "the date this case's run_actions were measured", "root_timestamp": "the timestamp the dashboard orders a pull request's items by; cases are stored and measured in that order, so batches match the ones it sends" }, "counts": { @@ -44,6 +45,7 @@ "review_state": null, "root_timestamp": "2026-03-04T12:14:08Z", "body": "Sorry I wasn't able to get to this due to lack of bandwidth. Anybody else want to review this?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -72,6 +74,7 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-04-12T15:31:02Z", "body": "Hi @alexcams, thanks for working on this. I've left some suggestions for your consideration, please take them (with a grain of salt) as improving ideas and me thinking out loud :)", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -100,6 +103,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-04-14T07:18:32Z", "body": "That's pretty much what on my head. Thanks for making those changes, Alex.\n\nWould you mind to post the benchmarks result as well? It'd be helpful to track the performance cost of this new feature.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -128,6 +132,7 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-04-17T12:45:43Z", "body": "The benchmarks looks good, thanks for posting them, Alex. We almost there, just small nits for better readability, otherwise looks good to me :)", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -156,6 +161,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-04-20T10:01:46Z", "body": "LGTM. I'm not official approver though, so let's see what code owners think.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -184,6 +190,7 @@ "review_state": null, "root_timestamp": "2026-02-11T20:45:07Z", "body": "Collector is already with go 1.25, you could move forward with this 🙏", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -212,6 +219,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-02-12T14:33:42Z", "body": "Looks like the commit used is forcing us to move to 1.25.5 🥲", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -240,6 +248,7 @@ "review_state": null, "root_timestamp": "2026-02-25T18:09:33Z", "body": "Just an update here. We discussed this PR in [slack](https://cloud-native.slack.com/archives/C07CCCMRXBK/p1771958044638699), and our first option is hope that consul maintainers are happy to downgrade their minimum Go version: https://github.com/hashicorp/consul/pull/23268\r\n\r\nIf we don't hear back from them in a week, we'll have to consider other options", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -268,6 +277,7 @@ "review_state": null, "root_timestamp": "2026-03-03T20:03:32Z", "body": "Status update, https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/46609 was opened", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -296,6 +306,7 @@ "review_state": null, "root_timestamp": "2026-04-02T23:01:44Z", "body": "I've opened https://github.com/open-telemetry/opentelemetry-collector/pull/15052, hopefully we can unblock this PR", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -324,6 +335,7 @@ "review_state": null, "root_timestamp": "2026-05-11T08:20:32Z", "body": "@ArthurSens , is it worth moving this PR to draft until we can resolve the associated issue?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -352,6 +364,7 @@ "review_state": null, "root_timestamp": "2026-07-07T20:53:19Z", "body": "https://github.com/open-telemetry/opentelemetry-collector/pull/15052 got merged! @aknuds1, could you rebase the PR?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -380,6 +393,7 @@ "review_state": null, "root_timestamp": "2026-07-15T06:15:33Z", "body": "Sorry, one more rebase for good measure!", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -408,6 +422,7 @@ "review_state": null, "root_timestamp": "2026-05-01T13:07:20Z", "body": "Not stale, will need a maintainer to take a look. Will see if I can get someone", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -436,6 +451,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-05-04T15:03:20Z", "body": "@paulojmdias As a codeowner, I think this looks good to me and I believe any conflicts were addressed", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -464,6 +480,7 @@ "review_state": null, "root_timestamp": "2026-05-04T15:04:45Z", "body": "@atoulme Can you give another look please ?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -492,6 +509,7 @@ "review_state": null, "root_timestamp": "2026-07-13T02:42:08Z", "body": "This needs one more approval from the approvers.\r\nCould someone from @open-telemetry/collector-contrib-approvers please take a look at it.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -520,6 +538,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-13T03:51:11Z", "body": "This won't work for cross-account observability (linked accounts), right? I think we would need to extract the account ID from the log group ARN in that case.\n\nShould the `autodiscovery` case capture the log group ARN and extract the account ID from that? I believe the STS addition may still be needed for the `named` config case.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -548,6 +567,7 @@ "review_state": null, "root_timestamp": "2026-07-14T00:26:08Z", "body": "> This won't work for cross-account observability (linked accounts), right? I think we would need to extract the account ID from the log group ARN in that case.\r\n> \r\n> Should the `autodiscovery` case capture the log group ARN and extract the account ID from that? I believe the STS addition may still be needed for the `named` config case.\r\n\r\nCross account observability currently does not work and requires a fix, https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/48762 \r\n\r\nIt could be worth trying to get that over the line before implementing this one.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -576,6 +596,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-04-01T01:45:34Z", "body": "I haven't reviewed this completely, but wanted to provide some initial feedback on what I have seen so far.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -604,6 +625,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-04-07T14:00:03Z", "body": "Hello helloooo 👋 -- Awesome progress here!!\n\nJust to align expectations, this PR creates the package for Collector's internal telemetry validation, right? It doesn't fix #44905 yet because we still need to wire this library with our CI somehow.\n\nAm I also understanding things correctly that this package spins up a new Weaver container for each OTLP message we want to validate? How would this wiring work if we're to validate hundreds of components at the same time?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -632,6 +654,7 @@ "review_state": null, "root_timestamp": "2026-04-07T14:12:03Z", "body": "We're also missing a README file with the codeowners of the new package :)", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -660,6 +683,7 @@ "review_state": null, "root_timestamp": "2026-04-07T22:09:49Z", "body": "Here is maybe an example @vesari thanks to @braydonk's https://gist.github.com/braydonk/7067f20dde350a2bf23ea208aa937d97\r\n\r\nhttps://github.com/jmacd/weaver/pull/1", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -688,6 +712,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-05-19T14:18:23Z", "body": "Because of the way we have to work with Weaver's /stop endpoint, the testing API ends up looking really awkward from my original design. I think we can simplify it.\n\nI'm thinking an external user should just call a public function from the package called semconvtest.Test. For metrics for example that function would look something like func TestMetrics(t *testing.T, metrics pmetric.Metrics, opts ...WeaverOption) (assuming weaver options were refactored to the functional option pattern I mentioned in the other comment).\n\nThis function will essentially do all of what's here - it'll start a weaver container with the supplied options, send the logs to it, call stop, parse the livecheck report, and return findings. It obscures all the weaver stuff that I previously had them calling as individual steps. All these steps are basically necessary to work, so instead we can abstract all these steps away from the user, so they only know they need to call semconvtest.Test with their pdata.\nAlso, if these functions accept a t directly, then it can find the violations and mark the test as succeeded or failed.\n\nThis can also help resolve the comments around storing context i ...[truncated]", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -716,6 +741,7 @@ "review_state": null, "root_timestamp": "2026-07-08T09:05:13Z", "body": "This addition will be really valuable, thank's @vesari and @braydonk! I'm looking forward to this. \r\nCouple of generic comments from my side.\r\n\r\n> This works successfully with synthetic data, however, I haven't applied it to any real component just yet. A good first candidate should be agreed upon. What is also yet to be addressed is the stability enforcement. Work is in progress.\r\n\r\n`k8s_attributes` processor is targeting v1/stability through https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/49274. I believe this component would make it for a great candidate since it's already based on stable Semantic Conventions and we don't have any strict validation for the schema in place.\r\n\r\nWith https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/49545 we only explicitly set the stability level \"manually\" and refer to the SemConv docs, but there is no guardrail or actual automatic linking from the component back to the spec/semconv.\r\n\r\n> Tests for the semconvtest package itself. These are not component-level compliance tests (those would be added to the components actually using the package).\r\n\r\nWould that make sense to have a sample component tested ...[truncated]", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -744,6 +770,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-11T13:27:05Z", "body": "Looks much better! I think this accomplishes what I had suggested last time, which is a simple API for receivers using this package of `Test` and pass a bunch of pdata. The Weaver stuff gets largely obscured from them, all they need to do is pass data and under the hood Weaver does the magic.\n\nI tried to think if it's worth trying to come up with a way to reuse the same `testcontainer` instance across all tests; currently each call to `Test` will result in the a new container being created, started, stopped, and shutdown within the call. \nI think this is probably fine, since as long as the same weaver tag is used for each test then I don't think it will pull a new container each time (I wonder if `latest` will work that way? worth a check).\n\nI left a couple small comments, but structurally I feel that this is in a good place!", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -772,6 +799,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-02-28T05:07:11Z", "body": "### 💡 Codex Review\n\nHere are some automated review suggestions for this pull request.\n\n**Reviewed commit:** `9fe03f420e`\n \n\n
ℹ️ About Codex in GitHub\n
\n\n[Your team has set up Codex to review pull requests in this repo](http://chatgpt.com/codex/settings/general). Reviews are triggered when you\n- Open a pull request for review\n- Mark a draft as ready\n- Comment \"@codex review\".\n\nIf Codex has suggestions, it will comment; otherwise it will react with 👍.\n\n\n\n\nCodex can also answer questions or update the PR. Try commenting \"@codex address that feedback\".\n \n
", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -800,6 +828,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-03-04T17:58:56Z", "body": "Hi @Rajneesh180! Thank you for working on this! Before I can thoughtfully review it, let's try to avoid adding all this extra reflection code. We can probably leverage the existing `functions.buildArgs` for that, as it's already processing all that logic. I might be missing something, but I think we can check the functions argument there using the `ottl.isLiteralGetter()`, without having to go thought the parameters.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -828,6 +857,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-03-13T13:05:25Z", "body": "Thanks for working on this @Rajneesh180, a few more ideas:\n - Considering almost all converters are deterministic, I think we could probably invert this logic, changing the non-deterministic ones to pass the new factory option.\n - Let's avoid the extra reflection. We can probably apply the same idea of the `functions.buildArgs` to drop the slice arg reflection part. Please also consider organizing the code a bit or adding some extra structs to improve readability, as retuning bare booleans for argument builders might be a bit confusing. \n - `ottl.WithIsDeterministic()` is exported, so I'd choose a name that describes this option a bit more, such as `ottl.NonDeterministicConverter()` or something like that. We should also validate this option allowing it to be used with converters only, not editors.\n - We probably don't need to export the `ottl.Factory.IsDeterministic()`. This interface is already \"private\" so not issues un-exporting this one as well, the less we export here, the better.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -856,6 +886,7 @@ "review_state": null, "root_timestamp": "2026-04-02T12:12:19Z", "body": "Thanks @Rajneesh180, sorry for the delay. I'll be back reviewing this one soon.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -884,6 +915,7 @@ "review_state": null, "root_timestamp": "2026-07-20T13:01:45Z", "body": "@paulojmdias I have a PR already for this https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/49466 - waiting on someone to review it.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -912,6 +944,7 @@ "review_state": null, "root_timestamp": "2026-07-24T15:06:28Z", "body": "Thanks for the clarification @paulojmdias , I should have looked more closely :P \r\n\r\nI will update the table here https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/22095#issuecomment-4649650180 to reference your PR.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -940,6 +973,7 @@ "review_state": null, "root_timestamp": "2026-03-31T11:03:35Z", "body": "I think it would be really good to have this, but apparently this has lost traction. Is there anything I can do to help to move it forward? I think it \"only\" lacks another round of reviews / an approval. :-)", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -968,6 +1002,7 @@ "review_state": null, "root_timestamp": "2026-04-01T18:32:52Z", "body": "Sorry for the slow review. In some ways, i'm surprised this configuration is needed. I suspect the need stems from the fact that we aren't properly differentiating between cases where we aren't on the platform at all, and cases where we are on the platform, but the request fails.\r\n\r\nIf we aren't on the platform (e.g. the metadata endpoint returns a 404), we should never fail -- we should silently ignore. Any other errors should always fail. I guess the question is: Is there a use-case where someone wants a 404 to fail the resource detector?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -996,6 +1031,7 @@ "review_state": null, "root_timestamp": "2026-04-02T06:45:12Z", "body": "> I suspect the need stems from the fact that we aren't properly differentiating between cases where we aren't on the platform at all, and cases where we are on the platform, but the request fails.\r\n> If we aren't on the platform (e.g. the metadata endpoint returns a 404), we should never fail -- we should silently ignore. Any other errors should always fail.\r\n\r\nNot quite: My main interest (and the reason I raised #46579) is the situation (broadly speaking) where we actually are on the platform, but the platform is configured in a way so the resource detector cannot do its work. (Example: EKS detector, IMDS not enabled, permission DescribeInstances not present). Maybe the default should be to propagate the error and crash the collector, but there should also be an option to fail gracefully, e.g. stop the detector, but let the collector continue. The EC2 detector already has `fail_on_missing_metadata` and it works exactly like that, so there is precedent/prior art for this -- if the platform is EC2 but the permissions are not correct, the error is logged and the collector startup continues. \r\n\r\n> I guess the question is: Is there a use-case where someone wants a 404 to fail the reso ...[truncated]", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -1024,6 +1060,7 @@ "review_state": null, "root_timestamp": "2026-05-07T09:20:25Z", "body": "Can we reopen this PR ? This fix is still expected", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -1052,6 +1089,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-05-25T12:46:55Z", "body": "PTAL into the Co-Pilot suggestions", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -1080,6 +1118,7 @@ "review_state": null, "root_timestamp": "2026-06-03T10:09:43Z", "body": "@Akash-Kumar-Sinha, do you plan to review the suggestions before I can review them?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -1108,6 +1147,7 @@ "review_state": null, "root_timestamp": "2026-06-08T13:54:05Z", "body": "@Akash-Kumar-Sinha, any updates here? This fix is still needed.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -1136,6 +1176,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-03-25T21:25:42Z", "body": "I feel you are trying to solve 3 different issues:\n1. Create a ticket for each one\n2. Ensure to add a changelog in the PRs\n3. Provide regression tests for the issues you want to fix", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -1164,6 +1205,7 @@ "review_state": null, "root_timestamp": "2026-04-23T08:30:27Z", "body": "> @iblancasa would you mind sharing which are the three issues? I only found 2 bugs\r\n\r\nSorry. I don't know why I wrote 3. There are 2.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -1192,6 +1234,7 @@ "review_state": null, "root_timestamp": "2026-04-23T15:06:02Z", "body": "Please also update to latest main and look at the checks CI.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -1220,6 +1263,7 @@ "review_state": null, "root_timestamp": "2026-05-08T07:12:00Z", "body": "@pathcl Please ensure all the govulncheck tests pass. I have taken a look at the functionality upgrade and looks good for now. Lets quickly fix and move forward", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -1248,6 +1292,7 @@ "review_state": null, "root_timestamp": "2026-07-11T12:38:47Z", "body": "Kindly look at the lint CI failures.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -1276,6 +1321,7 @@ "review_state": null, "root_timestamp": "2026-06-03T01:46:01Z", "body": "This is fairly domain-specific, so will need to be reviewed by code owners.\r\n\r\n@schmikei @ishleenk17 PTAL", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -1304,6 +1350,7 @@ "review_state": null, "root_timestamp": "2026-04-07T18:43:31Z", "body": "I have not looked into the code already but this this should have a changelog entry", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -1332,6 +1379,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-04-09T13:21:41Z", "body": "The PR LGTM, I'd like to first figure out whether the answer is instead to just [not default `top_n` to 1 at all](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/47444#issuecomment-4214505183) before pressing approve, but if there is something I'm missing and that's the wrong idea then I'll approve this one.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -1360,6 +1408,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-04-09T17:29:04Z", "body": "Thanks @Dylan-M for putting this together.\n\nLet's settle on the solution in the issue https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/47444 before moving forward with this PR.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -1388,6 +1437,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-05-07T14:52:15Z", "body": "Overall LGTM, just a small nit.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -1416,6 +1466,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-13T12:27:21Z", "body": "My strong preference is still to use the feature gate. The File Log receiver is widely used in big deployments where seemingly simple change of updating a config might require coordination between multiple teams. A staged rollout with a feature gate makes upgrading less painful for users.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -1444,6 +1495,7 @@ "review_state": null, "root_timestamp": "2026-04-13T23:15:23Z", "body": "Fixes https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/47573", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -1472,6 +1524,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-04-14T05:37:34Z", "body": "lgtm", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -1500,6 +1553,7 @@ "review_state": null, "root_timestamp": "2026-07-22T00:34:57Z", "body": "@skreuzer thanks to @paulojmdias this was already re-opened, ping us if you make progress on this PR.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -1528,6 +1582,7 @@ "review_state": null, "root_timestamp": "2026-04-23T00:57:15Z", "body": "Change seems fine, doc only.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -1556,6 +1611,7 @@ "review_state": null, "root_timestamp": "2026-06-19T00:22:50Z", "body": "@dmitryax please review as codeowner, thanks!", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -1584,6 +1640,7 @@ "review_state": null, "root_timestamp": "2026-06-23T15:20:46Z", "body": "> @atoulme do i need to do anything?\r\n\r\njust need a review by @dmitryax", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -1612,6 +1669,7 @@ "review_state": null, "root_timestamp": "2026-04-23T01:31:54Z", "body": "Please add a changelog and mark ready for review again.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -1640,6 +1698,7 @@ "review_state": null, "root_timestamp": "2026-06-22T14:38:50Z", "body": "@asweet-confluent, can you please review as codeowner?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -1668,6 +1727,7 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-07-10T19:26:02Z", "body": "See my comment [here](https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/47849/changes#r3561462235) - we can't merge this, not in this state. I'll follow up in https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/47845.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -1696,6 +1756,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-04-29T08:17:24Z", "body": "Nice work!", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -1724,6 +1785,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-04-30T03:39:21Z", "body": "LGTM! It would be great having a storage analysis of this improvement before moving the feature gate to beta 🎉", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -1752,6 +1814,7 @@ "review_state": null, "root_timestamp": "2026-05-15T03:50:17Z", "body": "@dmitryax @braydonk Any thoughts on these changes? (I am primarily in favor as removes the float64 arithmetic noise which ends up reducing the storage costs while maintaining the precision)", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -1780,6 +1843,7 @@ "review_state": null, "root_timestamp": "2026-05-29T22:27:48Z", "body": "We touched on this in the System Semantic Conventions SIG yesterday. \r\n\r\nI have to push back on this. I should have flagged concerns on #46153 before approving it, and this PR builds further on the same approach while adding much more complexity. I don't think this complexity can be justified.\r\n\r\nFrom my point of view, rounding a float's mantissa is meaningless by design as it's encoded as binary fractions, not as decimals. The PRs call the additional digits \"arithmetic noise\", but we cannot remove that noise we just push it further away behind more zeroes, e.g. 3.3333333333...4 becomes something like 3.330000000000...03232434\r\n\r\nIf users want values rounded this way, the transform processor is a better place, where it can be implement an OTTL function. Receivers should always optimize for accuracy of the emitted values not for their representation.\r\n\r\nI'd even suggest we also revisit #46153. @braydonk WDYT?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -1808,6 +1872,7 @@ "review_state": null, "root_timestamp": "2026-06-05T07:35:35Z", "body": "During the System SIG on 04/06/2026 we discussed whether https://github.com/open-telemetry/opentelemetrycollector-contrib/pull/46153 could be moved into a processor or OTTL function. The core challenge is that the PR does not apply fixed-decimal truncation, it derives the correct number of significant digits from the magnitude of the original integer inputs, so the rounding is context-aware and cannot be reconstructed once the float64 is emitted.\r\n\r\n Two cases:\r\n - `precision.Ratio`: used for utilization ratios, it could in principle become an OTTL function, but it would require the receiver to expose raw usage integers, so the function has both operands available. This means every utilization metric would need explicit pipeline configuration (usage values) to compute and store the result into a new utilization metric, making default setups verbose and leaving users who don't opt in with the values with false precision.\r\n - `precision.Scale`: cannot be moved downstream. The rounding corrects noise introduced by the receiver's own time-unit conversion (e.g. ticks → seconds). That information is lost once the float64 is emitted, a processor has no way to recover it. The only alter ...[truncated]", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -1836,6 +1901,7 @@ "review_state": null, "root_timestamp": "2026-06-22T08:12:42Z", "body": "Not stale. During System WG SIG on 04/06/2026 it has discussed if there was any gopsutil feature to retrieve the raw cputicks using the package. It seems that now at the moment, but it could be extended using the `Ex` structs strategy, see https://github.com/shirou/gopsutil#ex-struct-from-v4245 As this PR is under a feature flag, the cpu ticks could be added into an `Ex` gopsutil struct in a follow-up PR", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -1864,6 +1930,7 @@ "review_state": null, "root_timestamp": "2026-07-15T10:18:12Z", "body": "Friendly reminder for other codeowners @braydonk and @dmitryax", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -1892,6 +1959,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-05-14T10:32:14Z", "body": "Thanks for working on this @MohamedElDegwi! I'm still running a few tests but it looks good so far. To make reviews easier, could you please split these changes into separate PRs? one for the grammar, and another one for functions and contexts data paths? This functionally should be backward compatible so we shouldn't have any issues doing that. \n\nAs I mentioned, we also need to standardized the existing OTTL's context paths that are currently returning bare Go's slices instead of `pcommon.Slice`.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -1920,6 +1988,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-05-25T12:39:14Z", "body": "Thanks @MohamedElDegwi! I think we're almost there 🎉", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -1948,6 +2017,7 @@ "review_state": null, "root_timestamp": "2026-06-04T12:33:34Z", "body": "I'm sorry for not checking it first, but I just realized while reviewing this PR that this standardization might limit our list support, and what we could possibly express as slices in the future. From one hand, it should be fine as the `pcommon.Slice` only uses the pdata standard data types, and we're constrained to that, but on the other hand, OTTL value model is wider than `pcommon.Slice`, supporting types like `time.Time`, `time.Duration`, `pcommon.SpanID`, `pcommon.TraceID`, and `pprofile.ProfileID`. If we move forward with this, we wouldn't be able create lists of them anymore (e.g `[Duration(\"1s\"), Duration(\"2m\")]`), even if the values are only being used to pass as arguments to functions calls/context paths.\r\n\r\nWDYT @TylerHelmuth @evan-bradley @bogdandrutu? I'm not sure anymore if we should continue with this change, and I'm curious to also hear your take on that.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -1976,6 +2046,7 @@ "review_state": null, "root_timestamp": "2026-07-09T19:14:43Z", "body": "I don't think we should limit our ability to make lists like `[Duration(\"1s\"), Duration(\"2m\")]` especially with the new lambda function support. It seems likely that a slice of non-pdata types could be returned from one of those functions. \r\n\r\nI like @meldegwi idea of trying to support both as long as it is not underperformant.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -2004,6 +2075,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-05-04T17:31:07Z", "body": "needs `make gotidy`", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -2032,6 +2104,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-05-08T12:28:09Z", "body": "given that the old name still works, I think this is fine, but do we have a timeline for the full removal of the old name? we need to make sure that nothing will break on our side", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -2060,6 +2133,7 @@ "review_state": null, "root_timestamp": "2026-05-11T15:24:56Z", "body": "Please fix conflicts", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -2088,6 +2162,7 @@ "review_state": null, "root_timestamp": "2026-05-25T07:23:27Z", "body": "> given that the old name still works, I think this is fine, but do we have a timeline for the full removal of the old name? we need to make sure that nothing will break on our side\r\n\r\n> @andrzej-stencel, any ETA on when the old name will be fully removed?\r\n\r\nThere is currently no specific plan on when to remove the deprecated names. I believe the consensus among the maintainers is to keep them for as long as possible. There's definitely no short-term plan to remove them.\r\n\r\nThis definitely needs a discussion on its own.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -2116,6 +2191,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-28T15:20:42Z", "body": "~The documentation in is very helpful. I suggest adding a note indicating which version introduced this rename to make it easier for users to track when to expect the deprecation.~", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -2144,6 +2220,7 @@ "review_state": null, "root_timestamp": "2026-06-20T01:50:03Z", "body": "Please address conflicts.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -2172,6 +2249,7 @@ "review_state": null, "root_timestamp": "2026-05-30T01:15:37Z", "body": "So after a bit of reading around I found https://pkg.go.dev/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp#WithEndpointURL which I think does exactly what this PR implements. \r\nYou'd still need to join the extracted path with --otlp-http-url-path, but the parsing itself doesn't need to be reimplemented.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -2200,6 +2278,7 @@ "review_state": null, "root_timestamp": "2026-05-31T06:28:14Z", "body": "Please fix the conflict and mark ready for review again.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -2228,6 +2307,7 @@ "review_state": null, "root_timestamp": "2026-06-08T04:48:04Z", "body": "@jamesmoessis please review as codeowner", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -2256,6 +2336,7 @@ "review_state": null, "root_timestamp": "2026-07-17T02:08:59Z", "body": "Hey @ben-trans I had a look into your issue, it seems like it still occurs even when I rebase your branch on main, use the latest mdatagen. So I've raised a bug: https://github.com/open-telemetry/opentelemetry-collector/issues/15592.\r\n\r\nIn the meantime to unblock yourself, I _think_ you can still register the featuregate by hand by copying whatever the generated code is doing (see other components in the repo for prior art). I apologise for the inconvenience, hopefully it can be fixed soon.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -2284,6 +2365,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-06-06T22:56:45Z", "body": "Thank you @KyriosGN0! Some notes that worth taking a look.\r\n\r\nIt is also missing the README.md update about this new feature. I also think it's worth taking a look at the detectors, which also do native retries (like EC2), and mark the options there as deprecated for future removal (also creating follow-up issues for that)", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -2312,6 +2394,7 @@ "review_state": null, "root_timestamp": "2026-06-17T21:34:04Z", "body": "@KyriosGN0, please fix the conflicts.\r\n\r\nIt is enough to create the issues to be tackled later 👍", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -2340,6 +2423,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-13T20:40:09Z", "body": "LGTM! Thank you!\n\n@dashpole, please give your review when you get a chance 🙏", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -2368,6 +2452,7 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-07-10T00:02:11Z", "body": "Waiting for sponsor", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -2396,6 +2481,7 @@ "review_state": null, "root_timestamp": "2026-07-18T15:16:59Z", "body": "@sigmaris , if an intermediate cert has the same SAN / issuer / target I think we will end up with duplicate datapoints because we have no way to distinguish the intermediate cert from the leaf cert. Perhaps we need to add a fingerprint attribute or something? I am not sure, I have not deeply thought about it but there is a risk in some circumstances when intermediate certs \"look\" substantially similar to the leaf certificate.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -2424,6 +2510,7 @@ "review_state": null, "root_timestamp": "2026-07-20T15:46:53Z", "body": "Adding a fingerprint should not add any cardinality except for the case where we have clashing series. I don't think we can assume we won't hit instances where the intermediate and leaf certs look the same, it is mechanically possible and this receiver will be installed and enabled on millions of machines, so it is likely that we will hit this edge case I think. \r\n\r\nIs there a conventional hash or fingerprint that is part of the TLS spec we can use to distinguich clashing series, or would we just need to roll our own hash?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -2452,6 +2539,7 @@ "review_state": null, "root_timestamp": "2026-07-20T16:56:46Z", "body": "> I think it'd be better to use an SHA256 digest of the DER encoded certificate. \r\n\r\nIf we use this then wouldn't we have the same fingerprint for each cert in the chain? I think the entire chain is encoded in the DER, right?\r\n\r\n> @michael-burt Should the fingerprint be added by default as an attribute to all certificate metrics, or only when scrape_all_certs is enabled (and so there could be more than once certificate scraped)?\r\n\r\nWe could leave it off by default in all cases, document its existence and rationale, and give users the ability to enable the metric via config if they have a need for it.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -2480,6 +2568,7 @@ "review_state": null, "root_timestamp": "2026-07-20T18:00:12Z", "body": "> No, it's possible as each individual certificate in a chain is represented by a x509.Certificate and we can get its DER encoded bytes from [its Raw attribute](https://pkg.go.dev/crypto/x509#Certificate), and calculate the SHA256 of those bytes (only). So calculating a specific fingerprint for each individual certificate is doable.\r\n\r\nnice, we should add a test for uniqueness I think, I am wondering if we would need to hash some positional arg or something if other fields are identical, hopefully not though.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -2508,6 +2597,7 @@ "review_state": null, "root_timestamp": "2026-06-23T12:55:47Z", "body": "@nissessenap - thanks for the contribution. please fix the govet errors.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -2536,6 +2626,7 @@ "review_state": null, "root_timestamp": "2026-07-12T10:07:42Z", "body": "want to check-in on this. looks like builds were failing after the last fix. let's pull latest from main and kick off builds to make sure things pass. \r\n\r\nty for your patience on this!", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -2564,6 +2655,7 @@ "review_state": null, "root_timestamp": "2026-07-13T16:19:39Z", "body": "@nissessenap Please fix the lint issues: https://github.com/open-telemetry/opentelemetry-collector-contrib/actions/runs/28465548065/job/84364801745?pr=48539\r\n\r\nAnd pull latest from main.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -2592,6 +2684,7 @@ "review_state": null, "root_timestamp": "2026-07-22T12:35:59Z", "body": "Looks like the supervisor test failure is unrelated to this pull request.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -2620,6 +2713,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-09T23:54:59Z", "body": "All the changes make sense to me, I'll have the code owners give their final approval first.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -2648,6 +2742,7 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-07-18T07:03:55Z", "body": "Config schemas are out of date, you can run this - `make generate-schemas` and push the changes!", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -2676,6 +2771,7 @@ "review_state": null, "root_timestamp": "2026-07-19T05:56:05Z", "body": "/workflow-approve\r\n\r\nEDIT: Can someone approve the workflows here, ig I don't have the required permissions.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -2704,6 +2800,7 @@ "review_state": null, "root_timestamp": "2026-07-28T05:40:40Z", "body": "I think it would make more sense if we had a separate PR for `internal/aws` component and focus this one only on `awscloudwatchlogs` exporter.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -2732,6 +2829,7 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-05-21T17:17:44Z", "body": "I would really prefer to avoid this precheck hack. Is there no other binary we can use to probe this? The bbolt cli has some recovery related commands in it: https://github.com/etcd-io/bbolt/tree/main/cmd/bbolt/command. Surely one of them is immune to this panic.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -2760,6 +2858,7 @@ "review_state": null, "root_timestamp": "2026-05-25T16:11:55Z", "body": "I think we should add something to support last released version until https://github.com/etcd-io/bbolt/issues/1190 is finished. We can create a follow up pr to migrate when that happens.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -2788,6 +2887,7 @@ "review_state": null, "root_timestamp": "2026-05-25T16:42:11Z", "body": "If what we need right now is spawning a collector subprocess with a secret init function, then I'm against and would rather wait for bbolt release.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -2816,6 +2916,7 @@ "review_state": null, "root_timestamp": "2026-06-02T08:30:23Z", "body": "How do you guys feel to pinning bbolt version to https://github.com/etcd-io/bbolt/releases/tag/v1.5.0-rc.0 or https://github.com/etcd-io/bbolt/releases/tag/v1.5.0-beta.0?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -2844,6 +2945,7 @@ "review_state": null, "root_timestamp": "2026-06-02T10:10:13Z", "body": "> How do you guys feel to pinning bbolt version to https://github.com/etcd-io/bbolt/releases/tag/v1.5.0-rc.0 or https://github.com/etcd-io/bbolt/releases/tag/v1.5.0-beta.0?\r\n\r\nI'd rather wait until there's a final release. But we can definitely work on PRs using the beta in the meantime.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -2872,6 +2974,7 @@ "review_state": null, "root_timestamp": "2026-07-03T07:01:37Z", "body": "@briandavis-viz bbolt was released. I think we are ok to go with this PR.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -2900,6 +3003,7 @@ "review_state": null, "root_timestamp": "2026-07-20T08:55:15Z", "body": "@briandavis-viz are you still interested on working on the issue? I can take your commits and continue if you are ok with that.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -2928,6 +3032,7 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-06-24T20:30:14Z", "body": "Looks good, had two requests to get checks passing", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -2956,6 +3061,7 @@ "review_state": null, "root_timestamp": "2026-07-08T19:24:13Z", "body": "> Thanks for the reviews @VihasMakwana @antonblock, should be good to go now\r\n\r\nIt looks like the expected metrics in integration tests need to be updated", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -2984,6 +3090,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-05-25T10:45:45Z", "body": "### 💡 Codex Review\n\nHere are some automated review suggestions for this pull request.\n\n**Reviewed commit:** `117f0ded48`\n \n\n
ℹ️ About Codex in GitHub\n
\n\n[Your team has set up Codex to review pull requests in this repo](https://chatgpt.com/codex/cloud/settings/general). Reviews are triggered when you\n- Open a pull request for review\n- Mark a draft as ready\n- Comment \"@codex review\".\n\nIf Codex has suggestions, it will comment; otherwise it will react with 👍.\n\n\n\n\nCodex can also answer questions or update the PR. Try commenting \"@codex address that feedback\".\n \n
", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -3012,6 +3119,7 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-05-27T06:01:04Z", "body": "Thanks for tackling this, the testcontainers scaffolding is solid and this is the right direction. A few things need addressing before merge:\n\n1. The test isn't actually exercising `otel-v1`. I pulled the branch and ran go test tags=integration -run TestIntegration_OtelV1Mapping it passes in ~32s, but the resulting index uses the ss4o schema (fields like attributes.data_stream.*, status.code as text) rather than otel-v1 (which should have serviceName, durationInNanos, traceGroup, etc.). This happens because the MappingOTelV1 enum lives in #48612, which hasn't merged yet. Without it, cfg.Mode = \"otel-v1\" silently falls through to the default ss4o encoder. The green test is a false positive.\n\n2. The timestamp assertion encodes the bug it should catch. Issue #48615 exists because OpenSearch demotes date_nanos → date when no index template is present. Asserting typeValue == \"date\" means the test passes precisely when the precision regression is active the opposite of what we want.\n\n3. PR description and changelog overstate scope. The mention of \"unit tests validating successful requests, permanent errors, and retryable errors\" describes tests already on main this PR renames integration ...[truncated]", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -3040,6 +3148,7 @@ "review_state": null, "root_timestamp": "2026-06-02T22:17:20Z", "body": "Hi @harshitt1, the Otel-v1 PR is merged now can you please rebase your PRand update tests for otel-v1 logs and traces ingested in opensearch? also another question: does the integration test run with the github CI?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -3068,6 +3177,7 @@ "review_state": null, "root_timestamp": "2026-07-27T10:55:54Z", "body": "Sorry for the delay, I left a comment on the related issue", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -3096,6 +3206,7 @@ "review_state": null, "root_timestamp": "2026-05-29T18:16:47Z", "body": "_Converting to draft for now to [discuss further](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/48344#issuecomment-4578438607) in issue._", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -3124,6 +3235,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-06-26T21:42:24Z", "body": "Please add more thorough testing for this that proves the full text comes through as expected after obfuscate when enabled", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -3152,6 +3264,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-05-28T12:04:04Z", "body": "I like where this is going, thank you for working on this!\nAdded minor comments/questions.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -3180,6 +3293,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-05-29T07:55:26Z", "body": "Thank's for the PR. I would like to understand first how this will affect the user experience specially when we are deprecating existing settings that were used to server specific needs. In addition, we might need to split the PR into smaller ones once we have agreement, i.e. one for the internal lib's changes and 2 follow ups to surface the changes in the components (if needed). \n\nBTW, I wonder why the components' code need to change. Shouldn't that change only be an implementation detail that only affect the underlying internal library without changing anything on the components' API?\n\n/cc @dhruv-shah-sumo please take look since this affects the `storage` part you recently added.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -3208,6 +3322,7 @@ "review_state": null, "root_timestamp": "2026-06-02T08:48:44Z", "body": "> Update the k8sobjects receiver to use the newly implemented interface instead of the old one.\r\n\r\nThank's @kangyili, can you provide a short update/summary of what is the proposal for this so as we don't lose that information? (the 2 threads that it was discussed will be resolved, so let's get to the summary of those discussions)\r\n\r\nI would like to clarify if we will have breaking changes in the behaviour of the `k8sobjects` receiver and if so which those will be.\r\nAlso will we need to update the `k8sevents` receiver accordingly too? \r\n\r\nOnce we are aligned on the above, I will try to find the time to review the first PR soon.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -3236,6 +3351,7 @@ "review_state": null, "root_timestamp": "2026-06-05T10:13:18Z", "body": "Thank's for the [summary](https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/48663#issuecomment-4601249613) @kangyili! I think I agree with that proposal which essentially only removes the `resource_version` option. Since we can ensure that the user experience is not changed other than this removal and that checkpoints are honoured through the `storage` setting, I support the proposal.\r\n\r\nI don't think we need to make this new implementation configurable and an implementation plan like https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/48663#issuecomment-4600125637 would be fine. I'm not sure how much time I will have in the following weeks to review this but I don't want to block this if other code-owners are available to review :).", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -3264,6 +3380,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-06-19T11:05:54Z", "body": "thanks. The last time I tried implementing sharding locally, I was not able to prove that it resolves a bottleneck. Do you have benchmark numbers that would demonstrate the difference?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -3292,6 +3409,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-01T18:01:22Z", "body": "Thanks! At a high level I like the idea of sharding, as we'll eventually need that because of perf. On the other hand I appreciate reasoning behind the single background loop introduced in #43671 by code owner @csmarchbanks which made the code much simpler.\n\nIf you get buy in from @csmarchbanks it'll help drive the PR forward. The bottom line is we'll need to be conscious about the additional complexity from this design decision.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -3320,6 +3438,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-01T22:53:05Z", "body": "At a high level I am also happy with a sharded approach. That was one of my ideas for how to improve the TSP after the last refactor if I came across additional bottlenecks.\n\nThat said, my initial thought is to have sharding be fairly transparent to users. Is there a reason anyone should run in non-sharded mode if throughput is better when sharded? Ideally we could even calculate and rebalance shards dynamically in response to load.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -3348,6 +3467,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-13T14:46:22Z", "body": "Just a quick comment, I'll get a full review in soon! Happy to have auto sizing of shards be in a follow up PR.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -3376,6 +3496,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-16T17:51:35Z", "body": "thanks! if we're going to add counters for operation errors, what about adding counter for operations with outcome={success,failure} dimension? Then we can also infer that things are working even when error stays 0", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -3404,6 +3525,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-23T14:29:34Z", "body": "lgtm thanks!", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -3432,6 +3554,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-28T15:56:13Z", "body": "The fix correctly addresses the nil-pointer dereference in the method by delegating to , which is designed to be nil-safe. The addition of and provides appropriate regression testing for the reported issue. The changes look correct and well-justified.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -3460,6 +3583,7 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-07-27T14:26:56Z", "body": "Would you be able to add a test? `metricRows` currently has no direct coverage — every scraper test uses `fakeDbClient`, so the real query path (and this fix) isn't exercised. A go-sqlmock-based test asserting ExpectationsWereMet() would fail if the rows aren't closed and would guard against regressions.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -3488,6 +3612,7 @@ "review_state": null, "root_timestamp": "2026-07-17T16:14:45Z", "body": "It'd be good to get this into main before the next release so we consolidate changes in one release.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -3516,6 +3641,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-20T16:58:48Z", "body": "Would like to see codeowners' thoughts on multi-cluster behavior, but LGTM.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -3544,6 +3670,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-27T14:57:56Z", "body": "Thanks @dmytrohysht - I took a look at this alongside #49721 and I think this looks like the right approach.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -3572,6 +3699,7 @@ "review_state": null, "root_timestamp": "2026-07-20T13:37:13Z", "body": "@darkknight8670 Does the older database keep working in case new one has corrupted?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -3600,6 +3728,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-18T10:16:57Z", "body": "### 💡 Codex Review\n\nHere are some automated review suggestions for this pull request.\n\n**Reviewed commit:** `a0137cea99`\n \n\n
ℹ️ About Codex in GitHub\n
\n\n[Your team has set up Codex to review pull requests in this repo](https://chatgpt.com/codex/cloud/settings/general). Reviews are triggered when you\n- Open a pull request for review\n- Mark a draft as ready\n- Comment \"@codex review\".\n\nIf Codex has suggestions, it will comment; otherwise it will react with 👍.\n\n\n\n\nCodex can also answer questions or update the PR. Try commenting \"@codex address that feedback\".\n \n
", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -3628,6 +3757,7 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-07-21T13:09:31Z", "body": "Hi @harshitt13, Nice work overall, just a few comments for your consideration :)", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -3656,6 +3786,7 @@ "review_state": null, "root_timestamp": "2026-07-19T16:16:56Z", "body": "@floze-the-genius you havent check the `I, a human, wrote this pull request description myself` box and `easycla` sign in is also pending.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -3684,6 +3815,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-21T09:07:08Z", "body": "Shouldn't we update the README.md with the options and support for `http` config parameter?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -3712,6 +3844,7 @@ "review_state": null, "root_timestamp": "2026-07-27T18:45:25Z", "body": "I generally prefer adding the new field without any restrictions, and putting the removal of the old field behind a feature gate to deprecate it slowly.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -3740,6 +3873,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-20T16:59:24Z", "body": "> Generated code is out of date for group 'extension', please run \"make generate\" and commit the changes in this PR.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -3768,6 +3902,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-21T18:05:04Z", "body": "LGTM", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -3796,6 +3931,7 @@ "review_state": null, "root_timestamp": "2026-07-21T08:43:05Z", "body": "@dpaasman00 need a rebase here after the extension host interface fix.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -3824,6 +3960,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-27T15:27:47Z", "body": "### 💡 Codex Review\n\nHere are some automated review suggestions for this pull request.\n\n**Reviewed commit:** `5444f3db4f`\n \n\n
ℹ️ About Codex in GitHub\n
\n\n[Your team has set up Codex to review pull requests in this repo](https://chatgpt.com/codex/cloud/settings/general). Reviews are triggered when you\n- Open a pull request for review\n- Mark a draft as ready\n- Comment \"@codex review\".\n\nIf Codex has suggestions, it will comment; otherwise it will react with 👍.\n\n\n\n\nCodex can also answer questions or update the PR. Try commenting \"@codex address that feedback\".\n \n
", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -3852,6 +3989,7 @@ "review_state": null, "root_timestamp": "2026-07-22T08:46:12Z", "body": "PTAL into CI issues 👍", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -3880,6 +4018,7 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-07-22T09:36:06Z", "body": "Thanks for the fix — this addresses a real crash path our team has hit. A few notes:\n\nCorrectness: The `LEFT JOIN → INNER JOIN` change is semantically equivalent here since the existing `WHERE datname IS NOT NULL` clause was already filtering out unmatched rows. The `nil` guard in `scraper.go` is good defense-in-depth.\n\nCI: The misspell linter is failing on \"defence\" (line 435) — needs to be \"defense\" for US English.\n\nSuggestion: Adding a unit test that exercises the nil-guard code path (e.g., a synthetic row with a nil db.namespace key) to prove the skip behavior. The `WHERE datname IS NOT NULL` clause in the template is also now redundant after the `INNER JOIN` change — fine to leave but worth a comment or cleanup.\n\nI'll revisit once CI is green. Thanks!", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -3908,6 +4047,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-23T13:05:50Z", "body": "thanks for addressing internal feedback, happy to approve", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -3936,6 +4076,7 @@ "review_state": null, "root_timestamp": "2026-07-23T08:22:45Z", "body": "this feature would be awesome 👍", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -3964,6 +4105,7 @@ "review_state": null, "root_timestamp": "2026-07-23T09:29:12Z", "body": "Thanks for the PR @leocarrozzo, however, the component still does not have a sponsor, and that is mandatory for adopting it.\r\n\r\nPlease also look into the [new components donation guidelines](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/docs/new-components.md), and this PR does not seem to follow them.\r\n\r\nI suggest you join a collector SIG meeting to discuss the issue and ask for sponsorship and/or reach out to the team in the #otel-collector-dev Slack channel.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -3992,6 +4134,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-23T13:10:14Z", "body": "Can we please enrich the available E2E tests for oracle cloud with this new attribute detection? https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/processor/resourcedetectionprocessor/testdata/e2e/oraclecloud", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -4020,6 +4163,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-24T10:00:37Z", "body": "LGTM, thank you! \n\n@geekdave, we need your review here please 🙏", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -4048,6 +4192,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-28T16:56:08Z", "body": "I would also like to see a codeowner take a look", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -4076,6 +4221,7 @@ "review_state": null, "root_timestamp": "2026-07-28T18:48:20Z", "body": "@bgola-signalfx Thanks for the contribution! I appreciate your help in helping to make Oracle's Resource Detection Processor more compliant with the semantic conventions. A few questions and asks:\r\n\r\n1. The PR description has a \"tracking issue\" link that just points back to the same PR. Could you please open an issue that describes the problem being fixed? Or if the PR is self-documenting, better just to remove that section rather than have a self-referential PR link.\r\n\r\n3. I noticed that the Oracle Cloud detector already provides `host.id` which has the same source for its value:\r\n\r\n```\r\n\td.rb.SetCloudResourceID(compute.HostID)\r\n//...snip...\r\n\td.rb.SetHostID(compute.HostID)\r\n```\r\n\r\nI'm curious about the precedent for this. I noticed that AWS, GCP, and Azure all set only `host.id` but not `cloud.resource_id`. Interestingly, I do see that IBM Cloud does emit both attributes, but they come from different sources so they do not get set to the same value ([see IBM's code here](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/processor/resourcedetectionprocessor/internal/ibmcloud/vpc/ibmcloud_vpc.go#L66-L67)).\r\n\r\nWould be good to call out if this duplica ...[truncated]", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -4104,6 +4250,7 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-07-28T18:50:44Z", "body": "See above comment for requested changes/clarifications. Thanks!", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -4132,6 +4279,7 @@ "review_state": null, "root_timestamp": "2026-07-25T15:19:39Z", "body": "Please fix the linter issue.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -4160,6 +4308,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-24T12:31:49Z", "body": "looks good", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -4188,6 +4337,7 @@ "review_state": null, "root_timestamp": "2026-07-27T15:12:11Z", "body": "@yaten2302, can you review it as a codeowner? Thank you 🙏", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -4216,6 +4366,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-28T18:30:22Z", "body": "I'd strongly prefer to separate the `host.name` change from the rest of this PR, unless there's some reason it must be included here.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -4244,6 +4395,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-28T08:49:11Z", "body": "LGTM. Verified locally: `GOOS=windows go build/vet ./...` is clean, `TestComputeServiceInstanceID` passes with the new ComputerName-priority cases, and the golden files match (`CustomServer:1433` for the named instance; host-dependent value correctly ignored in the default test). The `ComputerName` branch is safely ordered below `DataSource/Server,` so direct-connection behavior is unchanged.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -4272,6 +4424,7 @@ "review_state": null, "root_timestamp": "2026-07-28T08:54:31Z", "body": "@avadla also can you edit MR desc to be:\r\n\r\n\"Fixes {URL of Issue}\" on the same line, that way the issue is linked to the MR and it auto closes the issue once MR is merged?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -4300,6 +4453,7 @@ "review_state": null, "root_timestamp": "2026-07-28T19:41:03Z", "body": "@avadla done now, i think there were upstresam issues earlier that seem to have resolved, you can rerun the jobs by posting a comment on this mr with ```/rerun```", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -4328,6 +4482,7 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-07-28T14:22:54Z", "body": "Few comments.\n\n@AmariahAK I also noticed that you didn't follow the PR template, which also indicates to me that the PR description was written by your agent and not by yourself. \n\nPlease update the PR description so that it matches the template and ensure it's written in your own words, according to the [GenAI policy](https://github.com/open-telemetry/community/blob/main/policies/genai.md).", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -4356,6 +4511,7 @@ "review_state": null, "root_timestamp": "2026-07-28T18:27:10Z", "body": "Needs \"make generate-schemas\"", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -4384,6 +4540,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-28T14:50:26Z", "body": "thanks. qq about persistent queue test, the mock pq looks like the in memory queue implementation to me and doesn't seem to provide additional coverage over using in memory queue, or the actual persistent queue test coverage in https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/exporter/elasticsearchexporter/integrationtest .", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -4412,6 +4569,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-28T17:12:15Z", "body": "approving to unblock, thanks for adding the tests. One comment about the allowMissingDocs", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -4440,6 +4598,7 @@ "review_state": null, "root_timestamp": "2026-03-29T11:43:23Z", "body": "> shouldn't we try to guard against self-referencing IEnumerables\r\n\r\nI think so, otherwise it would cause a stack overflow? Could just add a recursion limit to track such occurences.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -4468,6 +4627,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-04-23T14:41:10Z", "body": "Could you also extend the fuzz tests to cover this functionality please?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -4496,6 +4656,7 @@ "review_state": null, "root_timestamp": "2026-05-20T08:58:25Z", "body": "We are having trouble sending complex nested objects to an OpenTelemetryCollector. I have tested the fork of @juliuskoval by connecting NLog.Target.OpenTelemetryProtocol with the OpenTelemetry-dotnet. I only need to traslate the object into IEnumerable>, and the everything works. I would kindly ask you to merge this and release new version. This would be really important for us.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -4524,6 +4685,7 @@ "review_state": null, "root_timestamp": "2026-05-20T09:10:45Z", "body": "@SalvoDeveloper I'm waiting for at least one other approving review from one of the other maintainers before merging this. Once merged it will be part of the next release (which has no specific date at this time).\r\n\r\nAfter it's merged you can use consume prerelease builds from [MyGet](https://github.com/open-telemetry/opentelemetry-dotnet#releases) if you can't wait until then.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -4552,6 +4714,7 @@ "review_state": null, "root_timestamp": "2026-06-09T00:36:06Z", "body": "Before merging, I'd like us to align on:\r\n \r\n 1. Is implicit type-sniffing the path we want, or should we wait for typed AnyValue API so we don't ship two redundant contracts?\r\n 2. How do we address Blanch's reference-aliasing concern from #6052 - should this require SDK-level deep-copy on span end?\r\n \r\n Strong support for the underlying use case, but want to avoid locking in behavior we can't revisit.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -4580,6 +4743,7 @@ "review_state": null, "root_timestamp": "2026-06-23T08:57:03Z", "body": "These changes seem reasonable to me. Raj is on PTO until August so let's wait on another +1 from another maintainer.\r\n\r\nCan you also update the CHANGELOGs as-appropriate please?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -4608,6 +4772,7 @@ "review_state": null, "root_timestamp": "2026-04-17T04:17:53Z", "body": "@utpilla Could you please review this PR when you have a moment?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -4636,6 +4801,7 @@ "review_state": null, "root_timestamp": "2026-04-17T17:57:34Z", "body": "Before merging, I think we need to answer a core question: **how should the Metrics SDK handle non-finite values (NaN, +Inf, -Inf)?**\r\n\r\nRight now, we don't seem to have a deliberate answer. The behavior varies by instrument. Counters get their sum poisoned, explicit bucket histograms put `NaN` in the `+Inf` bucket, exponential histograms filter `NaN` from buckets but still corrupt `sum`/`min`/`max`, and gauges just store `NaN` directly. None of this is documented, and this PR would change behavior for some instruments but not others.\r\n\r\nThe spec ([Numerical limits handling](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#numerical-limits-handling:~:text=the%20SDK%20needs%20to%20handle%20NaNs%20and%20Infinites.)) says the SDK MUST handle `NaN`/`Inf` but leaves the *how* unspecified. For exponential histograms specifically, the [spec](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#base2-exponential-bucket-histogram-aggregation:~:text=Implementations%20SHOULD%20NOT%20incorporate%20non%2Dnormal%20values%20(i.e.%2C%20%2BInf%2C%20%2DInf%2C%20and%20NaNs)%20into%20the%20sum%2C%20min% ...[truncated]", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -4664,6 +4830,7 @@ "review_state": null, "root_timestamp": "2026-06-08T22:47:25Z", "body": "@Kielek how you are planning to handle this PR?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -4692,6 +4859,7 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-04-30T15:10:13Z", "body": "See\nhttps://github.com/open-telemetry/opentelemetry-dotnet/pull/7227/changes#r3168921622\n\nLet me know if I misunderstood the fix.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -4720,6 +4888,7 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-05-04T18:34:15Z", "body": "The current iteration looks very promising (thanks! This was indeed a hard problem!). \nRequesting changes to address below comment\nhttps://github.com/open-telemetry/opentelemetry-dotnet/pull/7227/changes#r3183554865\n\nAlso, we need to vastly improve test coverage when doing this fix- lot of potential edge cases. (I spotted only one about Exemplar value, but could be more)", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -4748,6 +4917,7 @@ "review_state": null, "root_timestamp": "2026-05-27T06:14:41Z", "body": "Asked Codex/GPT5.5 to check this PR.\r\nBoth of them marked as P1. Could you please double check this feedback?\r\n\r\n# Review Notes and Unit Test Proposals for PR #7227\r\n\r\nI think there are two correctness risks worth covering with targeted unit tests.\r\n\r\n## Issue 1: Delta temporality is not correct when filtered streams change\r\n\r\nThe current fix spatially aggregates async cumulative counter values by updating the already-filtered `MetricPoint` with:\r\n\r\n```text\r\nexisting running value + incoming measurement value\r\n```\r\n\r\nThat works for simple cumulative export cases where the same underlying streams are reported every cycle. The problem is delta export. `MetricPoint.TakeSnapshot(outputDelta: true)` computes the delta from the collapsed aggregate:\r\n\r\n```text\r\ncurrent collapsed aggregate - previous collapsed aggregate\r\n```\r\n\r\nThat is not equivalent to summing per-stream deltas if one original measurement stream disappears or reappears between callbacks.\r\n\r\nExample:\r\n\r\n```text\r\nCollection 1:\r\n A = 10\r\n B = 10\r\n Collapsed value = 20\r\n\r\nCollection 2:\r\n A = 15\r\n B is absent\r\n Collapsed value = 15\r\n```\r\n\r\nIf the SDK diffs collapsed values, delta export becomes:\r\n\r\n```text\r\n15 - 20 = -5\r ...[truncated]", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -4776,6 +4946,7 @@ "review_state": null, "root_timestamp": "2026-05-30T14:14:26Z", "body": "The CLA needs to be signed.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -4804,6 +4975,7 @@ "review_state": null, "root_timestamp": "2026-06-09T19:42:20Z", "body": "I haven't review this PR in detail but one comment to think though:\r\n1. I am unsure if we need to let end users worry about this option - it is adding more cognitive overhead to endusers when setting up OTel.\r\n2. it may be better to change the default itself to follow this. This will cause increased jitter due to re-allocations as things get warmed up, and then settles. But once in steady state, no re-allocation needed, as we don't ever reclaim the underlying array from the Dictionary.\r\n\r\nWould be good if this can be discussed in SIG too.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -4832,6 +5004,7 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-06-11T16:04:59Z", "body": "Please use the SIG meeting to discuss this more.\nI don't think we should expand the configuration to add this feature - its forcing end users to make yet another decision.\nIf the original pre-allocation is not preferred, then it is better to change that itself and make it the default behavior.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -4860,6 +5033,7 @@ "review_state": null, "root_timestamp": "2026-06-11T16:05:53Z", "body": "@saguiitay Would you be able to join the community call to discuss more on this? (I won't be able to join, unfortunately, but I'll share some thoughts in the issue itself)", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -4888,6 +5062,7 @@ "review_state": null, "root_timestamp": "2026-06-11T17:44:39Z", "body": "That's probably not workable for you then, as I think that would make the SIG at 2200 for you (it's usually at 1900 for me and I'm in the UK).", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -4916,6 +5091,7 @@ "review_state": null, "root_timestamp": "2026-06-16T10:59:03Z", "body": "I've added it to [today's SIG agenda](https://docs.google.com/document/d/1yjjD6aBcLxlRazYrawukDgrhZMObwHARJbB9glWdHj8/edit?usp=sharing).", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -4944,6 +5120,7 @@ "review_state": null, "root_timestamp": "2026-06-24T03:55:24Z", "body": "> I've added it to [today's SIG agenda](https://docs.google.com/document/d/1yjjD6aBcLxlRazYrawukDgrhZMObwHARJbB9glWdHj8/edit?usp=sharing).\r\n\r\n@martincostello was this discussed? Can you update if any decision was made.\r\n\r\n\r\n> if it's up to me, we keep the option for a while, gather feedback from users, and only enable it by default if it's proven in production. I'd hate changing the default behavior, and getting a backlash from users that we broke them.\r\n\r\n@saguiitay Thanks, this idea makes sense. (And OTEL_DOTNET_EXPERIMENTAL_METRICS_ENABLE_LAZY_ALLOCATION approach would mean only advanced users will need to make this decision, and after evaluation, we can in future make this the default).", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -4972,6 +5149,7 @@ "review_state": null, "root_timestamp": "2026-06-24T06:58:05Z", "body": "> @martincostello was this discussed? Can you update if any decision was made.\r\n\r\nYes, Itay joined the SIG call and myself and Alan discussed it. TL;DR was Itay was going to speak to Raj about it internally and then look to make an experimental opt-in to allow for it to be tested in the wild. I forget exactly what else we discussed, but it'll be in the recording.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -5000,6 +5178,7 @@ "review_state": null, "root_timestamp": "2026-06-25T09:56:25Z", "body": "> > @martincostello was this discussed? Can you update if any decision was made.\r\n> \r\n> Yes, Itay joined the SIG call and myself and Alan discussed it. TL;DR was Itay was going to speak to Raj about it internally and then look to make an experimental opt-in to allow for it to be tested in the wild. I forget exactly what else we discussed, but it'll be in the recording.\r\n\r\nGot it. I agree with this approach. The experimental opt-in is via ENV variables right? \r\n\r\n@saguiitay Can you make the opt-in via ENV variable, and fix CI. I'll help review.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -5028,6 +5207,7 @@ "review_state": null, "root_timestamp": "2026-06-25T09:59:24Z", "body": "> The experimental opt-in is via ENV variables right?\r\n\r\nYep.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -5056,6 +5236,7 @@ "review_state": null, "root_timestamp": "2026-07-15T10:19:50Z", "body": "@saguiitay Sorry, I am getting back to this now only - could you fix conflicts, and update the PR desc to reflect the current status?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -5084,6 +5265,7 @@ "review_state": null, "root_timestamp": "2026-07-20T17:17:46Z", "body": "Thanks for working on this! This PR would help unblock our migration to OpenTelemetry. Could it please be prioritized for review and merge?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -5112,6 +5294,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-06-17T16:30:23Z", "body": "Overall approach here looks really good to me.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -5140,6 +5323,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-06-19T12:16:44Z", "body": "This looks pretty good and it's great to see declarative config work in the .NET SDK.\n\nWould you consider splitting this before it goes out of draft? Even setting tests aside (which are most of the diff), the 2k lines of source to add the package, the YAML reader, env substitution, the model, the flat-key converter, and the IConfiguration overlay wiring all at once is overwhelming.\n\nPRs of this size are hard to review with confidence and expect a reviewer to hold the whole pipeline in their head.\n\nI think breaking into these broader areas would be helpful:\n- package skeleton + public API surface + OTEL1006 plumbing\n- YAML parse to typed model (reader, substitution, file-format validation)\n- model to IConfiguration overlay (converter + DI wiring + precedence)", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -5168,6 +5352,7 @@ "review_state": null, "root_timestamp": "2026-06-19T12:24:56Z", "body": "I'm happy to defer if the consensus is to split this up, but as I've already reviewed it all I'd rather not review three new PRs that are all the same code 😄 (even if they're the same, still got to review them).", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -5196,6 +5381,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-06-29T11:12:47Z", "body": "LGTM - just the question regarding publishing needs resolving.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -5224,6 +5410,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-06-30T07:06:27Z", "body": "Only technical check.\nSorry for late feedback, still need more time to understand full flow for this component,", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -5252,6 +5439,7 @@ "review_state": null, "root_timestamp": "2026-07-13T06:30:53Z", "body": "@cijothomas, I asked Codex /GPT 5.6 Sol. Briefly checked findings and it looks valid to me:\r\n\r\nFollowing comment is AI driven\r\n\r\n---------\r\n\r\nReviewed commit: [`2a53a4a`](https://github.com/open-telemetry/opentelemetry-dotnet/commit/2a53a4a09702fd223f701ccc3514b3ced3104f3e)\r\n\r\n## 1. Count batch success at exporter submission, not queue admission\r\n\r\nPriority: P1\r\n\r\n[`BatchLogRecordExportProcessor.cs:108`](https://github.com/open-telemetry/opentelemetry-dotnet/blob/2a53a4a09702fd223f701ccc3514b3ced3104f3e/src/OpenTelemetry/Logs/Processor/BatchLogRecordExportProcessor.cs#L108)\r\nincrements the success counter immediately after `TryExport` enqueues the log\r\nrecord.\r\n\r\nThe semantic convention says that, for simple and batching processors, a log\r\nrecord is considered processed when it has been submitted to the exporter, not\r\nwhen the corresponding export call finishes. For the batching processor,\r\nsubmission happens later on the worker thread. The current implementation can\r\ntherefore report records as successfully processed while they are still queued.\r\nIf draining does not complete, some of those records may never be submitted to\r\nthe exporter.\r\n\r\nThe successful increment should happen ...[truncated]", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -5280,6 +5468,7 @@ "review_state": null, "root_timestamp": "2026-07-14T11:35:35Z", "body": "Moving to draft until v1.44 is released.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -5308,6 +5497,7 @@ "review_state": null, "root_timestamp": "2026-07-28T18:56:55Z", "body": "How much performance impact does adding the IsEventFullNameValid check introduce? \r\nIs there a customer ask for this check?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -5336,6 +5526,7 @@ "review_state": null, "root_timestamp": "2025-10-10T16:07:13Z", "body": "> The JFR consumer API is 9+ only\r\n\r\nThat isn't completely true. Openjdk contains jfr apis backported from 11 since 8u262. It is a bit weird in the sense that the oracle jdk doesn't contain the backported apis but rather an older version of the jfr api. So actually you don't need to set the java version to 11. One way to make it build would be to use.\r\n\r\n```\r\ntasks {\r\n compileJava {\r\n sourceCompatibility = \"1.8\"\r\n targetCompatibility = \"1.8\"\r\n options.release.set(null as Int?)\r\n }\r\n}\r\n```\r\n\r\nThe issue you have happens because gradle is smart and prevents you adding a dependency that works only with 11 to code that should work with 8. You can disable it with\r\n\r\n```\r\njava {\r\n disableAutoTargetJvm()\r\n}\r\n```", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -5364,6 +5555,7 @@ "review_state": null, "root_timestamp": "2025-10-13T12:50:18Z", "body": "> @laurit hmm, interesting plot twist there, thanks! That almost but not quite works - the compiler is happy, but animal sniffer is not. I assume it's either no longer sure what version it's supposed to be testing against, or thinks jfr isn't in 8.\r\n\r\nanimal sniffer checks run agains apis supported by android, see https://github.com/open-telemetry/opentelemetry-java/blob/da295cfa3c93b73376e4c32f2dd450b2035483b3/animal-sniffer-signature/build.gradle.kts#L27-L28 I doubt that these will pass no matter what java version you use for compiling. You will probably have to disable animal sniffer (could remove https://github.com/open-telemetry/opentelemetry-java/blob/da295cfa3c93b73376e4c32f2dd450b2035483b3/exporters/otlp/profiles/build.gradle.kts#L6) for the module if you wish to use jfr or move jfr code into separate module or maybe a separate source set would also be enough to trick it (see https://github.com/open-telemetry/opentelemetry-java/blob/da295cfa3c93b73376e4c32f2dd450b2035483b3/buildSrc/src/main/kotlin/otel.animalsniffer-conventions.gradle.kts#L14)", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -5392,6 +5584,7 @@ "review_state": null, "root_timestamp": "2026-04-02T20:28:20Z", "body": "This may do what you need https://github.com/marschall/jfr-opentelemetry-bridge. Let me know what you think.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -5420,6 +5613,7 @@ "review_state": null, "root_timestamp": "2025-10-28T19:31:56Z", "body": "> This prevents the instantiation of Senders outside of the SDK.\r\n\r\nWhy?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -5448,6 +5642,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-02-17T22:59:36Z", "body": "These contracts look like a good start to me. Missing implementation corresponding spec though so need to talk about how to proceed with that.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -5476,6 +5671,7 @@ "review_state": null, "root_timestamp": "2026-04-29T21:01:49Z", "body": "> Also there is the issue of replacing a single value vs replacing a node subtree. It looks like I need two new APIs for that\r\n\r\nI agree you'll need APIs for that. Should probably be at the SDK level though (SdkConfigProvider) rather than API (ConfigProvider). API is accessible to instrumentation and we want instrumentation to be consuming config, not mucking around and editing it, right?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -5504,6 +5700,7 @@ "review_state": null, "root_timestamp": "2026-04-30T21:31:50Z", "body": "> preference? or alternative?\r\n\r\nI've sketched out a few ideas in: https://github.com/jackshirazi/opentelemetry-java/pull/1\r\n\r\nEasier to talk in code these days. \r\n\r\nMost notably:\r\n\r\n- The update API is SDK only\r\n- The update APIs are consolidated into a single `setConfig(String path, Object value)` method\r\n- Verification that when an update occurs, the update target path is either unset or that the type of the existing value matches the new value\r\n- The CAS loop notification path is simplified by just using a lock - in the process, this fixes a race condition where a listener could be notified of update _after_ a _later_ update. I.e. given update 1 at T1, update 2 at T2, listener receives update 2 then update 1, and is permanently left with out of date state", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -5532,6 +5729,7 @@ "review_state": null, "root_timestamp": "2026-06-09T19:03:15Z", "body": "Sorry for the delay. Missed that you had replied to this.\r\n\r\n> My concern with this implementation is how do I call the setConfig from an extension? GlobalOpenTelemetry.get() is returned as an obfuscated wrapper, so can't be cast to ExtendedOpenTelemetrySdk\r\n\r\nI assume you mean agent extension. Agent extensions get access to `OpenTelemetrySdk` and so would have access to ExtendedOpenTelemetrySdk.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -5560,6 +5758,7 @@ "review_state": null, "root_timestamp": "2026-06-09T19:27:20Z", "body": "I like the code here and would be happy to approve / merge it once corresponding spec is available 👍", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -5588,6 +5787,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-03-11T20:20:44Z", "body": "Couple comments but I like the direction", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -5616,6 +5816,7 @@ "review_state": null, "root_timestamp": "2026-03-12T15:30:01Z", "body": "> One thing: now that pretty print isn't on by default, there's no public API for users to actually enable it. The capability only lives on the internal builders\r\n\r\nI think you're referring to the fact that `OtlpStdoutSpanExporter` and `OtlpStdoutSpanExporterBuilder` live in internal packages. In this context, internal indicates that they're still in development and APIs are subject to breaking changes, but users willing to accept that can use:\r\n\r\n```\r\nOtlpStdoutSpanExporter.builder()\r\n .setPrettyPrint(true)\r\n .build();\r\n```\r\n\r\nAs you note, we could add a parameter to `OtlpJsonLoggingSpanExporter#create` to allow this to be configured.\r\n\r\n`OtlpStdoutSpanExporter` is supposed to be the replacement for `OtlpJsonLoggingSpanExporter`, but is still in an internal package because we're waiting for the [corresponding spec document](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/file-exporter.md) to stabilize. \r\n\r\n> Add it as a field in the declarative configuration via the ComponentProvider classes\r\n\r\nI support this.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -5644,6 +5845,7 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-03-17T20:34:43Z", "body": "The behavior of our OTLP exporters and corresponding environment variables is dictated by the spec: https://github.com/open-telemetry/opentelemetry-java/blob/main/CONTRIBUTING.md#project-scope\n\nWe have some examples of java specific programmatic configuration options, like the ability to set the executor service and proxy options. But these accommodate well established configuration expectations of network clients. I.e. the absence of options would be a glaring deficiency in the API.\n\nThis fallback endpoint is more complicated and more controversial, and so I would like to see it go through the spec before we consider adding it in opentelemetry-java. \n\nPersonally, wearing my other hat as a spec contributor, I would expect this problem to be solved through load balancing and retry against a single endpoint. I.e. a single endpoint routes to multiple backing instances. If an attempt against the first fails, it does so in a way that triggers the retry policy to execute a subsequent request, which has the opportunity to resolve a different instance.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -5672,6 +5874,7 @@ "review_state": null, "root_timestamp": "2026-04-02T16:38:39Z", "body": "Sorry for the delay - went on vacation and lost track of this. \r\n\r\n> A load balancer is a heavyweight solution when the actual need is simple: \"if this endpoint is down, try that one.\" The SDK already has the context to make this decision at export time.\r\n\r\nYou could write a delegating exporter, which accepts multiple OTLP exporters as constructor parameters and calls the second if the first fails. The OTLP exporter's CompletableResultCodes return all the information you would need for a caller to determine why the first export failed and if it should try the second or abandon: https://github.com/open-telemetry/opentelemetry-java/blob/47c970d6c2ebc145f44612d7f04005c855939f3f/exporters/common/src/main/java/io/opentelemetry/exporter/internal/http/HttpExporter.java#L111-L126\r\n\r\n> Would you be open to keeping this PR as a reference implementation while the spec discussion happens? \r\n\r\nSure no problem", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -5700,6 +5903,7 @@ "review_state": null, "root_timestamp": "2026-04-09T11:11:09Z", "body": "Thanks for the contribution!\r\n\r\nPlease run the bench before/after and add results here - see https://github.com/open-telemetry/opentelemetry-java/pull/8271 on how to do it.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -5728,6 +5932,7 @@ "review_state": null, "root_timestamp": "2026-04-22T21:51:09Z", "body": "Which ones did you try? Benchmarks collect dust and eventually get out of date, especially the ones not regularly used. Would [RecordSpanBenchmark](https://github.com/open-telemetry/opentelemetry-java/blob/main/sdk/all/src/jmh/java/io/opentelemetry/sdk/SpanRecordBenchmark.java) show the changes? That's one of the few that we run on an ongoing basis and publish results to https://open-telemetry.github.io/opentelemetry-java/benchmarks/", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -5756,6 +5961,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-05-20T07:52:15Z", "body": "Nice metadata additions. Two suggestions:\n\n**Hoist hot-loop dictionary lookups.** In `JfrExecutionSampleEventConverter.accept()` the `\"thread.name\"` key index and `KeyValueAndUnitData` are rebuilt for every sample event. Same in `JfrLocationDataCompositor.frameToLocation()` for `\"profile.frame.type\"`/`\"jvm\"` per frame. The dict dedupes so output is correct, but each call still allocates a string + `KeyValueAndUnitData`. Since the key/value pair is constant per converter, compute it once (e.g. in the constructor or lazily cached) and reuse the int index.\n\nFor the thread sample, only `threadName`/`threadNameData` vary — pre-compute the `\"thread.name\"` key index once.\n\n**Null `sampledThread`.** `recordedEvent.getValue(\"sampledThread\")` can be `null` for some ExecutionSample variants. A null guard (skip or fall back to \"unknown\") would harden the converter against truncated/synthetic events.\n\nLGTM otherwise — the `ValueTypeData` fix and frame-type attribute look right.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -5784,6 +5990,7 @@ "review_state": null, "root_timestamp": "2026-05-15T15:13:21Z", "body": "You need to run `spotlessApply` to fix some of the failures.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -5812,6 +6019,7 @@ "review_state": null, "root_timestamp": "2026-05-18T17:22:30Z", "body": "Hey I want to take a step back on this. This PR represents a precedent that we're going to jump through additional hoops to support groovy.\r\n\r\nWe don't say anything about groovy or other JVM based languages in our [versioning policy](https://github.com/open-telemetry/opentelemetry-java/blob/main/VERSIONING.md). And its not that a don't want to support these. But I don't understand what supporting entails. Groovy for example, has different class loading semantics that has runs into issues with the patterns we use to activate different capabilities based on whether or not certain dependencies are present. For example, the metrics, logs, and trace SDKs all behave differently based on whether `opentelemetry-api-incubator` is present, using techniques like [this](https://github.com/open-telemetry/opentelemetry-java/blob/main/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/SdkMeter.java#L54-L63) to detect if the module is present, and helps like [this](https://github.com/open-telemetry/opentelemetry-java/blob/main/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/IncubatingUtil.java) that interact with the standard java classloader in such a way that the incubating classes ar ...[truncated]", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -5840,6 +6048,7 @@ "review_state": null, "root_timestamp": "2026-05-19T06:10:16Z", "body": "Did you try using `@CompileStatic`? As far as I understand the only relevant change is in the signature of `setConfigProvider`. Is calling `IncubatingUtil` via reflection necessary?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -5868,6 +6077,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-06-10T17:53:32Z", "body": "A few small comments, but looks pretty good! Thanks for working on this!", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -5896,6 +6106,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-06-16T20:15:30Z", "body": "Looks like there are still some build failures from static analysis. \n\nCouple more nits to fix along with the build, but looks good to me", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -5924,6 +6135,7 @@ "review_state": null, "root_timestamp": "2026-07-13T21:16:36Z", "body": "@open-telemetry/java-approvers - heads up - this PR adds a default OTLP message / request body size of 64mb, where none currently exists. You could make the argument that this is a breaking behavior change, and that we should proceed but only once there is a env var / declarative config property to configure a different limit. (Ive opened https://github.com/open-telemetry/opentelemetry-configuration/issues/695 to add declarative config schema to configure this)\r\n\r\nPersonally, I think the lack of current limit is a bug and adding a default of 64mb is a generous limit few will hit.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -5952,6 +6164,7 @@ "review_state": null, "root_timestamp": "2026-07-14T22:32:20Z", "body": "I'm fine with the API changes in this PR", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -5980,6 +6193,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-06-25T14:13:22Z", "body": "Looking pretty good", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -6008,6 +6222,7 @@ "review_state": null, "root_timestamp": "2026-07-13T18:15:58Z", "body": "PR with my remaining feedback here: https://github.com/jsuereth/opentelemetry-java/pull/1", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -6036,6 +6251,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-13T18:59:10Z", "body": "Looks good from my side. I don't have any preference on \"raw attributes\" vs \"unassociated attributes\" naming. Choose whatever is most likely to pass spec scrutiny, but we can merge here even without spec and update our naming later if needed.\n\nStill need to merge main and fix some minor things.\n\nAlso, should update the PR description to reflect the final state, so its accurate for any future readers.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -6064,6 +6280,7 @@ "review_state": null, "root_timestamp": "2026-06-10T16:50:51Z", "body": "What's the motivation behind this change? Is this related to https://github.com/open-telemetry/opentelemetry-java/issues/8198 ?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -6092,6 +6309,7 @@ "review_state": null, "root_timestamp": "2026-07-13T21:36:11Z", "body": "Blocked by codeql support:\r\n\r\n> Caused by: com.semmle.extractor.java.interceptors.KotlinInterceptor$KotlinVersionTooRecentError: Kotlin version 2.4.0 is too recent. CodeQL currently supports versions below 2.3.30", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -6120,6 +6338,7 @@ "review_state": null, "root_timestamp": "2026-07-16T15:28:13Z", "body": "> Blocked by codeql support:\r\n> \r\n> > Caused by: com.semmle.extractor.java.interceptors.KotlinInterceptor$KotlinVersionTooRecentError: Kotlin version 2.4.0 is too recent. CodeQL currently supports versions below 2.3.30\r\n\r\nYeah, codeql is a real problem here. We've been stuck on them being always behind for a while now. Maybe we should reconsider codeql for kotlin if we want to keep up with the kotlin versioning.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -6148,6 +6367,7 @@ "review_state": null, "root_timestamp": "2026-07-01T19:36:52Z", "body": "> This violated the OpenTelemetry specification, which requires that\r\nattribute name alone determines identity — last write wins regardless of type.\r\n\r\nCan you provide a link the portion of the spec you're referring to? Thanks.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -6176,6 +6396,7 @@ "review_state": null, "root_timestamp": "2026-07-01T20:27:06Z", "body": "> I'm not sure if you're asking because the PR description doesn't provide enough context, or if you think I might be misinterpreting the spec and solving a problem that doesn't actually exist. Happy to clarify either way.\r\n\r\nThe spec is big and occasionally contradictory. Always good to have a reference!\r\n\r\n> I originally found this through the linked issue. \r\n\r\nMissing the linked issue", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -6204,6 +6425,7 @@ "review_state": null, "root_timestamp": "2026-07-02T21:38:54Z", "body": "Hey so I've been thinking about this. AttributesMap exists because the default implementation of Attributes / AttributesBuilder is not limits aware. I think that the fact that its backed by HashMap is a function of convenience: we need a simple implementation that we can apply limits too as its being built up. I think performance is important, and a Map based implementation also has better put performance than the default array based implementations ImmutableKeyValuePairs / ArrayBackedAttributesBuilder. But I think this is coincidence, since I've never heard us telling people \"prefer using Span.setAttribute because it uses a more performant map based implementation\". \r\n\r\nI've never loved the fact that AttributesMap exists. I'd rather have one implementation. And I think your changes to AttributesMap reinforces this because while it still tries to do some map things for performance, other parts of it are starting to look more like ImmutableKeyValuePairs / ArrayBackedAttributesBuilder. And so its got me thinking about whether we can evolve ImmutableKeyValuePairs / ArrayBackedAttributesBuilder to meet the limits requirements and rip out AttributesMap altogether.\r\n\r\nI've got two protot ...[truncated]", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -6232,6 +6454,7 @@ "review_state": null, "root_timestamp": "2026-07-02T21:39:15Z", "body": "@open-telemetry/java-approvers PTAL at my message above and let me know if you have thoughts.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -6260,6 +6483,7 @@ "review_state": null, "root_timestamp": "2026-07-10T16:04:03Z", "body": "I would love to get us to a single implementation as well. The de-duping logic in the array based approach has always been the scary bit to my avoiding use using it instead of AttributesMap. If we're ok eating that performance hit, option 3 seems fine to me (as does 2, tbh). it's all the usual tradeoff of memory vs. speed, slightly coupled with maintenance complexity, I suppose (it's more maintenance to have 2 implementations vs. just 1). \r\n\r\nI wonder if there's a clever solution to the deduping, using some sort of lightweight sketch-based approach, alongside the array to save the linear scan in most cases. Could be an interesting research project for someone with time on their hands. ;)", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -6288,6 +6512,7 @@ "review_state": null, "root_timestamp": "2026-07-21T04:37:06Z", "body": "cc @trask to review first", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -6316,6 +6541,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-22T18:23:11Z", "body": "LGTM, but you could wait until we merge similar in other Java repos and go through release to verify", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -6344,6 +6570,7 @@ "review_state": null, "root_timestamp": "2026-07-16T20:19:08Z", "body": "Hey I gave this issue some thought and shared my analysis here: https://github.com/open-telemetry/opentelemetry-java/issues/7573#issuecomment-4996197430\r\n\r\nI think we should go in a different direction with this: #8610, which embodies option d in the comment.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -6372,6 +6599,7 @@ "review_state": null, "root_timestamp": "2026-07-17T01:25:12Z", "body": "Hey @jack-berg, thanks for the detailed analysis. \r\n\r\nI had one question about test coverage: is there a test that verifies the actual motivating scenario from #7573 — successfully reaching a real TLSv1/TLSv1.1-only server with enabledProtocols set and the JVM floor opened?\r\n\r\nAs written, `enabledProtocols()` only exercises TLS 1.2/1.3 against a TLS 1.2/1.3 server, which would still pass if `setEnabledProtocols()` were a no-op since that is already the default range.\r\nI don't see a test covering the full \"both gates open\" scenario against a legacy server, which was the original bug report.\r\n\r\nI ran into a related issue testing this in my [PR](https://github.com/open-telemetry/opentelemetry-java/pull/8599):` jdk.tls.disabledAlgorithms` behavior is JVM-wide, and changing it at runtime can be problematic once TLS initialization has already happened. That made a legacy-server test order-dependent in practice. \r\nIf that is why this scenario was intentionally omitted, that makes sense as a stability tradeoff.\r\n\r\nI just wanted to confirm whether that was the reasoning, or if I missed an existing test covering it.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -6400,6 +6628,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-27T21:07:56Z", "body": "Just a couple more minor comments. Looks pretty good!", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -6428,6 +6657,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-04-20T01:58:58Z", "body": "Purely AI generated review below\n\n---------------------------\n\nReview of the Failsafe 3.0 javaagent instrumentation + testing module extraction. A few correctness/style issues worth addressing:\n\n- **javaagent advice**: `@Advice.OnMethodExit` has `onThrowable = Throwable.class` but the body is return-only (it casts `@Advice.Return` and derives values from it). On the exceptional path the return is `null` and the cast triggers a (suppressed) exception for no benefit — drop `onThrowable`.\n- **Default policy name in javaagent mode**: `impl.toString()` is used as the retry-policy name, which will produce uninformative values like `dev.failsafe.internal.RetryPolicyImpl@2f4d3e5a` as the `failsafe.retry_policy.name` attribute. Worth considering a cleaner fallback (e.g., the implementation class simple name, or just leaving the attribute out when no user-supplied name exists).\n- **Reflection-based mutation of `PolicyConfig`**: writing to private `failureListener`/`successListener` fields is brittle and muzzle cannot protect against these fields being renamed/removed in a future Failsafe version. Worth noting in a comment and/or considering a `fail` muzzle block or explicit guard.\n- **Style ...[truncated]", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -6456,6 +6686,7 @@ "review_state": null, "root_timestamp": "2026-03-19T02:05:25Z", "body": "@steverao thanks for the detailed explanation! I think that's ok, not sure any way around it, just update the testLatestDeps test to reflect the reality of the disconnected trace.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -6484,6 +6715,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-05-31T07:12:17Z", "body": "LGTM, just a nit:\n\nhttps://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/4c57afb6c7460b05eda858114d74ad8da4794973/instrumentation/spring/spring-webflux/spring-webflux-5.3/library/README.md?plain=1#L81\n\nSuggested change: `return webfluxServerTelemetry.createWebFilter();`", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -6512,6 +6744,7 @@ "review_state": null, "root_timestamp": "2026-06-01T07:22:40Z", "body": "May I ask for a review from @open-telemetry/java-instrumentation-approvers when any of them get a chance?\r\n\r\nThanks!", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -6540,6 +6773,7 @@ "review_state": null, "root_timestamp": "2026-06-02T11:56:03Z", "body": "https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/17858 asks whether the reactor hook should be always registered. Did you attempt to fina an answer to that question? What are the benefits of registering the reactor hook? What would we loose if we did it the other way around and never registered it in these methods?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -6568,6 +6802,7 @@ "review_state": null, "root_timestamp": "2026-07-08T17:01:44Z", "body": "@laurit,\r\nIt's been a while, may we get your review here?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -6596,6 +6831,7 @@ "review_state": null, "root_timestamp": "2026-06-09T13:58:51Z", "body": "Is there prior art for this in otel ecosystem? There is a [moratorium](https://github.com/open-telemetry/opentelemetry-specification/issues/2891#issuecomment-1289241503) in otel spec for introducing new environment variables with complex encoding. The moratorium was originally in place to incentivize development of declarative config, which is done now. While we are not strictly bound by this moratorium the question is still whether there could be a better solution for this that does not involve complex encodings. Perhaps some sort of include/exclude pair would work better?\r\ncc @trask", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -6624,6 +6860,7 @@ "review_state": null, "root_timestamp": "2026-06-12T03:28:58Z", "body": "check out declarative config and the pattern for includes/excludes: \r\n\r\n- https://github.com/open-telemetry/opentelemetry-configuration\r\n- https://github.com/open-telemetry/opentelemetry-configuration/blob/main/CONTRIBUTING.md#properties-requiring-pattern-matching\r\n- https://github.com/open-telemetry/opentelemetry-java/blob/main/sdk/common/src/main/java/io/opentelemetry/sdk/common/internal/IncludeExcludePredicate.java", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -6652,6 +6889,7 @@ "review_state": null, "root_timestamp": "2026-07-14T21:48:10Z", "body": "> Is it true that opentelemetry-sdk-common (and therefore `IncludeExcludePredicate`) cannot be used by instrumentations?\r\n\r\noh yeah, instrumentation should only rely on OpenTelemetry API and not on the SDK\r\n\r\nI pushed a commit to copy in those classes, let's give that a try", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -6680,6 +6918,7 @@ "review_state": null, "root_timestamp": "2026-07-08T21:22:12Z", "body": "Can you do some analysis on what (quantitative) benefits this has, so we can weigh the risks? thanks", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -6708,6 +6947,7 @@ "review_state": null, "root_timestamp": "2026-07-13T20:32:18Z", "body": "thanks @somiljain2006!\r\n\r\nI'm wondering if this is needed, I think same can be accomplished via Metric Views, e.g.\r\n\r\n```\r\nmeter_provider:\r\n views:\r\n - selector:\r\n meter_name: io.opentelemetry.micrometer-1.5\r\n instrument_name: jvm.*\r\n stream:\r\n aggregation:\r\n drop: {}\r\n - selector:\r\n meter_name: io.opentelemetry.micrometer-1.5\r\n instrument_name: process.cpu.usage\r\n stream:\r\n aggregation:\r\n drop: {}\r\n```\r\n\r\ncc @SylvainJuge who opened the linked issue", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -6736,6 +6976,7 @@ "review_state": null, "root_timestamp": "2026-07-15T09:52:28Z", "body": "> thanks @somiljain2006!\r\n> \r\n> I'm wondering if this is needed, I think same can be accomplished via Metric Views, e.g.\r\n> \r\n> ```\r\n> meter_provider:\r\n> views:\r\n> - selector:\r\n> meter_name: io.opentelemetry.micrometer-1.5\r\n> instrument_name: jvm.*\r\n> stream:\r\n> aggregation:\r\n> drop: {}\r\n> - selector:\r\n> meter_name: io.opentelemetry.micrometer-1.5\r\n> instrument_name: process.cpu.usage\r\n> stream:\r\n> aggregation:\r\n> drop: {}\r\n> ```\r\n> \r\n> cc @SylvainJuge who opened the linked issue\r\n\r\nI think this should solve the \"opt-out\" strategy described in the issue, however I don't think this very practical, in particular when conflicts are now known in advance.\r\n\r\nSee also related [slack discussion](https://cloud-native.slack.com/archives/C014L2KCTE3/p1783586947945979) where a few other options are being discussed.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -6764,6 +7005,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-03T07:56:12Z", "body": "Library instrumentation should have a readme with instructions on how to set it up. Since this instrumentation produces metrics without semantic conventions it might be best to also list these in the readme, maybe add a table with the metric names and descriptions (and units?) similarly to what runtime-telemetry library module has.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -6792,6 +7034,7 @@ "review_state": null, "root_timestamp": "2026-07-02T09:00:38Z", "body": "I think we should add these for all messaging instrumentation (doing just kafka in this PR is fine, the rest can be handled separately). It is not ideal that we only have `messaging.receive.duration` and `messaging.publish.duration` but nothing for `process`. I think we have the following choices\r\n- leave it as is, don't add anything for `process`\r\n- add the missing `process` metrics based on https://github.com/open-telemetry/semantic-conventions/blob/v1.24.0/docs/messaging/messaging-metrics.md (there the metric names contains `deliver` instead of `process`)\r\n- switch to metrics from current semconv https://github.com/open-telemetry/semantic-conventions/blob/main/docs/messaging/messaging-metrics.md Since our messaging implementation doesn't really follow any version of the semantic conventions this might not be that bad. Since the messaging metrics are currently only implemented for pulsar the backwards compatibility concerns are limited.\r\n\r\n@trask do you have any preference on how we should handle this?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -6820,6 +7063,7 @@ "review_state": null, "root_timestamp": "2026-07-14T21:30:36Z", "body": "> @trask do you have any preference on how we should handle this?\r\n\r\nIt has downsides, but I'd lean towards continuing to follow https://github.com/open-telemetry/semantic-conventions/blob/main/docs/messaging/README.md, which basically says, emit the old semconv by default, and only emit new semconv under `OTEL_SEMCONV_STABILITY_OPT_IN` (and in our case starting with 3.0, `OTEL_SEMCONV_STABILITY_PREVIEW`).\r\n\r\nWe could bump to latest (even before stable), but ideally only after we have implemented the same conventions consistently across all of our messaging instrumentations. It would probably be confusing for some of our messaging instrumentations to emit earlier semconv and some later semconv by default.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -6848,6 +7092,7 @@ "review_state": null, "root_timestamp": "2026-07-15T14:24:27Z", "body": "> It has downsides, but I'd lean towards continuing to follow https://github.com/open-telemetry/semantic-conventions/blob/main/docs/messaging/README.md, which basically says, emit the old semconv by default, and only emit new semconv under `OTEL_SEMCONV_STABILITY_OPT_IN` (and in our case starting with 3.0, `OTEL_SEMCONV_STABILITY_PREVIEW`).\r\n> \r\n> We could bump to latest (even before stable), but ideally only after we have implemented the same conventions consistently across all of our messaging instrumentations. It would probably be confusing for some of our messaging instrumentations to emit earlier semconv and some later semconv by default.\r\n\r\nThe thing is that our messaging semconv don't really follow a particular version of the semconv. The version that is closest to how we emit spans doesn't define metrics at all. The messaging metrics are currently only enabled for pulsar so I'd say we can probably allow incompatible changes if we so desire since their usage is limited.\r\n@trask do I understand correctly that your preference is to use https://github.com/open-telemetry/semantic-conventions/blob/v1.25.0/docs/messaging/messaging-metrics.md for metrics (1.24.0 has `deliver` inste ...[truncated]", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -6876,6 +7121,7 @@ "review_state": null, "root_timestamp": "2026-07-16T04:30:24Z", "body": "> The thing is that our messaging semconv don't really follow a particular version of the semconv.\r\n\r\nYou've convinced me 😅.\r\n\r\nBut if we're going to do it, let's take the opportunity to sync them all up for 3.0:\r\n\r\nhttps://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/19233", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -6904,6 +7150,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-07T07:17:01Z", "body": "@jaydeluca with this PR quartz instrumentation will emit an event in case you wish to add this to the collected metadata", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -6932,6 +7179,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-07T07:21:38Z", "body": "Open question is whether emitting the event should be configurable or whether there is any reason why it shouldn't be emitted by default.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -6960,6 +7208,7 @@ "review_state": null, "root_timestamp": "2026-07-11T18:35:50Z", "body": "is there a specific reason you had in mind for adding these particular tests? thanks", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -6988,6 +7237,7 @@ "review_state": null, "root_timestamp": "2026-07-13T09:37:39Z", "body": "From what I understand the goal of this PR is to provide a way to enhance the span create by http client instrumentation. The problem is that in user code you can't usually easily observe the http client span so you could add attributes to it. When using library instrumentation you could add an attribute extractor with https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/e49a4a9cb1261eaa22af9849786f036a63288e81/instrumentation/ktor/ktor-common-2.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/common/v2_0/AbstractKtorClientTelemetryBuilder.kt#L68 It is mentioned that you could use `currentCoroutineContext().getOpenTelemetryContext()` but this feels more like a coincidence, typically for callbacks our instrumentations expose the parent context of the http client span not the http client span itself. This issue isn't really specific to ktor http client instrumentation, but rather affects all instrumentations that create client or producer spans. It does come up once in a while. I tried solving it with https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6191 but it didn't get enough traction. There were some concerns that it mi ...[truncated]", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -7016,6 +7266,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-09T05:54:28Z", "body": "## Pull request overview\n\nThis PR adds a new `redisson-metrics-2.3` javaagent instrumentation module that emits database connection pool metrics (used/idle connections, min/max pool sizes, and pending requests) for Redisson versions `[2.3.0, 3.18.0)`. It fills a gap in the existing Redisson instrumentation (which only provides client spans/metrics for 3.0+) by instrumenting the internal `ClientConnectionsEntry` construction and `RedisClient.shutdownAsync()` lifecycle, mirroring the established connection-pool-metrics pattern used by the c3p0 and Tomcat JDBC modules.\n\n**Changes:**\n- New instrumentation module instruments the 7-arg `ClientConnectionsEntry` constructor to register `DbConnectionPoolMetrics` and `RedisClient.shutdownAsync()` to unregister them.\n- Reads pool counters via reflection through `AsyncSemaphoreAccessor`, supporting both the legacy `AtomicInteger` and newer `AsyncSemaphore` free-connection counters, with pending-requests support only when an `AsyncSemaphore` is present.\n- Adds Testcontainers-based coverage (default 2.3.0 and latest 3.17.x), plus build/config/docs wiring (settings, FOSSA, CI instrumentation list, latest-dep pin, supported-libraries doc).\n\n### Re ...[truncated]", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -7044,6 +7295,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-28T19:41:03Z", "body": "## Pull request overview\n\nCopilot reviewed 13 out of 13 changed files in this pull request and generated no new comments.\n\n\n\n\n
\nComments suppressed due to low confidence (2)\n\n**instrumentation/redisson/redisson-metrics-2.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/redissonmetrics/v2_3/RedissonConnectionPoolMetricsTest.java:144**\n* [Testing] Use the exact attribute assertion here. `hasAttributesSatisfying(...)` silently accepts unexpected attributes, so this test would not catch extra dimensions on the used point.\n```\n .hasAttributesSatisfying(\n```\n**instrumentation/redisson/redisson-metrics-2.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/redissonmetrics/v2_3/RedissonConnectionPoolMetricsTest.java:138**\n* [Testing] Use the exact attribute assertion here. `hasAttributesSatisfying(...)` silently accepts unexpected attributes, so this test would not catch extra dimensions on the idle point.\n\nThis issue also appears on line 144 of the same file.\n```\n .hasAttributesSatisfying(\n```\n
", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -7072,6 +7324,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-29T18:41:23Z", "body": "AI-generated review", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -7100,6 +7353,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-31T05:50:30Z", "body": "## Pull request overview\n\nCopilot reviewed 13 out of 13 changed files in this pull request and generated no new comments.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -7128,6 +7382,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-31T16:13:11Z", "body": "Lightly filtered AI-generated feedback — push back freely", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -7156,6 +7411,7 @@ "review_state": null, "root_timestamp": "2026-07-10T18:06:45Z", "body": "can you update PR description to include motivation for why this PR? e.g.\r\n\r\n> Decouple Apache DBCP 2.0 connection-pool metrics from JMX registration. The javaagent currently detects the datasource lifecycle only through `MBeanRegistration` callbacks, so an active `BasicDataSource` produces no metrics unless it is registered with an `MBeanServer`.\r\n>\r\n> Register metrics after successful pool initialization by instrumenting `startPoolMaintenance()`, and unregister them when the datasource is closed. Keep `postDeregister()` cleanup for JMX-managed pools. This also supports datasource restart without requiring JMX.\r\n>\r\n> Preserve meaningful pool names by preferring the configured JMX name, then the registered JMX `ObjectName`, and falling back to a generated `dbcp2-N` name. Add coverage for non-JMX pools and each naming path, and update the instrumentation documentation to reflect that JMX is no longer required.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -7184,6 +7440,7 @@ "review_state": null, "root_timestamp": "2026-07-16T15:54:16Z", "body": "is this a breaking change? if so, let's put it behind the v3preview flag and try to get it into v2.30.0", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -7212,6 +7469,7 @@ "review_state": null, "root_timestamp": "2026-07-19T22:38:55Z", "body": "> is this a breaking change?\r\n\r\nI think no:\r\n\r\nExisting users on the normal successful JMX path retain the name derived from their registered ObjectName. Non-JMX users previously received no Apache DBCP metrics, so the JDBC-derived or fallback name belongs to newly added telemetry and has no existing name to preserve.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -7240,6 +7498,7 @@ "review_state": null, "root_timestamp": "2026-07-13T21:49:04Z", "body": "hi @amit306!\r\n\r\nI think the goal of #17858 is more about fixing the API (not just documentation), along the lines of your #18844", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -7268,6 +7527,7 @@ "review_state": null, "root_timestamp": "2026-07-14T16:30:27Z", "body": "When I created #17858 I thought that maybe it would make sense to always register the reactor hook. Note that #17858 is phrased as a question so answering no is a valid option. After looking more into it I realized that we don't actually have any tests that fail without the reactor hook so apparently rector hook isn't always required. There is also https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/18932 that hints that perhaps we should rethink our reactor instrumentation. Spring folks have a blog post series that explores different options for reactor context propagation https://spring.io/blog/2023/03/28/context-propagation-with-project-reactor-1-the-basics Because of that I think that for now it might be better to improve our documentation to provide guidance when the user should also register the rector hook and when they could get away without registering one.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -7296,6 +7556,7 @@ "review_state": null, "root_timestamp": "2026-07-14T16:15:03Z", "body": "I'm not sure about this. The intent here is to augment the server span with additional attributes as stated in https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/instrumentation/apache-shenyu-2.4/metadata.yaml While the span from context is probably also the server span, unless someone uses method instrumentation or something like that to create additional span, using `LocalRootSpan` feels more clear. An alternative would be to rethink this instrumentation. Perhaps instead of augmenting the server span it could create a controller span like other framework instrumentations?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -7324,6 +7585,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-21T18:01:36Z", "body": "@trask should there be a corresponding semconv issue for this?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -7352,6 +7614,7 @@ "review_state": null, "root_timestamp": "2026-03-18T16:36:11Z", "body": "This is currently blocked on https://github.com/open-telemetry/opentelemetry-js/pull/6356 and similar changes in the other SDKs. We currently see issues where if there's not OTel collector, app shutdowns take significantly longer; this PR here would make that issue worse as it stands now.\r\n\r\nWe still want to merge this once the other PR is in though.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -7380,6 +7643,7 @@ "review_state": null, "root_timestamp": "2026-04-06T15:23:35Z", "body": "Will give this a look once back from vacation at the end of april", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -7408,6 +7672,7 @@ "review_state": null, "root_timestamp": "2026-04-07T08:39:59Z", "body": "hi @overbalance \r\n\r\nwe looked at that PR a couple of weeks ago in the JavaScript SIG. We were wondering if possible to make the migration from Karma to vitest in a follow up PR so this one becomes smaller and easier to review.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -7436,6 +7701,7 @@ "review_state": null, "root_timestamp": "2026-06-15T12:59:38Z", "body": "> Compile on Node ^26.3.0 in CI while tests continue to run on the full supported matrix (build-on-26, run-on-matrix); drop npm install -g npm@latest steps where the bundled npm already meets the 11.16.0 floor; build the w3c integration server's dependencies at the repo root\r\n\r\nAs long as we test on all supported versions and compile the same way for publishing I'm okay with this change. WDYT @open-telemetry/javascript-approvers ?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -7464,6 +7730,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-06-15T13:53:48Z", "body": "This looks very good. Just a couple of questions.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -7492,6 +7759,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-06-15T17:21:44Z", "body": "Thank you for the refactor on this, left a few comments. Personally very much in favour of getting this in.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -7520,6 +7788,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-06-16T17:53:17Z", "body": "Everything I could think of has been addressed :)", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -7548,6 +7817,7 @@ "review_state": null, "root_timestamp": "2026-02-16T14:09:48Z", "body": "@cjihrig @vitorvasc can we please get this merged?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -7576,6 +7846,7 @@ "review_state": null, "root_timestamp": "2026-02-16T15:40:26Z", "body": "@Dhruv-Garg79 this still requires review and approval from a project maintainer.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -7604,6 +7875,7 @@ "review_state": null, "root_timestamp": "2026-02-12T20:24:52Z", "body": "@zakcutner - my original idea was to provide an interface for people to pass their own transports to the exporters, because different transport implementations are so widely requested.\r\n\r\nMy idea was that people would be able to do something like this, but I ran out of time to work on it:\r\n\r\n```ts\r\nconst exporter = createOtlpExporter({\r\n serializer: ProtobufTraceSerializer,\r\n transport: createMyCustomFetchTransport() // returns a custom IExporterTransport impl\r\n});\r\n```\r\n\r\nthe underlying implementation is already somewhat setup for such a thing, but requires a bit more boilerplate\r\n```ts\r\n\r\nimport { createOtlpNetworkExportDelegate } from '@opentelemetry/otlp-exporter-base';\r\nimport { ProtobufTraceSerializer } from `@opentelemetry/otlp-transformer`;\r\n\r\nconst exporter: SpanExporter = new createOtlpExportDelegate({\r\n options: { /** provide all required options accoding to type**/ },\r\n serializer: ProtobufTraceSerializer, // or JsonTraceSerializer\r\n transport: createMyCustomFetchTransport() /** implement this yourself, a wrapper around your custom fetch, make sure to set the content-type header to what you're sending: `application/x-protobuf` or `application/json` **/\r\n}); /* dele ...[truncated]", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -7632,6 +7904,7 @@ "review_state": null, "root_timestamp": "2026-02-12T20:31:45Z", "body": "So my recommendation for this feature is:\r\n- decline for now\r\n- use workaround for the time being\r\n- drop `node:http` based exporter transport in July 2026\r\n- add this feature as proposed in this PR to the now streamlined interface where it also works for Node.js\r\n- feature is released alongside SDK 3.0, everybody can use it", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -7660,6 +7933,7 @@ "review_state": null, "root_timestamp": "2026-03-03T22:48:14Z", "body": "Of course, my comments only apply if js core team approves.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -7688,6 +7962,7 @@ "review_state": null, "root_timestamp": "2026-03-10T13:46:36Z", "body": "Is this needed only for the JS SDK or for other languages as well? Have other language SDKs already solved this problem?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -7716,6 +7991,7 @@ "review_state": null, "root_timestamp": "2026-03-20T00:45:33Z", "body": "Please do not rebase or otherwise change history and force push as it breaks the link between conversation threads and the code they're referencing", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -7744,6 +8020,7 @@ "review_state": null, "root_timestamp": "2026-06-03T23:02:33Z", "body": "I ran into this gap while wiring exemplar support into my own metrics and traces stack (Grafana Cloud Mimir and Tempo, fed from a Node agent) and found this PR with all of the earlier review feedback already addressed in the later commits. Since it has been sitting with conflicts for a couple of months, I rebased it onto current main to keep it moving: https://github.com/rnavarro/opentelemetry-js/tree/feat/metrics-exemplars-rebased\n\nThe rebase preserves all five of @CharlieTLe's commits unchanged. Conflicts were limited to the two CHANGELOG files (entries moved to the current Unreleased section) and `ExemplarReservoir.ts`, where I kept this PR's non-mutating `collect()` together with the `for...of` conversion that landed on main since. On the rebased branch the `sdk-metrics` suite passes 431 tests and `otlp-transformer` passes 220, lint is clean on the changed files, and I verified end to end that a histogram recorded under a sampled span context serializes an exemplar with the correct traceId and spanId through `JsonMetricsSerializer`.\n\n@CharlieTLe if you want to pull that branch into this PR, it is yours to take. If you are short on time, I am happy to open a successor PR that ke ...[truncated]", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -7772,6 +8049,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-03-17T13:32:00Z", "body": "is there any test that would have caught this scenario that can be added here?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -7800,6 +8078,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-03-19T12:20:39Z", "body": "This PR replaces _httpPatched/_httpsPatched booleans with isWrapped() checks to avoid double-wrapping http when loaded by both require and import.\n\nDone well: Using isWrapped from the instrumentation base is the right abstraction—it checks the actual patched state instead of tracking it manually. Removing the state flags simplifies enable/disable and avoids races if modules are loaded concurrently. The instrumentation.disable() in test afterEach ensures clean teardown between tests. License header change to SPDX is a nice consistency fix.\n\nSuggestion: The double-instr test verifies no double instrumentation when http is loaded by both require and import. With isWrapped the second load would skip patching. Consider adding a test that explicitly loads http twice (e.g. via two different import paths) and asserts request/emit are wrapped only once, to guard against future regressions.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -7828,6 +8107,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-04-29T08:48:42Z", "body": "From the experience I had in browser this is a good solution assuming only this instrumentation is patching the HTTP module. `isWrapped` does not ensure your wrapper is the one applied to the wrapped function.\n\nShould we assume http instrumentation is the only patching htese methods?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -7856,6 +8136,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-03-19T12:20:46Z", "body": "This PR adds maxScale option to ExponentialHistogramAggregation to limit the scale of exponential histograms.\n\nDone well: maxScale threads through cleanly—ExponentialHistogramAggregation, ExponentialHistogramAggregator, and ExponentialHistogramAccumulation all get the parameter with DEFAULT_MAX_SCALE=20 preserving backward compatibility. The mapping ?? getMapping(maxScale) in the accumulation constructor handles the optional mapping param correctly. AggregationOption type and toAggregation wiring are updated. Tests cover default (20), custom maxScale, and multiple accumulations sharing the same maxScale.\n\nMinor: The ExponentialHistogramAccumulation constructor signature changed—mapping is now optional and maxScale is a new positional arg. Any external code that passed mapping explicitly might need to use keyword args. Worth a changelog note if this is a public API.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -7884,6 +8165,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-03-23T13:10:55Z", "body": "This looks good to me but I'd like to have someone like @pichlermarc or @legendecas who is a bit closer to the implementation take a look", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -7912,6 +8194,7 @@ "review_state": null, "root_timestamp": "2026-03-22T17:18:23Z", "body": "Please do not rebase or otherwise change history as it breaks the link between review comment threads and the code", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -7940,6 +8223,7 @@ "review_state": null, "root_timestamp": "2026-04-01T15:02:23Z", "body": ">I'm not entirely happy about pulling time-related logic outside of _getTime but figure it's worth it given the improvement. Happy to switch to something else if we can maintain the improvement.\r\n\r\nThe span creation benchmark currently shows around `500,000` ops per second which works out to 2 microseconds per operation. Improving that by 10% is only a shave of 200 ns. Is it really worth it?\r\n\r\nEspecially since that benchmark seems to fluctuate by 5-10% on any given run without changes, are you sure you're even getting the expected gains? I've found you need to run the benchmarks several times to get reasonable results. You may want to also consider using the microtime option when running the benchmark in order to make sure you're catching small differences.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -7968,6 +8252,7 @@ "review_state": null, "root_timestamp": "2026-04-01T16:51:05Z", "body": "I was able to achieve a similar speedup by saving the performance start time from the constructor in a class property instead of calling `performance.now` in `_getTime` and combining the `if` statements like so (avoids second branch check):\r\n\r\n```typescript\r\n constructor(opts: SpanOptions) {\r\n const now = Date.now();\r\n this._spanContext = opts.spanContext;\r\n this._performanceStartTime = performance.now();\r\n this._performanceOffset =\r\n now - (this._performanceStartTime + performance.timeOrigin);\r\n this.startTime = this._getTime(opts.startTime ?? now);\r\n /* [...] */\r\n }\r\n\r\n private _getTime(inp?: TimeInput): HrTime {\r\n if (typeof inp === 'number') {\r\n if (inp <= this._performanceStartTime) {\r\n // performance.now() timestamp — apply offset to convert to wall-clock\r\n return hrTime(inp + this._performanceOffset);\r\n }\r\n // Date.now() timestamp\r\n return millisToHrTime(inp);\r\n }\r\n /* [...] */\r\n }\r\n```\r\n\r\nedit: running the benchmark multiple times results in a wide swing. Sometimes I get 900k ops/s and sometimes 600k. This does seem to improve the midpoint, but I am not 100% confident the benchmark is actually showing a r ...[truncated]", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -7996,6 +8281,7 @@ "review_state": null, "root_timestamp": "2026-04-15T09:26:20Z", "body": "> In bundled ESM environments, that unnecessary hook can lead to runtime failures when the app later uses createRequire(), for example `ReferenceError: require is not defined`\r\n\r\nIt's not clear why ESM, bundling and `require-in-the-middle` causes the above to occur. Do you have a reproduction to try?\r\n\r\nIt feels like this PR works around an issue rather than actually finding and fixing the root cause!", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -8024,6 +8310,7 @@ "review_state": null, "root_timestamp": "2026-04-15T15:34:53Z", "body": "Ah, I see that essentially your PR [here](https://github.com/getsentry/sentry-electron/pull/1354) is a reproduction of the issue. I'll take a look!", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -8052,6 +8339,7 @@ "review_state": null, "root_timestamp": "2026-04-15T15:49:07Z", "body": "@biw your example from the Electron PR referenced some really old dependencies. When I updated everything to below, it doesn't result in a runtime error.\r\n\r\n```json\r\n \"dependencies\": {\r\n \"electron-squirrel-startup\": \"^1.0.1\"\r\n },\r\n \"devDependencies\": {\r\n \"@sentry/electron\": \"7.11.0\",\r\n \"@sentry/vue\": \"10.47.0\",\r\n \"@vitejs/plugin-vue\": \"^6.0.6\",\r\n \"electron\": \"^41.2.0\",\r\n \"electron-vite\": \"^5.0.0\",\r\n \"vite\": \"^8.0.8\",\r\n \"vue\": \"^3.5.32\"\r\n }\r\n```\r\n\r\nIf you can supply a failing reproduction of the issue I might be able to help further.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -8080,6 +8368,7 @@ "review_state": null, "root_timestamp": "2026-05-11T10:37:41Z", "body": "Hi @biw - were you able to repro this on more recent versions? 🙂", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -8108,6 +8397,7 @@ "review_state": null, "root_timestamp": "2026-05-11T11:43:56Z", "body": "I would say this should be closed.\r\n\r\nRather than init `require-in-the-middle` lazily, there should be a way to create Node instrumentations that don't use `require-in-the-middle` and `import-in-the-middle` at all. ie. some higher level base instrumentation that doesn't expect module hooking. This would be useful for instrumentations that use `TracingChannel`!", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -8136,6 +8426,7 @@ "review_state": null, "root_timestamp": "2026-04-16T14:50:03Z", "body": "from @pichlermarc on otel-js-dev channel:\r\n`We are planning to publish SDK 3.0 around June/July, and have a milestone set up for it already. I'd suggest we do it there and communicate the change accordingly.`\r\n\r\nI'm not sure when we can start adding changes only for 3.0. Can you provide guidance @pichlermarc ?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -8164,6 +8455,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-04-17T08:22:42Z", "body": "@maryliag - I thought it'd be best to do this at the same time as 3.x (possibly also bumping this package from experimental to stable in the process).\n\nI think this change will break a lot of users that rely on the default behavior being that it binds to all available network interfaces instead of `localhost`.\n\n**suggestion:** let's put a warning there for now, letting people know that this will only bind to `localhost` in the future. Once we're working on 3.x, we change the behavior and we release it together with 3.x, and include guidance on what to do in the migration document. This way people have some time to change their settings accordingly instead of being broken without warning.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -8192,6 +8484,7 @@ "review_state": null, "root_timestamp": "2026-07-22T16:46:14Z", "body": "> > Waiting on: Author\r\n> \r\n> Is that accurate?\r\n\r\nno - automation made a mistake - we'll merge this once we start working on 3.0 :)", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -8220,6 +8513,7 @@ "review_state": null, "root_timestamp": "2026-04-29T08:10:45Z", "body": "@abhisheksurve45 thanks for your contribution. I left a comment about the scope of the new function. Maybe other @open-telemetry/javascript-approvers want to give their view.\r\n\r\nIn the meantime you may want to `npm run lint:fix` to make the CI happy", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -8248,6 +8542,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-05-14T12:25:44Z", "body": "Hello, hello, thanks for starting the effort!\n\nI don't know JS at all, so I can't do a very complete review. \n\nLooking at your tests, and comparing them with the ones we did for go (https://github.com/prometheus/otlptranslator/blob/main/metric_namer_test.go), it seems like a lot of edge cases aren't covered here. Is it worth covering them?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -8276,6 +8571,7 @@ "review_state": null, "root_timestamp": "2026-05-14T14:30:04Z", "body": "Yep, 1200 LOC is a lot 😅. Let me try to parse that into human-readable text:\r\n\r\nUnderscore Scaping:\r\n* `metric@with#special$chars` -> `metric_with_special_chars`\r\n* `123metric` -> `_123metric` (metric names starting with digit are unnallowed unless UTF-8 is enabled)\r\n* `` (empty) -> Should error\r\n* `metric@@##$$name` -> `metric_name` (multiple special characters become a single underscore)\r\n* `@#$%` -> Should error since it's only special characters, translating into a single underscore\r\n\r\n\r\nNo underscore scaping:\r\n* The cases above are allowed untransformed, besides the empty string", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -8304,6 +8600,7 @@ "review_state": null, "root_timestamp": "2026-05-18T08:47:33Z", "body": "nice, almost there :) \r\nlooks like we still need a config\r\n\r\n\r\n**logs:**\r\n```\r\n/opt/hostedtoolcache/node/22.22.2/x64/bin/npx size-limit --json\r\nInstall Size Limit preset depends on type of the project\r\n\r\nFor application, where you send JS bundle directly to users\r\n npm install --save-dev @size-limit/preset-app\r\n\r\nFor frameworks, components and big libraries\r\n npm install --save-dev @size-limit/preset-big-lib\r\n\r\nFor small (< 10 kB) libraries\r\n npm install --save-dev @size-limit/preset-small-lib\r\n\r\nCheck out docs for more complicated cases\r\n https://github.com/ai/size-limit/\r\n```", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -8332,6 +8629,7 @@ "review_state": null, "root_timestamp": "2026-05-18T08:49:36Z", "body": "or is the failure reason the one you mentioned in the PR description? 🤔", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -8360,6 +8658,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-06-02T16:45:04Z", "body": "Great fix! Just one question.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -8388,6 +8687,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-06-12T19:02:51Z", "body": "I think that covers the issues found for browsers. I'll be curious to see what's in the node review", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -8416,6 +8716,7 @@ "review_state": null, "root_timestamp": "2026-06-12T19:15:30Z", "body": "One more thing the 🤖 mentioned:\r\n\r\nShould a send timeout be classified as `retryable` instead of `failure`, given that the Node HTTP transport treats its own timeout as `retryable` (http-transport-utils.ts), or is the intent to keep the fetch transport's **existing** drop-on-timeout semantics?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -8444,6 +8745,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-20T12:03:28Z", "body": "Sorry for the late review on this PR. I think having `AggreagationSelector` be a function was a mistake we made, since it implies that changing the returned values after configuring the `MetricReader` (in this case `PrometheusExporter`) would re-configure already-created instruments, which is not the case.\n\n**Suggestion:** let's implement this feature in a why that there's an `aggregationPreference` option that people can configure like so:\n\n```ts\nnew PrometheusExporter({\n aggregationPreference: {\n gauge: {\n // this here is an AggregationOption\n type: AggregationType.LAST_VALUE\n },\n // non-specified ones fall back to default\n }\n})\n```\n\nInternally, the `aggregationPreference` is converted to an `aggregationSelector` and published this way. This way it's clearer that nothing can be changed once the exporter has been instantiated. :)", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -8472,6 +8774,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-06-19T20:34:11Z", "body": "Great example! Thank you for adding!", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -8500,6 +8803,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-09T21:54:20Z", "body": "A number of nits, but looks good to me (along with a proposal to have a separate \"telemetry.ts\").", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -8528,6 +8832,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-20T21:02:54Z", "body": "LGTM. Thanks, Marc!\n\nI gather your `subscriberWithContextManagement` utility from https://github.com/open-telemetry/opentelemetry-js/pull/6387/changes#diff-516a26260144caf8cae79053916963fc7c5ca8aa24771ccc4d022b532c013b4c will need to be updated to use `token.dispose()`.\nHave you played with that?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -8556,6 +8861,7 @@ "review_state": null, "root_timestamp": "2026-07-07T20:11:38Z", "body": "Please don't forget to include a changelog.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -8584,6 +8890,7 @@ "review_state": null, "root_timestamp": "2026-07-09T13:55:55Z", "body": "Hi @matthieusieben - thanks for opening this PR for discussion. This is actually an issue that's mentioned in our docs: https://opentelemetry.io/docs/concepts/context-propagation/#security-best-practices\r\n\r\n**Q:** why not strip these headers at at something like a reverse proxy so that these never end up in the app at all? I suppose the idea is to don't allow external callers to add trace context/baggage but to allow internal ones. This is the most common way I've seen this issue handled in production deployments.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -8612,6 +8919,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-09T22:39:20Z", "body": "Thanks for the PR. One requested change below.\n\n(Also, it would be good to have a follow-up issue/PR that updates usage in this repo. E.g. here: https://github.com/open-telemetry/opentelemetry-js/blob/a79c4a4fc27b1a29286e60de4e9ce884e7c23c58/experimental/packages/opentelemetry-sdk-node/src/utils.ts#L1353-L1386)", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -8640,6 +8948,7 @@ "review_state": null, "root_timestamp": "2026-07-17T09:40:00Z", "body": "> Also, it would be good to have a follow-up issue/PR that updates usage in this repo\r\n\r\nref: #6925", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -8668,6 +8977,7 @@ "review_state": null, "root_timestamp": "2026-07-17T16:20:16Z", "body": "Having `AggreagationSelector` be a function was a mistake we made, since it implies that changing the returned values after configuring the `MetricReader` (in this case `PrometheusExporter`) would re-configure already-created instruments, which is not the case.\r\n\r\n**Suggestion:** let's implement this feature in a why that there's an `aggregationPreference` option that people can configure like so.\r\n\r\n```ts\r\nnew PrometheusExporter({\r\n aggregationPreference: {\r\n gauge: {\r\n // this here is an AggregationOption\r\n type: AggregationType.LAST_VALUE\r\n },\r\n // non-specified ones fall back to default\r\n }\r\n})\r\n```\r\n\r\nInternally, the `aggregationPreference` is converted to an `aggregationSelector` and published this way.\r\nThis way it's clearer that nothing can be changed once the exporter has been instantiated. :)", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -8696,6 +9006,7 @@ "review_state": null, "root_timestamp": "2026-07-27T15:06:28Z", "body": "@Babul422 This looks like a breaking change for gRPC. Have you tested it?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -8724,6 +9035,7 @@ "review_state": null, "root_timestamp": "2026-07-27T17:16:38Z", "body": "Right, but now they have differing behavior if your proposal is the new standard. I'll wait for others to chime in.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -8752,6 +9064,7 @@ "review_state": null, "root_timestamp": "2025-08-13T17:01:13Z", "body": "@lucas-gregoire - looks like the messaging semantic conventions are not *actually* stable yet, so this PR is blocked until is is formally marked as such. \r\n\r\n(Unless this is part of an effort to create prototypes to mark the messaging semconv as stable, if that is the case, please link the corresponding issue from SemConv here, thanks 🙂)", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -8780,6 +9093,7 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2025-09-10T16:31:13Z", "body": "See comments above.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -8808,6 +9122,7 @@ "review_state": null, "root_timestamp": "2025-09-24T16:47:07Z", "body": "Some context: We discussed this in the OTel JS SIG call today. Doing semconv changes to this instrumentation to get it closer to the current semconv spec state isn't *blocked* until messaging semconv is stabilized. However, because messaging semconv isn't stable yet, we cannot *call these changes \"stable\"* and we probably shouldn't use the `OTEL_SEMCONV_STABILITY_OPT_IN` envvar.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -8836,6 +9151,7 @@ "review_state": null, "root_timestamp": "2025-10-17T11:49:44Z", "body": "I opened https://github.com/open-telemetry/semantic-conventions/issues/2928 on the semconv repo to find a way how we could accept this change. With that proposal we would be able to use `messaging_latest_experimental` over `messaging` for updating to newer experimental semconv.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -8864,6 +9180,7 @@ "review_state": null, "root_timestamp": "2025-11-05T17:53:13Z", "body": "cc @blumamir @naseemkullah (component owners)", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -8892,6 +9209,7 @@ "review_state": null, "root_timestamp": "2025-12-10T20:28:24Z", "body": "Fixes and makes #3276 redundant", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -8920,6 +9238,7 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2025-11-20T11:52:47Z", "body": "This repo is very delicate with its dependencies. \n\nEssentially I agree most of what this PR is doing **except for the catch-all `:pinDevDependencies`** - we have a lockfile so that the few `devDependencies` that are unpinned don't cause havoc.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -8948,6 +9267,7 @@ "review_state": null, "root_timestamp": "2025-11-20T12:56:47Z", "body": ">Would it work if we add an explicit don't pin dev otel package rule?\r\n\r\nCould work. But also I think we're kind of okay with the settings we have now. Though, we may want to have our actions pinned, and potentially images pinned too as we did in the core repo.\r\n\r\nOther than that I don't think we necessarily need this change and I suspect blindly applying best practice settings right now will make us spend a lot of time sorting though issues that don't really move the needle for anyone. I'd leave it to folks that regularly update dependencies in this repo and have a feeling about what usually goes wrong with these sorts of things.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -8976,6 +9296,7 @@ "review_state": null, "root_timestamp": "2025-11-21T09:39:54Z", "body": "> So what prompted me to look at this was the fact that markdownlint was not reporting violations. What I saw was markdownlint was out of date and was pinned on the old one. I know from other projects pinning dev dependencies resulted in them being updated. \r\n\r\nThe problem here is not pinning. We have dependency Dependency Dashboard approvals on for most packages.\r\n\r\nWe don't update all packages at once since we have a massive amount of them here in this repo. We found that:\r\n- auto updating packages individually takes time away from reviewing contributions made by real people.\r\n- auto updating package in batches makes it pretty much impossible to troubleshoot when something goes wrong, you'll have to update on a package-by-package basis anyway.\r\n\r\nBoth approaches burnt people out as they had to deal with dependency updates all day. Some worked immediately, some did not. But all of them took visibility away from actual PRs people opened. So I pulled the plug on it and made Dashboard Approvals required. That means that if somebody has a few minutes to spare and they have Triage permissions on the repo, they can trigger and update and work through any problems without it always being ...[truncated]", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -9004,6 +9325,7 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-01-16T15:14:35Z", "body": "See linked PR that was manually updated.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -9032,6 +9354,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-01-05T15:42:49Z", "body": "Thank you for your contribution.\r\nYou will need to add a changelog and tests to this PR before we can merge it.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -9060,6 +9383,7 @@ "review_state": null, "root_timestamp": "2026-01-13T19:35:56Z", "body": "> You will need to add a changelog and tests to this PR before we can merge it.\r\n\r\n@maryliag This gets me and I have to check again almost everytime. The **core** repo requires PRs to explicitly include a CHANGELOG.md entry. The **contrib** repo does not, because the release process automatically adds changelog entries based on the commit titles.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -9088,6 +9412,7 @@ "review_state": null, "root_timestamp": "2026-01-13T21:28:16Z", "body": ">The contrib repo does not\r\n\r\nthis is what I get for having a bunch of tabs open to review and not checking the repo 🤦 \r\nNow just wait for tests to pass then 😄", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -9116,6 +9441,7 @@ "review_state": null, "root_timestamp": "2026-01-16T14:03:47Z", "body": "Silly question: is this not what the `View` concept is for (defining a custom aggregation based on meter+metric name?)\r\nhttps://opentelemetry.io/docs/languages/js/instrumentation/#configure-metric-views", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -9144,6 +9470,7 @@ "review_state": null, "root_timestamp": "2026-01-16T15:07:42Z", "body": "> > Silly question: is this not what the `View` concept is for (defining a custom aggregation based on meter+metric name?) https://opentelemetry.io/docs/languages/js/instrumentation/#configure-metric-views\r\n> \r\n> Does that work if the view buckets are more granular or have larger ranges than the underlying histogram? I've never heard of views and no idea how they work internally (and docs aren't clear)\r\n\r\nYes. The underlying mechanism the same for `View` and `advice` - when you use `Histogram#record()` it passes the data to the `Aggregation` that was created based on either the default, the `advice` or the `View`. In any case, you'll not loose data-granularity and there's no estimation going on.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -9172,6 +9499,7 @@ "review_state": null, "root_timestamp": "2026-02-02T15:03:51Z", "body": "@lukeramsden did you have time to review the latest comments?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -9200,6 +9528,7 @@ "review_state": null, "root_timestamp": "2026-04-15T16:59:16Z", "body": "Is there any sort of spec or documentation for this feature? Can we be certain this will always continue to work into the future?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -9228,6 +9557,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-03-09T17:12:30Z", "body": "Solution looks good, but I'm wondering whether we might want to tackle this from the instrumentation package given it's not technically unique to Lambda", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -9256,6 +9586,7 @@ "review_state": null, "root_timestamp": "2026-04-14T13:06:30Z", "body": "Hi @neilime \r\n\r\nThanks for working on this. I'm okay with having a new and more generic instrumentation (let's wait for more feedback). IMHO I would split this PR into smaller ones with this order\r\n\r\n- [ ] PR to add the new instrumentation (this one)\r\n- [ ] wait for the publication of the new instrumentation\r\n- [ ] PR to add the deprecation notice into `@opentelemetry/instrumentation-nestjs-core`\r\n- [ ] PR to replace it in auto instrumentations\r\n- [ ] after a period of time remove the deprecated instrumentation. Leaving a README for people looking at the instrumentation\r\n\r\n`@opentelemetry/instrumentation-fastify` had a similar process. Ref: https://github.com/open-telemetry/opentelemetry-js-contrib/issues/2647\r\n\r\nIf you agree with this process I think this PR should only contain the new instrumentation. And I'd prefer to not refactor the current instrumentation so we avoid maintenance on it.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -9284,6 +9615,7 @@ "review_state": null, "root_timestamp": "2026-04-29T08:59:09Z", "body": "@neilime any feedback from my previous comment?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -9312,6 +9644,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-06-23T00:55:09Z", "body": "Hey @garysassano, I was interested in the cold-start impact since this detector previously only read environment variables and this adds an HTTP request on the init path.\n\nI compared the current detector vs. this PR, on average (10 runs) the one-time cold-start cost was `+53 ms` at `128 MB` and `+5.5 ms` at `512 MB` of memory. So the performance impact seems okay, wondering if you had similar results or other thoughts?\n\nRegarding populating `cloud.availability_zone` with the AZ ID instead of the AZ name, it technically still fits the spec, but it will be inconsistent with the other AWS EC2 and ECS detectors in this package, which set the value to be the AZ name instead. So I suppose if consumers tries to ever query resources grouped by `cloud.availability_zone`, they'd need to account for this discrepancy. Given the limitation of Lambda only exposing the ID, I think this is okay for now, as you also mentioned in the description. I'm thinking that if the AZ name is ever exposed in the future (if that even makes sense for Lambda), we should switch to it as a breaking change. For now, could you leave a comment under `_fetchAvailabilityZone along` these lines?\n```\n// Lambda's metadata ...[truncated]", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -9340,6 +9673,7 @@ "review_state": null, "root_timestamp": "2026-05-13T13:56:35Z", "body": "@Genmin thanks for your contribution. Could you please sign the CLA so we can move on with this PR?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -9368,6 +9702,7 @@ "review_state": null, "root_timestamp": "2026-06-22T12:53:17Z", "body": "@Genmin \r\n\r\nCLA is still missing. Did you have any issue signing it?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -9396,6 +9731,7 @@ "review_state": null, "root_timestamp": "2026-05-05T17:04:00Z", "body": "ref #3379", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -9424,6 +9760,7 @@ "review_state": null, "root_timestamp": "2026-07-08T19:00:30Z", "body": "I briefly tried to get instr-koa working with `@koa/router@15` changes, but fell short and moved on.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -9452,6 +9789,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-13T16:22:25Z", "body": "thank you for working on this! You do have a lot of failed tests, so make sure those are fixed. I also added a few more test cases that should be added", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -9480,6 +9818,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-14T13:11:10Z", "body": "I added a few suggestion to help performance, but I'm still debating if this is the right approach for this feature.\n\nIn the spec we have: \"The collection name SHOULD NOT be extracted from db.query.text, when the database system supports query text with multiple collections in non-batch operations.\", which is the case for PG.\n\nPart of it is because the query can have several tables, so it can be hard to decide which one to select, but also because parsing itself can be very costly. If we have a lot of queries being executed, having to match the regex can be an issue for the performance, so I'm thinking there could be an opt-in somehow, at least initially.\nI'll discuss this with the maintainers on our weekly (wednesday), and get back to you.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -9508,6 +9847,7 @@ "review_state": null, "root_timestamp": "2026-07-15T19:21:05Z", "body": "@tejaswiverma121-byte we won't be able to merge this PR, because it doesn't follow the spec. \r\nA few other languages have the value of the collection easily available, but JS + PG doesn't, which is why you had to parse the query text, which is something that the spec tells you're not suppose to do.\r\nFor this reason, this feature can't be implemented here.\r\n\r\nI'm going to ask you to update the README instead, clarifying that this attribute is not being collected and that is expected. You can make the update in this PR (after reverting all the other changes) or if it's easier, just close this PR and open a new one with just the readme update.\r\n\r\nI appreciate you working on this either way!", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -9536,6 +9876,7 @@ "review_state": null, "root_timestamp": "2026-07-22T16:56:30Z", "body": "Same pattern is used in https://github.com/open-telemetry/opentelemetry-js-contrib/blob/main/scripts/bitrot.mjs it would be good to cover that one as well", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -9564,6 +9905,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-22T18:05:28Z", "body": "LGTM, I validated on my side, fix is working in Windows machines", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -9592,6 +9934,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-23T15:44:10Z", "body": "Thank you for working on this!\nCan you also update the readme for this package, to include the new metric", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -9620,6 +9963,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-24T17:17:32Z", "body": "Nice catch! Thanks", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -9648,6 +9992,7 @@ "review_state": "APPROVED", "root_timestamp": "2021-10-21T17:06:21Z", "body": "I don't have a way to verify the change. If anyone is able please do.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -9676,6 +10021,7 @@ "review_state": null, "root_timestamp": "2022-07-06T14:44:04Z", "body": "@open-telemetry/specs-approvers can anyone review/verify this PR? If no-one knows how I am going to close it as \"stale\".", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -9704,6 +10050,7 @@ "review_state": null, "root_timestamp": "2023-12-14T21:41:14Z", "body": "Related:\r\n\r\n* https://github.com/open-telemetry/opentelemetry-cpp/pull/2455", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -9732,6 +10079,7 @@ "review_state": null, "root_timestamp": "2023-12-18T09:13:59Z", "body": "@hdost \r\n\r\nPlease note that I used `-z`, lowercase, not `-Z`, uppercase, for https://github.com/open-telemetry/opentelemetry-cpp/pull/2455. Not sure which one you need to generate proto files.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -9760,6 +10108,7 @@ "review_state": null, "root_timestamp": "2023-12-18T15:24:28Z", "body": "@marcalff @ThomsonTan can you please review this PR? If we get both your approvals we should be able to move forward.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -9788,6 +10137,7 @@ "review_state": "APPROVED", "root_timestamp": "2023-12-19T09:10:32Z", "body": "Verified that code generation fails without the fix for SELINUX systems.\r\n\r\n```\r\n[malff@malff-desktop opentelemetry-proto]$ make\r\nrm -rf ./gen/cpp\r\nmkdir -p ./gen/cpp\r\ndocker run --rm -u 1000 -v/data/malff/CODE/MY_GITHUB/opentelemetry-proto:/data/malff/CODE/MY_GITHUB/opentelemetry-proto -w/data/malff/CODE/MY_GITHUB/opentelemetry-proto otel/build-protobuf:0.9.0 --proto_path=/data/malff/CODE/MY_GITHUB/opentelemetry-proto --cpp_out=./gen/cpp opentelemetry/proto/resource/v1/resource.proto\r\nEmulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.\r\nopentelemetry/proto/resource/v1/resource.proto: File does not reside within any path specified using --proto_path (or -I). You must specify a --proto_path which encompasses this file. Note that the proto_path must be an exact prefix of the .proto file names -- protoc is too dumb to figure out when two paths (e.g. absolute and relative) are equivalent (it's harder than you think).\r\nmake: *** [Makefile:56: gen-cpp] Error 1\r\n```\r\n\r\nVerified that code generation works with the fix for SELINUX systems.\r\n\r\nApproved, thanks for the fix.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -9816,6 +10166,7 @@ "review_state": null, "root_timestamp": "2024-03-17T22:25:00Z", "body": "@open-telemetry/specs-approvers please take a look. I think this is good to merge and verified to work by @marcalff.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -9844,6 +10195,7 @@ "review_state": null, "root_timestamp": "2026-03-15T23:20:36Z", "body": "@tigrannajaryan should we put the effort to merge this ?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -9872,6 +10224,7 @@ "review_state": "COMMENTED", "root_timestamp": "2025-05-30T06:31:32Z", "body": "In general, LGTM", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -9900,6 +10253,7 @@ "review_state": null, "root_timestamp": "2025-07-01T15:33:31Z", "body": "@DylanRussell, given https://github.com/open-telemetry/opentelemetry-proto/pull/669 is merged I think you can update this PR :wink:", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -9928,6 +10282,7 @@ "review_state": null, "root_timestamp": "2025-07-30T16:28:04Z", "body": "@open-telemetry/spec-sponsors, @open-telemetry/technical-committee, PTAL", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -9956,6 +10311,7 @@ "review_state": "COMMENTED", "root_timestamp": "2025-08-20T14:17:43Z", "body": "@DylanRussell can you please also attach a prototype implementation that shows how this works? You can take existing implementation in one of the languages or in the Collector and fork/modify it to demonstrate the change. Collectors otlp receiver/exporter fork would be ideal since it shows both the client and server side.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -9984,6 +10340,7 @@ "review_state": null, "root_timestamp": "2025-08-20T14:21:42Z", "body": "I think we need a description of how the new and old implementations should interoperate. Explain the operation in the following 3 scenarios:\r\n- Old client implementing the spec before this change connects to a new server implementing the spec after this change.\r\n- New client connects to old server.\r\n- New client connects to new server.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -10012,6 +10369,7 @@ "review_state": null, "root_timestamp": "2026-04-08T19:33:27Z", "body": "A bit unusual to have such a large value in the dictionary but makes sense overall.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -10040,6 +10398,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-17T18:42:39Z", "body": "Removing dependency from `rules_go` solves bazel 9.x issues. \r\n\r\nOn BCR release, unless bcr publish workflow allows generating MODULE.bazel similar to build-check.yaml, this might get stuck with same error.\r\n\r\n`Keeping bazel out of core` and `Automated BCR` seems mutually exclusive unless there is way include bazel files in release archive. Happy to be proven wrong, and learn something new.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -10068,6 +10427,7 @@ "review_state": null, "root_timestamp": "2026-07-17T19:43:47Z", "body": "> What we do _not_ do in this proto repo is generate client libraries that are consumable by others, we expect that to be owned/maintained donwstream. E.g. In many Otel SDKs / Collector there's bespoke / optimised OTLP proto generation that wouldn't use the protoc generated code.\r\n\r\nThe approach makes more sense given this ask. I'm still doubtful about overlay as I haven't seen any working example or doc references. I'll keep digging.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -10096,6 +10456,7 @@ "review_state": null, "root_timestamp": "2026-07-19T09:53:45Z", "body": "Overlay won't be useful as its a downstream mechanism/control for non bazel upstream deps. It can be done locally, and also in BCR by creating manual entry with Overlay (BCR release process so far).\r\n\r\nBut automated BCR needs the lib to be bazel module, and a bazel module cannot be overlayed (Immutable entry in BCR).\r\n\r\nAssuming automated BCR is still preferred, the cleanest way is to make it a bazel module, and release proto only as baseline, let downstream generate the lib bindings.\r\n\r\nIf more time is needed for downstream community, we have to include proto+lang bindings (same as last BCR overlay) before eventually switching to proto only release.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -10124,6 +10485,7 @@ "review_state": null, "root_timestamp": "2026-09-01T19:45:40Z", "body": "@Shriprasad-P the test is unfortunately still failing here", + "measurement_date": "2026-09-02", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -10152,6 +10514,7 @@ "review_state": null, "root_timestamp": "2026-07-07T16:30:16Z", "body": "@lmolkova \r\n\r\nDoes this pr have conflicts with https://github.com/open-telemetry/opentelemetry-python-genai/pull/90?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -10180,6 +10543,7 @@ "review_state": null, "root_timestamp": "2026-06-08T13:52:35Z", "body": "Hi @Nik-Reddy , will have to wait for a maintainer to merge.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -10208,6 +10572,7 @@ "review_state": null, "root_timestamp": "2026-06-21T19:19:23Z", "body": "@lmolkova I am thinking we can use openinference for adding instrumentation coverage for cohere. wdyt?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -10236,6 +10601,7 @@ "review_state": null, "root_timestamp": "2026-06-26T04:46:48Z", "body": "> @lmolkova I am thinking we can use openinference for adding instrumentation coverage for cohere. wdyt?\r\n\r\nit doesn't seem like they have instrumentation for it", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -10264,6 +10630,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-22T19:36:45Z", "body": "A few more comments. \n\nThe key question, @Nik-Reddy are you going to follow up with real instrumentation right away? We already have a couple of empty instrumentations in this repo and unless you'd like to work on actual cohere instrumentation, I would prefer to not add another one.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -10292,6 +10659,7 @@ "review_state": null, "root_timestamp": "2026-07-08T14:50:26Z", "body": "Everything LGTM!! We can wait till this https://github.com/open-telemetry/semantic-conventions-genai/issues/344 is addressed as I believe we need to update semantic conventions and bring in new semconv package. Lmk Wdyt @wrisa .", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -10320,6 +10688,7 @@ "review_state": null, "root_timestamp": "2026-06-19T21:10:46Z", "body": "Howdy there!! Thanks for your contribution. PR looks good to me. I had few questions around the direction we want to take for this issue, so I posted few questions on the issue. We will get to this once those are confirmed.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -10348,6 +10717,7 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-07-21T00:15:13Z", "body": "This PR covers up a bigger issue that we, instead of returning original stream, we return parsed one that has different API shape.\n\nI'm proposing to fix the bigger issue here - https://github.com/open-telemetry/opentelemetry-python-genai/pull/278 so that we keep instrumentation transparent and don't modify types / expectations from pure underlying libraries.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -10376,6 +10746,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-14T23:12:15Z", "body": "LGTM, just some minor questions and concerns around build and release", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -10404,6 +10775,7 @@ "review_state": null, "root_timestamp": "2026-07-15T01:48:49Z", "body": "@lmolkova do you think we should separate this into two PRs like say:\n\n1. Basic setup(examples, gh, lint and other setup) for agent scope like we did in python contrib.\n2. Add all the instrumentation as this is a donation.\n\nThis might basically reduce the cognitive load on reviewers. Wdyt?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -10432,6 +10804,7 @@ "review_state": null, "root_timestamp": "2026-07-15T02:06:40Z", "body": "@eternalcuriouslearner it's not a super-strong opinion, but I'd prefer to have minimal viable instrumentation in one PR. \r\n\r\n1. It allows us to simplify migration for openinference and other instrumentations\r\n2. it prevents shell instrumentations we have today (weaviate, claude agents sdk)\r\n3. My mental load is actually lower when I look at everything at once\r\n4. Given that instrumentation code in the library is super-thin and leverages util-genai, it's easy to review\r\n5. We'll improve these over time, conformance and general safety is the top priority for me at the moment.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -10460,6 +10833,7 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-07-21T00:12:21Z", "body": "it seems agentscope comes with native otel instrumentation (but in a broken way since it depends on the exporter) - do we know if instrumentation is good? does it follow conventions? \n\nSince there is an existing instrumentation, we should not invent another one. Was there an attempt to contribute directly to https://github.com/agentscope-ai/agentscope if there any gaps?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -10488,6 +10862,7 @@ "review_state": null, "root_timestamp": "2026-07-23T04:24:44Z", "body": "Ran into one concrete case of this while looking at the native extractor recently: gen_ai.response.finish_reasons is hardcoded as the JSON string '[\"stop\"]' (with a FIXME) where the registry type is string[] — https://github.com/agentscope-ai/agentscope/blob/7af58b119bddeb42b12d8d93460d424fad253234/src/agentscope/middleware/_tracing/_extractor.py#L337-L339", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -10516,6 +10891,7 @@ "review_state": null, "root_timestamp": "2026-07-23T04:43:38Z", "body": "@123liuziming @AgentGymLeader we should try checking if AgentScope authors would be open to contributions before moving on with instrumentation here.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -10544,6 +10920,7 @@ "review_state": null, "root_timestamp": "2026-07-15T22:49:34Z", "body": "Thanks for picking this up @lmolkova! I'm glad to see the streaming timing work from my original PR #13 moving forward. Happy to review this since I worked on the initial implementation.\n\nAlso happy to co-author this if it helps, since a lot of the design decisions here came out of the iteration on #13. Let me know!", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -10572,6 +10949,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-25T19:05:31Z", "body": "Couple of questions and minor comment to change tests to be more realistic, looks good otherwise!", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -10600,6 +10978,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-27T12:48:39Z", "body": "Thanks a lot for your contribution. Couple of things:\n\n1. Can you please check if groq has native instrumentation?\n2. If yes, can you see if they're emitting genai spans or not.\n3. After this analysis can you please cut down the verbosity of this pr? You can space it like: skeleton, inference spans, agent spans etc. For now if you don't mind can you please close this pr and first let me know if 1 & 2 are not in place before we proceed adding the telemetry support.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -10628,6 +11007,7 @@ "review_state": null, "root_timestamp": "2026-07-27T11:15:53Z", "body": "@srinjoy356 the `README.rst` and several modules point readers at `MIGRATION_REPORT.md` for the gap list, but that filename is excluded by the root `.gitignore` (\"Migration review reports generated by the review-migration skill\"), and it isn't in this PR's head or anywhere else in the tree.\n\nSince `README.rst` becomes the PyPI description, that leaves a dangling pointer for anyone installing the package. Dropping the references, or folding the parts readers need into the README itself, would probably be cleaner than pointing at a file that by design never gets committed.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -10656,6 +11036,7 @@ "review_state": null, "root_timestamp": "2026-07-27T11:33:41Z", "body": "@srinjoy356 checked 647446e — the references are gone from the package and the Known limitations section reads well on its own. Thanks for turning it around so fast.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -10684,6 +11065,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-27T12:47:14Z", "body": "Thanks a lot for your contribution @srinjoy356. Couple of things:\n\n1. Can you please check if haystack has native instrumentation?\n2. If yes, can you see if they're emitting genai spans or not.\n3. After this analysis can you please cut down the verbosity of this pr? You can space it like: skeleton, inference spans, agent spans etc. For now if you don't mind can you please close this pr and first let me know if 1 & 2 are not in place before we proceed adding the telemetry support.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -10712,6 +11094,7 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-07-28T19:04:25Z", "body": "This seems to be a duplicate of #24\n\nA more generic solution to this problem is coming via https://github.com/open-telemetry/opentelemetry-specification/pull/4931\n\nIn the current scope this problem can be solved with custom context key and custom log record processor which would be a recommended approach until context-scoped attributes land in OTel spec and python implementation. \n\nIs there a strong use-case to stamp information on events only and avoid spans?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -10740,6 +11123,7 @@ "review_state": null, "root_timestamp": "2026-07-28T19:39:14Z", "body": "> > According to the official OpenTelemetry GenAI Semantic Conventions (0.65b0), gen_ai.tool.definitions is only defined on inference/chat spans (chat, text_completion), not on agent invocation (invoke_agent) spans.\r\n> \r\n> This seems wrong -- why can't tool definitions be on invoke agent spans ? You can define tools on an agent\r\n\r\nwhere did it come from? let's fix, I agree it's wrong", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -10768,6 +11152,7 @@ "review_state": null, "root_timestamp": "2026-07-13T08:22:47Z", "body": "@maestro24 when you sign the CLA we can review the PR, thanks", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -10796,6 +11181,7 @@ "review_state": null, "root_timestamp": "2026-07-23T14:31:33Z", "body": "👋 Please update this PR's branch over the latest `main`: it predates our link-checker [switch to Lychee][#10911] and can hit CI failures unrelated to your changes. For the how — including conflicts and after-update steps — see [#10990][]. Thanks for your contribution!\n\n[#10911]: https://github.com/open-telemetry/opentelemetry.io/pull/10911\n[#10990]: https://github.com/open-telemetry/opentelemetry.io/issues/10990", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -10824,6 +11210,7 @@ "review_state": null, "root_timestamp": "2026-07-28T05:07:22Z", "body": "Hi @jraleigh, could you run the `npm run fix:link-cache` script locally and commit the results?\r\n\r\nIt looks like the maintainers don't have permission to push changes to this PR.\r\n\r\nThanks!", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -10852,6 +11239,7 @@ "review_state": null, "root_timestamp": "2026-07-16T06:24:26Z", "body": "Hi @Goran-n, could you please review and sign the CLA? Thanks!", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -10880,6 +11268,7 @@ "review_state": null, "root_timestamp": "2026-07-18T18:42:32Z", "body": "what @vitorvasc said re CLA, but this is also a vendor not an application integration", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -10908,6 +11297,7 @@ "review_state": null, "root_timestamp": "2026-07-23T14:31:36Z", "body": "👋 Please update this PR's branch over the latest `main`: it predates our link-checker [switch to Lychee][#10911] and can hit CI failures unrelated to your changes. For the how — including conflicts and after-update steps — see [#10990][]. Thanks for your contribution!\n\n[#10911]: https://github.com/open-telemetry/opentelemetry.io/pull/10911\n[#10990]: https://github.com/open-telemetry/opentelemetry.io/issues/10990", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -10936,6 +11326,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-17T22:23:19Z", "body": "I like where this is heading towards. However, I do have several questions about the terminology and whether the statements are precise or confusing. I don't see these as blockers though.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -10964,6 +11355,7 @@ "review_state": null, "root_timestamp": "2026-07-23T14:31:39Z", "body": "👋 Please update this PR's branch over the latest `main`: it predates our link-checker [switch to Lychee][#10911] and can hit CI failures unrelated to your changes. For the how — including conflicts and after-update steps — see [#10990][]. Thanks for your contribution!\n\n[#10911]: https://github.com/open-telemetry/opentelemetry.io/pull/10911\n[#10990]: https://github.com/open-telemetry/opentelemetry.io/issues/10990", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -10992,6 +11384,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-23T05:27:14Z", "body": "LGTM", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -11020,6 +11413,7 @@ "review_state": null, "root_timestamp": "2026-07-23T14:31:44Z", "body": "👋 Please update this PR's branch over the latest `main`: it predates our link-checker [switch to Lychee][#10911] and can hit CI failures unrelated to your changes. For the how — including conflicts and after-update steps — see [#10990][]. Thanks for your contribution!\n\n[#10911]: https://github.com/open-telemetry/opentelemetry.io/pull/10911\n[#10990]: https://github.com/open-telemetry/opentelemetry.io/issues/10990", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -11048,6 +11442,7 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-07-24T19:29:09Z", "body": "Thanks @fractalwrench! I've updated the branch over the latest `main`. One alert-syntax fix needed (suggestion below), then this is good to go from the docs side.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -11076,6 +11471,7 @@ "review_state": null, "root_timestamp": "2026-07-23T14:31:52Z", "body": "👋 Please update this PR's branch over the latest `main`: it predates our link-checker [switch to Lychee][#10911] and can hit CI failures unrelated to your changes. For the how — including conflicts and after-update steps — see [#10990][]. Thanks for your contribution!\n\n[#10911]: https://github.com/open-telemetry/opentelemetry.io/pull/10911\n[#10990]: https://github.com/open-telemetry/opentelemetry.io/issues/10990", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -11104,6 +11500,7 @@ "review_state": null, "root_timestamp": "2026-07-17T14:48:17Z", "body": "This seems to fix the issue, @chalin\r\n\r\n\"Screenshot", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -11132,6 +11529,7 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-07-18T11:05:28Z", "body": "- Thanks, I'll take a look soon.\n- @Babul422 please answer the questions added to the opening comment of the PR.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -11160,6 +11558,7 @@ "review_state": null, "root_timestamp": "2026-07-23T14:31:55Z", "body": "👋 Please update this PR's branch over the latest `main`: it predates our link-checker [switch to Lychee][#10911] and can hit CI failures unrelated to your changes. For the how — including conflicts and after-update steps — see [#10990][]. Thanks for your contribution!\n\n[#10911]: https://github.com/open-telemetry/opentelemetry.io/pull/10911\n[#10990]: https://github.com/open-telemetry/opentelemetry.io/issues/10990", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -11188,6 +11587,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-21T13:19:33Z", "body": "LGTM to ja docs", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -11216,6 +11616,7 @@ "review_state": null, "root_timestamp": "2026-07-23T14:32:01Z", "body": "👋 Please update this PR's branch over the latest `main`: it predates our link-checker [switch to Lychee][#10911] and can hit CI failures unrelated to your changes. For the how — including conflicts and after-update steps — see [#10990][]. Thanks for your contribution!\n\n[#10911]: https://github.com/open-telemetry/opentelemetry.io/pull/10911\n[#10990]: https://github.com/open-telemetry/opentelemetry.io/issues/10990", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -11244,6 +11645,7 @@ "review_state": null, "root_timestamp": "2026-07-23T14:32:04Z", "body": "👋 Please update this PR's branch over the latest `main`: it predates our link-checker [switch to Lychee][#10911] and can hit CI failures unrelated to your changes. For the how — including conflicts and after-update steps — see [#10990][]. Thanks for your contribution!\n\n[#10911]: https://github.com/open-telemetry/opentelemetry.io/pull/10911\n[#10990]: https://github.com/open-telemetry/opentelemetry.io/issues/10990", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -11272,6 +11674,7 @@ "review_state": null, "root_timestamp": "2026-07-27T10:41:10Z", "body": "Will do today, thanks @mwimpelberg28 🙏🏻", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -11300,6 +11703,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-27T11:49:34Z", "body": "Thanks @mwimpelberg28, looking great, just a few small suggestions, feel free to accept them or not. Except the `lock-in`, which I would not call bloque in Spanish, and the fediverse being fediverso, the rest is up to your taste.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -11328,6 +11732,7 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-07-21T09:00:13Z", "body": "1箇所、リンクの箇所だけコメントしました。\n\n本文は完璧です。", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -11356,6 +11761,7 @@ "review_state": null, "root_timestamp": "2026-07-23T14:32:10Z", "body": "👋 Please update this PR's branch over the latest `main`: it predates our link-checker [switch to Lychee][#10911] and can hit CI failures unrelated to your changes. For the how — including conflicts and after-update steps — see [#10990][]. Thanks for your contribution!\n\n[#10911]: https://github.com/open-telemetry/opentelemetry.io/pull/10911\n[#10990]: https://github.com/open-telemetry/opentelemetry.io/issues/10990", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -11384,6 +11790,7 @@ "review_state": null, "root_timestamp": "2026-07-23T14:32:12Z", "body": "👋 Please update this PR's branch over the latest `main`: it predates our link-checker [switch to Lychee][#10911] and can hit CI failures unrelated to your changes. For the how — including conflicts and after-update steps — see [#10990][]. Thanks for your contribution!\n\n[#10911]: https://github.com/open-telemetry/opentelemetry.io/pull/10911\n[#10990]: https://github.com/open-telemetry/opentelemetry.io/issues/10990", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -11412,6 +11819,7 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-07-27T12:11:46Z", "body": "Kindly correct the links on all sections, the rest LGTM, thanks @manduinca 🎉", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -11440,6 +11848,7 @@ "review_state": null, "root_timestamp": "2026-07-23T14:26:00Z", "body": "👋 Please update this PR's branch over the latest `main`: it predates our link-checker [switch to Lychee][#10911] and can hit CI failures unrelated to your changes. For the how — including conflicts and after-update steps — see [#10990][]. Thanks for your contribution!\n\n[#10911]: https://github.com/open-telemetry/opentelemetry.io/pull/10911\n[#10990]: https://github.com/open-telemetry/opentelemetry.io/issues/10990", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -11468,6 +11877,7 @@ "review_state": null, "root_timestamp": "2026-07-28T05:09:03Z", "body": "Hi @cxdy, could you run the `npm run fix:link-cache` script locally and commit the results?\r\n\r\nThanks!", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -11496,6 +11906,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-28T05:56:05Z", "body": "@cxdy, thanks!", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -11524,6 +11935,7 @@ "review_state": null, "root_timestamp": "2026-07-21T07:07:34Z", "body": "@snowmen233 please make sure you sign the CLA otherwise we can not accept the PR. Thank you.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -11552,6 +11964,7 @@ "review_state": null, "root_timestamp": "2026-07-23T14:25:47Z", "body": "👋 Please update this PR's branch over the latest `main`: it predates our link-checker [switch to Lychee][#10911] and can hit CI failures unrelated to your changes. For the how — including conflicts and after-update steps — see [#10990][]. Thanks for your contribution!\n\n[#10911]: https://github.com/open-telemetry/opentelemetry.io/pull/10911\n[#10990]: https://github.com/open-telemetry/opentelemetry.io/issues/10990", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -11580,6 +11993,7 @@ "review_state": null, "root_timestamp": "2026-07-22T13:33:24Z", "body": "@SergeyKleyman as second maintainer of the distro, can you please do a technical review and give us a thumbs up or down. After this PR is merged we can do the infra changes such that @open-telemetry/php-distro-approvers has ownership of these files.", + "measurement_date": "2026-08-01", "role": "context", "stability": null, "recorded_label": null, @@ -11607,6 +12021,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-22T14:10:31Z", "body": "LGTM for JA part", + "measurement_date": "2026-08-01", "role": "context", "stability": null, "recorded_label": null, @@ -11634,6 +12049,7 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-07-22T15:49:37Z", "body": "I reviewed with copilot, here's it's main comment when I asked to ensure completeness per our repo's ownership conventions:\r\n\r\n> The new child mappings overlap the existing content/en/docs/zero-code/go ownership and label rules. Our component-owner logic accumulates every prefix match, and the labeler applies every matching glob, so compile-time PRs will still involve go-approvers and go-instrumentation-approvers and receive both SIG labels. Please adjust the parent mappings so the compile-time subtree is excluded or otherwise represented without overlapping ownership.\r\n\r\nOf course, the shared ownership might have been intentional. Pls clarify.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -11662,6 +12078,7 @@ "review_state": null, "root_timestamp": "2026-07-23T13:52:58Z", "body": "👋 Please update this PR's branch over the latest `main`: it predates our link-checker [switch to Lychee][#10911] and can hit CI failures unrelated to your changes. For the how — including conflicts and after-update steps — see [#10990][]. Thanks for your contribution!\n\n[#10911]: https://github.com/open-telemetry/opentelemetry.io/pull/10911\n[#10990]: https://github.com/open-telemetry/opentelemetry.io/issues/10990", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -11690,6 +12107,7 @@ "review_state": null, "root_timestamp": "2026-07-23T13:58:54Z", "body": "(I've done the rebase :)", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -11718,6 +12136,7 @@ "review_state": null, "root_timestamp": "2026-07-28T02:14:03Z", "body": "@IrinaKarantoniou @dnanuti @elifsamedin Please add your reviews. Thanks!", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -11746,6 +12165,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-28T12:16:57Z", "body": "LGTM ✅", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -11774,6 +12194,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-28T05:09:59Z", "body": "Could you also run the `npm run fix:link-cache` script locally and commit the results?\n\nThanks!", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -11802,6 +12223,7 @@ "review_state": null, "root_timestamp": "2026-07-27T05:30:41Z", "body": "@mwimpelberg28 I believe the `co-authored` added by Claude messed up the EasyCLA.\r\nIt should be `assisted by` instead.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -11830,6 +12252,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-27T04:41:53Z", "body": "LGTM!", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -11858,6 +12281,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-27T04:39:31Z", "body": "LGTM!", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -11886,6 +12310,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-27T04:29:18Z", "body": "LGTM!", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -11914,6 +12339,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-27T04:31:19Z", "body": "LGTM!", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -11942,6 +12368,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-08T15:30:32Z", "body": "Thanks for pushing this through, and for the register-once fix. Reusing the signal streams across both waits is the right call to avoid dropping a second signal during the drain.\n\nOn the outstanding copilot-reviewer note about the dropped `JoinHandle`, I don't think it's a problem. In Rust the process terminates when `main` returns, and the detached signal-handler thread is reaped at that point rather than keeping the process alive. It would only block exit if something joined it, and nothing does. Your code comments already capture this, so I'm comfortable leaving the thread detached.\n\nThe double-signal convention matches the Go Collector, and a 60s drain deadline aligned with the default Kubernetes `terminationGracePeriodSeconds` is a sensible default. LGTM.\n\nOne optional and non-blocking follow-up: patch coverage on the new signal paths is on the lighter side, so a small test exercising the second-signal force-exit path would be welcome if it is easy to add.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -11970,6 +12397,7 @@ "review_state": null, "root_timestamp": "2026-07-20T20:36:22Z", "body": "@drewrelmas should we update this PR to reflect the progress in https://github.com/open-telemetry/otel-arrow/pull/3454?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -11998,6 +12426,7 @@ "review_state": null, "root_timestamp": "2026-07-20T21:17:00Z", "body": "Yes - @ThomsonTan could you please take a look at https://github.com/open-telemetry/otel-arrow/blob/main/rust/otap-dataflow/docs/telemetry/item-attributes.md and update this implementation accordingly?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -12026,6 +12455,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-06-30T16:03:32Z", "body": "I'm looking forward to seeing this proposal implemented!\n\nI haven't finished my review yet, but I'd rather send this first batch of feedback now and continue as soon as possible, probably tomorrow.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -12054,6 +12484,7 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-07-01T13:48:16Z", "body": "Thank you, Lalit, for putting together this excellent specification for integrating an eBPF-based load-balancing mechanism with `SO_REUSEPORT` socket groups.\n\nMy main comment is that we should split this document into two parts: one focused on NUMA discovery (see one of my comment), and the other on eBPF load balancing.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -12082,6 +12513,7 @@ "review_state": null, "root_timestamp": "2026-07-20T21:46:01Z", "body": "Note there is a heavy discussion about how to package eBPF artifacts for Collector.\r\n\r\nhttps://github.com/open-telemetry/opentelemetry-collector/issues/15430#issuecomment-4907834085", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -12110,6 +12542,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-20T21:42:23Z", "body": "Looks good. @lquerel should have a look at synchronization primitives, the Mutex. Note we merged https://github.com/open-telemetry/otel-arrow/pull/3323 as a foundation for this.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -12138,6 +12571,7 @@ "review_state": null, "root_timestamp": "2026-06-29T16:14:24Z", "body": "Nit: Can we update `rust/otap-dataflow/docs/memory-limiter-phase1.md` too? The “Receiver Behavior Under Hard Pressure” section still says Soft never rejects, but with `soft_action: shed`, Soft can now use the same receiver shedding behavior in `enforce` mode.\r\n\r\nAlso, should we rename `memory-limiter-phase1.md` to something like `global-memory-limiter.md` and replace the “Phase 1” wording in the title/intro with “global/ process-wide memory limiter”? This PR still changes the global limiter, while the retained-work budgeting / tenant/group isolation direction is tracked separately in #3272. A behavior-based doc name may avoid Phase 1/Phase 2 confusion as both efforts evolve.\r\n\r\nFor the same reason, could we also reword “Phase 2 of the process-wide memory limiter” in the PR description? Maybe “follow-up to the process-wide memory limiter” would be clearer.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -12166,6 +12600,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-06-29T16:15:05Z", "body": "Thanks. LGTM. Good to have doc update before merge.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -12194,6 +12629,7 @@ "review_state": null, "root_timestamp": "2026-06-30T17:52:49Z", "body": "> Rename memory-limiter-phase1.md → global-memory-limiter.md + drop the \"Phase 1\" wording — I'd prefer to keep this out of this PR and do it as a dedicated follow-up. The rename touches cross-references in docs/configuration.md (which this PR otherwise doesn't change), and the \"Phase 1 → global / process-wide\" reframing is really part of the naming evolution tracked in https://github.com/open-telemetry/otel-arrow/issues/3272, so it reads cleaner as its own focused change than mixing a doc-wide rename into the soft_action feature PR. I'm happy to open that follow-up right after this merges — target name global-memory-limiter.md, intro reworded to \"global / process-wide memory limiter\". Let me know if you'd rather I bundle it here instead.\r\n\r\nAgree on this.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -12222,6 +12658,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-09T19:19:36Z", "body": "Reviewed critically and validated locally (checked out the branch, ran targeted tests + compiled the new bench). Approving — everything below is non-blocking.\n\n## Validation\n\n- `config` 66 passed, `engine::memory_limiter` 24 passed (incl. all 8 new), `memory_admission` bench compiles, clippy clean on `config`.\n- All three `should_shed_ingress()` sites and every ingress consumer (otlp/otap/syslog_cef/user_events receivers + the tower layer) route through the new predicate; no stray `== Hard` shed comparisons remain.\n- `soft_action` propagation mirrors the existing `mode` pattern exactly (atomic on the process inner, plain field on receiver inners), and `configure()` runs before receivers snapshot.\n- Default path is byte-identical (`shed_decision(_, _, Observe)` == old `Enforce && Hard`). Hysteresis is sound: with `soft_action: shed`, the existing Soft→Normal reopen threshold (`soft_limit − hysteresis`) becomes the shed-reopen point, so no oscillation.\n- Doc sweep is thorough — grepped untouched receiver `.rs`/README files for stale \"Soft is informational / Hard-only\" wording; none remain.\n\n## Non-blocking notes\n\n1. **Process-wide `MemoryPressureState::should_shed_ingress()` has no p ...[truncated]", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -12250,6 +12687,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-10T04:54:39Z", "body": "@timr-dev Overall, this looks good to me. I think there's a small issue in the logic around the soft/hard limits combined with hysteresis.\n\n@lalit During the review of this PR, I noticed a few things related to a previous PR on the memory limiter side that seemed suboptimal. I'll file a separate report for those.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -12278,6 +12716,7 @@ "review_state": null, "root_timestamp": "2026-07-10T16:31:52Z", "body": "> @lalit During the review of this PR, I noticed a few things related to a previous PR on the memory limiter side that seemed suboptimal. I'll file a separate report for those.\r\n\r\nSure @lquerel. Thanks for reviewing that part. Will look forward to your findings.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -12306,6 +12745,7 @@ "review_state": null, "root_timestamp": "2026-07-13T22:35:54Z", "body": "> > @lalit During the review of this PR, I noticed a few things related to a previous PR on the memory limiter side that seemed suboptimal. I'll file a separate report for those.\r\n> \r\n> Sure @lquerel. Thanks for reviewing that part. Will look forward to your findings.\r\n\r\n@lquerel - I dug a bit more into the memory limiter after your comment. \r\n\r\nThe [docs](https://github.com/open-telemetry/otel-arrow/blob/main/rust/otap-dataflow/docs/memory-limiter-phase1.md#tradeoffs) describe the mechanics: sampling is periodic and enforcement happens only at ingress when the state reaches `Hard`. So the limitation is mostly implied rather than stated directly: this can reduce new memory growth, but it is not a strict cap and it cannot immediately reclaim memory already held inside the process.\r\n\r\n The real issues I found are more specific:\r\n\r\n - The default hysteresis is derived from the soft-to-hard gap and can become very large. With a wide gap, `Soft` can become sticky because memory must fall far below the soft limit to return to `Normal`. This mainly affects state reporting today because `Soft` does not reject ingress.\r\n - `source: auto` only checks the current/leaf cgroup limit. In nes ...[truncated]", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -12334,6 +12774,7 @@ "review_state": null, "root_timestamp": "2026-07-22T18:02:18Z", "body": "@timr-dev - One context update since this PR was first opened: while you were out, #3484 landed the pressure-aware rate throttling design, and #3529 is now implementing that path. That design uses Soft pressure as the trigger for selective receiver-local throttling, where over-rate scopes are throttled while within-rate scopes continue.\r\n\r\nGiven that newer direction, should we still keep `soft_action: shed` in this PR as a separate global Soft-pressure emergency mode? Or should this PR avoid adding that behavior now, and leave Soft-pressure throttling to the #3529 rate-throttling implementation? I realize this may mean some rework, but I think it is worth checking now so we do not land two overlapping Soft-pressure control models.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -12362,6 +12803,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-01T18:19:27Z", "body": "Looks good to me. @lalitb please take another look.\nThanks @AvinashDevX.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -12390,6 +12832,7 @@ "review_state": null, "root_timestamp": "2026-07-01T18:20:04Z", "body": "@AvinashDevX how would you feel setting the default batch size to 64KiB instead of disabled by default?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -12418,6 +12861,7 @@ "review_state": null, "root_timestamp": "2026-07-08T15:22:53Z", "body": "@AvinashDevX thanks for iterating on this. Two items before I merge.\n\n1. Are you still planning to switch `batch_size` from a record count to a byte size defaulting to 64 KiB, as we discussed on July 1? I don't see it in the current diff. I'd like the receiver to batch by default so downstream messages are reasonably sized without extra configuration.\n\n2. Could you confirm the early-return path lalitb flagged in `internal_telemetry_receiver/mod.rs`? His concern is that returning there without flushing can drop up to `threshold - 1` buffered records, and I want to be sure a shutdown or channel close on that branch cannot lose a partial batch. Tests covering the timer-flush and byte-split paths would also help lock this down.\n\nHappy to re-review once those are addressed. Thanks!", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -12446,6 +12890,7 @@ "review_state": null, "root_timestamp": "2026-07-14T16:02:26Z", "body": "@AvinashDevX this looks good to me - before merging can you please update the PR title and description to denote batching is now on by default for future viewing in Git history?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -12474,6 +12919,7 @@ "review_state": null, "root_timestamp": "2026-07-20T22:46:09Z", "body": "@AvinashDevX will you please resolve? Thanks!", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -12502,6 +12948,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-07T22:19:41Z", "body": "Thanks for looking into this change @ethanchewy! Have a few comments below to hopefully future proof this solution.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -12530,6 +12977,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-24T21:12:24Z", "body": "LGTM, left one small maintainability comment", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -12558,6 +13006,7 @@ "review_state": null, "root_timestamp": "2026-07-20T20:14:31Z", "body": "In @albertlockett’s absence, I’d like @JakeDern to review this PR. Thanks", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -12586,6 +13035,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-21T04:54:32Z", "body": "Thank you for working hard on this! I see there have been a lot of iterations between this PR and the previous.\n\nApologies if this has already been discussed elsewhere, but I'm wondering why the solution is not to call `OtapArrowRecords::decode_transport_optimized_ids` in the various `try_from` implementations? That would be a very small change (3 lines or so) and I think solve the problem.\n\nIn my opinion view creation should not fail unless we're seeing data corruption as there's no alternative for a caller in the failure case. This PR seems to take the same opinion, as we have updated all callers to decode before creating the view to avoid it. \n\nI'm wondering then why we're letting callers make the mistake of not decoding in a way that can only be found at runtime instead of proactively doing it in the implementation.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -12614,6 +13064,7 @@ "review_state": null, "root_timestamp": "2026-07-24T03:10:40Z", "body": "> > Thank you for working hard on this! I see there have been a lot of iterations between this PR and the previous.\r\n> \r\n> > \r\n> \r\n> > Apologies if this has already been discussed elsewhere, but I'm wondering why the solution is not to call `OtapArrowRecords::decode_transport_optimized_ids` in the various `try_from` implementations? That would be a very small change (3 lines or so) and I think solve the problem.\r\n> \r\n> > \r\n> \r\n> > In my opinion view creation should not fail unless we're seeing data corruption as there's no alternative for a caller in the failure case. This PR seems to take the same opinion, as we have updated all callers to decode before creating the view to avoid it.\r\n> \r\n> > \r\n> \r\n> > I'm wondering then why we're letting callers make the mistake of not decoding in a way that can only be found at runtime instead of proactively doing it in the implementation.\r\n> \r\n> \r\n> \r\n> Thanks for raising this. \r\n> \r\n> \r\n> \r\n> I agree that callers should not have to remember to decode first, and that this error should not normally surface in application code.\r\n> \r\n> \r\n> \r\n> The current TryFrom<&OtapArrowRecords> implementation only has immutable access, whereas decoding require ...[truncated]", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -12642,6 +13093,7 @@ "review_state": null, "root_timestamp": "2026-07-28T03:07:10Z", "body": "I think we should scope this PR to the main issue, which is preventing the construction of invalid views. The `DecodedOtapLogsResources` construct that the router and validator are using look like an unrelated optimization, as those components were calling the normal `try_from` implementation before this PR. \r\n\r\nBesides the optimizations being unrelated, I'm not sure that the solutions in this PR are the best we can do. For example, having a second `OtapLogsResourcesView` construct that is a subset of the `OtapLogsView` seems like a maintenance burden and complicates the API surface area. I don't have a perfect solution here, but I'll put a couple of ideas at the bottom. \r\n\r\nAdditionally, the benefit of these optimizations is not demonstrated anywhere that I can see and there may not be sufficient impact. For example, I previously considered optimizing the views for the temporal reaggregation processor, but after profiling I did not see significant time spent there. Did you make a different discovery?\r\n\r\nIn short, I think 2000 lines of code is a lot for the issue that this PR is meant to solve and I think each extra optimization needs to be examined separately to make sure they (1) ...[truncated]", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -12670,6 +13122,7 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-07-24T00:52:01Z", "body": "I believe there are few issues to fix before merging this PR. Thanks", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -12698,6 +13151,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-23T00:48:05Z", "body": "### 💡 Codex Review\n\nHere are some automated review suggestions for this pull request.\n\n**Reviewed commit:** `c05d8b7fc2`\n \n\n
ℹ️ About Codex in GitHub\n
\n\n[Your team has set up Codex to review pull requests in this repo](https://chatgpt.com/codex/cloud/settings/general). Reviews are triggered when you\n- Open a pull request for review\n- Mark a draft as ready\n- Comment \"@codex review\".\n\nIf Codex has suggestions, it will comment; otherwise it will react with 👍.\n\n\n\n\nCodex can also answer questions or update the PR. Try commenting \"@codex address that feedback\".\n \n
", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -12726,6 +13180,7 @@ "review_state": null, "root_timestamp": "2026-07-23T22:48:17Z", "body": "@daviddahl can you please take a look at this PR and how it may relate to https://github.com/open-telemetry/otel-arrow/issues/3435?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -12754,6 +13209,7 @@ "review_state": null, "root_timestamp": "2026-07-24T00:32:04Z", "body": "> @daviddahl can you please take a look at this PR and how it may relate to #3435?\r\n\r\nThis is very much complementary - we should converge if possible. A stability Enum could be added to the `component_inventory` macro - we discussed this a little bit but did not want to go all the way down to individual signal types as is done in the go collector as we are inventorying components that do not handle signals as well. Perhaps we can follow up my PR with a stability property and converge. What do you think @lquerel ?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -12782,6 +13238,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-24T18:57:32Z", "body": "Looks good to me! Excited to see the generic implementation coming in!", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -12810,6 +13267,7 @@ "review_state": null, "root_timestamp": "2026-07-25T00:27:13Z", "body": "Moved out from merge queue, as @pritishnahar95 is making some updates to align the design with the Azure identity extension.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -12838,6 +13296,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-27T21:15:25Z", "body": "LGTM. Thanks for putting together this detailed design and aligning it with the Azure identity extension. This will be a useful addition. \n\nI left a few comments to clarify config validation, the RFC 7523 flow, and the HTTP/TLS behavior. Approving with the expectation that these are addressed before merge.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -12866,6 +13325,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-24T16:57:58Z", "body": "Thanks", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -12894,6 +13354,7 @@ "review_state": null, "root_timestamp": "2026-07-24T16:58:56Z", "body": "@gnanirahulnutakki could you sign the CLA and mark the PR ready for review.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -12922,6 +13383,7 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-07-25T06:31:03Z", "body": "Thanks for working on this, i.e. the goal of keeping `--validate-and-exit` consistent with runtime startup is definitely right.\n\nAfter reproducing #3569 against the parent commit, we found that the original analysis attributing the failure to missing type-router outputs was incorrect. We’re sorry for the confusion.\n\nThe type router recognizes three well-known named routes: `logs`, `metrics`, and `traces`. Under the existing configuration model, the outputs list is an optional declaration/allowlist; the actual ports can be established directly by connections such as router[\"logs\"], router[\"metrics\"], and router[\"traces\"]. I verified that this configuration, without an outputs list, validates, starts ready, and processes all three signal types on the parent commit.\n\nThe exact configuration from #3569 actually fails because all traffic-generator signal weights default to zero: at least one of metric_weight, trace_weight, or log_weight must be > 0\n\nRuntime calls `TrafficConfig::validate()`, but static validation only deserializes Config, so `--validate-and-exit` misses this semantic constraint.\n\nSuggested fix:\n\n- Remove the minimum-declared-output requirement from type_router.\n- Reuse ...[truncated]", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -12950,6 +13412,7 @@ "review_state": null, "root_timestamp": "2026-07-27T16:51:05Z", "body": "@lquerel Thanks for detailed clarification. I have updated the title and description of #3569 to reflect the above.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -12978,6 +13441,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-27T18:01:55Z", "body": "Looks good! Could you potentially add a `trafficgen` example with a few variations of this processor to validate behavior is as expected and document the results in PR description? Something like https://github.com/open-telemetry/otel-arrow/blob/main/rust/otap-dataflow/configs/trafficgen-flow-metrics-demo.yaml and #3552", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -13006,6 +13470,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-28T13:42:44Z", "body": "LGTM! Left one further comment about changelog.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -13034,6 +13499,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-28T19:02:45Z", "body": "LGTM. I left a comment about further improving the changelog, not a blocker now.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -13062,6 +13528,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-28T19:03:03Z", "body": "LGTM. I left a comment about further improving the changelog, not a blocker now.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -13090,6 +13557,7 @@ "review_state": null, "root_timestamp": "2026-07-27T16:40:03Z", "body": "@Shaurya2k06 : Can we also parse hidden files as in the [example](https://raw.githubusercontent.com/lycheeverse/lychee/lychee-v0.24.2/lychee.example.toml) - `Lychee` skips hidden directories by default, so this currently misses files such as `rust/otap-dataflow/.chloggen/README.md`. That means broken relative links in those docs can still merge, contrary to the issue’s “all Markdown files” acceptance criterion.\r\n\r\n```toml\r\n# Do not skip hidden directories and files\r\nhidden = true\r\n```", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -13118,6 +13586,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-28T06:10:55Z", "body": "i noticed Copilot is complaining about `include_fragments = \"full\"`, but didn't `lychee` migrate this setting from a boolean to an enum (`\"none\"`, `\"anchor-only\"`, `\"full\"`) in recent versions (like v0.23+)? \n\nif so, setting it to `true` as Copilot suggests would actually cause a config parsing error now right? \n\nit looks like the author's original string value is correct here but just wanted to double-check with the team if I'm missing something", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -13146,6 +13615,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-28T19:32:34Z", "body": "Thanks for tackling this -- I'm fully behind the goal. A runtime error like \"No space left on device (os error 28)\" being reduced to `(1 dropped)` is exactly the kind of thing that makes internal logs useless when you need them most, and prioritizing the `error` field within the bounded record budget is the right instinct. The compact `error` / `error_kind` / `event_type` shape in the \"After\" output is a clear improvement over the old rendering.\n\nMy main concern is that a fair amount of this reimplements logic we already have, and I'd rather not carry two copies of the truncation machinery. Details below.\n\n## 1. Duplicated truncation logic\n\n`TruncatingBoundedBufFmt` + `encode_debug_string_truncating` reimplement what already exists in `crates/pdata/src/otlp/common.rs`:\n\n- `encode_string_bounded` / `encode_string_truncating` (common.rs:498-570) already handle suffix reservation, UTF-8 boundary truncation (via `truncate_utf8`), and the tri-state `Ok(false)` / `Ok(true)` / `Err(Dropped)` contract.\n- `BoundedBufFmt` (encoder.rs:238) is now a strict special case of the new adapter, so we carry two nearly identical `fmt::Write` adapters.\n- `encode_debug_attribute_truncating_to` is a copy ...[truncated]", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -13174,6 +13644,7 @@ "review_state": null, "root_timestamp": "2025-11-25T14:49:52Z", "body": "Nice! Thanks @thompson-tomo. I'll get back to you as soon as I can (it's a very busy EOM).", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -13202,6 +13673,7 @@ "review_state": null, "root_timestamp": "2025-11-26T03:19:09Z", "body": "> By switching to front matter directly it enables turning on the first line h1 check.\r\n>\r\n> Note the awk/sed command are necessary so that the document still passes the lint rules and we don't have a formatter to format it.\r\n\r\ncan these be done in a follow-up PR? if so, please revert those changes to keep this large mechanical PR easy to review", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -13230,6 +13702,7 @@ "review_state": "COMMENTED", "root_timestamp": "2025-11-26T19:51:28Z", "body": "A first few questions and comments:\r\n\r\n- Spec and semconv folks expressedly asked me to include the comment \"Hugo front matter used to generate the website version of this page\" so that all contributors know why the front matter is there (and hopefully realize that it's important to keep it up to date). Maybe this preference has changed. I'll let @open-telemetry/specs-maintainers @open-telemetry/specs-semconv-maintainers comment about this. I'm ok either way.\r\n- Why not keep the (inline) array syntax for aliases?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -13258,6 +13731,7 @@ "review_state": null, "root_timestamp": "2025-11-26T19:52:11Z", "body": "> The one thing which i am not sure on is if the aliases in [resource-and-entities.md](https://github.com/open-telemetry/semantic-conventions/pull/2971/files#diff-45c6c3aee5d1ce4f912148cf9efc0f39984b2c4c8f67f806bd99d6ce263dac21) are still valid. If not we could switch to a redirect\r\n\r\nYes the alias is valid, but I'd rather keep the inline array syntax, esp. in these cases that have a single entry.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -13286,6 +13760,7 @@ "review_state": null, "root_timestamp": "2025-11-26T19:54:44Z", "body": "FYI, I haven't had the time to look into this more, but in support of #1821 and https://github.com/open-telemetry/opentelemetry.io/issues/6101. We probably will need to have a `title` field in the front matter too.\r\n\r\nI'm wrapping up and will be OOO so I won't be able to get back to this until the new year (FYI).", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -13314,6 +13789,7 @@ "review_state": null, "root_timestamp": "2025-11-27T00:11:26Z", "body": "> Build issue due to current toc generator having known issues with front matter syntax & isn't mantained. Will need to be addressed by switching to https://www.npmjs.com/package/doctoc\r\n\r\nOMG, the last update was 8 years ago in NPM. doctoc seems like an interesting choice; https://npm-compare.com/doctoc,markdown-toc\r\n\r\nMaybe it would be worth switching toc tools before working further on this PR?\r\n\r\n/cc @vitorvasc", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -13342,6 +13818,7 @@ "review_state": null, "root_timestamp": "2025-11-27T13:03:55Z", "body": "Hmm. Ok, maybe another tool then.\r\n(Creating a markdownlint rule to handle tocs might be a fun project ;))", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -13370,6 +13847,7 @@ "review_state": null, "root_timestamp": "2025-11-28T20:10:22Z", "body": "Btw, if you still need to massage the files, rathr than awk & sed, I'd suggest using Perl because it is more portable across OSs and container environments in my experience (esp. if you avoid Perl modules that don't ship with Perl).", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -13398,6 +13876,7 @@ "review_state": null, "root_timestamp": "2026-01-27T18:34:24Z", "body": "> Note I am still working towards a release of doctoc which will eliminate the grep etc but that will be seperate PR.\r\n\r\nGreat that you've been able to get some doctoc changes merged. 🙌🏻 \r\n\r\nWhile I'm eager to make progress here, our current setup works. I'd rather not move forward until we have a full solution that includes:\r\n\r\n- `title` field in the front matter. Both the in-page h1 and title text should match (could be enforced in this repo)\r\n- I think that if we're going to change the toc tool, that should be done first\r\n- IMHO, we should reach a point where the only change introduced in the .md files by this PR would be to replace the HTML comment delimiters (used to wrap the front matter) by YAML `---` section delimiters.\r\n\r\nWDYT?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -13426,6 +13905,7 @@ "review_state": null, "root_timestamp": "2026-02-13T19:09:08Z", "body": "> There doesn't appear to be any usage of `title` front matter\r\n\r\n- The OTel.io layouts expect a `title`, so it needs to be there (at least for now).\r\n\r\n---\r\n\r\n- Thinking about the Hugo comment: at this point I think that there's enough maintainer / community experience that we could probably drop it.\r\n\r\n- I'd vote to switch tools first.\r\n\r\n---\r\n\r\n> I assume you don't mean to create a seperate PR to split the hugo comment to be on a separate line if you want to keep the hugo comment?\r\n\r\nYeah, assuming the Hugo comment goes it mostly looks like what is done in this PR (execpt for the array format changes that should be undone):\r\n\r\n\"image\"", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -13454,6 +13934,7 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-02-23T13:22:22Z", "body": "> @chalin i have now gone and added title front matter to all the pages, \n\nGreat!\n\nLet's follow convention and ensure that the `title` is the first front-matter field.\n\n> there is a custom textlint rule which ensures that they match if not report an error, \n\nNice, though I'd rather not introduce textlint into the toolchain if we can avoid it. I'd prefer that we use a custom Markdownlint rule instead (I've been writing custom mdl2 rules lately, so I can look into it if that can help).\n\nThat being said, if we don't want to delay the front-matter conversion of this PR, we can move the tooling bits to a followup PR. \n\n> @chalin this repo can now switch to doctoc as update is now available. PR to switch is #3463 ...\n\nWell done! I left a review comment with suggested changes. Looking forward to seeing that merged once suggestions are addressed.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -13482,6 +13963,7 @@ "review_state": null, "root_timestamp": "2026-02-23T14:36:52Z", "body": "> Let's do tooling seperate, would you rather I remove textlint from this PR or keep it and then remove the custom rule later?\r\n\r\nFactor it out into another PR (and remove it from this one).", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -13510,6 +13992,7 @@ "review_state": null, "root_timestamp": "2026-02-23T16:04:33Z", "body": "To followup on the textlint rule: I'm not against it, esp. if textlint does become a part of the toolchain (as it has in the main spec repo), but we can discuss that separately now that there will be another PR for the tool.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -13538,6 +14021,7 @@ "review_state": null, "root_timestamp": "2026-04-24T18:43:16Z", "body": "@thompson-tomo - let's get #3463 in first, I just submitted a review.\r\n\r\nBtw, I was OOO in March. Been juggling other priorities since then, but this is still on my radar. Note that the follow will be supportive of the work done in this PR:\r\n\r\n- https://github.com/open-telemetry/opentelemetry-specification/issues/5049", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -13566,6 +14050,7 @@ "review_state": null, "root_timestamp": "2026-07-03T17:01:26Z", "body": "@thompson-tomo - can you ensure that this is up to date, and I'll start final tests on the otel.io side to ensure that this can land smoothly.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -13594,6 +14079,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-03T17:28:25Z", "body": "@thompson-tomo - FYI, I'll be making some progress on this this month. See inline comments for a tweak and question.\n\nBtw, what's in place to ensure that the Hugo fm title and page title match. I thought that we had a checker in place, but I'm not seeing that in the scope of this PR.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -13622,6 +14108,7 @@ "review_state": null, "root_timestamp": "2026-07-03T18:05:05Z", "body": "> I have just updated the branch and completed an audit so it is up to date.\r\n\r\nHow did you \"complete the audit\"? Do you have a script? Is it a part of this repo already?\r\n\r\n> So currently there is nothing in place to keep them in sync ...\r\n\r\nI'm ok landing this earlier and brining in the sync check later.\r\n\r\n> ... I did previously have a custom textlint rule in-place.\r\n\r\n~Right, that rings a bell. Where is that work? Is textlint already a part of this repo's toolchain? I vaguely recall commenting on this (in this repo or the main spec repo?); possibly even mentioning that I'd more likely favor use of markdownlint instead.~ (Nm, found the context) In any case, we can land the changes first so that this PR isn't delayed.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -13650,6 +14137,7 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-07-06T07:14:09Z", "body": "Before even reviewing this, I'd like us to have a mechanism to:\n\n1. Check that each page has the frontmatter\n2. That it matches the header\n\nWithout it this will become stale, so I think it needs to be done together and not as a follow up.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -13678,6 +14166,7 @@ "review_state": null, "root_timestamp": "2026-03-10T02:51:43Z", "body": "re-opening for discussion!\r\n\r\n@PascalSenn also check out https://github.com/open-telemetry/opentelemetry-specification/pull/4906\r\n\r\nthere has been quite a bit of progress since we discussed last year about the option of hosting semantic conventions outside of this repository. I think the work is pretty bleeding edge still but may be ready for external usage soon. In the meantime, let's review and discuss the GraphQL semantic conventions here.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -13706,6 +14195,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-05-03T16:15:58Z", "body": "Just a couple of small doc tweaks which will need a fresh markdown generated.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -13734,6 +14224,7 @@ "review_state": null, "root_timestamp": "2026-06-04T15:27:05Z", "body": "Any news on this feature ? Tks.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -13762,6 +14253,7 @@ "review_state": null, "root_timestamp": "2026-06-22T12:29:43Z", "body": "@PascalSenn unfortunately i dont have permissions to. Ping @open-telemetry/specs-semconv-approvers @open-telemetry/specs-semconv-maintainers", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -13790,6 +14282,7 @@ "review_state": null, "root_timestamp": "2026-06-22T12:36:56Z", "body": "Here you go @PascalSenn", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -13818,6 +14311,7 @@ "review_state": null, "root_timestamp": "2026-07-13T12:35:05Z", "body": "Hi maintainers (@AlexanderWert , @arminru , @kamphaus , @joaopgrassi , @jsuereth , @lmolkova , @trask ), is there anything that's blocking progress on this? Would be really great to get the sem conv merged to unblock implementations.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -13846,6 +14340,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-17T18:29:19Z", "body": "A few minor suggestions, looks good otherwise!", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -13874,6 +14369,7 @@ "review_state": null, "root_timestamp": "2026-07-02T11:21:41Z", "body": "Not stale.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -13902,6 +14398,7 @@ "review_state": null, "root_timestamp": "2026-07-06T07:08:03Z", "body": "@chalin I guess \"code owner\" here would be you. Can you approve to make sure from the OTel website things look ok?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -13930,6 +14427,7 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-05-11T09:44:27Z", "body": "From a Profiling perspective this is a hard breaking change that will cause issues around symbolization.\nInstead of replacing `process.executable.path` with `process.entrypoint`, `process.entrypoint` should be a complementary attribute and independent from `process.executable.path`.\n\nE.g. in a process that uses pytorch to do something, there will be multiple `process.executable.path` from a Profiling perspective. One for `/usr/bin/python` (or any other path to a python runtime executable), one for something like `libtorch` and maybe something device/GPU specific.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -13958,6 +14456,7 @@ "review_state": null, "root_timestamp": "2026-05-11T11:46:05Z", "body": "I view the `process.executable.*` namespace, which encompasses `build_id.gnu`, `build_id.go`, `build_id.htlhash`, `name`, and `path`, as a cohesive unit. Collectively, these attributes serve to uniquely identify an element.\r\n\r\nRedirecting the `process.executable.build_id.*` sub-namespace to `process.entrypoint` for executable identification does not seem appropriate. Consequently, I consider `process.entrypoint` to be a complementary addition rather than a replacement for `process.executable.path`.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -13986,6 +14485,7 @@ "review_state": null, "root_timestamp": "2026-05-11T13:52:10Z", "body": ">> Redirecting the process.executable.build_id.* sub-namespace to process.entrypoint for executable identification does not seem appropriate.\r\n>>\r\n> Isn't the build_id.htlhash sufficient to uniquely identify the executable? \r\n\r\nYes - `process.executable.build_id.htlhash` can uniquely identify a executable. But as hashes are hard for human to comprehent and trigger actions, more descriptive attributes (like `process.executable.path`) are needed.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14014,6 +14514,7 @@ "review_state": null, "root_timestamp": "2026-06-01T13:15:59Z", "body": "My concern is just that this seems really hard to instrument, and really hard to implement as a general `process` namespace attribute. The instructions for how to parse the cmdline of the process to get the nominal \"entrypoint\" (like the example instructions for Python in the description) are probably going to need to exist for any runtime. Wouldn't it be better to introduce entities for the different runtimes that have their own proper instructions for determining entrypoint where that is actually useful? A majority of usecases in the general process world won't really need anything more than uniquely identifying the current executable of the process.\r\n\r\n> Compliment it with either process.launch_path or process.run_path to be capturing what is being launched/run by the entrypoint.\r\n\r\nTo my knowledge, there isn't a way to know this at any given time (short of parsing cmdline but often that doesn't actually contain the full path). You could only instrument it if you were the process yourself, guaranteeing you know when you start and reporting your own entrypoint at that point in time. Any other usecase couldn't guarantee that what they are reading is definitely the path to the exec ...[truncated]", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14042,6 +14543,7 @@ "review_state": null, "root_timestamp": "2026-06-23T17:15:26Z", "body": "> my updated proposal is to simply to rename the attribute and move the new attribute to the process entity. The note is made more explicit see suggestion https://github.com/open-telemetry/semantic-conventions/pull/3686#discussion_r3219625536\r\n\r\nI don't think we need to rename `process.executable.path` to add `process.entrypoint`, I think they're distinct. The proposal in the linked comment seems to describe `process.entrypoint` as being the same as `/proc/[pid]/exe`; to my understanding, that is explicitly not the goal of the attribute. Why can't we leave `process.executable.path` to mean that and `process.entrypoint` as a new attribute?\r\n\r\n> move the new attribute to the process entity\r\n\r\nThis attribute likely does make the most sense on the `process` entity, but I think we should keep `process.executable.path` in the `process.executable` entity. This attribute is different and not a sufficient replacement.\r\n\r\n> Yes this would be an opt-in self reported descriptive attribute and it could be discussed further in the future.\r\n\r\nI guess I can get behind it as long as it's clear that default instrumentation is unlikely to report it. The attribute can be designed for situations where ...[truncated]", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14070,6 +14572,7 @@ "review_state": null, "root_timestamp": "2026-07-23T15:09:00Z", "body": "> This PR seeks to rename Process.executable.path to process.launch_path, there is no change in how it is sourced.\r\n\r\nThat's actually even more incorrect. The expected instrumentation for the process entity could never tell at any given time that the detected executable for the process is the same as the path that it was launched with. It could be changed at any time. So the name is very misleading, and as written I don't think it should be on the entity.\r\n\r\n> This PR moves the attribute from the process.executable entity to the process entity. This is so process.executable supports the scenario of multiple identical executables on a system. If we didn' we would have un-necessary churn of the value of the descriptive attribute & could lead to wrong info. There would be no way to distinguish between the different copies on the same system.\r\n\r\nBut you still can't do that. Removing `process.executable.path` from the `process.executable` entity doesn't make it so you are able to do that. You have a very similar problem as before. You get rid of the path as a churn, but two different copies of the executable would still look like one to the backend since the only identifying attributes ...[truncated]", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14098,6 +14601,7 @@ "review_state": null, "root_timestamp": "2026-07-23T15:52:49Z", "body": "> If you are not a supporter of process.launch_path what about process.run_path/process.running_path that way we can say it is path to what is running in the process?\r\n\r\nMy favourite option is to keep it named `process.executable.path`. That's a literal description of what it is that matches common nomenclature. \r\n\r\nP.S. I think `process.executable.name` and `process.executable.path` being attributes on `process.executable` is actually kind of weird; `process.executable.path` is a reasonable name here because when describing this property of a process, yes it is the current path to the process's executable. But a `process.executable` entity is nominally just representing a file, so there is no reason for attributes like `process.executable.path` and `process.executable.name` to exist separate from simply `file.path` and `file.name`.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14126,6 +14630,7 @@ "review_state": null, "root_timestamp": "2026-07-23T16:54:32Z", "body": "> Then we have 2 usages/meanings for process.executable\r\n\r\nIt's not really different. It is the path to the process's executable. It's about an executable. That's not ambiguous to me at all.\r\n\r\n> what about process.executing_path or process.execution_path?\r\n\r\nNot a fan of those; non-standard nomenclature, and could be very confusing. What we're trying to describe is \"the path to the executable run by the process\", so calling that \"execution path\" isn't exactly stating that clearly.\r\n\r\n> The problem is profiling needs to be able to get a name for the executable identified by the id.\r\n\r\nYeah the `process.executable` entity can have a name and path, I think that's fine. My problem with the `process.executable` entity is that most of its properties are just normal properties of a file. Having special `process.executable` attributes for `path` and `name` don't make sense on that entity when you could communicate identical information with `file.path` and `file.name`. `process.executable.path` is a name that made sense in context of the `process` entity itself, because a \"process\" has an \"executable\" that it's running and you can retrieve the \"path\" to it. But all of that relevance was e ...[truncated]", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14154,6 +14659,7 @@ "review_state": null, "root_timestamp": "2026-07-27T16:39:18Z", "body": "> The ambiguity is, is it describing a file on the file system or is it an executable which can be deployed to 1 or more locations.\r\n\r\nThat doesn't make sense to me. An executable is always a file on the filesystem.\r\n\r\n> My preferred was *.executing_path as That would complement *.working_directory.\r\n\r\nI assume you just mean the pattern of naming is the complement, but just in case you mean otherwise; the path to the executable and the working directory of the process have absolutely no conceptual relationship.\r\n\r\n> That sounds like an entity relationship\r\n\r\n(Assuming Linux to simplify the technical explanation of this)\r\n\r\nYou could call it that. The process has an executable, which is a file on disk with an inode that at some point throughout the process's lifetime had a named hard link on the filesystem to the inode. You can identify that file by its inode. But at any given point, that file could be \"moved\" in the filesystem changing the name of the link, or be unlinked and thus no longer be accessible via the filesystem (but the inode will be present on disk until any process using it has concluded). It is conceptually a `has-a` relationship. But that relationship is to a file o ...[truncated]", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14182,6 +14688,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-06-24T09:19:49Z", "body": "Two things worth fixing:\r\n\r\n1. [BLOCKER] `TonicLogsClient` maps all errors to `failed` — \r\n`timed_out` case is missing. Match on `OTelSdkError::Timeout(_)` \r\nlike `BatchLogProcessor` does, otherwise the POC doesn't fully \r\nvalidate the spec.\r\n\r\n2. [NIT] `cfg_attr` on `component_name` will need manual update \r\nwhen a third feature uses it. Allocating it always is simpler.\r\n\r\nRest looks good for a POC.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14210,6 +14717,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-06-25T17:22:35Z", "body": "Look great! the only important comment I have is adding `error.type` instead of result attribute.\r\n\r\n[UPDATE] Added another comment on modeling it as a span", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14238,6 +14746,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-18T14:35:35Z", "body": "On a first pass it looked good however on thinking about usages etc, i get worried about the broadness of the definition and the explosion of spans that would be created. Which is why a prototype & mapping for other tools is important.\n\nMy suggestion is clearly narrow the scope of the span to cli operations which interact with a remote and complement it with 1 or more events to capture the cli calls.\n\nThese events should be in a seperate pr and i would suggest for the prototype you could use the github reciever in the collector to generate the events based on the scraped logs.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14266,6 +14775,7 @@ "review_state": null, "root_timestamp": "2026-07-20T21:10:00Z", "body": "> bringing it to the system semconv working group\r\n\r\nYes, that's a good way forward.\r\nAlso you might want to periodically merge in the latest changes from main.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -14294,6 +14804,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-07T18:53:10Z", "body": "### 💡 Codex Review\n\nHere are some automated review suggestions for this pull request.\n\n**Reviewed commit:** `eaf4a67aca`\n \n\n
ℹ️ About Codex in GitHub\n
\n\n[Your team has set up Codex to review pull requests in this repo](https://chatgpt.com/codex/cloud/settings/general). Reviews are triggered when you\n- Open a pull request for review\n- Mark a draft as ready\n- Comment \"@codex review\".\n\nIf Codex has suggestions, it will comment; otherwise it will react with 👍.\n\n\n\n\nCodex can also answer questions or update the PR. Try commenting \"@codex address that feedback\".\n \n
", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -14322,6 +14833,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-08T07:27:33Z", "body": "nice changes! just a couple of minor things:\r\n\r\n- in model/azure/registry.yaml, should we add stability: development to the new registry.azure.container_apps group too? just to be consistent with other groups like cosmosdb.\r\n\r\n - since replicas scale under a specific revision, did you think about adding azure.container_app.revision (mapped to CONTAINER_APP_REVISION_NAME)? might be useful for tracking version of the app later on", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14350,6 +14862,7 @@ "review_state": null, "root_timestamp": "2026-07-08T11:25:35Z", "body": "> since replicas scale under a specific revision, did you think about adding azure.container_app.revision (mapped to CONTAINER_APP_REVISION_NAME)? might be useful for tracking version of the app later on\n\nThis would reside on a seperate entity which is identified using this attribute and the name, just like done with the service entities.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -14378,6 +14891,7 @@ "review_state": null, "root_timestamp": "2026-07-08T15:48:19Z", "body": "looks great just a quick heads up yamllint check is failing because changelog note line is >200 chars once u wrap/shorten it pr looks goods", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14406,6 +14920,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-17T18:45:01Z", "body": "That's awesome!\n\nI'd like to check if we can deprecate or remove V$SESSION section", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14434,6 +14949,7 @@ "review_state": null, "root_timestamp": "2026-07-23T18:32:37Z", "body": "```\r\nRun make textlint\r\nsemantic-conventions@ /home/runner/work/semantic-conventions/semantic-conventions\r\n├─┬ textlint-filter-rule-allowlist@4.0.0\r\n│ └── textlint@15.7.1 deduped\r\n├─┬ textlint-rule-period-in-header@0.1.2\r\n│ └── textlint@15.7.1 deduped\r\n└── textlint@15.7.1\r\n\r\n\r\n/home/runner/work/semantic-conventions/semantic-conventions/.chloggen/web_vital-attributes.yaml\r\n 13:69 ✓ error Incorrect term: “id”, use “ID” instead terminology\r\n\r\n✖ 1 problem (1 error, 0 warnings, 0 infos)\r\n✓ 1 fixable problem.\r\nTry to run: $ textlint --fix [file]\r\nmake: *** [Makefile:115: textlint] Error 1\r\n```", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14462,6 +14978,7 @@ "review_state": null, "root_timestamp": "2026-07-23T18:33:57Z", "body": "@copilot now go fix the violation and update title to say that we fixed it", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14490,6 +15007,7 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-07-24T01:07:22Z", "body": "This actually removes functionality. Currently the filepath is used to report it as an annotation\n![image](https://github.com/user-attachments/assets/3bedefc4-12ec-484c-b198-8778bbe08a56)\n\nThis enables a reviewer to see directly on a changed file the issues hence is easier then scrolling through logs.\n\n![image](https://github.com/user-attachments/assets/433c6777-93b0-460b-9fb9-345cb4f2693c)\n\nAlso the fix is not correct as id represents a field name which is lowercased and not upper, to fix either chang config aka #3929 to ignore the directory or #3930 which uses backticks to indicate it is a field/attribute name.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14518,6 +15036,7 @@ "review_state": null, "root_timestamp": "2026-07-24T02:00:13Z", "body": "Annotation is not as helpful as ci log, much harder to see regardless is you're a human or an agent. It's nice to have, but not at a cost of not having filename in the plain text.\r\n\r\nEspecially problematic when error is on the file that's not in the PR - current problem.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14546,6 +15065,7 @@ "review_state": null, "root_timestamp": "2026-07-24T02:15:01Z", "body": "Current problem is an edge case which only occurs during merge process and files changing before merge.\n\nI am not following how it is harder on humans for issues going forward as for everyone reviewing the files via the changes tab can see issues on the file inline that way they have max context. They can also see them at the top of the check which is where they go to get their logs.\n\nIt should be easier for agents as now the check itself provides a list of errors & their location.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14574,6 +15094,7 @@ "review_state": null, "root_timestamp": "2026-07-24T02:21:03Z", "body": "Edge case maybe, it's not the last time it happens - textlint will get new rules and they'll fail in random places. \r\n\r\nThere literally no information when it does on where the problem is\r\n\r\n\"Screenshot_20260723-190553\"\r\n \r\nI'm going to repeat that path on log is a must, github comment is nice to have. Going through all github comments is much harder than looking into ci logs.", + "measurement_date": "2026-08-01", "role": "context", "stability": null, "recorded_label": null, @@ -14601,6 +15122,7 @@ "review_state": null, "root_timestamp": "2026-07-24T02:29:17Z", "body": "It's right there in the annotations section at the top of the screen. Annotations are similar to comments but are different.\n\nAlso all rules are implemented as npm packages so we need to explicitly enable the additional npm package we have added to our project for it to result in a difference in behaviour.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -14629,6 +15151,7 @@ "review_state": null, "root_timestamp": "2026-07-24T16:56:28Z", "body": "It's wonderful to see annotations.\r\nIt's not wonderful that they show up collapsed - that's probably why I never look at them. Thanks for educating me they show up there.\r\nIt's not acceptable that they turn off file locations in the CI logs. \r\n\r\n\r\n------\r\n\r\nyamllint for example, does it well: https://github.com/open-telemetry/semantic-conventions/actions/runs/29971521674/job/89094348620?pr=3919\r\n\r\n\"image\"\r\n\r\nannotations and CI logs are not mutually exclusive. If textlint does it right, I'll be happy to enable github format.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14657,6 +15180,7 @@ "review_state": null, "root_timestamp": "2026-07-26T13:23:00Z", "body": "No worries, key thing is annotations appear in 3 spots: workflow, job & file if the file location is in the diff view.\n\nIt is always hard as what is right for 1 person might not be for another especially in terms of log message content in a structured log.\n\nTbh i had expected when viewing the raw logs to see the details used to build the annotation but it is even removed from there. As such i have https://github.com/textlint/textlint/pull/2094 to log an additional line prior to the annotation with the location details. This avoids duplicated info appearing in the annotation while still providing location info in raw logs.\n\nGiven we have the annotations, all rules are dedicated npm packages hence we won't run into updates detecting new issues & i have a pr to log an additional line with the location. Could we leave it as is?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14685,6 +15209,7 @@ "review_state": null, "root_timestamp": "2026-05-08T06:05:11Z", "body": "@Krishnachaitanyakc This is well thought out, the prior art table and design decisions section are really helpful. One thing I noticed, the scenarios only emit trigger=\"direct\" but never trigger=\"agent\". \r\n\r\nI get that the current scenarios don't naturally exercise delegation, but would it be worth adding a minimal multi-agent scenario (even a simple A delegates to B mock) to validate the agent value e2e? Otherwise LGTM on the model and registry changes.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -14713,6 +15238,7 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-05-14T12:01:25Z", "body": "Thanks @Krishnachaitanyakc - I agree we should just use `agent` and removing `direct` for the enum.\n\nMarking as Request Changes so we know it's waiting on an update from you 👍🏻", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14741,6 +15267,7 @@ "review_state": null, "root_timestamp": "2026-05-14T22:42:49Z", "body": "@Krishnachaitanyakc Upon reading the motivation I believe the intention behind this enum is to provide visibility into delegation events. Did I get this right?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14769,6 +15296,7 @@ "review_state": null, "root_timestamp": "2026-06-17T10:54:34Z", "body": "Like the direction of folding handoff into `execute_tool`, and scoping `invocation.trigger` down to what's actually derivable from typed state makes sense.\n\nCouple of things:\n\n1. The delegation link lives entirely on the `execute_tool` span's `handoff.target.name`, and the receiving `invoke_agent` is a sibling, right? In a run with concurrent handoffs, or where two agents share a name, joining the edge back to the right `invoke_agent` span comes down to name matching. Did you consider a span link from `execute_tool` to the target `invoke_agent`, or a stable agent id separate from the name? The blast-radius query in the motivation feels like it'd get ambiguous on name collisions too.\n2. Since the link sits in attributes rather than parentage, if that span gets dropped by sampling you keep both `invoke_agent` spans but lose the edge between them. Is that intended?\n\nBoth are edge-case-y, so feel free to punt if it's out of scope.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14797,6 +15325,7 @@ "review_state": null, "root_timestamp": "2026-07-17T16:37:37Z", "body": "Hi @Krishnachaitanyakc - thanks for this PR.\r\n\r\n+1 to @AgentGymLeader point in https://github.com/open-telemetry/semantic-conventions-genai/pull/98#issuecomment-4729151864 about the edge relying on name matching.\r\n\r\nThe handoff creates 3 spans:\r\n\r\n1.  invoke_agent triage-agent \r\n2.  execute_tool transfer_to_billing_agent  (under 1)\r\n3.  invoke_agent billing-agent \r\n\r\nProblem: #3 sits beside #2, not under it — so no parent→child line connects the handoff to the agent it handed off to.\r\n\r\n• trace_id — shared by all spans in the run, so it can't tell which handoff went to which agent when there are several.\r\n• parent_span_id — would be the clean link, but #3's parent is the run root, not #2. So it's useless here.\r\n\r\nResult: the only way to connect them is matching the name — which breaks under concurrent handoffs or duplicate names.\r\n\r\nProposal: add  gen_ai.agent.handoff.{source,target}.id  alongside  .name mirroring  gen_ai.agent.id  on  invoke_agent , as a stable join key.\r\n\r\ncc @singankit, @trask", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14825,6 +15354,7 @@ "review_state": null, "root_timestamp": "2026-05-11T19:15:02Z", "body": "@Mandark-droid can you fill out the new PR template? thanks! https://raw.githubusercontent.com/open-telemetry/semantic-conventions-genai/refs/heads/main/.github/PULL_REQUEST_TEMPLATE.md", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14853,6 +15383,7 @@ "review_state": null, "root_timestamp": "2026-05-11T20:10:33Z", "body": "let's add the scenario to #142, get that merged, then you can update that scenario in this PR", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14881,6 +15412,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-06-06T00:39:04Z", "body": "I like where it's going, but Blob part is just one of the parts that has large content problem.\n\nI'd prefer if we\n\n- documented a configuration option that limits size of any content in any part \n- if size is exceeded we wouldn't capture it; or, for text, we would rather trim to the size\n- we could let users know that content was there by including its size on each affected part (especially blob) as you're proposing in the https://github.com/open-telemetry/semantic-conventions-genai/pull/143, but also on the text part\n- we could potentially mimic OTLP 'dropped attributes' property and record something like that on a generic part (e.g. content_dropped: true) if that's necessary (presence of non-0 size would be an indication on its own)\n\nHaving a reason seems interesting, but it's not a common approach in OTel - if something is dropped, we don't record why, it could introduce interesting problems on its own.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14909,6 +15441,7 @@ "review_state": null, "root_timestamp": "2026-05-21T17:35:25Z", "body": "@Jwrede the suggestion is to clarify description of the existing metric in OTel to align with the spirit behind ITL (which I believe it already does). OTel metrics are independent of vLLM definitions.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -14937,6 +15470,7 @@ "review_state": null, "root_timestamp": "2026-06-09T13:29:02Z", "body": "> **\"Per-request average\" framing:** This comes from vLLM's deprecation rationale in [vllm-project/vllm#24015](https://github.com/vllm-project/vllm/pull/24015). TPOT is computed as (total_generation_time - TTFT) / (output_tokens - 1) and recorded once per request. That single value is an average over all tokens in the request.\r\n\r\nTPOT can be calculated either after all requests have returned, or in the manner of ITL as you mentioned. I believe these two approaches are essentially equivalent, at least in terms of average values. I agree with Liudmila's perspective — in my view, TPOT and ITL refer to the same concept, and if you feel that TPOT is lacking in some way, perhaps the more appropriate approach would be to enrich and extend it rather than introducing a separate metric.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -14965,6 +15499,7 @@ "review_state": null, "root_timestamp": "2026-05-26T22:12:47Z", "body": "hi @hippoley, not sure what happened, but PR content appears empty now, thanks", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14993,6 +15528,7 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-05-28T12:20:09Z", "body": "Adding a Request Changes as changes have been lost in the last force-push", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -15021,6 +15557,7 @@ "review_state": null, "root_timestamp": "2026-05-27T03:27:27Z", "body": "@hippoley Idea of evaluation result as event was that it can be linked to the span via traceId, spanId for any span be it invoke_agent span , chat span etc.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -15049,6 +15586,7 @@ "review_state": null, "root_timestamp": "2026-06-04T23:20:48Z", "body": "Can you please help provide clarity on following questions:\r\n- How will evaluation span be associated to operation being evaluated?\r\n- What is evaluation span supposed to capture? The process of evaluation or evaluation results or both?\r\n- Currently evaluation results can be association with span being evaluated. After this change will evaluation result be associated with both evaluation span and span being evaluated?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -15077,6 +15615,7 @@ "review_state": null, "root_timestamp": "2026-05-26T02:56:28Z", "body": "The `invoke_node` span fills a real gap — in multi-agent workflows the current span hierarchy jumps from `invoke_workflow` directly to `invoke_agent` or `execute_tool`, with no way to represent intermediate processing nodes (validators, routers, formatters, etc.).\n\nThe design is consistent with the existing `gen_ai.execute_tool.internal` / `gen_ai.plan.internal` pattern, which is good. A few observations:\n\n1. **Naming**: `invoke_node` reads naturally alongside `invoke_agent` and `invoke_workflow`. The `gen_ai.node.name` attribute is clean.\n\n2. **Relationship to `gen_ai.evaluation.internal`**: I have an open PR (#185) that adds a similar `gen_ai.evaluation.internal` span for evaluation steps. Both follow the same `gen_ai..internal` pattern. It might be worth a quick cross-check to make sure the attribute sets are consistent (both use `gen_ai.operation.name` as required, `gen_ai.agent.name` as conditionally required, etc.).\n\n3. **`gen_ai.client.workflow.node.invocation.details` event**: the name is quite long. Is there a shorter form that still conveys the scope? Something like `gen_ai.node.invocation.details` might be easier to type and remember.\n\nOverall this is a solid ...[truncated]", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -15105,6 +15644,7 @@ "review_state": null, "root_timestamp": "2026-05-27T01:29:41Z", "body": "hi @RKest, can you mark this as \"Resolves #187\" (assuming it does)? thanks!", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -15133,6 +15673,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-07T01:00:59Z", "body": "Looks good, just a few comments", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -15161,6 +15702,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-23T03:11:24Z", "body": "I support the PR - it helps us cover operations that are not categorized yet.\n\nNow bikeshed time: *node* vs *step* vs *task* \n\n## Agent frameworks\n\n| lib | native term | concerns with \"node\" | concerns with \"step\" | concerns with \"task\" |\n|---|---|---|---|---|\n| langchain (LangGraph) | node | 🟢 none — native | foreign, no collision | overloaded (generic) |\n| google-adk 2.0 | node + edge | 🟢 none — native | 🟢 none — ADK calls a node \"a single step\" | overloaded (generic) |\n| pydantic-ai (pydantic-graph) | node (BaseNode) | 🟢 none — native | foreign, no collision | overloaded (generic) |\n| autogen (GraphFlow) | node (= agent) | 🟢 none — native | foreign | wrong level — task = the input/overall job |\n| agent-framework (MAF) | executor + edge | foreign | foreign | overloaded (generic) |\n| haystack (Pipeline) | component | 🔴 deprecated v1 term | foreign | overloaded (generic) |\n| llamaindex (Workflows) | step (@step) | 🔴 collides with Node/TextNode | 🟢 none — native | 🔴 collides with Task/TaskStep (core agent API) |\n| crewai | step (Flow); Task (Crew) | foreign | 🟢 none — native (Flow) | 🔴 collides with Task (core class) |\n| dspy | module (forward) | quite foreign | foreign | overloaded ...[truncated]", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -15189,6 +15731,7 @@ "review_state": null, "root_timestamp": "2026-06-08T16:22:44Z", "body": "Hi @eternalcuriouslearner,\r\nThis is a rather large PR, is there any way we could break this down into separate PRs? Perhaps \"Spans and Attributes\", \"Metrics\", and \"Reference Scenarios\"? Thanks!", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -15217,6 +15760,7 @@ "review_state": null, "root_timestamp": "2026-06-08T22:46:00Z", "body": "Reference scenarios should live with the relevant semconv PR, since we use them to evaluate the semconv changes themselves.\r\n\r\nmaybe\r\n\r\n- Core A2A attributes + spans\r\n- Operation duration + response body size metrics\r\n- Streaming metrics", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -15245,6 +15789,7 @@ "review_state": null, "root_timestamp": "2026-06-08T22:56:58Z", "body": "Hey @eternalcuriouslearner,\r\nI've recently opened a more exhaustive proposal for A2A semantic conventions #254 (previous one was #70) and I'd like to align with you on that since the issue is still untriaged AFAIK. It seems compatible for the most part, with only some minor differences.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -15273,6 +15818,7 @@ "review_state": null, "root_timestamp": "2026-06-09T02:07:34Z", "body": "Thanks Surya. Let me know when this PR is ready for another look. Maybe we can split up a few PRs between yourself and @pwkowalski ?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -15301,6 +15847,7 @@ "review_state": null, "root_timestamp": "2026-06-09T05:21:29Z", "body": "I'd be happy to work on the implementation if it doesn't introduce unnecessary overhead - I wouldn't want to disrupt the workflow. So if there's some way I could help, please let me know.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -15329,6 +15876,7 @@ "review_state": null, "root_timestamp": "2026-06-12T13:03:49Z", "body": "Hey @eternalcuriouslearner, thanks for your hard work!\r\n\r\nI agree about the removal of `gen_ai.agent.name` and `gen_ai.agent.id`, I've documented it in #254 already. I think it should be a follow-up once #243 is decided, in order to not block work.\r\n\r\n`a2a.client.response.time_to_first_event` seems to be aligned with `gen_ai.client.operation.time_to_first_chunk` - is the server one actually necessary?\r\n\r\n`a2a.client.response.body.size` seems dependent on transport method. Not sure it should be there.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -15357,6 +15905,7 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-06-16T10:58:31Z", "body": "Looks good, had some feedback but it should be minor.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -15385,6 +15934,7 @@ "review_state": null, "root_timestamp": "2026-06-16T12:46:29Z", "body": "I'm also not sure about these method names in general - A2A doesn't define them per se, but uses Pascal Case. However, I think a good reason to keep them like that (`message/send` instead of `SendMessage`) is keeping them aligned with existing MCP semconv method names.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -15413,6 +15963,7 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-06-16T14:06:09Z", "body": "Sorry, one more thing I've noticed after re-reading.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -15441,6 +15992,7 @@ "review_state": null, "root_timestamp": "2026-06-18T09:19:02Z", "body": "Sorry for the slight delay @eternalcuriouslearner.\r\n\r\nFollowing up on \"splitting up the work\" from earlier, is there something I could help with?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -15469,6 +16021,7 @@ "review_state": null, "root_timestamp": "2026-06-19T04:53:23Z", "body": "One modeling question on the core attrs, while this one's getting wrapped up: `a2a.message.referenced_task_ids` and `a2a.task.artifact_ids` are ID arrays pointing at other operations. Have you weighed span links for the task references instead of an ID-array attribute? If a referenced task is itself a span, a link keeps the causal edge queryable without baking high-cardinality arrays into attributes. Artifacts probably stay fine as attributes since they aren't spans. Referenced tasks feel link-shaped though.\n\nNot blocking the split work. Just worth settling deliberately while the core set is still being shaped, and probably in the #254 alignment too.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -15497,6 +16050,7 @@ "review_state": null, "root_timestamp": "2026-06-21T21:58:54Z", "body": "@eternalcuriouslearner yeah, that layered split sounds right to me. `referenced_task_ids` is what instrumentation can always read straight off the payload, so it makes sense as the core attribute. The span link is the better causal edge, but you can only build it when the referenced task's span context is actually resolvable, which generic instrumentation often won't have.\n\nOnly tweak I'd suggest: instead of leaving the link purely optional, word it as \"emit a link when the context is resolvable.\" That keeps the queryable causal edge I was after originally, without forcing anything on instrumentation that only has the IDs.\n\nThe deciding factor is the SDK side though, so I'll leave that call to @pwkowalski: for a2a-python, do we actually get span/trace context for the tasks in `referenceTaskIds`, or just the IDs from the payload?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -15525,6 +16079,7 @@ "review_state": null, "root_timestamp": "2026-06-22T14:52:05Z", "body": "> > I'm also not sure about these method names in general - A2A doesn't define them per se, but uses Pascal Case. However, I think a good reason to keep them like that (`message/send` instead of `SendMessage`) is keeping them aligned with existing MCP semconv method names.\r\n> \r\n> Howdy @aabmass can you pitch in here. Should we use pascal case or keep it as `message/send`?\r\n\r\nThese are attributes values right? IMO we should copy what's in the protocol so keeping the pascal case, see the generic RPC conventions https://opentelemetry.io/docs/specs/semconv/registry/attributes/rpc/#rpc-method", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -15553,6 +16108,7 @@ "review_state": null, "root_timestamp": "2026-06-22T15:06:28Z", "body": "> @AgentGymLeader Good point. I’m trying to reason through how feasible this is for generic A2A instrumentation.\r\n\r\n+1 on making sure it's feasible to instrument. Let's push this to a follow up PR if nothing is blocking, I think regardless we should capture the `referenced_task_ids` which are sent on the wire.\r\n\r\n@AgentGymLeader can you file a follow up issue for documenting downstream span linking?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -15581,6 +16137,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-06-25T22:42:55Z", "body": "Looks great, thank you! The only real blocking question I have is on the inclusion of http or RPC span attributes.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -15609,6 +16166,7 @@ "review_state": null, "root_timestamp": "2026-05-26T20:15:43Z", "body": "@trask Read through the full PR including the FAQ and alternatives.\r\n Three questions on the choices made, framed against the three points I was planning to surface at SIG this week before #197 landed:\r\n \r\n **Three counters vs one with all axes.** The FAQ explains the cross-product issue cleanly: providers report cache and phase marginally, not jointly with modality, so a single counter with all four dimensions would force producers to invent cross-product cells they don't actually have. Accepting that. One follow-up: the three-counter split means `sum(inference.tokens)` and `sum(input_tokens_by_cache)` should reconcile on input total, and consumers will likely write queries that subtract or compare across them. Is it worth documenting the invariant explicitly in the metric description so downstream dashboards can rely on it (e.g. \"sum over `input_tokens_by_cache.cache` equals sum over inference.tokens where token.type=input\")?\r\n \r\n **Modality value set consistency.** `gen_ai.token.modality` uses `text | image | audio | video | document | unknown`. The existing `gen_ai.output.type` uses `text | json | image | speech` and the message-part schemas use `image | video | audio | docum ...[truncated]", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -15637,6 +16195,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-05-27T02:01:43Z", "body": "Wow, thanks for this proposal! Made a first pass with some questions", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -15665,6 +16224,7 @@ "review_state": null, "root_timestamp": "2026-05-27T09:09:02Z", "body": "> and reasoning output (priced separately from response output on o-series and Gemini 2.5+).\r\n\r\nPeople do want to track reasoning tokens, but I don't think they have their own pricing on current models, and if I'm wrong I'd love to know.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -15693,6 +16253,7 @@ "review_state": null, "root_timestamp": "2026-05-30T13:26:30Z", "body": "\"Reasoning\" tokens as far as I know aren't priced differently, they are important to track as the scratch work often ends up being a large differentiator of cost between different models ex.\n\nOpus 4.5, 4.6 , 4.7, 4.8 for example all currently priced the same main differentiator in cost I've observed has been the extent to which the models are configured to aggressively use the \"reasoning\" block revealing this to end users in an easy way to show what \"reasoning\" is costing them and to help them make the determination if the \"reasoning\" provides any value to a given use case.\n\nThank you for this pr from downstream \n\nIt should greatly simplify observability.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -15721,6 +16282,7 @@ "review_state": null, "root_timestamp": "2026-06-04T22:10:54Z", "body": "We're going to discuss this on an ad-hoc call tomorrow at 12pm PT: [Calendar](https://calendar.google.com/calendar/u/0/r/day/2026/6/5?eid=MnI0NDloamxmdGU3b2ZnYXNsYzR1ZmZoZ2wgY18yYmY3M2UzYjZiNTMwZGE0YmFiZDQ0NGU3MmI3NmE2YWQ4OTNhNWMzZjQzY2Y0MDQ2N2FiYzdhOWE4OTdmOTc3QGc), [Zoom](https://zoom.us/j/91357539844?pwd=MU9ZRUNyVUwrcHdiclU2b2lPOVBkQT09).\r\n\r\nPlease join if you can @alexmojaki @Nik-Reddy @aabmass @trask", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -15749,6 +16311,7 @@ "review_state": null, "root_timestamp": "2026-06-05T18:16:30Z", "body": "> making cost queries that match the provider pricing structure (per-modality, per-cache-state unit prices) answerable from metrics alone\r\n\r\ncosts can't be calculated from metrics in general anyway since the price per token often depends on the number of (input) tokens in a request, unless you added another metric attribute indicating the pricing bucket.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -15777,6 +16340,7 @@ "review_state": null, "root_timestamp": "2026-06-05T18:29:23Z", "body": "> Why don't gen_ai.token.modality and gen_ai.output.type use the same enum values?\r\n\r\nwas this removed?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -15805,6 +16369,7 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-06-08T13:52:56Z", "body": "Since Bedrock support isn't wired up to the right field yet, should remove the scenario until it does support it?\n\nAlso, I believe `invoke_agent.common` should be `invoke_agent.client`", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -15833,6 +16398,7 @@ "review_state": null, "root_timestamp": "2026-06-07T22:36:15Z", "body": "@singankit Can we add some test scenarios for this?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -15861,6 +16427,7 @@ "review_state": null, "root_timestamp": "2026-06-14T08:39:18Z", "body": "Thanks for porting this. One clarification that would help implementers: when an agent service already emits an HTTP/RPC server span for the incoming request, should **gen_ai.invoke_agent.server** be emitted as a child span for the agent operation boundary? A short note or reference scenario would make this easier to apply consistently across SDKs and hosted agent services.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -15889,6 +16456,7 @@ "review_state": null, "root_timestamp": "2026-06-08T22:41:28Z", "body": "@nagkumar91 this is a solid shape. The verdict-vs-action split is the right call — keeping \"what the guardrail returned\" separate from \"what the caller actually enforced\" matches how these break in practice (a framework can return block and the caller still lets it through, and you want both on the trace).\n\nTwo things I'd pin down while it's still in development:\n\n1. The blocked case should be readable from absence, not only from the finding event. If a guardrail returns block and the caller enforces it before the operation runs, the guarded op should have no success span. A terminal block plus no downstream execute span is what lets a reader tell \"blocked\" apart from \"never ran\" or \"telemetry got dropped.\" Worth stating normatively how the guardrail span and finding link back to the operation they gated (a span relationship, not just a shared trace), so the block and the missing span are tied together. This also lines up with the span-relationship discussion in #243.\n\n2. Keep external_finding_id an opaque, producer-defined correlation handle, and keep the finding record itself out of scope. The convention should define a reference you can join on (SIEM, incident, etc.), not reach ...[truncated]", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -15917,6 +16485,7 @@ "review_state": null, "root_timestamp": "2026-06-11T17:07:03Z", "body": "Hey @nagkumar91, does this address the feedback we went through in the call and in [this doc](https://docs.google.com/document/d/14CD0_ApP0W_ek9VNJN1N_2Bz5ifiJ978LTv5AD4mf_o/edit?tab=t.0)? Just lmk and I'll review and get someone from Model Armor to review.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -15945,6 +16514,7 @@ "review_state": null, "root_timestamp": "2026-06-11T18:02:14Z", "body": "Strong direction, and the privacy posture is already careful: `content.input.value`/`.output.value` are opt_in with sensitive-content warnings, `finding.evidence` is a non-content channel with a MUST NOT on sensitive data, and `verdict.type` (what the guardrail returned) is kept separate from `action.type` (what the caller enforced). Those boundaries are right, and the `run_guardrail.client` vs `.internal` split captures out-of-process vs in-process cleanly.\n\n`gen_ai.security.content.input.hash` is the field everyone gets pointed to as the privacy-preserving path, and as written it doesn't pin what it hashes. Its purpose is forensic correlation, but that only works if two implementations agree on the exact bytes and the algorithm, and \"hash of the input content\" leaves open the encoding, any normalization, and what the input even is for a structured `tool_call_input` (a JSON object, not a string). Two services inspecting the same content can produce different hashes, so the field can't do its one job.\n\nSmall fix: name a canonical byte form plus a hash. For structured targets, RFC 8785 (JCS) over the arguments object then SHA-256; for free-text, state UTF-8 and any normalization. Th ...[truncated]", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -15973,6 +16543,7 @@ "review_state": null, "root_timestamp": "2026-06-15T17:32:55Z", "body": "This is the right shape now: tagging the algorithm, naming the canonical byte form, and splitting text (UTF-8 plus a stated normalization) from structured (deterministic serialization for a tool_call) is what lets the hash do its forensic-correlation job. The piece that keeps it working across two implementations is a named default per target subtype, not only a tag: if each service tags whatever it used, two inspectors of the same tool_call still diverge, so one default canonical form plus algorithm per subtype (JCS then SHA-256 for tool_call, UTF-8 with a named normalization then SHA-256 for text) makes an unkeyed hash reproducible without coordination. The HMAC path for sensitive content is the right call; it correlates within a shared-key domain rather than across orgs, which is the honest bound to state alongside it.\n\nIf useful I can add two worked examples as a small follow-up, one tool_call and one free-text, with the exact bytes and resulting digest so a second implementation reproduces them from the example alone. Glad to keep it out of this review if you'd rather land the convention first.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -16001,6 +16572,7 @@ "review_state": null, "root_timestamp": "2026-06-15T17:55:25Z", "body": "Here are the two worked examples, reproducible from the bytes alone.\n\nStructured (`tool_call`), RFC 8785 (JCS) over the input object then SHA-256:\n\n- input: `{\"name\":\"query_table\",\"arguments\":{\"table\":\"employees\",\"limit\":10}}`\n- canonical bytes: `{\"arguments\":{\"limit\":10,\"table\":\"employees\"},\"name\":\"query_table\"}`\n- `sha256:40cfbce5aa3b2794c73996f4db4e6c2b45a5492cdc2a4bc71773e88eeebc3d95`\n\nFree-text, Unicode NFC then UTF-8 then SHA-256, on `café au lait`:\n\n- normalized bytes (hex): `636166c3a9206175206c616974`\n- `sha256:7c413039fbb2248e2b18b98e7a8d4d85bdcac7cd79b9477a0923f97e3a1f2b50`\n- the same text as NFC and as NFD (`e` + U+0301) both reach that digest once NFC-normalized; the raw NFD bytes hash to `sha256:2f97bdcfe9d8165b6d25df3f56d7b83e16637d199505d01eff4a3e94eb1d9e15` instead, which is why the normalization has to be named rather than implementation-defined.\n\nThe JCS recipe is exact for objects of strings and integers; floating-point values would also need RFC 8785 number formatting. Happy to drop these into the reference scenario or a doc example, whichever you prefer.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -16029,6 +16601,7 @@ "review_state": null, "root_timestamp": "2026-06-26T00:14:27Z", "body": "On correlation, I'd keep the shape from #132: alongside `external_finding_id` (SIEM-facing), keep a producer-side correlation that stays opaque and producer-defined, so the span stays lean and the heavier context lives in the producer's own store, joined by id at audit time. I'd leave the external record format unspecified rather than mandate a canonical form.\n\nOn `escalate`: it works well as a real \"doesn't proceed yet\" decision. Worth saying the later resolution (allow or block by a human or external workflow) is a separate event rather than an overwrite, so both the decision-time state and the outcome survive.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -16057,6 +16630,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-05T20:03:13Z", "body": "Could you please list the systems that were evaluated (that could be instrumented with run_guardrail span) and share research on which properties they have and how they map to attributes being defined.\n\nIt seems we don't have any scenarios that would show how auto-instrumentation can record this span - are there any open source libraries or clients for server-side guardrails that can be instrumented and then can we add them to reference scenarios?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -16085,6 +16659,7 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-06-17T15:03:27Z", "body": "Nice work @AgentGymLeader - I think this is a good start.\n\nI've left a couple of comments & suggestions. I think it's worth adding tests for the new parsing/counting paths so regressions get caught too.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -16113,6 +16688,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-06-22T17:57:48Z", "body": "Left a few comments, mainly wondering about the previous comment, as we can still derive token usage and operation duration from the existing span.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -16141,6 +16717,7 @@ "review_state": null, "root_timestamp": "2026-06-15T21:22:15Z", "body": "The namespace form is the right call. `gen_ai.agent.drift_score` acting as both a key and the parent of `...drift_score.method` has no precedent in the registry and trips the policy check, as you noted. `gen_ai.agent.{trust,drift}.{score,method}` and `gen_ai.agent.scan.{verdict,method}` validate cleanly and keep each score visibly paired with its method token. No objection to the rename from our side; the semantics are unchanged.\n\nOn Recommended vs Opt-In: Recommended wherever the corresponding score is emitted is correct. A producer-scoped score is close to uninterpretable to a consumer correlating against outcomes without knowing the method epoch that produced it, and the cardinality cost is low since the token only moves on a re-fit or re-threshold, not per span. Opt-In would let a producer emit the score without the token, which is the exact failure mode the token exists to prevent.\n\nOne addition worth pinning in the attribute notes: state explicitly that the `.method` token is opaque and compared for equality only, with no consumer parsing of the method or version substructure. That keeps producers free to change their internal versioning without consumers building brittle par ...[truncated]", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -16169,6 +16746,7 @@ "review_state": null, "root_timestamp": "2026-06-16T14:27:39Z", "body": "Picking up from the gap I noted in #180 (comment above, today) — after the PR merges, one gap remains: the eight attributes are all producer-scoped. A consumer comparing spans across producers, or auditing a single producer's records without trusting that producer, has no recomputable handle.\n\nProposing a ninth attribute to close it:\n\n`gen_ai.agent.action_ref`\n- type: string\n- brief: SHA-256 of the JCS-canonical (RFC 8785) preimage `{agent_id, action_type, scope, timestamp_ms}` per action-ref-v1.\n- requirement_level: recommended\n- note: A deterministic, cross-producer correlation key. Any implementation that follows the derivation produces the same 32-byte hex digest from the same preimage fields — no service call required (no_giskard_api invariant). A verifier comparing records across producers, or auditing without trusting the emitting producer, can recompute it independently.\n\nThe `.method` companion tokens (`trust.method`, `drift.method`, `scan.method`) handle within-producer stability. This attribute handles the cross-producer case.\n\nNormative reference: draft-giskard-aeoess-action-ref-00\nhttps://github.com/giskard09/draft-giskard-aeoess-action-ref\n\nThree independent productio ...[truncated]", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -16197,6 +16775,7 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-06-24T06:22:55Z", "body": "could you please add reference scenarios showing which instrumentation can capture these attributes, how they would do it, and which signals these attributes would appear on? Check out https://github.com/open-telemetry/semantic-conventions-genai/blob/main/CONTRIBUTING.md#4-update-reference-scenarios for the details", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -16225,6 +16804,7 @@ "review_state": null, "root_timestamp": "2026-06-24T19:46:47Z", "body": "Thank you, good to see this moving forward. If there is anything else we can help with, please just shout.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -16253,6 +16833,7 @@ "review_state": null, "root_timestamp": "2026-06-24T20:12:14Z", "body": "+1 to the inline note on the method tokens. A score and its `.method` token are one unit: a score emitted without the token that produced it is close to uninterpretable to a consumer correlating against outcomes, because they cannot tell a re-fit or re-threshold from a real move in the agent. A flat `opt_in` on the token permits exactly that, a producer emitting the score and dropping the token, which is the failure the token exists to prevent.\n\nTying each token to its score reads cleaner than a blanket opt_in: recommend (or conditionally require) `gen_ai.agent.trust.method` when `gen_ai.agent.trust.score` is set, and the same for drift and scan. The registry notes already say recommended wherever the score is emitted, so this just lines the span requirement level up with the note. The token only moves on a method change, not per span, so the cardinality cost stays low.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -16281,6 +16862,7 @@ "review_state": null, "root_timestamp": "2026-06-25T05:49:39Z", "body": "The attributes here are not things a GenAI framework's `invoke_agent` instrumentation can populate on its own. Instrumentation around the model call, the tools, and the response only sees what the model and the immediate execution environment produce. It does not see the agent's authorized capability, its trust or drift signals, or a scan verdict, because none of those are properties of the inference itself. They are outputs of whatever component made the authorization decision: a policy decision point, an agent gateway, or a governance layer sitting in front of the action.\n\nThat component already computes, or has access to, the capability being invoked, the authority or delegation basis the decision was measured against, and any producer-scoped trust, drift, or scan signals, as part of making its allow or deny decision. The instrumentation that populates these span attributes is therefore instrumentation of that decision, emitted on the `invoke_agent` span or correlated to it. The values come from the decision context, not from the model call and not from constants.\n\nThat is also why hardcoding the values does not demonstrate real instrumentation. A more useful scenario would mode ...[truncated]", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -16309,6 +16891,7 @@ "review_state": null, "root_timestamp": "2026-06-25T10:04:31Z", "body": "`argentum-core` implements the authorization gate that produces these values. The capability, trust/drift signals, and scan verdict are outputs of that gate's decision — the gate computes them and the instrumentation sets the span attributes from the returned decision object, not from the invocation framework.\n\nRunnable examples: `giskard09/argentum-core/examples/conformance/`", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -16337,6 +16920,7 @@ "review_state": null, "root_timestamp": "2026-06-25T13:01:32Z", "body": "@lmolkova you are right to push on this. The hardcoded values are a fair criticism of the scenario as written. To your question: yes, there is a real component, and it is not the model-call instrumentation. @aeoess described it well: these attributes are outputs of whatever made the authorization decision, a policy decision point, an agent gateway, or a governance layer in front of the action. The inference instrumentation never sees a trust or drift signal, because none of them are properties of the inference.\n\nWe work on the producer side of this, a control-plane layer that sits in front of agent actions, so this is the vantage point we see it from. The deciding component already holds the capability, the trust and drift signals, and the scan verdict, since those are the inputs to its allow or deny decision. The instrumentation that populates these attributes is instrumentation of that decision, correlated onto the `invoke_agent` span, not of the model call.\n\nFor the scenario, a minimal authorization step that returns a decision object, with the span attributes set from that returned decision rather than from literals, would show both the instrumentation point (the gate) and the ...[truncated]", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -16365,6 +16949,7 @@ "review_state": null, "root_timestamp": "2026-06-26T00:43:26Z", "body": "> these attributes are outputs of whatever made the authorization decision\r\n\r\nIf this is a shared library or component, please share the details about it. There seem to be no auto-instrumentations or infra pieces that are able to report it today, so it's not in scope of this project to cover such conventions. You can document them for your own app.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -16393,6 +16978,7 @@ "review_state": null, "root_timestamp": "2026-06-26T05:08:07Z", "body": "On \"is there a real component\" — I came across one in AGT: microsoft/agent-governance-toolkit#3190 wires decision events (allow/deny/warn…) over OTel across its SDKs, under an `acs_*` namespace. Whether that's in scope here or just app-level is your call — flagging it as a concrete producer in case it helps.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -16421,6 +17007,7 @@ "review_state": null, "root_timestamp": "2026-06-26T14:30:46Z", "body": "> On \"is there a real component\" — I came across one in AGT: [microsoft/agent-governance-toolkit#3190](https://github.com/microsoft/agent-governance-toolkit/pull/3190) wires decision events (allow/deny/warn…) over OTel across its SDKs, under an `acs_*` namespace. Whether that's in scope here or just app-level is your call — flagging it as a concrete producer in case it helps.\r\n\r\nThanks, this is a useful context! If there is a second example of a shared component, the path forward would be to:\r\n\r\n1. Decide which operations should be instrumented (not attributes emitted on arbitrary spans). Agent-level instrumentation won't have access to this information, so stamping on agent spans can't be done with auto-instrumentation\r\n2. Add reference scenarios for applicable shared components\r\n3. Think broad about agent identity and governance and how to model it in general rather than about a handful of attributes", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -16449,6 +17036,7 @@ "review_state": null, "root_timestamp": "2026-06-26T14:50:50Z", "body": "Building on my prior comment on the gate as producer —\n\nThe connection to AGT#3190 is direct: that PR defines `acs_intervention_{allow,deny,warn,escalate,transform}_total` counters per evaluation and explicitly omits `action_identity`. The gap it leaves: the counter records how many interventions occurred, not which specific action was decided on. `action_ref` fills that field — SHA-256(JCS({agent_id, action_type, scope, timestamp})) — recomputable by any downstream verifier without trusting the emitting component.\n\nOn the path you've outlined:\n\n**Operations to instrument** — the gate's evaluation call, not an agent span. We added §Telemetry Integration to the working draft today ([f3628b7](https://github.com/giskard09/draft-etcheverry-action-ref/commit/f3628b769d0ba953f1f53237e6c9b95801361606)) that formalizes this: \"Implementations MUST NOT derive action_ref at the invoking agent's span level. Agent-level instrumentation does not have access to the complete intent tuple as presented to the gate.\" The gate derives the value before returning the verdict; downstream spans carry it as a correlation key.\n\n**Reference scenarios** — [draft-etcheverry-action-ref](https://datatracker.ietf ...[truncated]", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -16477,6 +17065,7 @@ "review_state": null, "root_timestamp": "2026-06-26T14:56:51Z", "body": "@giskard09 I would not recommend bulk AI-generating semantic convention proposal for governance. It needs a lot of human judgement.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -16505,6 +17094,7 @@ "review_state": null, "root_timestamp": "2026-07-01T15:56:22Z", "body": "@thebenignhacker A first cut as the decision operation sounds right. On the enum/metric shape you'd coordinate cross-producer, one distinction is worth building into the outcome vocabulary from the start. The superset under discussion (`allow`/`deny`/`warn`/...) collapses two cases that an auditor reads differently:\n\n- **A control resolved an attempt.** The agent tried something and a policy denied it. There's a decision, and a terminal `deny`.\n- **Nothing was in the reachable surface.** There was no attempt, because there was nothing to attempt, so no decision happens at all.\n\nDownstream both read as \"didn't run\", but they aren't the same fact: one is \"a control fired\", the other is \"it was never on the table\". If the enum folds them together, that difference is lost.\n\nOne paired point on the telemetry:\n\n- A denied decision should still leave a trace. The deliberately-absent child execute span next to a terminal `deny` is what keeps the denial auditable after the fact.\n- If a blocked call emits nothing, \"denied\" and \"never attempted\" look identical downstream.\n\nBoth are structural properties of the decision point, separate from the measured signals (trust / drift) in this PR.\n\nOn ...[truncated]", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -16533,6 +17123,7 @@ "review_state": null, "root_timestamp": "2026-07-02T16:43:11Z", "body": "This is shaping up well, and making the authorization decision its own operation is the right call. Two thoughts on the `deny` span, since that is the load-bearing case.\n\n**1. The producer-scoped `.method` tokens are the correct default, and they also define the one thing a downstream auditor cannot do, which is recompute.** By design `trust.score` with `trust.method` lets an observer correlate a score against outcomes, but not re-derive the score or verify that a given span corresponds to a specific decision. For most telemetry that is exactly right. For the `deny` span you are making first-class (outcome plus reason plus policy, no child execute span beneath it), the property an auditor usually wants is not the score itself but \"is this recorded decision the one that actually gated the action, and can I check that offline without trusting the producer's number.\" That is a different need, and an opaque score cannot serve it.\n\n**2. An optional content-addressed decision reference closes that gap without making anything normative.** A single optional string attribute in `gen_ai.agent.*`, a hash over the canonical decision inputs (capability, policy version, outcome, the identity fro ...[truncated]", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -16561,6 +17152,7 @@ "review_state": null, "root_timestamp": "2026-07-02T17:48:03Z", "body": "That's the standalone proposal — draft-etcheverry-action-ref-01 (https://datatracker.ietf.org/doc/draft-etcheverry-action-ref/), single frozen JCS derivation profile, conformance vectors across independent implementers (argentum-core as reference implementation). Doesn't touch this operation's shape — separate primitive, separate surface, same distinction you're drawing here between trace structure and content-addressed identity.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -16589,6 +17181,7 @@ "review_state": null, "root_timestamp": "2026-07-02T17:49:59Z", "body": "@thebenignhacker Making it a structural invariant rather than an enum value is the right call. The absence of a child execute span is load-bearing in a way a producer can't fake by labeling, which is what makes it hold up for an auditor. The `execute_authorization` sketch reads clean too. Happy to help pressure-test the invariants as it lands.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -16617,6 +17210,7 @@ "review_state": null, "root_timestamp": "2026-07-19T15:49:09Z", "body": "Thanks for the mention, and for taking the fail-closed algorithm point on board.\n\nOne related thing we shipped today: `jcs_edge_v1`, an open (Apache-2.0) RFC 8785 conformance set pinning the canonicalisation edge cases a naive serialiser gets wrong (U+2028 and U+2029 as literal UTF-8, property ordering by UTF-16 code units, `1.0` versus `1`), validated byte-for-byte across ten independent JCS implementations.\n\nhttps://github.com/chopmob-cloud/algovoi-jcs-conformance-vectors/tree/main/vectors/jcs_edge_v1", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -16645,6 +17239,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-10T18:12:15Z", "body": "Can we go ahead and rebase and regenerate the `uv.lock`? Believe `opentelemetry-instrumentation==0.63b1` requires `opentelemetry-semantic-conventions==0.63b1` but we're currently on `opentelemetry-semantic-conventions==0.64b0`. Think we should consider upgrading `opentelemetry-instrumentation` or allow it to resolve a compatible version.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -16673,6 +17268,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-13T18:46:45Z", "body": "LGTM, can we go ahead and update the package version to `opentelemetry-util-genai==1.0b0` in the description and file a follow up issue(s) for Compaction in either this repo and/or the python genai repo based on any actionable findings here? Encryption via `encrypted_content` and support within the handler for `CompactionPart` are the two that came to mind, if not already present. Thanks!", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -16701,6 +17297,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-06-30T18:35:00Z", "body": "Small note, but I believe the Towncrier type in the changelog filename should enhancement? Otherwise, looks good!", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -16729,6 +17326,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-10T00:24:01Z", "body": "LGTM, just a few cosmetic suggestions", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -16757,6 +17355,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-14T15:29:18Z", "body": "LGTM", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -16785,6 +17384,7 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-07-21T02:31:48Z", "body": "I asked AI to do research on azure-ai-evals, deep-evals, dspy (the ones we're adding scenarios for) to check which of these attributes we could capture with generic instrumentations\n\n\n| Attribute | deepeval | dspy | azure-ai-evaluation |\n|---|---|---|---|\n| `evaluator.id` | **Constructed, not native.** Metric has `.name`; build id from name+model: `f\"{metric.__name__}-{metric.evaluation_model}\"` | **Constructed.** Metric is a callable → `metric.__name__` | **Constructed.** Evaluator is a class → `type(ev).__name__` + `model_config[\"model\"]` |\n| `evaluator.version` | **Not native** — no version concept. Scenario hardcodes `\"1.0\"` | **Not native.** Function has no version | **Not native** (not claimed in report). Would be hardcoded |\n| `evaluator.type` | **Inferable.** Native metrics use an LLM (`using_native_model`/`evaluation_model` set) → `llm_judge`; custom `BaseMetric` w/o model → `deterministic` | **Weak.** Metric is just a callable; DSPy can't tell if it calls an LLM. Scenario assumes `deterministic` | **Inferable from class:** AI-assisted (`RelevanceEvaluator`…) → `llm_judge`; NLP/math (`F1ScoreEvaluator`, `BleuScoreEvaluator`, `RougeScoreEvaluator`) → `deterministic` |\n| `r ...[truncated]", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -16813,6 +17413,7 @@ "review_state": null, "root_timestamp": "2026-07-25T16:23:06Z", "body": "We built this pattern independently (an app-level tag, `augmentloop.grade.source`, on `gen_ai.evaluation.result`) and it maps onto your `evaluator.type` enum: our `math` (a deterministic checker compares against a provably-correct answer) is your `deterministic`; our `ai_judge` is your `llm_judge`. We never let `ai_judge` grades into anything that feeds a headline \"correct\" number, filtered by source rather than by convention.\n\nOne gap: our third source, `reality`, has no slot. It's a grade that arrives asynchronously, after the decision's span has closed, from an actual outcome rather than a check or an opinion: the clip was actually kept, the appointment actually landed. Not `deterministic` (nothing computes it at eval time), not `llm_judge` (no model ever scores it). We handle it by re-emitting the event with a span link back to the original decision span, plus the same `gen_ai.response.id` as correlation fallback, so a late verdict stays one hop from the decision it judges. Might be worth a fifth value, or a line in the spec noting that resolution timing is orthogonal to evaluator type: `reality` could pair with any of the current four.\n\nOn @lmolkova's capturability table: it m ...[truncated]", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -16841,6 +17442,7 @@ "review_state": null, "root_timestamp": "2026-07-26T18:09:10Z", "body": "`INFORMED_BY` captures provenance between a tool result and a later generation.\nI measured a related quantity that current usage attributes cannot expose: how\nmuch of each model input is new versus logical replay from the preceding\nconversation context.\n\nI recorded two real coding-agent sessions and verified prompt continuity with\nblock-level prefix hashes; the quantity is the exact token LCP between adjacent\nprompts, computed while token IDs were in memory and stored as one integer. No\nraw text is retained. Counts here come from a local `cl100k_base` tokenizer\nrather than provider-reported usage, which matters for units — see below.\n\n| session | steps | total prompt tokens | logical replay |\n|---|---:|---:|---:|\n| A | 17 | 170,714 | 147,902 (87%) |\n| B | 27 | 400,211 | 368,620 (92%) |\n\nIn session B, one source step added 2,841 tokens to the next prompt. Those\ntokens appeared in 19 later prompts, totalling 53,979 replayed tokens (19x).\n\n`gen_ai.usage.input_tokens` records total input, while\n`gen_ai.usage.cache_read.input_tokens` records provider-cache delivery. Neither\nanswers whether input is new or already present in the logical agent context.\nCould a companion split on `chat` sp ...[truncated]", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -16869,6 +17471,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-22T09:38:14Z", "body": "The TC has write permissions on every repo, so their approval would be counted as someone with write access but the ruleset may not allow maintainers to merge the PR. It's a small edge case, but maybe it could be confusing?\n\nOtherwise looks good to me, if you think that this is not worth worrying about feel free to merge :)", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -16897,6 +17500,7 @@ "review_state": null, "root_timestamp": "2025-07-13T12:08:24Z", "body": "Had a quick look. This appears to be a freeform yaml block. Could `annotations` be used instead? We recently backed-out of spec change for `value_type` in favour of `annotations`. This feels the same to me.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -16925,6 +17529,7 @@ "review_state": null, "root_timestamp": "2025-07-14T22:32:45Z", "body": "I don't think we make any requirement on delta vs. cumulative, but we should have a way to specify histogram boundaries.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -16953,6 +17558,7 @@ "review_state": null, "root_timestamp": "2025-08-18T12:12:12Z", "body": "Are packages versioned? What's the difference between a package and just a repository directly?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -16981,6 +17587,7 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-01-07T18:12:20Z", "body": "We should change the tests to validate with the real archive URLs.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -17009,6 +17616,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-16T21:33:24Z", "body": "@lmolkova FYI - I forgot to approve this oh so long ago - Approving now if we still consider this a blocker - hopefully it's easy to bring this back up to date and merge if we want it.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -17037,6 +17645,7 @@ "review_state": null, "root_timestamp": "2026-07-23T12:25:17Z", "body": "Generally fix looks good - would approve, but I think Co-pilot found the bug in the windows build. Please fix tests and then I think we can merge.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -17065,6 +17674,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-23T18:31:22Z", "body": "Overall - this looks like a nice addition, thanks for contributing!", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -17093,6 +17703,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-24T16:16:35Z", "body": "@McGluut thanks for the contribution!\n\nI'd like to start release sometime soon and would love to get this change in - it'd be really useful in some tests we have in otel. \n\nIf you're around, could you please take a look at the open discussion. Otherwise I might just auto-apply the suggestion I left and merge the PR in a hope you won't mind it. Thanks!", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -17121,6 +17732,7 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-07-24T17:15:51Z", "body": "Thank you - I think this is pretty close and useful. I would like it to be aligned with how we handle `resource` though. And in solving how to pass the `instrumentation_scope` to rego we can do the same for `resource`.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -17149,6 +17761,7 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-25T21:43:14Z", "body": "We're very close. This is really good. Let's remove that Template section and make sure to update the docs: https://github.com/open-telemetry/weaver/tree/main/crates/weaver_live_check#custom-advisors", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -17177,6 +17790,7 @@ "review_state": null, "root_timestamp": "2026-07-27T19:53:54Z", "body": "> Middle's re-export is stamped with middle's schema_url instead of base's, so resolution fails with\r\n\r\nThis is a bug - I assume this happened from a previous release before we had better multi-dependency resolution?", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -17205,6 +17819,7 @@ "review_state": null, "root_timestamp": "2026-07-27T20:05:14Z", "body": "100% agree 2 + 3 are our bugs that must be fixed.\r\n\r\nFor 1 - it's an interesting, temporary, problem. Fixing the `include_unrefereenced` flag to remember provenance *should* fix this issue once published repositories upstream are fixed. If we have a fix - I think it should be a temporary issue.", + "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -17233,6 +17848,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-28T15:55:10Z", "body": "Fix looks good, thanks for catching this bug. Looks like an opportunity to refacor/clean up the logic after a point fix release!", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -17261,6 +17877,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-28T15:59:38Z", "body": "Fix is good but lots of duplicated code or repeated logic.\n\nGiven urgency of the bugs we can clean this up post submission.\n\nThis is also one of those \"duh\" moments - we know attributes can change across version and treating the whole group as a version is a good thing", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -17289,6 +17906,7 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-28T16:02:20Z", "body": "Another thing we should have fixed! Good catch", + "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", diff --git a/.github/scripts/pull-request-dashboard/test_eval_baseline.py b/.github/scripts/pull-request-dashboard/test_eval_baseline.py index 2eb8712f3da..4452236fe5a 100644 --- a/.github/scripts/pull-request-dashboard/test_eval_baseline.py +++ b/.github/scripts/pull-request-dashboard/test_eval_baseline.py @@ -157,6 +157,10 @@ def test_counts_and_configuration_describe_the_new_measurement(self) -> None: rebuilt["baseline_generated_at"], rebuilt["measurements_updated_at"], ) + self.assertEqual( + {rebuilt["baseline_generated_at"]}, + {case["measurement_date"] for case in rebuilt["cases"]}, + ) self.assertNotIn("generated_at", rebuilt) diff --git a/.github/scripts/pull-request-dashboard/test_eval_fixture.py b/.github/scripts/pull-request-dashboard/test_eval_fixture.py index de5be41311a..f4951de68fd 100644 --- a/.github/scripts/pull-request-dashboard/test_eval_fixture.py +++ b/.github/scripts/pull-request-dashboard/test_eval_fixture.py @@ -42,9 +42,18 @@ def test_counts_match_the_cases(self) -> None: def test_measurement_dates_describe_the_mixed_vintage_corpus(self) -> None: baseline = date.fromisoformat(self.data["baseline_generated_at"]) updated = date.fromisoformat(self.data["measurements_updated_at"]) + measurements = [] self.assertLessEqual(baseline, updated) self.assertNotIn("generated_at", self.data) + for case in self.cases: + with self.subTest(case=case["id"]): + measured = date.fromisoformat(case["measurement_date"]) + self.assertLessEqual(baseline, measured) + self.assertLessEqual(measured, updated) + measurements.append(measured) + self.assertEqual(baseline, min(measurements)) + self.assertEqual(updated, max(measurements)) def test_case_ids_are_unique(self) -> None: ids = [case["id"] for case in self.cases] From fa667bc39d2132d39fdbc139e5e15f7e20b9d9f5 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Wed, 2 Sep 2026 15:44:08 -0700 Subject: [PATCH 07/30] Remove per-case measurement dates Restore the accepted corpus provenance policy without per-case dates or regeneration machinery. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../eval/regenerate_baseline.py | 3 +- .../eval/reviewer_feedback_cases.json | 618 ------------------ .../test_eval_baseline.py | 4 - .../test_eval_fixture.py | 9 - 4 files changed, 1 insertion(+), 633 deletions(-) diff --git a/.github/scripts/pull-request-dashboard/eval/regenerate_baseline.py b/.github/scripts/pull-request-dashboard/eval/regenerate_baseline.py index 0b2ed1bfbe5..3a20d6e2502 100644 --- a/.github/scripts/pull-request-dashboard/eval/regenerate_baseline.py +++ b/.github/scripts/pull-request-dashboard/eval/regenerate_baseline.py @@ -181,7 +181,6 @@ def work(task: tuple[list[dict], str]) -> tuple[str, dict[str, str]]: def rebuild(payload: dict, trials: list[dict[str, str]], model: str) -> dict: - measurement_date = datetime.now(UTC).strftime("%Y-%m-%d") cases = [] for case in payload["cases"]: raw = [trial.get(case["id"]) for trial in trials] @@ -198,7 +197,6 @@ def rebuild(payload: dict, trials: list[dict[str, str]], model: str) -> dict: **{k: case[k] for k in ("id", "repo", "pull_request", "requester", "pr_author", "review_state", "root_timestamp", "body")}, - "measurement_date": measurement_date, "role": role, "stability": stability, "recorded_label": recorded, @@ -211,6 +209,7 @@ def rebuild(payload: dict, trials: list[dict[str, str]], model: str) -> dict: cases.sort(key=lambda c: (c["repo"], c["pull_request"], c["root_timestamp"])) roles = Counter(c["role"] for c in cases) stabilities = Counter(c["stability"] for c in cases) + measurement_date = datetime.now(UTC).strftime("%Y-%m-%d") return { **payload, "baseline_generated_at": measurement_date, diff --git a/.github/scripts/pull-request-dashboard/eval/reviewer_feedback_cases.json b/.github/scripts/pull-request-dashboard/eval/reviewer_feedback_cases.json index 711c43c0c10..da1e29c9141 100644 --- a/.github/scripts/pull-request-dashboard/eval/reviewer_feedback_cases.json +++ b/.github/scripts/pull-request-dashboard/eval/reviewer_feedback_cases.json @@ -24,7 +24,6 @@ "run_labels": "the answers it did give, mapped through action_labels", "recorded_label": "the label the baseline settled on, null when it did not settle", "adjudicated_label": "the label a human decided, null until one has", - "measurement_date": "the date this case's run_actions were measured", "root_timestamp": "the timestamp the dashboard orders a pull request's items by; cases are stored and measured in that order, so batches match the ones it sends" }, "counts": { @@ -45,7 +44,6 @@ "review_state": null, "root_timestamp": "2026-03-04T12:14:08Z", "body": "Sorry I wasn't able to get to this due to lack of bandwidth. Anybody else want to review this?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -74,7 +72,6 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-04-12T15:31:02Z", "body": "Hi @alexcams, thanks for working on this. I've left some suggestions for your consideration, please take them (with a grain of salt) as improving ideas and me thinking out loud :)", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -103,7 +100,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-04-14T07:18:32Z", "body": "That's pretty much what on my head. Thanks for making those changes, Alex.\n\nWould you mind to post the benchmarks result as well? It'd be helpful to track the performance cost of this new feature.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -132,7 +128,6 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-04-17T12:45:43Z", "body": "The benchmarks looks good, thanks for posting them, Alex. We almost there, just small nits for better readability, otherwise looks good to me :)", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -161,7 +156,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-04-20T10:01:46Z", "body": "LGTM. I'm not official approver though, so let's see what code owners think.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -190,7 +184,6 @@ "review_state": null, "root_timestamp": "2026-02-11T20:45:07Z", "body": "Collector is already with go 1.25, you could move forward with this 🙏", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -219,7 +212,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-02-12T14:33:42Z", "body": "Looks like the commit used is forcing us to move to 1.25.5 🥲", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -248,7 +240,6 @@ "review_state": null, "root_timestamp": "2026-02-25T18:09:33Z", "body": "Just an update here. We discussed this PR in [slack](https://cloud-native.slack.com/archives/C07CCCMRXBK/p1771958044638699), and our first option is hope that consul maintainers are happy to downgrade their minimum Go version: https://github.com/hashicorp/consul/pull/23268\r\n\r\nIf we don't hear back from them in a week, we'll have to consider other options", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -277,7 +268,6 @@ "review_state": null, "root_timestamp": "2026-03-03T20:03:32Z", "body": "Status update, https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/46609 was opened", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -306,7 +296,6 @@ "review_state": null, "root_timestamp": "2026-04-02T23:01:44Z", "body": "I've opened https://github.com/open-telemetry/opentelemetry-collector/pull/15052, hopefully we can unblock this PR", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -335,7 +324,6 @@ "review_state": null, "root_timestamp": "2026-05-11T08:20:32Z", "body": "@ArthurSens , is it worth moving this PR to draft until we can resolve the associated issue?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -364,7 +352,6 @@ "review_state": null, "root_timestamp": "2026-07-07T20:53:19Z", "body": "https://github.com/open-telemetry/opentelemetry-collector/pull/15052 got merged! @aknuds1, could you rebase the PR?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -393,7 +380,6 @@ "review_state": null, "root_timestamp": "2026-07-15T06:15:33Z", "body": "Sorry, one more rebase for good measure!", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -422,7 +408,6 @@ "review_state": null, "root_timestamp": "2026-05-01T13:07:20Z", "body": "Not stale, will need a maintainer to take a look. Will see if I can get someone", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -451,7 +436,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-05-04T15:03:20Z", "body": "@paulojmdias As a codeowner, I think this looks good to me and I believe any conflicts were addressed", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -480,7 +464,6 @@ "review_state": null, "root_timestamp": "2026-05-04T15:04:45Z", "body": "@atoulme Can you give another look please ?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -509,7 +492,6 @@ "review_state": null, "root_timestamp": "2026-07-13T02:42:08Z", "body": "This needs one more approval from the approvers.\r\nCould someone from @open-telemetry/collector-contrib-approvers please take a look at it.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -538,7 +520,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-13T03:51:11Z", "body": "This won't work for cross-account observability (linked accounts), right? I think we would need to extract the account ID from the log group ARN in that case.\n\nShould the `autodiscovery` case capture the log group ARN and extract the account ID from that? I believe the STS addition may still be needed for the `named` config case.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -567,7 +548,6 @@ "review_state": null, "root_timestamp": "2026-07-14T00:26:08Z", "body": "> This won't work for cross-account observability (linked accounts), right? I think we would need to extract the account ID from the log group ARN in that case.\r\n> \r\n> Should the `autodiscovery` case capture the log group ARN and extract the account ID from that? I believe the STS addition may still be needed for the `named` config case.\r\n\r\nCross account observability currently does not work and requires a fix, https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/48762 \r\n\r\nIt could be worth trying to get that over the line before implementing this one.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -596,7 +576,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-04-01T01:45:34Z", "body": "I haven't reviewed this completely, but wanted to provide some initial feedback on what I have seen so far.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -625,7 +604,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-04-07T14:00:03Z", "body": "Hello helloooo 👋 -- Awesome progress here!!\n\nJust to align expectations, this PR creates the package for Collector's internal telemetry validation, right? It doesn't fix #44905 yet because we still need to wire this library with our CI somehow.\n\nAm I also understanding things correctly that this package spins up a new Weaver container for each OTLP message we want to validate? How would this wiring work if we're to validate hundreds of components at the same time?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -654,7 +632,6 @@ "review_state": null, "root_timestamp": "2026-04-07T14:12:03Z", "body": "We're also missing a README file with the codeowners of the new package :)", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -683,7 +660,6 @@ "review_state": null, "root_timestamp": "2026-04-07T22:09:49Z", "body": "Here is maybe an example @vesari thanks to @braydonk's https://gist.github.com/braydonk/7067f20dde350a2bf23ea208aa937d97\r\n\r\nhttps://github.com/jmacd/weaver/pull/1", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -712,7 +688,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-05-19T14:18:23Z", "body": "Because of the way we have to work with Weaver's /stop endpoint, the testing API ends up looking really awkward from my original design. I think we can simplify it.\n\nI'm thinking an external user should just call a public function from the package called semconvtest.Test. For metrics for example that function would look something like func TestMetrics(t *testing.T, metrics pmetric.Metrics, opts ...WeaverOption) (assuming weaver options were refactored to the functional option pattern I mentioned in the other comment).\n\nThis function will essentially do all of what's here - it'll start a weaver container with the supplied options, send the logs to it, call stop, parse the livecheck report, and return findings. It obscures all the weaver stuff that I previously had them calling as individual steps. All these steps are basically necessary to work, so instead we can abstract all these steps away from the user, so they only know they need to call semconvtest.Test with their pdata.\nAlso, if these functions accept a t directly, then it can find the violations and mark the test as succeeded or failed.\n\nThis can also help resolve the comments around storing context i ...[truncated]", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -741,7 +716,6 @@ "review_state": null, "root_timestamp": "2026-07-08T09:05:13Z", "body": "This addition will be really valuable, thank's @vesari and @braydonk! I'm looking forward to this. \r\nCouple of generic comments from my side.\r\n\r\n> This works successfully with synthetic data, however, I haven't applied it to any real component just yet. A good first candidate should be agreed upon. What is also yet to be addressed is the stability enforcement. Work is in progress.\r\n\r\n`k8s_attributes` processor is targeting v1/stability through https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/49274. I believe this component would make it for a great candidate since it's already based on stable Semantic Conventions and we don't have any strict validation for the schema in place.\r\n\r\nWith https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/49545 we only explicitly set the stability level \"manually\" and refer to the SemConv docs, but there is no guardrail or actual automatic linking from the component back to the spec/semconv.\r\n\r\n> Tests for the semconvtest package itself. These are not component-level compliance tests (those would be added to the components actually using the package).\r\n\r\nWould that make sense to have a sample component tested ...[truncated]", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -770,7 +744,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-11T13:27:05Z", "body": "Looks much better! I think this accomplishes what I had suggested last time, which is a simple API for receivers using this package of `Test` and pass a bunch of pdata. The Weaver stuff gets largely obscured from them, all they need to do is pass data and under the hood Weaver does the magic.\n\nI tried to think if it's worth trying to come up with a way to reuse the same `testcontainer` instance across all tests; currently each call to `Test` will result in the a new container being created, started, stopped, and shutdown within the call. \nI think this is probably fine, since as long as the same weaver tag is used for each test then I don't think it will pull a new container each time (I wonder if `latest` will work that way? worth a check).\n\nI left a couple small comments, but structurally I feel that this is in a good place!", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -799,7 +772,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-02-28T05:07:11Z", "body": "### 💡 Codex Review\n\nHere are some automated review suggestions for this pull request.\n\n**Reviewed commit:** `9fe03f420e`\n \n\n
ℹ️ About Codex in GitHub\n
\n\n[Your team has set up Codex to review pull requests in this repo](http://chatgpt.com/codex/settings/general). Reviews are triggered when you\n- Open a pull request for review\n- Mark a draft as ready\n- Comment \"@codex review\".\n\nIf Codex has suggestions, it will comment; otherwise it will react with 👍.\n\n\n\n\nCodex can also answer questions or update the PR. Try commenting \"@codex address that feedback\".\n \n
", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -828,7 +800,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-03-04T17:58:56Z", "body": "Hi @Rajneesh180! Thank you for working on this! Before I can thoughtfully review it, let's try to avoid adding all this extra reflection code. We can probably leverage the existing `functions.buildArgs` for that, as it's already processing all that logic. I might be missing something, but I think we can check the functions argument there using the `ottl.isLiteralGetter()`, without having to go thought the parameters.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -857,7 +828,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-03-13T13:05:25Z", "body": "Thanks for working on this @Rajneesh180, a few more ideas:\n - Considering almost all converters are deterministic, I think we could probably invert this logic, changing the non-deterministic ones to pass the new factory option.\n - Let's avoid the extra reflection. We can probably apply the same idea of the `functions.buildArgs` to drop the slice arg reflection part. Please also consider organizing the code a bit or adding some extra structs to improve readability, as retuning bare booleans for argument builders might be a bit confusing. \n - `ottl.WithIsDeterministic()` is exported, so I'd choose a name that describes this option a bit more, such as `ottl.NonDeterministicConverter()` or something like that. We should also validate this option allowing it to be used with converters only, not editors.\n - We probably don't need to export the `ottl.Factory.IsDeterministic()`. This interface is already \"private\" so not issues un-exporting this one as well, the less we export here, the better.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -886,7 +856,6 @@ "review_state": null, "root_timestamp": "2026-04-02T12:12:19Z", "body": "Thanks @Rajneesh180, sorry for the delay. I'll be back reviewing this one soon.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -915,7 +884,6 @@ "review_state": null, "root_timestamp": "2026-07-20T13:01:45Z", "body": "@paulojmdias I have a PR already for this https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/49466 - waiting on someone to review it.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -944,7 +912,6 @@ "review_state": null, "root_timestamp": "2026-07-24T15:06:28Z", "body": "Thanks for the clarification @paulojmdias , I should have looked more closely :P \r\n\r\nI will update the table here https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/22095#issuecomment-4649650180 to reference your PR.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -973,7 +940,6 @@ "review_state": null, "root_timestamp": "2026-03-31T11:03:35Z", "body": "I think it would be really good to have this, but apparently this has lost traction. Is there anything I can do to help to move it forward? I think it \"only\" lacks another round of reviews / an approval. :-)", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -1002,7 +968,6 @@ "review_state": null, "root_timestamp": "2026-04-01T18:32:52Z", "body": "Sorry for the slow review. In some ways, i'm surprised this configuration is needed. I suspect the need stems from the fact that we aren't properly differentiating between cases where we aren't on the platform at all, and cases where we are on the platform, but the request fails.\r\n\r\nIf we aren't on the platform (e.g. the metadata endpoint returns a 404), we should never fail -- we should silently ignore. Any other errors should always fail. I guess the question is: Is there a use-case where someone wants a 404 to fail the resource detector?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -1031,7 +996,6 @@ "review_state": null, "root_timestamp": "2026-04-02T06:45:12Z", "body": "> I suspect the need stems from the fact that we aren't properly differentiating between cases where we aren't on the platform at all, and cases where we are on the platform, but the request fails.\r\n> If we aren't on the platform (e.g. the metadata endpoint returns a 404), we should never fail -- we should silently ignore. Any other errors should always fail.\r\n\r\nNot quite: My main interest (and the reason I raised #46579) is the situation (broadly speaking) where we actually are on the platform, but the platform is configured in a way so the resource detector cannot do its work. (Example: EKS detector, IMDS not enabled, permission DescribeInstances not present). Maybe the default should be to propagate the error and crash the collector, but there should also be an option to fail gracefully, e.g. stop the detector, but let the collector continue. The EC2 detector already has `fail_on_missing_metadata` and it works exactly like that, so there is precedent/prior art for this -- if the platform is EC2 but the permissions are not correct, the error is logged and the collector startup continues. \r\n\r\n> I guess the question is: Is there a use-case where someone wants a 404 to fail the reso ...[truncated]", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -1060,7 +1024,6 @@ "review_state": null, "root_timestamp": "2026-05-07T09:20:25Z", "body": "Can we reopen this PR ? This fix is still expected", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -1089,7 +1052,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-05-25T12:46:55Z", "body": "PTAL into the Co-Pilot suggestions", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -1118,7 +1080,6 @@ "review_state": null, "root_timestamp": "2026-06-03T10:09:43Z", "body": "@Akash-Kumar-Sinha, do you plan to review the suggestions before I can review them?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -1147,7 +1108,6 @@ "review_state": null, "root_timestamp": "2026-06-08T13:54:05Z", "body": "@Akash-Kumar-Sinha, any updates here? This fix is still needed.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -1176,7 +1136,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-03-25T21:25:42Z", "body": "I feel you are trying to solve 3 different issues:\n1. Create a ticket for each one\n2. Ensure to add a changelog in the PRs\n3. Provide regression tests for the issues you want to fix", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -1205,7 +1164,6 @@ "review_state": null, "root_timestamp": "2026-04-23T08:30:27Z", "body": "> @iblancasa would you mind sharing which are the three issues? I only found 2 bugs\r\n\r\nSorry. I don't know why I wrote 3. There are 2.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -1234,7 +1192,6 @@ "review_state": null, "root_timestamp": "2026-04-23T15:06:02Z", "body": "Please also update to latest main and look at the checks CI.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -1263,7 +1220,6 @@ "review_state": null, "root_timestamp": "2026-05-08T07:12:00Z", "body": "@pathcl Please ensure all the govulncheck tests pass. I have taken a look at the functionality upgrade and looks good for now. Lets quickly fix and move forward", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -1292,7 +1248,6 @@ "review_state": null, "root_timestamp": "2026-07-11T12:38:47Z", "body": "Kindly look at the lint CI failures.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -1321,7 +1276,6 @@ "review_state": null, "root_timestamp": "2026-06-03T01:46:01Z", "body": "This is fairly domain-specific, so will need to be reviewed by code owners.\r\n\r\n@schmikei @ishleenk17 PTAL", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -1350,7 +1304,6 @@ "review_state": null, "root_timestamp": "2026-04-07T18:43:31Z", "body": "I have not looked into the code already but this this should have a changelog entry", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -1379,7 +1332,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-04-09T13:21:41Z", "body": "The PR LGTM, I'd like to first figure out whether the answer is instead to just [not default `top_n` to 1 at all](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/47444#issuecomment-4214505183) before pressing approve, but if there is something I'm missing and that's the wrong idea then I'll approve this one.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -1408,7 +1360,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-04-09T17:29:04Z", "body": "Thanks @Dylan-M for putting this together.\n\nLet's settle on the solution in the issue https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/47444 before moving forward with this PR.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -1437,7 +1388,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-05-07T14:52:15Z", "body": "Overall LGTM, just a small nit.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -1466,7 +1416,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-13T12:27:21Z", "body": "My strong preference is still to use the feature gate. The File Log receiver is widely used in big deployments where seemingly simple change of updating a config might require coordination between multiple teams. A staged rollout with a feature gate makes upgrading less painful for users.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -1495,7 +1444,6 @@ "review_state": null, "root_timestamp": "2026-04-13T23:15:23Z", "body": "Fixes https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/47573", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -1524,7 +1472,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-04-14T05:37:34Z", "body": "lgtm", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -1553,7 +1500,6 @@ "review_state": null, "root_timestamp": "2026-07-22T00:34:57Z", "body": "@skreuzer thanks to @paulojmdias this was already re-opened, ping us if you make progress on this PR.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -1582,7 +1528,6 @@ "review_state": null, "root_timestamp": "2026-04-23T00:57:15Z", "body": "Change seems fine, doc only.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -1611,7 +1556,6 @@ "review_state": null, "root_timestamp": "2026-06-19T00:22:50Z", "body": "@dmitryax please review as codeowner, thanks!", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -1640,7 +1584,6 @@ "review_state": null, "root_timestamp": "2026-06-23T15:20:46Z", "body": "> @atoulme do i need to do anything?\r\n\r\njust need a review by @dmitryax", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -1669,7 +1612,6 @@ "review_state": null, "root_timestamp": "2026-04-23T01:31:54Z", "body": "Please add a changelog and mark ready for review again.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -1698,7 +1640,6 @@ "review_state": null, "root_timestamp": "2026-06-22T14:38:50Z", "body": "@asweet-confluent, can you please review as codeowner?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -1727,7 +1668,6 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-07-10T19:26:02Z", "body": "See my comment [here](https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/47849/changes#r3561462235) - we can't merge this, not in this state. I'll follow up in https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/47845.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -1756,7 +1696,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-04-29T08:17:24Z", "body": "Nice work!", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -1785,7 +1724,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-04-30T03:39:21Z", "body": "LGTM! It would be great having a storage analysis of this improvement before moving the feature gate to beta 🎉", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -1814,7 +1752,6 @@ "review_state": null, "root_timestamp": "2026-05-15T03:50:17Z", "body": "@dmitryax @braydonk Any thoughts on these changes? (I am primarily in favor as removes the float64 arithmetic noise which ends up reducing the storage costs while maintaining the precision)", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -1843,7 +1780,6 @@ "review_state": null, "root_timestamp": "2026-05-29T22:27:48Z", "body": "We touched on this in the System Semantic Conventions SIG yesterday. \r\n\r\nI have to push back on this. I should have flagged concerns on #46153 before approving it, and this PR builds further on the same approach while adding much more complexity. I don't think this complexity can be justified.\r\n\r\nFrom my point of view, rounding a float's mantissa is meaningless by design as it's encoded as binary fractions, not as decimals. The PRs call the additional digits \"arithmetic noise\", but we cannot remove that noise we just push it further away behind more zeroes, e.g. 3.3333333333...4 becomes something like 3.330000000000...03232434\r\n\r\nIf users want values rounded this way, the transform processor is a better place, where it can be implement an OTTL function. Receivers should always optimize for accuracy of the emitted values not for their representation.\r\n\r\nI'd even suggest we also revisit #46153. @braydonk WDYT?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -1872,7 +1808,6 @@ "review_state": null, "root_timestamp": "2026-06-05T07:35:35Z", "body": "During the System SIG on 04/06/2026 we discussed whether https://github.com/open-telemetry/opentelemetrycollector-contrib/pull/46153 could be moved into a processor or OTTL function. The core challenge is that the PR does not apply fixed-decimal truncation, it derives the correct number of significant digits from the magnitude of the original integer inputs, so the rounding is context-aware and cannot be reconstructed once the float64 is emitted.\r\n\r\n Two cases:\r\n - `precision.Ratio`: used for utilization ratios, it could in principle become an OTTL function, but it would require the receiver to expose raw usage integers, so the function has both operands available. This means every utilization metric would need explicit pipeline configuration (usage values) to compute and store the result into a new utilization metric, making default setups verbose and leaving users who don't opt in with the values with false precision.\r\n - `precision.Scale`: cannot be moved downstream. The rounding corrects noise introduced by the receiver's own time-unit conversion (e.g. ticks → seconds). That information is lost once the float64 is emitted, a processor has no way to recover it. The only alter ...[truncated]", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -1901,7 +1836,6 @@ "review_state": null, "root_timestamp": "2026-06-22T08:12:42Z", "body": "Not stale. During System WG SIG on 04/06/2026 it has discussed if there was any gopsutil feature to retrieve the raw cputicks using the package. It seems that now at the moment, but it could be extended using the `Ex` structs strategy, see https://github.com/shirou/gopsutil#ex-struct-from-v4245 As this PR is under a feature flag, the cpu ticks could be added into an `Ex` gopsutil struct in a follow-up PR", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -1930,7 +1864,6 @@ "review_state": null, "root_timestamp": "2026-07-15T10:18:12Z", "body": "Friendly reminder for other codeowners @braydonk and @dmitryax", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -1959,7 +1892,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-05-14T10:32:14Z", "body": "Thanks for working on this @MohamedElDegwi! I'm still running a few tests but it looks good so far. To make reviews easier, could you please split these changes into separate PRs? one for the grammar, and another one for functions and contexts data paths? This functionally should be backward compatible so we shouldn't have any issues doing that. \n\nAs I mentioned, we also need to standardized the existing OTTL's context paths that are currently returning bare Go's slices instead of `pcommon.Slice`.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -1988,7 +1920,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-05-25T12:39:14Z", "body": "Thanks @MohamedElDegwi! I think we're almost there 🎉", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -2017,7 +1948,6 @@ "review_state": null, "root_timestamp": "2026-06-04T12:33:34Z", "body": "I'm sorry for not checking it first, but I just realized while reviewing this PR that this standardization might limit our list support, and what we could possibly express as slices in the future. From one hand, it should be fine as the `pcommon.Slice` only uses the pdata standard data types, and we're constrained to that, but on the other hand, OTTL value model is wider than `pcommon.Slice`, supporting types like `time.Time`, `time.Duration`, `pcommon.SpanID`, `pcommon.TraceID`, and `pprofile.ProfileID`. If we move forward with this, we wouldn't be able create lists of them anymore (e.g `[Duration(\"1s\"), Duration(\"2m\")]`), even if the values are only being used to pass as arguments to functions calls/context paths.\r\n\r\nWDYT @TylerHelmuth @evan-bradley @bogdandrutu? I'm not sure anymore if we should continue with this change, and I'm curious to also hear your take on that.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -2046,7 +1976,6 @@ "review_state": null, "root_timestamp": "2026-07-09T19:14:43Z", "body": "I don't think we should limit our ability to make lists like `[Duration(\"1s\"), Duration(\"2m\")]` especially with the new lambda function support. It seems likely that a slice of non-pdata types could be returned from one of those functions. \r\n\r\nI like @meldegwi idea of trying to support both as long as it is not underperformant.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -2075,7 +2004,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-05-04T17:31:07Z", "body": "needs `make gotidy`", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -2104,7 +2032,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-05-08T12:28:09Z", "body": "given that the old name still works, I think this is fine, but do we have a timeline for the full removal of the old name? we need to make sure that nothing will break on our side", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -2133,7 +2060,6 @@ "review_state": null, "root_timestamp": "2026-05-11T15:24:56Z", "body": "Please fix conflicts", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -2162,7 +2088,6 @@ "review_state": null, "root_timestamp": "2026-05-25T07:23:27Z", "body": "> given that the old name still works, I think this is fine, but do we have a timeline for the full removal of the old name? we need to make sure that nothing will break on our side\r\n\r\n> @andrzej-stencel, any ETA on when the old name will be fully removed?\r\n\r\nThere is currently no specific plan on when to remove the deprecated names. I believe the consensus among the maintainers is to keep them for as long as possible. There's definitely no short-term plan to remove them.\r\n\r\nThis definitely needs a discussion on its own.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -2191,7 +2116,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-28T15:20:42Z", "body": "~The documentation in is very helpful. I suggest adding a note indicating which version introduced this rename to make it easier for users to track when to expect the deprecation.~", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -2220,7 +2144,6 @@ "review_state": null, "root_timestamp": "2026-06-20T01:50:03Z", "body": "Please address conflicts.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -2249,7 +2172,6 @@ "review_state": null, "root_timestamp": "2026-05-30T01:15:37Z", "body": "So after a bit of reading around I found https://pkg.go.dev/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp#WithEndpointURL which I think does exactly what this PR implements. \r\nYou'd still need to join the extracted path with --otlp-http-url-path, but the parsing itself doesn't need to be reimplemented.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -2278,7 +2200,6 @@ "review_state": null, "root_timestamp": "2026-05-31T06:28:14Z", "body": "Please fix the conflict and mark ready for review again.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -2307,7 +2228,6 @@ "review_state": null, "root_timestamp": "2026-06-08T04:48:04Z", "body": "@jamesmoessis please review as codeowner", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -2336,7 +2256,6 @@ "review_state": null, "root_timestamp": "2026-07-17T02:08:59Z", "body": "Hey @ben-trans I had a look into your issue, it seems like it still occurs even when I rebase your branch on main, use the latest mdatagen. So I've raised a bug: https://github.com/open-telemetry/opentelemetry-collector/issues/15592.\r\n\r\nIn the meantime to unblock yourself, I _think_ you can still register the featuregate by hand by copying whatever the generated code is doing (see other components in the repo for prior art). I apologise for the inconvenience, hopefully it can be fixed soon.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -2365,7 +2284,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-06-06T22:56:45Z", "body": "Thank you @KyriosGN0! Some notes that worth taking a look.\r\n\r\nIt is also missing the README.md update about this new feature. I also think it's worth taking a look at the detectors, which also do native retries (like EC2), and mark the options there as deprecated for future removal (also creating follow-up issues for that)", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -2394,7 +2312,6 @@ "review_state": null, "root_timestamp": "2026-06-17T21:34:04Z", "body": "@KyriosGN0, please fix the conflicts.\r\n\r\nIt is enough to create the issues to be tackled later 👍", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -2423,7 +2340,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-13T20:40:09Z", "body": "LGTM! Thank you!\n\n@dashpole, please give your review when you get a chance 🙏", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -2452,7 +2368,6 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-07-10T00:02:11Z", "body": "Waiting for sponsor", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -2481,7 +2396,6 @@ "review_state": null, "root_timestamp": "2026-07-18T15:16:59Z", "body": "@sigmaris , if an intermediate cert has the same SAN / issuer / target I think we will end up with duplicate datapoints because we have no way to distinguish the intermediate cert from the leaf cert. Perhaps we need to add a fingerprint attribute or something? I am not sure, I have not deeply thought about it but there is a risk in some circumstances when intermediate certs \"look\" substantially similar to the leaf certificate.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -2510,7 +2424,6 @@ "review_state": null, "root_timestamp": "2026-07-20T15:46:53Z", "body": "Adding a fingerprint should not add any cardinality except for the case where we have clashing series. I don't think we can assume we won't hit instances where the intermediate and leaf certs look the same, it is mechanically possible and this receiver will be installed and enabled on millions of machines, so it is likely that we will hit this edge case I think. \r\n\r\nIs there a conventional hash or fingerprint that is part of the TLS spec we can use to distinguich clashing series, or would we just need to roll our own hash?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -2539,7 +2452,6 @@ "review_state": null, "root_timestamp": "2026-07-20T16:56:46Z", "body": "> I think it'd be better to use an SHA256 digest of the DER encoded certificate. \r\n\r\nIf we use this then wouldn't we have the same fingerprint for each cert in the chain? I think the entire chain is encoded in the DER, right?\r\n\r\n> @michael-burt Should the fingerprint be added by default as an attribute to all certificate metrics, or only when scrape_all_certs is enabled (and so there could be more than once certificate scraped)?\r\n\r\nWe could leave it off by default in all cases, document its existence and rationale, and give users the ability to enable the metric via config if they have a need for it.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -2568,7 +2480,6 @@ "review_state": null, "root_timestamp": "2026-07-20T18:00:12Z", "body": "> No, it's possible as each individual certificate in a chain is represented by a x509.Certificate and we can get its DER encoded bytes from [its Raw attribute](https://pkg.go.dev/crypto/x509#Certificate), and calculate the SHA256 of those bytes (only). So calculating a specific fingerprint for each individual certificate is doable.\r\n\r\nnice, we should add a test for uniqueness I think, I am wondering if we would need to hash some positional arg or something if other fields are identical, hopefully not though.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -2597,7 +2508,6 @@ "review_state": null, "root_timestamp": "2026-06-23T12:55:47Z", "body": "@nissessenap - thanks for the contribution. please fix the govet errors.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -2626,7 +2536,6 @@ "review_state": null, "root_timestamp": "2026-07-12T10:07:42Z", "body": "want to check-in on this. looks like builds were failing after the last fix. let's pull latest from main and kick off builds to make sure things pass. \r\n\r\nty for your patience on this!", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -2655,7 +2564,6 @@ "review_state": null, "root_timestamp": "2026-07-13T16:19:39Z", "body": "@nissessenap Please fix the lint issues: https://github.com/open-telemetry/opentelemetry-collector-contrib/actions/runs/28465548065/job/84364801745?pr=48539\r\n\r\nAnd pull latest from main.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -2684,7 +2592,6 @@ "review_state": null, "root_timestamp": "2026-07-22T12:35:59Z", "body": "Looks like the supervisor test failure is unrelated to this pull request.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -2713,7 +2620,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-09T23:54:59Z", "body": "All the changes make sense to me, I'll have the code owners give their final approval first.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -2742,7 +2648,6 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-07-18T07:03:55Z", "body": "Config schemas are out of date, you can run this - `make generate-schemas` and push the changes!", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -2771,7 +2676,6 @@ "review_state": null, "root_timestamp": "2026-07-19T05:56:05Z", "body": "/workflow-approve\r\n\r\nEDIT: Can someone approve the workflows here, ig I don't have the required permissions.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -2800,7 +2704,6 @@ "review_state": null, "root_timestamp": "2026-07-28T05:40:40Z", "body": "I think it would make more sense if we had a separate PR for `internal/aws` component and focus this one only on `awscloudwatchlogs` exporter.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -2829,7 +2732,6 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-05-21T17:17:44Z", "body": "I would really prefer to avoid this precheck hack. Is there no other binary we can use to probe this? The bbolt cli has some recovery related commands in it: https://github.com/etcd-io/bbolt/tree/main/cmd/bbolt/command. Surely one of them is immune to this panic.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -2858,7 +2760,6 @@ "review_state": null, "root_timestamp": "2026-05-25T16:11:55Z", "body": "I think we should add something to support last released version until https://github.com/etcd-io/bbolt/issues/1190 is finished. We can create a follow up pr to migrate when that happens.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -2887,7 +2788,6 @@ "review_state": null, "root_timestamp": "2026-05-25T16:42:11Z", "body": "If what we need right now is spawning a collector subprocess with a secret init function, then I'm against and would rather wait for bbolt release.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -2916,7 +2816,6 @@ "review_state": null, "root_timestamp": "2026-06-02T08:30:23Z", "body": "How do you guys feel to pinning bbolt version to https://github.com/etcd-io/bbolt/releases/tag/v1.5.0-rc.0 or https://github.com/etcd-io/bbolt/releases/tag/v1.5.0-beta.0?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -2945,7 +2844,6 @@ "review_state": null, "root_timestamp": "2026-06-02T10:10:13Z", "body": "> How do you guys feel to pinning bbolt version to https://github.com/etcd-io/bbolt/releases/tag/v1.5.0-rc.0 or https://github.com/etcd-io/bbolt/releases/tag/v1.5.0-beta.0?\r\n\r\nI'd rather wait until there's a final release. But we can definitely work on PRs using the beta in the meantime.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -2974,7 +2872,6 @@ "review_state": null, "root_timestamp": "2026-07-03T07:01:37Z", "body": "@briandavis-viz bbolt was released. I think we are ok to go with this PR.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -3003,7 +2900,6 @@ "review_state": null, "root_timestamp": "2026-07-20T08:55:15Z", "body": "@briandavis-viz are you still interested on working on the issue? I can take your commits and continue if you are ok with that.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -3032,7 +2928,6 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-06-24T20:30:14Z", "body": "Looks good, had two requests to get checks passing", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -3061,7 +2956,6 @@ "review_state": null, "root_timestamp": "2026-07-08T19:24:13Z", "body": "> Thanks for the reviews @VihasMakwana @antonblock, should be good to go now\r\n\r\nIt looks like the expected metrics in integration tests need to be updated", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -3090,7 +2984,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-05-25T10:45:45Z", "body": "### 💡 Codex Review\n\nHere are some automated review suggestions for this pull request.\n\n**Reviewed commit:** `117f0ded48`\n \n\n
ℹ️ About Codex in GitHub\n
\n\n[Your team has set up Codex to review pull requests in this repo](https://chatgpt.com/codex/cloud/settings/general). Reviews are triggered when you\n- Open a pull request for review\n- Mark a draft as ready\n- Comment \"@codex review\".\n\nIf Codex has suggestions, it will comment; otherwise it will react with 👍.\n\n\n\n\nCodex can also answer questions or update the PR. Try commenting \"@codex address that feedback\".\n \n
", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -3119,7 +3012,6 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-05-27T06:01:04Z", "body": "Thanks for tackling this, the testcontainers scaffolding is solid and this is the right direction. A few things need addressing before merge:\n\n1. The test isn't actually exercising `otel-v1`. I pulled the branch and ran go test tags=integration -run TestIntegration_OtelV1Mapping it passes in ~32s, but the resulting index uses the ss4o schema (fields like attributes.data_stream.*, status.code as text) rather than otel-v1 (which should have serviceName, durationInNanos, traceGroup, etc.). This happens because the MappingOTelV1 enum lives in #48612, which hasn't merged yet. Without it, cfg.Mode = \"otel-v1\" silently falls through to the default ss4o encoder. The green test is a false positive.\n\n2. The timestamp assertion encodes the bug it should catch. Issue #48615 exists because OpenSearch demotes date_nanos → date when no index template is present. Asserting typeValue == \"date\" means the test passes precisely when the precision regression is active the opposite of what we want.\n\n3. PR description and changelog overstate scope. The mention of \"unit tests validating successful requests, permanent errors, and retryable errors\" describes tests already on main this PR renames integration ...[truncated]", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -3148,7 +3040,6 @@ "review_state": null, "root_timestamp": "2026-06-02T22:17:20Z", "body": "Hi @harshitt1, the Otel-v1 PR is merged now can you please rebase your PRand update tests for otel-v1 logs and traces ingested in opensearch? also another question: does the integration test run with the github CI?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -3177,7 +3068,6 @@ "review_state": null, "root_timestamp": "2026-07-27T10:55:54Z", "body": "Sorry for the delay, I left a comment on the related issue", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -3206,7 +3096,6 @@ "review_state": null, "root_timestamp": "2026-05-29T18:16:47Z", "body": "_Converting to draft for now to [discuss further](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/48344#issuecomment-4578438607) in issue._", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -3235,7 +3124,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-06-26T21:42:24Z", "body": "Please add more thorough testing for this that proves the full text comes through as expected after obfuscate when enabled", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -3264,7 +3152,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-05-28T12:04:04Z", "body": "I like where this is going, thank you for working on this!\nAdded minor comments/questions.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -3293,7 +3180,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-05-29T07:55:26Z", "body": "Thank's for the PR. I would like to understand first how this will affect the user experience specially when we are deprecating existing settings that were used to server specific needs. In addition, we might need to split the PR into smaller ones once we have agreement, i.e. one for the internal lib's changes and 2 follow ups to surface the changes in the components (if needed). \n\nBTW, I wonder why the components' code need to change. Shouldn't that change only be an implementation detail that only affect the underlying internal library without changing anything on the components' API?\n\n/cc @dhruv-shah-sumo please take look since this affects the `storage` part you recently added.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -3322,7 +3208,6 @@ "review_state": null, "root_timestamp": "2026-06-02T08:48:44Z", "body": "> Update the k8sobjects receiver to use the newly implemented interface instead of the old one.\r\n\r\nThank's @kangyili, can you provide a short update/summary of what is the proposal for this so as we don't lose that information? (the 2 threads that it was discussed will be resolved, so let's get to the summary of those discussions)\r\n\r\nI would like to clarify if we will have breaking changes in the behaviour of the `k8sobjects` receiver and if so which those will be.\r\nAlso will we need to update the `k8sevents` receiver accordingly too? \r\n\r\nOnce we are aligned on the above, I will try to find the time to review the first PR soon.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -3351,7 +3236,6 @@ "review_state": null, "root_timestamp": "2026-06-05T10:13:18Z", "body": "Thank's for the [summary](https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/48663#issuecomment-4601249613) @kangyili! I think I agree with that proposal which essentially only removes the `resource_version` option. Since we can ensure that the user experience is not changed other than this removal and that checkpoints are honoured through the `storage` setting, I support the proposal.\r\n\r\nI don't think we need to make this new implementation configurable and an implementation plan like https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/48663#issuecomment-4600125637 would be fine. I'm not sure how much time I will have in the following weeks to review this but I don't want to block this if other code-owners are available to review :).", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -3380,7 +3264,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-06-19T11:05:54Z", "body": "thanks. The last time I tried implementing sharding locally, I was not able to prove that it resolves a bottleneck. Do you have benchmark numbers that would demonstrate the difference?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -3409,7 +3292,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-01T18:01:22Z", "body": "Thanks! At a high level I like the idea of sharding, as we'll eventually need that because of perf. On the other hand I appreciate reasoning behind the single background loop introduced in #43671 by code owner @csmarchbanks which made the code much simpler.\n\nIf you get buy in from @csmarchbanks it'll help drive the PR forward. The bottom line is we'll need to be conscious about the additional complexity from this design decision.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -3438,7 +3320,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-01T22:53:05Z", "body": "At a high level I am also happy with a sharded approach. That was one of my ideas for how to improve the TSP after the last refactor if I came across additional bottlenecks.\n\nThat said, my initial thought is to have sharding be fairly transparent to users. Is there a reason anyone should run in non-sharded mode if throughput is better when sharded? Ideally we could even calculate and rebalance shards dynamically in response to load.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -3467,7 +3348,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-13T14:46:22Z", "body": "Just a quick comment, I'll get a full review in soon! Happy to have auto sizing of shards be in a follow up PR.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -3496,7 +3376,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-16T17:51:35Z", "body": "thanks! if we're going to add counters for operation errors, what about adding counter for operations with outcome={success,failure} dimension? Then we can also infer that things are working even when error stays 0", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -3525,7 +3404,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-23T14:29:34Z", "body": "lgtm thanks!", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -3554,7 +3432,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-28T15:56:13Z", "body": "The fix correctly addresses the nil-pointer dereference in the method by delegating to , which is designed to be nil-safe. The addition of and provides appropriate regression testing for the reported issue. The changes look correct and well-justified.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -3583,7 +3460,6 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-07-27T14:26:56Z", "body": "Would you be able to add a test? `metricRows` currently has no direct coverage — every scraper test uses `fakeDbClient`, so the real query path (and this fix) isn't exercised. A go-sqlmock-based test asserting ExpectationsWereMet() would fail if the rows aren't closed and would guard against regressions.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -3612,7 +3488,6 @@ "review_state": null, "root_timestamp": "2026-07-17T16:14:45Z", "body": "It'd be good to get this into main before the next release so we consolidate changes in one release.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -3641,7 +3516,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-20T16:58:48Z", "body": "Would like to see codeowners' thoughts on multi-cluster behavior, but LGTM.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -3670,7 +3544,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-27T14:57:56Z", "body": "Thanks @dmytrohysht - I took a look at this alongside #49721 and I think this looks like the right approach.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -3699,7 +3572,6 @@ "review_state": null, "root_timestamp": "2026-07-20T13:37:13Z", "body": "@darkknight8670 Does the older database keep working in case new one has corrupted?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -3728,7 +3600,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-18T10:16:57Z", "body": "### 💡 Codex Review\n\nHere are some automated review suggestions for this pull request.\n\n**Reviewed commit:** `a0137cea99`\n \n\n
ℹ️ About Codex in GitHub\n
\n\n[Your team has set up Codex to review pull requests in this repo](https://chatgpt.com/codex/cloud/settings/general). Reviews are triggered when you\n- Open a pull request for review\n- Mark a draft as ready\n- Comment \"@codex review\".\n\nIf Codex has suggestions, it will comment; otherwise it will react with 👍.\n\n\n\n\nCodex can also answer questions or update the PR. Try commenting \"@codex address that feedback\".\n \n
", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -3757,7 +3628,6 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-07-21T13:09:31Z", "body": "Hi @harshitt13, Nice work overall, just a few comments for your consideration :)", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -3786,7 +3656,6 @@ "review_state": null, "root_timestamp": "2026-07-19T16:16:56Z", "body": "@floze-the-genius you havent check the `I, a human, wrote this pull request description myself` box and `easycla` sign in is also pending.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -3815,7 +3684,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-21T09:07:08Z", "body": "Shouldn't we update the README.md with the options and support for `http` config parameter?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -3844,7 +3712,6 @@ "review_state": null, "root_timestamp": "2026-07-27T18:45:25Z", "body": "I generally prefer adding the new field without any restrictions, and putting the removal of the old field behind a feature gate to deprecate it slowly.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -3873,7 +3740,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-20T16:59:24Z", "body": "> Generated code is out of date for group 'extension', please run \"make generate\" and commit the changes in this PR.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -3902,7 +3768,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-21T18:05:04Z", "body": "LGTM", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -3931,7 +3796,6 @@ "review_state": null, "root_timestamp": "2026-07-21T08:43:05Z", "body": "@dpaasman00 need a rebase here after the extension host interface fix.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -3960,7 +3824,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-27T15:27:47Z", "body": "### 💡 Codex Review\n\nHere are some automated review suggestions for this pull request.\n\n**Reviewed commit:** `5444f3db4f`\n \n\n
ℹ️ About Codex in GitHub\n
\n\n[Your team has set up Codex to review pull requests in this repo](https://chatgpt.com/codex/cloud/settings/general). Reviews are triggered when you\n- Open a pull request for review\n- Mark a draft as ready\n- Comment \"@codex review\".\n\nIf Codex has suggestions, it will comment; otherwise it will react with 👍.\n\n\n\n\nCodex can also answer questions or update the PR. Try commenting \"@codex address that feedback\".\n \n
", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -3989,7 +3852,6 @@ "review_state": null, "root_timestamp": "2026-07-22T08:46:12Z", "body": "PTAL into CI issues 👍", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -4018,7 +3880,6 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-07-22T09:36:06Z", "body": "Thanks for the fix — this addresses a real crash path our team has hit. A few notes:\n\nCorrectness: The `LEFT JOIN → INNER JOIN` change is semantically equivalent here since the existing `WHERE datname IS NOT NULL` clause was already filtering out unmatched rows. The `nil` guard in `scraper.go` is good defense-in-depth.\n\nCI: The misspell linter is failing on \"defence\" (line 435) — needs to be \"defense\" for US English.\n\nSuggestion: Adding a unit test that exercises the nil-guard code path (e.g., a synthetic row with a nil db.namespace key) to prove the skip behavior. The `WHERE datname IS NOT NULL` clause in the template is also now redundant after the `INNER JOIN` change — fine to leave but worth a comment or cleanup.\n\nI'll revisit once CI is green. Thanks!", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -4047,7 +3908,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-23T13:05:50Z", "body": "thanks for addressing internal feedback, happy to approve", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -4076,7 +3936,6 @@ "review_state": null, "root_timestamp": "2026-07-23T08:22:45Z", "body": "this feature would be awesome 👍", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -4105,7 +3964,6 @@ "review_state": null, "root_timestamp": "2026-07-23T09:29:12Z", "body": "Thanks for the PR @leocarrozzo, however, the component still does not have a sponsor, and that is mandatory for adopting it.\r\n\r\nPlease also look into the [new components donation guidelines](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/docs/new-components.md), and this PR does not seem to follow them.\r\n\r\nI suggest you join a collector SIG meeting to discuss the issue and ask for sponsorship and/or reach out to the team in the #otel-collector-dev Slack channel.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -4134,7 +3992,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-23T13:10:14Z", "body": "Can we please enrich the available E2E tests for oracle cloud with this new attribute detection? https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/processor/resourcedetectionprocessor/testdata/e2e/oraclecloud", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -4163,7 +4020,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-24T10:00:37Z", "body": "LGTM, thank you! \n\n@geekdave, we need your review here please 🙏", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -4192,7 +4048,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-28T16:56:08Z", "body": "I would also like to see a codeowner take a look", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -4221,7 +4076,6 @@ "review_state": null, "root_timestamp": "2026-07-28T18:48:20Z", "body": "@bgola-signalfx Thanks for the contribution! I appreciate your help in helping to make Oracle's Resource Detection Processor more compliant with the semantic conventions. A few questions and asks:\r\n\r\n1. The PR description has a \"tracking issue\" link that just points back to the same PR. Could you please open an issue that describes the problem being fixed? Or if the PR is self-documenting, better just to remove that section rather than have a self-referential PR link.\r\n\r\n3. I noticed that the Oracle Cloud detector already provides `host.id` which has the same source for its value:\r\n\r\n```\r\n\td.rb.SetCloudResourceID(compute.HostID)\r\n//...snip...\r\n\td.rb.SetHostID(compute.HostID)\r\n```\r\n\r\nI'm curious about the precedent for this. I noticed that AWS, GCP, and Azure all set only `host.id` but not `cloud.resource_id`. Interestingly, I do see that IBM Cloud does emit both attributes, but they come from different sources so they do not get set to the same value ([see IBM's code here](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/processor/resourcedetectionprocessor/internal/ibmcloud/vpc/ibmcloud_vpc.go#L66-L67)).\r\n\r\nWould be good to call out if this duplica ...[truncated]", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -4250,7 +4104,6 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-07-28T18:50:44Z", "body": "See above comment for requested changes/clarifications. Thanks!", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -4279,7 +4132,6 @@ "review_state": null, "root_timestamp": "2026-07-25T15:19:39Z", "body": "Please fix the linter issue.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -4308,7 +4160,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-24T12:31:49Z", "body": "looks good", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -4337,7 +4188,6 @@ "review_state": null, "root_timestamp": "2026-07-27T15:12:11Z", "body": "@yaten2302, can you review it as a codeowner? Thank you 🙏", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -4366,7 +4216,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-28T18:30:22Z", "body": "I'd strongly prefer to separate the `host.name` change from the rest of this PR, unless there's some reason it must be included here.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -4395,7 +4244,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-28T08:49:11Z", "body": "LGTM. Verified locally: `GOOS=windows go build/vet ./...` is clean, `TestComputeServiceInstanceID` passes with the new ComputerName-priority cases, and the golden files match (`CustomServer:1433` for the named instance; host-dependent value correctly ignored in the default test). The `ComputerName` branch is safely ordered below `DataSource/Server,` so direct-connection behavior is unchanged.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -4424,7 +4272,6 @@ "review_state": null, "root_timestamp": "2026-07-28T08:54:31Z", "body": "@avadla also can you edit MR desc to be:\r\n\r\n\"Fixes {URL of Issue}\" on the same line, that way the issue is linked to the MR and it auto closes the issue once MR is merged?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -4453,7 +4300,6 @@ "review_state": null, "root_timestamp": "2026-07-28T19:41:03Z", "body": "@avadla done now, i think there were upstresam issues earlier that seem to have resolved, you can rerun the jobs by posting a comment on this mr with ```/rerun```", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -4482,7 +4328,6 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-07-28T14:22:54Z", "body": "Few comments.\n\n@AmariahAK I also noticed that you didn't follow the PR template, which also indicates to me that the PR description was written by your agent and not by yourself. \n\nPlease update the PR description so that it matches the template and ensure it's written in your own words, according to the [GenAI policy](https://github.com/open-telemetry/community/blob/main/policies/genai.md).", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -4511,7 +4356,6 @@ "review_state": null, "root_timestamp": "2026-07-28T18:27:10Z", "body": "Needs \"make generate-schemas\"", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -4540,7 +4384,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-28T14:50:26Z", "body": "thanks. qq about persistent queue test, the mock pq looks like the in memory queue implementation to me and doesn't seem to provide additional coverage over using in memory queue, or the actual persistent queue test coverage in https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/exporter/elasticsearchexporter/integrationtest .", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -4569,7 +4412,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-28T17:12:15Z", "body": "approving to unblock, thanks for adding the tests. One comment about the allowMissingDocs", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -4598,7 +4440,6 @@ "review_state": null, "root_timestamp": "2026-03-29T11:43:23Z", "body": "> shouldn't we try to guard against self-referencing IEnumerables\r\n\r\nI think so, otherwise it would cause a stack overflow? Could just add a recursion limit to track such occurences.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -4627,7 +4468,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-04-23T14:41:10Z", "body": "Could you also extend the fuzz tests to cover this functionality please?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -4656,7 +4496,6 @@ "review_state": null, "root_timestamp": "2026-05-20T08:58:25Z", "body": "We are having trouble sending complex nested objects to an OpenTelemetryCollector. I have tested the fork of @juliuskoval by connecting NLog.Target.OpenTelemetryProtocol with the OpenTelemetry-dotnet. I only need to traslate the object into IEnumerable>, and the everything works. I would kindly ask you to merge this and release new version. This would be really important for us.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -4685,7 +4524,6 @@ "review_state": null, "root_timestamp": "2026-05-20T09:10:45Z", "body": "@SalvoDeveloper I'm waiting for at least one other approving review from one of the other maintainers before merging this. Once merged it will be part of the next release (which has no specific date at this time).\r\n\r\nAfter it's merged you can use consume prerelease builds from [MyGet](https://github.com/open-telemetry/opentelemetry-dotnet#releases) if you can't wait until then.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -4714,7 +4552,6 @@ "review_state": null, "root_timestamp": "2026-06-09T00:36:06Z", "body": "Before merging, I'd like us to align on:\r\n \r\n 1. Is implicit type-sniffing the path we want, or should we wait for typed AnyValue API so we don't ship two redundant contracts?\r\n 2. How do we address Blanch's reference-aliasing concern from #6052 - should this require SDK-level deep-copy on span end?\r\n \r\n Strong support for the underlying use case, but want to avoid locking in behavior we can't revisit.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -4743,7 +4580,6 @@ "review_state": null, "root_timestamp": "2026-06-23T08:57:03Z", "body": "These changes seem reasonable to me. Raj is on PTO until August so let's wait on another +1 from another maintainer.\r\n\r\nCan you also update the CHANGELOGs as-appropriate please?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -4772,7 +4608,6 @@ "review_state": null, "root_timestamp": "2026-04-17T04:17:53Z", "body": "@utpilla Could you please review this PR when you have a moment?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -4801,7 +4636,6 @@ "review_state": null, "root_timestamp": "2026-04-17T17:57:34Z", "body": "Before merging, I think we need to answer a core question: **how should the Metrics SDK handle non-finite values (NaN, +Inf, -Inf)?**\r\n\r\nRight now, we don't seem to have a deliberate answer. The behavior varies by instrument. Counters get their sum poisoned, explicit bucket histograms put `NaN` in the `+Inf` bucket, exponential histograms filter `NaN` from buckets but still corrupt `sum`/`min`/`max`, and gauges just store `NaN` directly. None of this is documented, and this PR would change behavior for some instruments but not others.\r\n\r\nThe spec ([Numerical limits handling](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#numerical-limits-handling:~:text=the%20SDK%20needs%20to%20handle%20NaNs%20and%20Infinites.)) says the SDK MUST handle `NaN`/`Inf` but leaves the *how* unspecified. For exponential histograms specifically, the [spec](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#base2-exponential-bucket-histogram-aggregation:~:text=Implementations%20SHOULD%20NOT%20incorporate%20non%2Dnormal%20values%20(i.e.%2C%20%2BInf%2C%20%2DInf%2C%20and%20NaNs)%20into%20the%20sum%2C%20min% ...[truncated]", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -4830,7 +4664,6 @@ "review_state": null, "root_timestamp": "2026-06-08T22:47:25Z", "body": "@Kielek how you are planning to handle this PR?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -4859,7 +4692,6 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-04-30T15:10:13Z", "body": "See\nhttps://github.com/open-telemetry/opentelemetry-dotnet/pull/7227/changes#r3168921622\n\nLet me know if I misunderstood the fix.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -4888,7 +4720,6 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-05-04T18:34:15Z", "body": "The current iteration looks very promising (thanks! This was indeed a hard problem!). \nRequesting changes to address below comment\nhttps://github.com/open-telemetry/opentelemetry-dotnet/pull/7227/changes#r3183554865\n\nAlso, we need to vastly improve test coverage when doing this fix- lot of potential edge cases. (I spotted only one about Exemplar value, but could be more)", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -4917,7 +4748,6 @@ "review_state": null, "root_timestamp": "2026-05-27T06:14:41Z", "body": "Asked Codex/GPT5.5 to check this PR.\r\nBoth of them marked as P1. Could you please double check this feedback?\r\n\r\n# Review Notes and Unit Test Proposals for PR #7227\r\n\r\nI think there are two correctness risks worth covering with targeted unit tests.\r\n\r\n## Issue 1: Delta temporality is not correct when filtered streams change\r\n\r\nThe current fix spatially aggregates async cumulative counter values by updating the already-filtered `MetricPoint` with:\r\n\r\n```text\r\nexisting running value + incoming measurement value\r\n```\r\n\r\nThat works for simple cumulative export cases where the same underlying streams are reported every cycle. The problem is delta export. `MetricPoint.TakeSnapshot(outputDelta: true)` computes the delta from the collapsed aggregate:\r\n\r\n```text\r\ncurrent collapsed aggregate - previous collapsed aggregate\r\n```\r\n\r\nThat is not equivalent to summing per-stream deltas if one original measurement stream disappears or reappears between callbacks.\r\n\r\nExample:\r\n\r\n```text\r\nCollection 1:\r\n A = 10\r\n B = 10\r\n Collapsed value = 20\r\n\r\nCollection 2:\r\n A = 15\r\n B is absent\r\n Collapsed value = 15\r\n```\r\n\r\nIf the SDK diffs collapsed values, delta export becomes:\r\n\r\n```text\r\n15 - 20 = -5\r ...[truncated]", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -4946,7 +4776,6 @@ "review_state": null, "root_timestamp": "2026-05-30T14:14:26Z", "body": "The CLA needs to be signed.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -4975,7 +4804,6 @@ "review_state": null, "root_timestamp": "2026-06-09T19:42:20Z", "body": "I haven't review this PR in detail but one comment to think though:\r\n1. I am unsure if we need to let end users worry about this option - it is adding more cognitive overhead to endusers when setting up OTel.\r\n2. it may be better to change the default itself to follow this. This will cause increased jitter due to re-allocations as things get warmed up, and then settles. But once in steady state, no re-allocation needed, as we don't ever reclaim the underlying array from the Dictionary.\r\n\r\nWould be good if this can be discussed in SIG too.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -5004,7 +4832,6 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-06-11T16:04:59Z", "body": "Please use the SIG meeting to discuss this more.\nI don't think we should expand the configuration to add this feature - its forcing end users to make yet another decision.\nIf the original pre-allocation is not preferred, then it is better to change that itself and make it the default behavior.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -5033,7 +4860,6 @@ "review_state": null, "root_timestamp": "2026-06-11T16:05:53Z", "body": "@saguiitay Would you be able to join the community call to discuss more on this? (I won't be able to join, unfortunately, but I'll share some thoughts in the issue itself)", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -5062,7 +4888,6 @@ "review_state": null, "root_timestamp": "2026-06-11T17:44:39Z", "body": "That's probably not workable for you then, as I think that would make the SIG at 2200 for you (it's usually at 1900 for me and I'm in the UK).", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -5091,7 +4916,6 @@ "review_state": null, "root_timestamp": "2026-06-16T10:59:03Z", "body": "I've added it to [today's SIG agenda](https://docs.google.com/document/d/1yjjD6aBcLxlRazYrawukDgrhZMObwHARJbB9glWdHj8/edit?usp=sharing).", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -5120,7 +4944,6 @@ "review_state": null, "root_timestamp": "2026-06-24T03:55:24Z", "body": "> I've added it to [today's SIG agenda](https://docs.google.com/document/d/1yjjD6aBcLxlRazYrawukDgrhZMObwHARJbB9glWdHj8/edit?usp=sharing).\r\n\r\n@martincostello was this discussed? Can you update if any decision was made.\r\n\r\n\r\n> if it's up to me, we keep the option for a while, gather feedback from users, and only enable it by default if it's proven in production. I'd hate changing the default behavior, and getting a backlash from users that we broke them.\r\n\r\n@saguiitay Thanks, this idea makes sense. (And OTEL_DOTNET_EXPERIMENTAL_METRICS_ENABLE_LAZY_ALLOCATION approach would mean only advanced users will need to make this decision, and after evaluation, we can in future make this the default).", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -5149,7 +4972,6 @@ "review_state": null, "root_timestamp": "2026-06-24T06:58:05Z", "body": "> @martincostello was this discussed? Can you update if any decision was made.\r\n\r\nYes, Itay joined the SIG call and myself and Alan discussed it. TL;DR was Itay was going to speak to Raj about it internally and then look to make an experimental opt-in to allow for it to be tested in the wild. I forget exactly what else we discussed, but it'll be in the recording.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -5178,7 +5000,6 @@ "review_state": null, "root_timestamp": "2026-06-25T09:56:25Z", "body": "> > @martincostello was this discussed? Can you update if any decision was made.\r\n> \r\n> Yes, Itay joined the SIG call and myself and Alan discussed it. TL;DR was Itay was going to speak to Raj about it internally and then look to make an experimental opt-in to allow for it to be tested in the wild. I forget exactly what else we discussed, but it'll be in the recording.\r\n\r\nGot it. I agree with this approach. The experimental opt-in is via ENV variables right? \r\n\r\n@saguiitay Can you make the opt-in via ENV variable, and fix CI. I'll help review.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -5207,7 +5028,6 @@ "review_state": null, "root_timestamp": "2026-06-25T09:59:24Z", "body": "> The experimental opt-in is via ENV variables right?\r\n\r\nYep.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -5236,7 +5056,6 @@ "review_state": null, "root_timestamp": "2026-07-15T10:19:50Z", "body": "@saguiitay Sorry, I am getting back to this now only - could you fix conflicts, and update the PR desc to reflect the current status?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -5265,7 +5084,6 @@ "review_state": null, "root_timestamp": "2026-07-20T17:17:46Z", "body": "Thanks for working on this! This PR would help unblock our migration to OpenTelemetry. Could it please be prioritized for review and merge?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -5294,7 +5112,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-06-17T16:30:23Z", "body": "Overall approach here looks really good to me.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -5323,7 +5140,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-06-19T12:16:44Z", "body": "This looks pretty good and it's great to see declarative config work in the .NET SDK.\n\nWould you consider splitting this before it goes out of draft? Even setting tests aside (which are most of the diff), the 2k lines of source to add the package, the YAML reader, env substitution, the model, the flat-key converter, and the IConfiguration overlay wiring all at once is overwhelming.\n\nPRs of this size are hard to review with confidence and expect a reviewer to hold the whole pipeline in their head.\n\nI think breaking into these broader areas would be helpful:\n- package skeleton + public API surface + OTEL1006 plumbing\n- YAML parse to typed model (reader, substitution, file-format validation)\n- model to IConfiguration overlay (converter + DI wiring + precedence)", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -5352,7 +5168,6 @@ "review_state": null, "root_timestamp": "2026-06-19T12:24:56Z", "body": "I'm happy to defer if the consensus is to split this up, but as I've already reviewed it all I'd rather not review three new PRs that are all the same code 😄 (even if they're the same, still got to review them).", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -5381,7 +5196,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-06-29T11:12:47Z", "body": "LGTM - just the question regarding publishing needs resolving.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -5410,7 +5224,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-06-30T07:06:27Z", "body": "Only technical check.\nSorry for late feedback, still need more time to understand full flow for this component,", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -5439,7 +5252,6 @@ "review_state": null, "root_timestamp": "2026-07-13T06:30:53Z", "body": "@cijothomas, I asked Codex /GPT 5.6 Sol. Briefly checked findings and it looks valid to me:\r\n\r\nFollowing comment is AI driven\r\n\r\n---------\r\n\r\nReviewed commit: [`2a53a4a`](https://github.com/open-telemetry/opentelemetry-dotnet/commit/2a53a4a09702fd223f701ccc3514b3ced3104f3e)\r\n\r\n## 1. Count batch success at exporter submission, not queue admission\r\n\r\nPriority: P1\r\n\r\n[`BatchLogRecordExportProcessor.cs:108`](https://github.com/open-telemetry/opentelemetry-dotnet/blob/2a53a4a09702fd223f701ccc3514b3ced3104f3e/src/OpenTelemetry/Logs/Processor/BatchLogRecordExportProcessor.cs#L108)\r\nincrements the success counter immediately after `TryExport` enqueues the log\r\nrecord.\r\n\r\nThe semantic convention says that, for simple and batching processors, a log\r\nrecord is considered processed when it has been submitted to the exporter, not\r\nwhen the corresponding export call finishes. For the batching processor,\r\nsubmission happens later on the worker thread. The current implementation can\r\ntherefore report records as successfully processed while they are still queued.\r\nIf draining does not complete, some of those records may never be submitted to\r\nthe exporter.\r\n\r\nThe successful increment should happen ...[truncated]", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -5468,7 +5280,6 @@ "review_state": null, "root_timestamp": "2026-07-14T11:35:35Z", "body": "Moving to draft until v1.44 is released.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -5497,7 +5308,6 @@ "review_state": null, "root_timestamp": "2026-07-28T18:56:55Z", "body": "How much performance impact does adding the IsEventFullNameValid check introduce? \r\nIs there a customer ask for this check?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -5526,7 +5336,6 @@ "review_state": null, "root_timestamp": "2025-10-10T16:07:13Z", "body": "> The JFR consumer API is 9+ only\r\n\r\nThat isn't completely true. Openjdk contains jfr apis backported from 11 since 8u262. It is a bit weird in the sense that the oracle jdk doesn't contain the backported apis but rather an older version of the jfr api. So actually you don't need to set the java version to 11. One way to make it build would be to use.\r\n\r\n```\r\ntasks {\r\n compileJava {\r\n sourceCompatibility = \"1.8\"\r\n targetCompatibility = \"1.8\"\r\n options.release.set(null as Int?)\r\n }\r\n}\r\n```\r\n\r\nThe issue you have happens because gradle is smart and prevents you adding a dependency that works only with 11 to code that should work with 8. You can disable it with\r\n\r\n```\r\njava {\r\n disableAutoTargetJvm()\r\n}\r\n```", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -5555,7 +5364,6 @@ "review_state": null, "root_timestamp": "2025-10-13T12:50:18Z", "body": "> @laurit hmm, interesting plot twist there, thanks! That almost but not quite works - the compiler is happy, but animal sniffer is not. I assume it's either no longer sure what version it's supposed to be testing against, or thinks jfr isn't in 8.\r\n\r\nanimal sniffer checks run agains apis supported by android, see https://github.com/open-telemetry/opentelemetry-java/blob/da295cfa3c93b73376e4c32f2dd450b2035483b3/animal-sniffer-signature/build.gradle.kts#L27-L28 I doubt that these will pass no matter what java version you use for compiling. You will probably have to disable animal sniffer (could remove https://github.com/open-telemetry/opentelemetry-java/blob/da295cfa3c93b73376e4c32f2dd450b2035483b3/exporters/otlp/profiles/build.gradle.kts#L6) for the module if you wish to use jfr or move jfr code into separate module or maybe a separate source set would also be enough to trick it (see https://github.com/open-telemetry/opentelemetry-java/blob/da295cfa3c93b73376e4c32f2dd450b2035483b3/buildSrc/src/main/kotlin/otel.animalsniffer-conventions.gradle.kts#L14)", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -5584,7 +5392,6 @@ "review_state": null, "root_timestamp": "2026-04-02T20:28:20Z", "body": "This may do what you need https://github.com/marschall/jfr-opentelemetry-bridge. Let me know what you think.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -5613,7 +5420,6 @@ "review_state": null, "root_timestamp": "2025-10-28T19:31:56Z", "body": "> This prevents the instantiation of Senders outside of the SDK.\r\n\r\nWhy?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -5642,7 +5448,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-02-17T22:59:36Z", "body": "These contracts look like a good start to me. Missing implementation corresponding spec though so need to talk about how to proceed with that.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -5671,7 +5476,6 @@ "review_state": null, "root_timestamp": "2026-04-29T21:01:49Z", "body": "> Also there is the issue of replacing a single value vs replacing a node subtree. It looks like I need two new APIs for that\r\n\r\nI agree you'll need APIs for that. Should probably be at the SDK level though (SdkConfigProvider) rather than API (ConfigProvider). API is accessible to instrumentation and we want instrumentation to be consuming config, not mucking around and editing it, right?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -5700,7 +5504,6 @@ "review_state": null, "root_timestamp": "2026-04-30T21:31:50Z", "body": "> preference? or alternative?\r\n\r\nI've sketched out a few ideas in: https://github.com/jackshirazi/opentelemetry-java/pull/1\r\n\r\nEasier to talk in code these days. \r\n\r\nMost notably:\r\n\r\n- The update API is SDK only\r\n- The update APIs are consolidated into a single `setConfig(String path, Object value)` method\r\n- Verification that when an update occurs, the update target path is either unset or that the type of the existing value matches the new value\r\n- The CAS loop notification path is simplified by just using a lock - in the process, this fixes a race condition where a listener could be notified of update _after_ a _later_ update. I.e. given update 1 at T1, update 2 at T2, listener receives update 2 then update 1, and is permanently left with out of date state", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -5729,7 +5532,6 @@ "review_state": null, "root_timestamp": "2026-06-09T19:03:15Z", "body": "Sorry for the delay. Missed that you had replied to this.\r\n\r\n> My concern with this implementation is how do I call the setConfig from an extension? GlobalOpenTelemetry.get() is returned as an obfuscated wrapper, so can't be cast to ExtendedOpenTelemetrySdk\r\n\r\nI assume you mean agent extension. Agent extensions get access to `OpenTelemetrySdk` and so would have access to ExtendedOpenTelemetrySdk.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -5758,7 +5560,6 @@ "review_state": null, "root_timestamp": "2026-06-09T19:27:20Z", "body": "I like the code here and would be happy to approve / merge it once corresponding spec is available 👍", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -5787,7 +5588,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-03-11T20:20:44Z", "body": "Couple comments but I like the direction", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -5816,7 +5616,6 @@ "review_state": null, "root_timestamp": "2026-03-12T15:30:01Z", "body": "> One thing: now that pretty print isn't on by default, there's no public API for users to actually enable it. The capability only lives on the internal builders\r\n\r\nI think you're referring to the fact that `OtlpStdoutSpanExporter` and `OtlpStdoutSpanExporterBuilder` live in internal packages. In this context, internal indicates that they're still in development and APIs are subject to breaking changes, but users willing to accept that can use:\r\n\r\n```\r\nOtlpStdoutSpanExporter.builder()\r\n .setPrettyPrint(true)\r\n .build();\r\n```\r\n\r\nAs you note, we could add a parameter to `OtlpJsonLoggingSpanExporter#create` to allow this to be configured.\r\n\r\n`OtlpStdoutSpanExporter` is supposed to be the replacement for `OtlpJsonLoggingSpanExporter`, but is still in an internal package because we're waiting for the [corresponding spec document](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/file-exporter.md) to stabilize. \r\n\r\n> Add it as a field in the declarative configuration via the ComponentProvider classes\r\n\r\nI support this.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -5845,7 +5644,6 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-03-17T20:34:43Z", "body": "The behavior of our OTLP exporters and corresponding environment variables is dictated by the spec: https://github.com/open-telemetry/opentelemetry-java/blob/main/CONTRIBUTING.md#project-scope\n\nWe have some examples of java specific programmatic configuration options, like the ability to set the executor service and proxy options. But these accommodate well established configuration expectations of network clients. I.e. the absence of options would be a glaring deficiency in the API.\n\nThis fallback endpoint is more complicated and more controversial, and so I would like to see it go through the spec before we consider adding it in opentelemetry-java. \n\nPersonally, wearing my other hat as a spec contributor, I would expect this problem to be solved through load balancing and retry against a single endpoint. I.e. a single endpoint routes to multiple backing instances. If an attempt against the first fails, it does so in a way that triggers the retry policy to execute a subsequent request, which has the opportunity to resolve a different instance.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -5874,7 +5672,6 @@ "review_state": null, "root_timestamp": "2026-04-02T16:38:39Z", "body": "Sorry for the delay - went on vacation and lost track of this. \r\n\r\n> A load balancer is a heavyweight solution when the actual need is simple: \"if this endpoint is down, try that one.\" The SDK already has the context to make this decision at export time.\r\n\r\nYou could write a delegating exporter, which accepts multiple OTLP exporters as constructor parameters and calls the second if the first fails. The OTLP exporter's CompletableResultCodes return all the information you would need for a caller to determine why the first export failed and if it should try the second or abandon: https://github.com/open-telemetry/opentelemetry-java/blob/47c970d6c2ebc145f44612d7f04005c855939f3f/exporters/common/src/main/java/io/opentelemetry/exporter/internal/http/HttpExporter.java#L111-L126\r\n\r\n> Would you be open to keeping this PR as a reference implementation while the spec discussion happens? \r\n\r\nSure no problem", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -5903,7 +5700,6 @@ "review_state": null, "root_timestamp": "2026-04-09T11:11:09Z", "body": "Thanks for the contribution!\r\n\r\nPlease run the bench before/after and add results here - see https://github.com/open-telemetry/opentelemetry-java/pull/8271 on how to do it.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -5932,7 +5728,6 @@ "review_state": null, "root_timestamp": "2026-04-22T21:51:09Z", "body": "Which ones did you try? Benchmarks collect dust and eventually get out of date, especially the ones not regularly used. Would [RecordSpanBenchmark](https://github.com/open-telemetry/opentelemetry-java/blob/main/sdk/all/src/jmh/java/io/opentelemetry/sdk/SpanRecordBenchmark.java) show the changes? That's one of the few that we run on an ongoing basis and publish results to https://open-telemetry.github.io/opentelemetry-java/benchmarks/", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -5961,7 +5756,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-05-20T07:52:15Z", "body": "Nice metadata additions. Two suggestions:\n\n**Hoist hot-loop dictionary lookups.** In `JfrExecutionSampleEventConverter.accept()` the `\"thread.name\"` key index and `KeyValueAndUnitData` are rebuilt for every sample event. Same in `JfrLocationDataCompositor.frameToLocation()` for `\"profile.frame.type\"`/`\"jvm\"` per frame. The dict dedupes so output is correct, but each call still allocates a string + `KeyValueAndUnitData`. Since the key/value pair is constant per converter, compute it once (e.g. in the constructor or lazily cached) and reuse the int index.\n\nFor the thread sample, only `threadName`/`threadNameData` vary — pre-compute the `\"thread.name\"` key index once.\n\n**Null `sampledThread`.** `recordedEvent.getValue(\"sampledThread\")` can be `null` for some ExecutionSample variants. A null guard (skip or fall back to \"unknown\") would harden the converter against truncated/synthetic events.\n\nLGTM otherwise — the `ValueTypeData` fix and frame-type attribute look right.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -5990,7 +5784,6 @@ "review_state": null, "root_timestamp": "2026-05-15T15:13:21Z", "body": "You need to run `spotlessApply` to fix some of the failures.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -6019,7 +5812,6 @@ "review_state": null, "root_timestamp": "2026-05-18T17:22:30Z", "body": "Hey I want to take a step back on this. This PR represents a precedent that we're going to jump through additional hoops to support groovy.\r\n\r\nWe don't say anything about groovy or other JVM based languages in our [versioning policy](https://github.com/open-telemetry/opentelemetry-java/blob/main/VERSIONING.md). And its not that a don't want to support these. But I don't understand what supporting entails. Groovy for example, has different class loading semantics that has runs into issues with the patterns we use to activate different capabilities based on whether or not certain dependencies are present. For example, the metrics, logs, and trace SDKs all behave differently based on whether `opentelemetry-api-incubator` is present, using techniques like [this](https://github.com/open-telemetry/opentelemetry-java/blob/main/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/SdkMeter.java#L54-L63) to detect if the module is present, and helps like [this](https://github.com/open-telemetry/opentelemetry-java/blob/main/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/IncubatingUtil.java) that interact with the standard java classloader in such a way that the incubating classes ar ...[truncated]", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -6048,7 +5840,6 @@ "review_state": null, "root_timestamp": "2026-05-19T06:10:16Z", "body": "Did you try using `@CompileStatic`? As far as I understand the only relevant change is in the signature of `setConfigProvider`. Is calling `IncubatingUtil` via reflection necessary?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -6077,7 +5868,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-06-10T17:53:32Z", "body": "A few small comments, but looks pretty good! Thanks for working on this!", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -6106,7 +5896,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-06-16T20:15:30Z", "body": "Looks like there are still some build failures from static analysis. \n\nCouple more nits to fix along with the build, but looks good to me", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -6135,7 +5924,6 @@ "review_state": null, "root_timestamp": "2026-07-13T21:16:36Z", "body": "@open-telemetry/java-approvers - heads up - this PR adds a default OTLP message / request body size of 64mb, where none currently exists. You could make the argument that this is a breaking behavior change, and that we should proceed but only once there is a env var / declarative config property to configure a different limit. (Ive opened https://github.com/open-telemetry/opentelemetry-configuration/issues/695 to add declarative config schema to configure this)\r\n\r\nPersonally, I think the lack of current limit is a bug and adding a default of 64mb is a generous limit few will hit.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -6164,7 +5952,6 @@ "review_state": null, "root_timestamp": "2026-07-14T22:32:20Z", "body": "I'm fine with the API changes in this PR", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -6193,7 +5980,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-06-25T14:13:22Z", "body": "Looking pretty good", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -6222,7 +6008,6 @@ "review_state": null, "root_timestamp": "2026-07-13T18:15:58Z", "body": "PR with my remaining feedback here: https://github.com/jsuereth/opentelemetry-java/pull/1", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -6251,7 +6036,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-13T18:59:10Z", "body": "Looks good from my side. I don't have any preference on \"raw attributes\" vs \"unassociated attributes\" naming. Choose whatever is most likely to pass spec scrutiny, but we can merge here even without spec and update our naming later if needed.\n\nStill need to merge main and fix some minor things.\n\nAlso, should update the PR description to reflect the final state, so its accurate for any future readers.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -6280,7 +6064,6 @@ "review_state": null, "root_timestamp": "2026-06-10T16:50:51Z", "body": "What's the motivation behind this change? Is this related to https://github.com/open-telemetry/opentelemetry-java/issues/8198 ?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -6309,7 +6092,6 @@ "review_state": null, "root_timestamp": "2026-07-13T21:36:11Z", "body": "Blocked by codeql support:\r\n\r\n> Caused by: com.semmle.extractor.java.interceptors.KotlinInterceptor$KotlinVersionTooRecentError: Kotlin version 2.4.0 is too recent. CodeQL currently supports versions below 2.3.30", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -6338,7 +6120,6 @@ "review_state": null, "root_timestamp": "2026-07-16T15:28:13Z", "body": "> Blocked by codeql support:\r\n> \r\n> > Caused by: com.semmle.extractor.java.interceptors.KotlinInterceptor$KotlinVersionTooRecentError: Kotlin version 2.4.0 is too recent. CodeQL currently supports versions below 2.3.30\r\n\r\nYeah, codeql is a real problem here. We've been stuck on them being always behind for a while now. Maybe we should reconsider codeql for kotlin if we want to keep up with the kotlin versioning.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -6367,7 +6148,6 @@ "review_state": null, "root_timestamp": "2026-07-01T19:36:52Z", "body": "> This violated the OpenTelemetry specification, which requires that\r\nattribute name alone determines identity — last write wins regardless of type.\r\n\r\nCan you provide a link the portion of the spec you're referring to? Thanks.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -6396,7 +6176,6 @@ "review_state": null, "root_timestamp": "2026-07-01T20:27:06Z", "body": "> I'm not sure if you're asking because the PR description doesn't provide enough context, or if you think I might be misinterpreting the spec and solving a problem that doesn't actually exist. Happy to clarify either way.\r\n\r\nThe spec is big and occasionally contradictory. Always good to have a reference!\r\n\r\n> I originally found this through the linked issue. \r\n\r\nMissing the linked issue", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -6425,7 +6204,6 @@ "review_state": null, "root_timestamp": "2026-07-02T21:38:54Z", "body": "Hey so I've been thinking about this. AttributesMap exists because the default implementation of Attributes / AttributesBuilder is not limits aware. I think that the fact that its backed by HashMap is a function of convenience: we need a simple implementation that we can apply limits too as its being built up. I think performance is important, and a Map based implementation also has better put performance than the default array based implementations ImmutableKeyValuePairs / ArrayBackedAttributesBuilder. But I think this is coincidence, since I've never heard us telling people \"prefer using Span.setAttribute because it uses a more performant map based implementation\". \r\n\r\nI've never loved the fact that AttributesMap exists. I'd rather have one implementation. And I think your changes to AttributesMap reinforces this because while it still tries to do some map things for performance, other parts of it are starting to look more like ImmutableKeyValuePairs / ArrayBackedAttributesBuilder. And so its got me thinking about whether we can evolve ImmutableKeyValuePairs / ArrayBackedAttributesBuilder to meet the limits requirements and rip out AttributesMap altogether.\r\n\r\nI've got two protot ...[truncated]", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -6454,7 +6232,6 @@ "review_state": null, "root_timestamp": "2026-07-02T21:39:15Z", "body": "@open-telemetry/java-approvers PTAL at my message above and let me know if you have thoughts.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -6483,7 +6260,6 @@ "review_state": null, "root_timestamp": "2026-07-10T16:04:03Z", "body": "I would love to get us to a single implementation as well. The de-duping logic in the array based approach has always been the scary bit to my avoiding use using it instead of AttributesMap. If we're ok eating that performance hit, option 3 seems fine to me (as does 2, tbh). it's all the usual tradeoff of memory vs. speed, slightly coupled with maintenance complexity, I suppose (it's more maintenance to have 2 implementations vs. just 1). \r\n\r\nI wonder if there's a clever solution to the deduping, using some sort of lightweight sketch-based approach, alongside the array to save the linear scan in most cases. Could be an interesting research project for someone with time on their hands. ;)", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -6512,7 +6288,6 @@ "review_state": null, "root_timestamp": "2026-07-21T04:37:06Z", "body": "cc @trask to review first", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -6541,7 +6316,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-22T18:23:11Z", "body": "LGTM, but you could wait until we merge similar in other Java repos and go through release to verify", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -6570,7 +6344,6 @@ "review_state": null, "root_timestamp": "2026-07-16T20:19:08Z", "body": "Hey I gave this issue some thought and shared my analysis here: https://github.com/open-telemetry/opentelemetry-java/issues/7573#issuecomment-4996197430\r\n\r\nI think we should go in a different direction with this: #8610, which embodies option d in the comment.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -6599,7 +6372,6 @@ "review_state": null, "root_timestamp": "2026-07-17T01:25:12Z", "body": "Hey @jack-berg, thanks for the detailed analysis. \r\n\r\nI had one question about test coverage: is there a test that verifies the actual motivating scenario from #7573 — successfully reaching a real TLSv1/TLSv1.1-only server with enabledProtocols set and the JVM floor opened?\r\n\r\nAs written, `enabledProtocols()` only exercises TLS 1.2/1.3 against a TLS 1.2/1.3 server, which would still pass if `setEnabledProtocols()` were a no-op since that is already the default range.\r\nI don't see a test covering the full \"both gates open\" scenario against a legacy server, which was the original bug report.\r\n\r\nI ran into a related issue testing this in my [PR](https://github.com/open-telemetry/opentelemetry-java/pull/8599):` jdk.tls.disabledAlgorithms` behavior is JVM-wide, and changing it at runtime can be problematic once TLS initialization has already happened. That made a legacy-server test order-dependent in practice. \r\nIf that is why this scenario was intentionally omitted, that makes sense as a stability tradeoff.\r\n\r\nI just wanted to confirm whether that was the reasoning, or if I missed an existing test covering it.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -6628,7 +6400,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-27T21:07:56Z", "body": "Just a couple more minor comments. Looks pretty good!", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -6657,7 +6428,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-04-20T01:58:58Z", "body": "Purely AI generated review below\n\n---------------------------\n\nReview of the Failsafe 3.0 javaagent instrumentation + testing module extraction. A few correctness/style issues worth addressing:\n\n- **javaagent advice**: `@Advice.OnMethodExit` has `onThrowable = Throwable.class` but the body is return-only (it casts `@Advice.Return` and derives values from it). On the exceptional path the return is `null` and the cast triggers a (suppressed) exception for no benefit — drop `onThrowable`.\n- **Default policy name in javaagent mode**: `impl.toString()` is used as the retry-policy name, which will produce uninformative values like `dev.failsafe.internal.RetryPolicyImpl@2f4d3e5a` as the `failsafe.retry_policy.name` attribute. Worth considering a cleaner fallback (e.g., the implementation class simple name, or just leaving the attribute out when no user-supplied name exists).\n- **Reflection-based mutation of `PolicyConfig`**: writing to private `failureListener`/`successListener` fields is brittle and muzzle cannot protect against these fields being renamed/removed in a future Failsafe version. Worth noting in a comment and/or considering a `fail` muzzle block or explicit guard.\n- **Style ...[truncated]", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -6686,7 +6456,6 @@ "review_state": null, "root_timestamp": "2026-03-19T02:05:25Z", "body": "@steverao thanks for the detailed explanation! I think that's ok, not sure any way around it, just update the testLatestDeps test to reflect the reality of the disconnected trace.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -6715,7 +6484,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-05-31T07:12:17Z", "body": "LGTM, just a nit:\n\nhttps://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/4c57afb6c7460b05eda858114d74ad8da4794973/instrumentation/spring/spring-webflux/spring-webflux-5.3/library/README.md?plain=1#L81\n\nSuggested change: `return webfluxServerTelemetry.createWebFilter();`", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -6744,7 +6512,6 @@ "review_state": null, "root_timestamp": "2026-06-01T07:22:40Z", "body": "May I ask for a review from @open-telemetry/java-instrumentation-approvers when any of them get a chance?\r\n\r\nThanks!", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -6773,7 +6540,6 @@ "review_state": null, "root_timestamp": "2026-06-02T11:56:03Z", "body": "https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/17858 asks whether the reactor hook should be always registered. Did you attempt to fina an answer to that question? What are the benefits of registering the reactor hook? What would we loose if we did it the other way around and never registered it in these methods?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -6802,7 +6568,6 @@ "review_state": null, "root_timestamp": "2026-07-08T17:01:44Z", "body": "@laurit,\r\nIt's been a while, may we get your review here?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -6831,7 +6596,6 @@ "review_state": null, "root_timestamp": "2026-06-09T13:58:51Z", "body": "Is there prior art for this in otel ecosystem? There is a [moratorium](https://github.com/open-telemetry/opentelemetry-specification/issues/2891#issuecomment-1289241503) in otel spec for introducing new environment variables with complex encoding. The moratorium was originally in place to incentivize development of declarative config, which is done now. While we are not strictly bound by this moratorium the question is still whether there could be a better solution for this that does not involve complex encodings. Perhaps some sort of include/exclude pair would work better?\r\ncc @trask", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -6860,7 +6624,6 @@ "review_state": null, "root_timestamp": "2026-06-12T03:28:58Z", "body": "check out declarative config and the pattern for includes/excludes: \r\n\r\n- https://github.com/open-telemetry/opentelemetry-configuration\r\n- https://github.com/open-telemetry/opentelemetry-configuration/blob/main/CONTRIBUTING.md#properties-requiring-pattern-matching\r\n- https://github.com/open-telemetry/opentelemetry-java/blob/main/sdk/common/src/main/java/io/opentelemetry/sdk/common/internal/IncludeExcludePredicate.java", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -6889,7 +6652,6 @@ "review_state": null, "root_timestamp": "2026-07-14T21:48:10Z", "body": "> Is it true that opentelemetry-sdk-common (and therefore `IncludeExcludePredicate`) cannot be used by instrumentations?\r\n\r\noh yeah, instrumentation should only rely on OpenTelemetry API and not on the SDK\r\n\r\nI pushed a commit to copy in those classes, let's give that a try", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -6918,7 +6680,6 @@ "review_state": null, "root_timestamp": "2026-07-08T21:22:12Z", "body": "Can you do some analysis on what (quantitative) benefits this has, so we can weigh the risks? thanks", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -6947,7 +6708,6 @@ "review_state": null, "root_timestamp": "2026-07-13T20:32:18Z", "body": "thanks @somiljain2006!\r\n\r\nI'm wondering if this is needed, I think same can be accomplished via Metric Views, e.g.\r\n\r\n```\r\nmeter_provider:\r\n views:\r\n - selector:\r\n meter_name: io.opentelemetry.micrometer-1.5\r\n instrument_name: jvm.*\r\n stream:\r\n aggregation:\r\n drop: {}\r\n - selector:\r\n meter_name: io.opentelemetry.micrometer-1.5\r\n instrument_name: process.cpu.usage\r\n stream:\r\n aggregation:\r\n drop: {}\r\n```\r\n\r\ncc @SylvainJuge who opened the linked issue", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -6976,7 +6736,6 @@ "review_state": null, "root_timestamp": "2026-07-15T09:52:28Z", "body": "> thanks @somiljain2006!\r\n> \r\n> I'm wondering if this is needed, I think same can be accomplished via Metric Views, e.g.\r\n> \r\n> ```\r\n> meter_provider:\r\n> views:\r\n> - selector:\r\n> meter_name: io.opentelemetry.micrometer-1.5\r\n> instrument_name: jvm.*\r\n> stream:\r\n> aggregation:\r\n> drop: {}\r\n> - selector:\r\n> meter_name: io.opentelemetry.micrometer-1.5\r\n> instrument_name: process.cpu.usage\r\n> stream:\r\n> aggregation:\r\n> drop: {}\r\n> ```\r\n> \r\n> cc @SylvainJuge who opened the linked issue\r\n\r\nI think this should solve the \"opt-out\" strategy described in the issue, however I don't think this very practical, in particular when conflicts are now known in advance.\r\n\r\nSee also related [slack discussion](https://cloud-native.slack.com/archives/C014L2KCTE3/p1783586947945979) where a few other options are being discussed.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -7005,7 +6764,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-03T07:56:12Z", "body": "Library instrumentation should have a readme with instructions on how to set it up. Since this instrumentation produces metrics without semantic conventions it might be best to also list these in the readme, maybe add a table with the metric names and descriptions (and units?) similarly to what runtime-telemetry library module has.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -7034,7 +6792,6 @@ "review_state": null, "root_timestamp": "2026-07-02T09:00:38Z", "body": "I think we should add these for all messaging instrumentation (doing just kafka in this PR is fine, the rest can be handled separately). It is not ideal that we only have `messaging.receive.duration` and `messaging.publish.duration` but nothing for `process`. I think we have the following choices\r\n- leave it as is, don't add anything for `process`\r\n- add the missing `process` metrics based on https://github.com/open-telemetry/semantic-conventions/blob/v1.24.0/docs/messaging/messaging-metrics.md (there the metric names contains `deliver` instead of `process`)\r\n- switch to metrics from current semconv https://github.com/open-telemetry/semantic-conventions/blob/main/docs/messaging/messaging-metrics.md Since our messaging implementation doesn't really follow any version of the semantic conventions this might not be that bad. Since the messaging metrics are currently only implemented for pulsar the backwards compatibility concerns are limited.\r\n\r\n@trask do you have any preference on how we should handle this?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -7063,7 +6820,6 @@ "review_state": null, "root_timestamp": "2026-07-14T21:30:36Z", "body": "> @trask do you have any preference on how we should handle this?\r\n\r\nIt has downsides, but I'd lean towards continuing to follow https://github.com/open-telemetry/semantic-conventions/blob/main/docs/messaging/README.md, which basically says, emit the old semconv by default, and only emit new semconv under `OTEL_SEMCONV_STABILITY_OPT_IN` (and in our case starting with 3.0, `OTEL_SEMCONV_STABILITY_PREVIEW`).\r\n\r\nWe could bump to latest (even before stable), but ideally only after we have implemented the same conventions consistently across all of our messaging instrumentations. It would probably be confusing for some of our messaging instrumentations to emit earlier semconv and some later semconv by default.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -7092,7 +6848,6 @@ "review_state": null, "root_timestamp": "2026-07-15T14:24:27Z", "body": "> It has downsides, but I'd lean towards continuing to follow https://github.com/open-telemetry/semantic-conventions/blob/main/docs/messaging/README.md, which basically says, emit the old semconv by default, and only emit new semconv under `OTEL_SEMCONV_STABILITY_OPT_IN` (and in our case starting with 3.0, `OTEL_SEMCONV_STABILITY_PREVIEW`).\r\n> \r\n> We could bump to latest (even before stable), but ideally only after we have implemented the same conventions consistently across all of our messaging instrumentations. It would probably be confusing for some of our messaging instrumentations to emit earlier semconv and some later semconv by default.\r\n\r\nThe thing is that our messaging semconv don't really follow a particular version of the semconv. The version that is closest to how we emit spans doesn't define metrics at all. The messaging metrics are currently only enabled for pulsar so I'd say we can probably allow incompatible changes if we so desire since their usage is limited.\r\n@trask do I understand correctly that your preference is to use https://github.com/open-telemetry/semantic-conventions/blob/v1.25.0/docs/messaging/messaging-metrics.md for metrics (1.24.0 has `deliver` inste ...[truncated]", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -7121,7 +6876,6 @@ "review_state": null, "root_timestamp": "2026-07-16T04:30:24Z", "body": "> The thing is that our messaging semconv don't really follow a particular version of the semconv.\r\n\r\nYou've convinced me 😅.\r\n\r\nBut if we're going to do it, let's take the opportunity to sync them all up for 3.0:\r\n\r\nhttps://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/19233", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -7150,7 +6904,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-07T07:17:01Z", "body": "@jaydeluca with this PR quartz instrumentation will emit an event in case you wish to add this to the collected metadata", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -7179,7 +6932,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-07T07:21:38Z", "body": "Open question is whether emitting the event should be configurable or whether there is any reason why it shouldn't be emitted by default.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -7208,7 +6960,6 @@ "review_state": null, "root_timestamp": "2026-07-11T18:35:50Z", "body": "is there a specific reason you had in mind for adding these particular tests? thanks", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -7237,7 +6988,6 @@ "review_state": null, "root_timestamp": "2026-07-13T09:37:39Z", "body": "From what I understand the goal of this PR is to provide a way to enhance the span create by http client instrumentation. The problem is that in user code you can't usually easily observe the http client span so you could add attributes to it. When using library instrumentation you could add an attribute extractor with https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/e49a4a9cb1261eaa22af9849786f036a63288e81/instrumentation/ktor/ktor-common-2.0/library/src/main/kotlin/io/opentelemetry/instrumentation/ktor/common/v2_0/AbstractKtorClientTelemetryBuilder.kt#L68 It is mentioned that you could use `currentCoroutineContext().getOpenTelemetryContext()` but this feels more like a coincidence, typically for callbacks our instrumentations expose the parent context of the http client span not the http client span itself. This issue isn't really specific to ktor http client instrumentation, but rather affects all instrumentations that create client or producer spans. It does come up once in a while. I tried solving it with https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/6191 but it didn't get enough traction. There were some concerns that it mi ...[truncated]", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -7266,7 +7016,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-09T05:54:28Z", "body": "## Pull request overview\n\nThis PR adds a new `redisson-metrics-2.3` javaagent instrumentation module that emits database connection pool metrics (used/idle connections, min/max pool sizes, and pending requests) for Redisson versions `[2.3.0, 3.18.0)`. It fills a gap in the existing Redisson instrumentation (which only provides client spans/metrics for 3.0+) by instrumenting the internal `ClientConnectionsEntry` construction and `RedisClient.shutdownAsync()` lifecycle, mirroring the established connection-pool-metrics pattern used by the c3p0 and Tomcat JDBC modules.\n\n**Changes:**\n- New instrumentation module instruments the 7-arg `ClientConnectionsEntry` constructor to register `DbConnectionPoolMetrics` and `RedisClient.shutdownAsync()` to unregister them.\n- Reads pool counters via reflection through `AsyncSemaphoreAccessor`, supporting both the legacy `AtomicInteger` and newer `AsyncSemaphore` free-connection counters, with pending-requests support only when an `AsyncSemaphore` is present.\n- Adds Testcontainers-based coverage (default 2.3.0 and latest 3.17.x), plus build/config/docs wiring (settings, FOSSA, CI instrumentation list, latest-dep pin, supported-libraries doc).\n\n### Re ...[truncated]", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -7295,7 +7044,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-28T19:41:03Z", "body": "## Pull request overview\n\nCopilot reviewed 13 out of 13 changed files in this pull request and generated no new comments.\n\n\n\n\n
\nComments suppressed due to low confidence (2)\n\n**instrumentation/redisson/redisson-metrics-2.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/redissonmetrics/v2_3/RedissonConnectionPoolMetricsTest.java:144**\n* [Testing] Use the exact attribute assertion here. `hasAttributesSatisfying(...)` silently accepts unexpected attributes, so this test would not catch extra dimensions on the used point.\n```\n .hasAttributesSatisfying(\n```\n**instrumentation/redisson/redisson-metrics-2.3/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/redissonmetrics/v2_3/RedissonConnectionPoolMetricsTest.java:138**\n* [Testing] Use the exact attribute assertion here. `hasAttributesSatisfying(...)` silently accepts unexpected attributes, so this test would not catch extra dimensions on the idle point.\n\nThis issue also appears on line 144 of the same file.\n```\n .hasAttributesSatisfying(\n```\n
", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -7324,7 +7072,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-29T18:41:23Z", "body": "AI-generated review", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -7353,7 +7100,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-31T05:50:30Z", "body": "## Pull request overview\n\nCopilot reviewed 13 out of 13 changed files in this pull request and generated no new comments.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -7382,7 +7128,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-31T16:13:11Z", "body": "Lightly filtered AI-generated feedback — push back freely", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -7411,7 +7156,6 @@ "review_state": null, "root_timestamp": "2026-07-10T18:06:45Z", "body": "can you update PR description to include motivation for why this PR? e.g.\r\n\r\n> Decouple Apache DBCP 2.0 connection-pool metrics from JMX registration. The javaagent currently detects the datasource lifecycle only through `MBeanRegistration` callbacks, so an active `BasicDataSource` produces no metrics unless it is registered with an `MBeanServer`.\r\n>\r\n> Register metrics after successful pool initialization by instrumenting `startPoolMaintenance()`, and unregister them when the datasource is closed. Keep `postDeregister()` cleanup for JMX-managed pools. This also supports datasource restart without requiring JMX.\r\n>\r\n> Preserve meaningful pool names by preferring the configured JMX name, then the registered JMX `ObjectName`, and falling back to a generated `dbcp2-N` name. Add coverage for non-JMX pools and each naming path, and update the instrumentation documentation to reflect that JMX is no longer required.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -7440,7 +7184,6 @@ "review_state": null, "root_timestamp": "2026-07-16T15:54:16Z", "body": "is this a breaking change? if so, let's put it behind the v3preview flag and try to get it into v2.30.0", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -7469,7 +7212,6 @@ "review_state": null, "root_timestamp": "2026-07-19T22:38:55Z", "body": "> is this a breaking change?\r\n\r\nI think no:\r\n\r\nExisting users on the normal successful JMX path retain the name derived from their registered ObjectName. Non-JMX users previously received no Apache DBCP metrics, so the JDBC-derived or fallback name belongs to newly added telemetry and has no existing name to preserve.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -7498,7 +7240,6 @@ "review_state": null, "root_timestamp": "2026-07-13T21:49:04Z", "body": "hi @amit306!\r\n\r\nI think the goal of #17858 is more about fixing the API (not just documentation), along the lines of your #18844", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -7527,7 +7268,6 @@ "review_state": null, "root_timestamp": "2026-07-14T16:30:27Z", "body": "When I created #17858 I thought that maybe it would make sense to always register the reactor hook. Note that #17858 is phrased as a question so answering no is a valid option. After looking more into it I realized that we don't actually have any tests that fail without the reactor hook so apparently rector hook isn't always required. There is also https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/18932 that hints that perhaps we should rethink our reactor instrumentation. Spring folks have a blog post series that explores different options for reactor context propagation https://spring.io/blog/2023/03/28/context-propagation-with-project-reactor-1-the-basics Because of that I think that for now it might be better to improve our documentation to provide guidance when the user should also register the rector hook and when they could get away without registering one.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -7556,7 +7296,6 @@ "review_state": null, "root_timestamp": "2026-07-14T16:15:03Z", "body": "I'm not sure about this. The intent here is to augment the server span with additional attributes as stated in https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/instrumentation/apache-shenyu-2.4/metadata.yaml While the span from context is probably also the server span, unless someone uses method instrumentation or something like that to create additional span, using `LocalRootSpan` feels more clear. An alternative would be to rethink this instrumentation. Perhaps instead of augmenting the server span it could create a controller span like other framework instrumentations?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -7585,7 +7324,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-21T18:01:36Z", "body": "@trask should there be a corresponding semconv issue for this?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -7614,7 +7352,6 @@ "review_state": null, "root_timestamp": "2026-03-18T16:36:11Z", "body": "This is currently blocked on https://github.com/open-telemetry/opentelemetry-js/pull/6356 and similar changes in the other SDKs. We currently see issues where if there's not OTel collector, app shutdowns take significantly longer; this PR here would make that issue worse as it stands now.\r\n\r\nWe still want to merge this once the other PR is in though.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -7643,7 +7380,6 @@ "review_state": null, "root_timestamp": "2026-04-06T15:23:35Z", "body": "Will give this a look once back from vacation at the end of april", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -7672,7 +7408,6 @@ "review_state": null, "root_timestamp": "2026-04-07T08:39:59Z", "body": "hi @overbalance \r\n\r\nwe looked at that PR a couple of weeks ago in the JavaScript SIG. We were wondering if possible to make the migration from Karma to vitest in a follow up PR so this one becomes smaller and easier to review.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -7701,7 +7436,6 @@ "review_state": null, "root_timestamp": "2026-06-15T12:59:38Z", "body": "> Compile on Node ^26.3.0 in CI while tests continue to run on the full supported matrix (build-on-26, run-on-matrix); drop npm install -g npm@latest steps where the bundled npm already meets the 11.16.0 floor; build the w3c integration server's dependencies at the repo root\r\n\r\nAs long as we test on all supported versions and compile the same way for publishing I'm okay with this change. WDYT @open-telemetry/javascript-approvers ?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -7730,7 +7464,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-06-15T13:53:48Z", "body": "This looks very good. Just a couple of questions.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -7759,7 +7492,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-06-15T17:21:44Z", "body": "Thank you for the refactor on this, left a few comments. Personally very much in favour of getting this in.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -7788,7 +7520,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-06-16T17:53:17Z", "body": "Everything I could think of has been addressed :)", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -7817,7 +7548,6 @@ "review_state": null, "root_timestamp": "2026-02-16T14:09:48Z", "body": "@cjihrig @vitorvasc can we please get this merged?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -7846,7 +7576,6 @@ "review_state": null, "root_timestamp": "2026-02-16T15:40:26Z", "body": "@Dhruv-Garg79 this still requires review and approval from a project maintainer.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -7875,7 +7604,6 @@ "review_state": null, "root_timestamp": "2026-02-12T20:24:52Z", "body": "@zakcutner - my original idea was to provide an interface for people to pass their own transports to the exporters, because different transport implementations are so widely requested.\r\n\r\nMy idea was that people would be able to do something like this, but I ran out of time to work on it:\r\n\r\n```ts\r\nconst exporter = createOtlpExporter({\r\n serializer: ProtobufTraceSerializer,\r\n transport: createMyCustomFetchTransport() // returns a custom IExporterTransport impl\r\n});\r\n```\r\n\r\nthe underlying implementation is already somewhat setup for such a thing, but requires a bit more boilerplate\r\n```ts\r\n\r\nimport { createOtlpNetworkExportDelegate } from '@opentelemetry/otlp-exporter-base';\r\nimport { ProtobufTraceSerializer } from `@opentelemetry/otlp-transformer`;\r\n\r\nconst exporter: SpanExporter = new createOtlpExportDelegate({\r\n options: { /** provide all required options accoding to type**/ },\r\n serializer: ProtobufTraceSerializer, // or JsonTraceSerializer\r\n transport: createMyCustomFetchTransport() /** implement this yourself, a wrapper around your custom fetch, make sure to set the content-type header to what you're sending: `application/x-protobuf` or `application/json` **/\r\n}); /* dele ...[truncated]", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -7904,7 +7632,6 @@ "review_state": null, "root_timestamp": "2026-02-12T20:31:45Z", "body": "So my recommendation for this feature is:\r\n- decline for now\r\n- use workaround for the time being\r\n- drop `node:http` based exporter transport in July 2026\r\n- add this feature as proposed in this PR to the now streamlined interface where it also works for Node.js\r\n- feature is released alongside SDK 3.0, everybody can use it", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -7933,7 +7660,6 @@ "review_state": null, "root_timestamp": "2026-03-03T22:48:14Z", "body": "Of course, my comments only apply if js core team approves.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -7962,7 +7688,6 @@ "review_state": null, "root_timestamp": "2026-03-10T13:46:36Z", "body": "Is this needed only for the JS SDK or for other languages as well? Have other language SDKs already solved this problem?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -7991,7 +7716,6 @@ "review_state": null, "root_timestamp": "2026-03-20T00:45:33Z", "body": "Please do not rebase or otherwise change history and force push as it breaks the link between conversation threads and the code they're referencing", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -8020,7 +7744,6 @@ "review_state": null, "root_timestamp": "2026-06-03T23:02:33Z", "body": "I ran into this gap while wiring exemplar support into my own metrics and traces stack (Grafana Cloud Mimir and Tempo, fed from a Node agent) and found this PR with all of the earlier review feedback already addressed in the later commits. Since it has been sitting with conflicts for a couple of months, I rebased it onto current main to keep it moving: https://github.com/rnavarro/opentelemetry-js/tree/feat/metrics-exemplars-rebased\n\nThe rebase preserves all five of @CharlieTLe's commits unchanged. Conflicts were limited to the two CHANGELOG files (entries moved to the current Unreleased section) and `ExemplarReservoir.ts`, where I kept this PR's non-mutating `collect()` together with the `for...of` conversion that landed on main since. On the rebased branch the `sdk-metrics` suite passes 431 tests and `otlp-transformer` passes 220, lint is clean on the changed files, and I verified end to end that a histogram recorded under a sampled span context serializes an exemplar with the correct traceId and spanId through `JsonMetricsSerializer`.\n\n@CharlieTLe if you want to pull that branch into this PR, it is yours to take. If you are short on time, I am happy to open a successor PR that ke ...[truncated]", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -8049,7 +7772,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-03-17T13:32:00Z", "body": "is there any test that would have caught this scenario that can be added here?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -8078,7 +7800,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-03-19T12:20:39Z", "body": "This PR replaces _httpPatched/_httpsPatched booleans with isWrapped() checks to avoid double-wrapping http when loaded by both require and import.\n\nDone well: Using isWrapped from the instrumentation base is the right abstraction—it checks the actual patched state instead of tracking it manually. Removing the state flags simplifies enable/disable and avoids races if modules are loaded concurrently. The instrumentation.disable() in test afterEach ensures clean teardown between tests. License header change to SPDX is a nice consistency fix.\n\nSuggestion: The double-instr test verifies no double instrumentation when http is loaded by both require and import. With isWrapped the second load would skip patching. Consider adding a test that explicitly loads http twice (e.g. via two different import paths) and asserts request/emit are wrapped only once, to guard against future regressions.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -8107,7 +7828,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-04-29T08:48:42Z", "body": "From the experience I had in browser this is a good solution assuming only this instrumentation is patching the HTTP module. `isWrapped` does not ensure your wrapper is the one applied to the wrapped function.\n\nShould we assume http instrumentation is the only patching htese methods?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -8136,7 +7856,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-03-19T12:20:46Z", "body": "This PR adds maxScale option to ExponentialHistogramAggregation to limit the scale of exponential histograms.\n\nDone well: maxScale threads through cleanly—ExponentialHistogramAggregation, ExponentialHistogramAggregator, and ExponentialHistogramAccumulation all get the parameter with DEFAULT_MAX_SCALE=20 preserving backward compatibility. The mapping ?? getMapping(maxScale) in the accumulation constructor handles the optional mapping param correctly. AggregationOption type and toAggregation wiring are updated. Tests cover default (20), custom maxScale, and multiple accumulations sharing the same maxScale.\n\nMinor: The ExponentialHistogramAccumulation constructor signature changed—mapping is now optional and maxScale is a new positional arg. Any external code that passed mapping explicitly might need to use keyword args. Worth a changelog note if this is a public API.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -8165,7 +7884,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-03-23T13:10:55Z", "body": "This looks good to me but I'd like to have someone like @pichlermarc or @legendecas who is a bit closer to the implementation take a look", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -8194,7 +7912,6 @@ "review_state": null, "root_timestamp": "2026-03-22T17:18:23Z", "body": "Please do not rebase or otherwise change history as it breaks the link between review comment threads and the code", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -8223,7 +7940,6 @@ "review_state": null, "root_timestamp": "2026-04-01T15:02:23Z", "body": ">I'm not entirely happy about pulling time-related logic outside of _getTime but figure it's worth it given the improvement. Happy to switch to something else if we can maintain the improvement.\r\n\r\nThe span creation benchmark currently shows around `500,000` ops per second which works out to 2 microseconds per operation. Improving that by 10% is only a shave of 200 ns. Is it really worth it?\r\n\r\nEspecially since that benchmark seems to fluctuate by 5-10% on any given run without changes, are you sure you're even getting the expected gains? I've found you need to run the benchmarks several times to get reasonable results. You may want to also consider using the microtime option when running the benchmark in order to make sure you're catching small differences.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -8252,7 +7968,6 @@ "review_state": null, "root_timestamp": "2026-04-01T16:51:05Z", "body": "I was able to achieve a similar speedup by saving the performance start time from the constructor in a class property instead of calling `performance.now` in `_getTime` and combining the `if` statements like so (avoids second branch check):\r\n\r\n```typescript\r\n constructor(opts: SpanOptions) {\r\n const now = Date.now();\r\n this._spanContext = opts.spanContext;\r\n this._performanceStartTime = performance.now();\r\n this._performanceOffset =\r\n now - (this._performanceStartTime + performance.timeOrigin);\r\n this.startTime = this._getTime(opts.startTime ?? now);\r\n /* [...] */\r\n }\r\n\r\n private _getTime(inp?: TimeInput): HrTime {\r\n if (typeof inp === 'number') {\r\n if (inp <= this._performanceStartTime) {\r\n // performance.now() timestamp — apply offset to convert to wall-clock\r\n return hrTime(inp + this._performanceOffset);\r\n }\r\n // Date.now() timestamp\r\n return millisToHrTime(inp);\r\n }\r\n /* [...] */\r\n }\r\n```\r\n\r\nedit: running the benchmark multiple times results in a wide swing. Sometimes I get 900k ops/s and sometimes 600k. This does seem to improve the midpoint, but I am not 100% confident the benchmark is actually showing a r ...[truncated]", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -8281,7 +7996,6 @@ "review_state": null, "root_timestamp": "2026-04-15T09:26:20Z", "body": "> In bundled ESM environments, that unnecessary hook can lead to runtime failures when the app later uses createRequire(), for example `ReferenceError: require is not defined`\r\n\r\nIt's not clear why ESM, bundling and `require-in-the-middle` causes the above to occur. Do you have a reproduction to try?\r\n\r\nIt feels like this PR works around an issue rather than actually finding and fixing the root cause!", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -8310,7 +8024,6 @@ "review_state": null, "root_timestamp": "2026-04-15T15:34:53Z", "body": "Ah, I see that essentially your PR [here](https://github.com/getsentry/sentry-electron/pull/1354) is a reproduction of the issue. I'll take a look!", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -8339,7 +8052,6 @@ "review_state": null, "root_timestamp": "2026-04-15T15:49:07Z", "body": "@biw your example from the Electron PR referenced some really old dependencies. When I updated everything to below, it doesn't result in a runtime error.\r\n\r\n```json\r\n \"dependencies\": {\r\n \"electron-squirrel-startup\": \"^1.0.1\"\r\n },\r\n \"devDependencies\": {\r\n \"@sentry/electron\": \"7.11.0\",\r\n \"@sentry/vue\": \"10.47.0\",\r\n \"@vitejs/plugin-vue\": \"^6.0.6\",\r\n \"electron\": \"^41.2.0\",\r\n \"electron-vite\": \"^5.0.0\",\r\n \"vite\": \"^8.0.8\",\r\n \"vue\": \"^3.5.32\"\r\n }\r\n```\r\n\r\nIf you can supply a failing reproduction of the issue I might be able to help further.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -8368,7 +8080,6 @@ "review_state": null, "root_timestamp": "2026-05-11T10:37:41Z", "body": "Hi @biw - were you able to repro this on more recent versions? 🙂", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -8397,7 +8108,6 @@ "review_state": null, "root_timestamp": "2026-05-11T11:43:56Z", "body": "I would say this should be closed.\r\n\r\nRather than init `require-in-the-middle` lazily, there should be a way to create Node instrumentations that don't use `require-in-the-middle` and `import-in-the-middle` at all. ie. some higher level base instrumentation that doesn't expect module hooking. This would be useful for instrumentations that use `TracingChannel`!", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -8426,7 +8136,6 @@ "review_state": null, "root_timestamp": "2026-04-16T14:50:03Z", "body": "from @pichlermarc on otel-js-dev channel:\r\n`We are planning to publish SDK 3.0 around June/July, and have a milestone set up for it already. I'd suggest we do it there and communicate the change accordingly.`\r\n\r\nI'm not sure when we can start adding changes only for 3.0. Can you provide guidance @pichlermarc ?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -8455,7 +8164,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-04-17T08:22:42Z", "body": "@maryliag - I thought it'd be best to do this at the same time as 3.x (possibly also bumping this package from experimental to stable in the process).\n\nI think this change will break a lot of users that rely on the default behavior being that it binds to all available network interfaces instead of `localhost`.\n\n**suggestion:** let's put a warning there for now, letting people know that this will only bind to `localhost` in the future. Once we're working on 3.x, we change the behavior and we release it together with 3.x, and include guidance on what to do in the migration document. This way people have some time to change their settings accordingly instead of being broken without warning.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -8484,7 +8192,6 @@ "review_state": null, "root_timestamp": "2026-07-22T16:46:14Z", "body": "> > Waiting on: Author\r\n> \r\n> Is that accurate?\r\n\r\nno - automation made a mistake - we'll merge this once we start working on 3.0 :)", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -8513,7 +8220,6 @@ "review_state": null, "root_timestamp": "2026-04-29T08:10:45Z", "body": "@abhisheksurve45 thanks for your contribution. I left a comment about the scope of the new function. Maybe other @open-telemetry/javascript-approvers want to give their view.\r\n\r\nIn the meantime you may want to `npm run lint:fix` to make the CI happy", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -8542,7 +8248,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-05-14T12:25:44Z", "body": "Hello, hello, thanks for starting the effort!\n\nI don't know JS at all, so I can't do a very complete review. \n\nLooking at your tests, and comparing them with the ones we did for go (https://github.com/prometheus/otlptranslator/blob/main/metric_namer_test.go), it seems like a lot of edge cases aren't covered here. Is it worth covering them?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -8571,7 +8276,6 @@ "review_state": null, "root_timestamp": "2026-05-14T14:30:04Z", "body": "Yep, 1200 LOC is a lot 😅. Let me try to parse that into human-readable text:\r\n\r\nUnderscore Scaping:\r\n* `metric@with#special$chars` -> `metric_with_special_chars`\r\n* `123metric` -> `_123metric` (metric names starting with digit are unnallowed unless UTF-8 is enabled)\r\n* `` (empty) -> Should error\r\n* `metric@@##$$name` -> `metric_name` (multiple special characters become a single underscore)\r\n* `@#$%` -> Should error since it's only special characters, translating into a single underscore\r\n\r\n\r\nNo underscore scaping:\r\n* The cases above are allowed untransformed, besides the empty string", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -8600,7 +8304,6 @@ "review_state": null, "root_timestamp": "2026-05-18T08:47:33Z", "body": "nice, almost there :) \r\nlooks like we still need a config\r\n\r\n\r\n**logs:**\r\n```\r\n/opt/hostedtoolcache/node/22.22.2/x64/bin/npx size-limit --json\r\nInstall Size Limit preset depends on type of the project\r\n\r\nFor application, where you send JS bundle directly to users\r\n npm install --save-dev @size-limit/preset-app\r\n\r\nFor frameworks, components and big libraries\r\n npm install --save-dev @size-limit/preset-big-lib\r\n\r\nFor small (< 10 kB) libraries\r\n npm install --save-dev @size-limit/preset-small-lib\r\n\r\nCheck out docs for more complicated cases\r\n https://github.com/ai/size-limit/\r\n```", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -8629,7 +8332,6 @@ "review_state": null, "root_timestamp": "2026-05-18T08:49:36Z", "body": "or is the failure reason the one you mentioned in the PR description? 🤔", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -8658,7 +8360,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-06-02T16:45:04Z", "body": "Great fix! Just one question.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -8687,7 +8388,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-06-12T19:02:51Z", "body": "I think that covers the issues found for browsers. I'll be curious to see what's in the node review", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -8716,7 +8416,6 @@ "review_state": null, "root_timestamp": "2026-06-12T19:15:30Z", "body": "One more thing the 🤖 mentioned:\r\n\r\nShould a send timeout be classified as `retryable` instead of `failure`, given that the Node HTTP transport treats its own timeout as `retryable` (http-transport-utils.ts), or is the intent to keep the fetch transport's **existing** drop-on-timeout semantics?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -8745,7 +8444,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-20T12:03:28Z", "body": "Sorry for the late review on this PR. I think having `AggreagationSelector` be a function was a mistake we made, since it implies that changing the returned values after configuring the `MetricReader` (in this case `PrometheusExporter`) would re-configure already-created instruments, which is not the case.\n\n**Suggestion:** let's implement this feature in a why that there's an `aggregationPreference` option that people can configure like so:\n\n```ts\nnew PrometheusExporter({\n aggregationPreference: {\n gauge: {\n // this here is an AggregationOption\n type: AggregationType.LAST_VALUE\n },\n // non-specified ones fall back to default\n }\n})\n```\n\nInternally, the `aggregationPreference` is converted to an `aggregationSelector` and published this way. This way it's clearer that nothing can be changed once the exporter has been instantiated. :)", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -8774,7 +8472,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-06-19T20:34:11Z", "body": "Great example! Thank you for adding!", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -8803,7 +8500,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-09T21:54:20Z", "body": "A number of nits, but looks good to me (along with a proposal to have a separate \"telemetry.ts\").", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -8832,7 +8528,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-20T21:02:54Z", "body": "LGTM. Thanks, Marc!\n\nI gather your `subscriberWithContextManagement` utility from https://github.com/open-telemetry/opentelemetry-js/pull/6387/changes#diff-516a26260144caf8cae79053916963fc7c5ca8aa24771ccc4d022b532c013b4c will need to be updated to use `token.dispose()`.\nHave you played with that?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -8861,7 +8556,6 @@ "review_state": null, "root_timestamp": "2026-07-07T20:11:38Z", "body": "Please don't forget to include a changelog.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -8890,7 +8584,6 @@ "review_state": null, "root_timestamp": "2026-07-09T13:55:55Z", "body": "Hi @matthieusieben - thanks for opening this PR for discussion. This is actually an issue that's mentioned in our docs: https://opentelemetry.io/docs/concepts/context-propagation/#security-best-practices\r\n\r\n**Q:** why not strip these headers at at something like a reverse proxy so that these never end up in the app at all? I suppose the idea is to don't allow external callers to add trace context/baggage but to allow internal ones. This is the most common way I've seen this issue handled in production deployments.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -8919,7 +8612,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-09T22:39:20Z", "body": "Thanks for the PR. One requested change below.\n\n(Also, it would be good to have a follow-up issue/PR that updates usage in this repo. E.g. here: https://github.com/open-telemetry/opentelemetry-js/blob/a79c4a4fc27b1a29286e60de4e9ce884e7c23c58/experimental/packages/opentelemetry-sdk-node/src/utils.ts#L1353-L1386)", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -8948,7 +8640,6 @@ "review_state": null, "root_timestamp": "2026-07-17T09:40:00Z", "body": "> Also, it would be good to have a follow-up issue/PR that updates usage in this repo\r\n\r\nref: #6925", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -8977,7 +8668,6 @@ "review_state": null, "root_timestamp": "2026-07-17T16:20:16Z", "body": "Having `AggreagationSelector` be a function was a mistake we made, since it implies that changing the returned values after configuring the `MetricReader` (in this case `PrometheusExporter`) would re-configure already-created instruments, which is not the case.\r\n\r\n**Suggestion:** let's implement this feature in a why that there's an `aggregationPreference` option that people can configure like so.\r\n\r\n```ts\r\nnew PrometheusExporter({\r\n aggregationPreference: {\r\n gauge: {\r\n // this here is an AggregationOption\r\n type: AggregationType.LAST_VALUE\r\n },\r\n // non-specified ones fall back to default\r\n }\r\n})\r\n```\r\n\r\nInternally, the `aggregationPreference` is converted to an `aggregationSelector` and published this way.\r\nThis way it's clearer that nothing can be changed once the exporter has been instantiated. :)", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -9006,7 +8696,6 @@ "review_state": null, "root_timestamp": "2026-07-27T15:06:28Z", "body": "@Babul422 This looks like a breaking change for gRPC. Have you tested it?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -9035,7 +8724,6 @@ "review_state": null, "root_timestamp": "2026-07-27T17:16:38Z", "body": "Right, but now they have differing behavior if your proposal is the new standard. I'll wait for others to chime in.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -9064,7 +8752,6 @@ "review_state": null, "root_timestamp": "2025-08-13T17:01:13Z", "body": "@lucas-gregoire - looks like the messaging semantic conventions are not *actually* stable yet, so this PR is blocked until is is formally marked as such. \r\n\r\n(Unless this is part of an effort to create prototypes to mark the messaging semconv as stable, if that is the case, please link the corresponding issue from SemConv here, thanks 🙂)", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -9093,7 +8780,6 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2025-09-10T16:31:13Z", "body": "See comments above.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -9122,7 +8808,6 @@ "review_state": null, "root_timestamp": "2025-09-24T16:47:07Z", "body": "Some context: We discussed this in the OTel JS SIG call today. Doing semconv changes to this instrumentation to get it closer to the current semconv spec state isn't *blocked* until messaging semconv is stabilized. However, because messaging semconv isn't stable yet, we cannot *call these changes \"stable\"* and we probably shouldn't use the `OTEL_SEMCONV_STABILITY_OPT_IN` envvar.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -9151,7 +8836,6 @@ "review_state": null, "root_timestamp": "2025-10-17T11:49:44Z", "body": "I opened https://github.com/open-telemetry/semantic-conventions/issues/2928 on the semconv repo to find a way how we could accept this change. With that proposal we would be able to use `messaging_latest_experimental` over `messaging` for updating to newer experimental semconv.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -9180,7 +8864,6 @@ "review_state": null, "root_timestamp": "2025-11-05T17:53:13Z", "body": "cc @blumamir @naseemkullah (component owners)", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -9209,7 +8892,6 @@ "review_state": null, "root_timestamp": "2025-12-10T20:28:24Z", "body": "Fixes and makes #3276 redundant", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -9238,7 +8920,6 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2025-11-20T11:52:47Z", "body": "This repo is very delicate with its dependencies. \n\nEssentially I agree most of what this PR is doing **except for the catch-all `:pinDevDependencies`** - we have a lockfile so that the few `devDependencies` that are unpinned don't cause havoc.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -9267,7 +8948,6 @@ "review_state": null, "root_timestamp": "2025-11-20T12:56:47Z", "body": ">Would it work if we add an explicit don't pin dev otel package rule?\r\n\r\nCould work. But also I think we're kind of okay with the settings we have now. Though, we may want to have our actions pinned, and potentially images pinned too as we did in the core repo.\r\n\r\nOther than that I don't think we necessarily need this change and I suspect blindly applying best practice settings right now will make us spend a lot of time sorting though issues that don't really move the needle for anyone. I'd leave it to folks that regularly update dependencies in this repo and have a feeling about what usually goes wrong with these sorts of things.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -9296,7 +8976,6 @@ "review_state": null, "root_timestamp": "2025-11-21T09:39:54Z", "body": "> So what prompted me to look at this was the fact that markdownlint was not reporting violations. What I saw was markdownlint was out of date and was pinned on the old one. I know from other projects pinning dev dependencies resulted in them being updated. \r\n\r\nThe problem here is not pinning. We have dependency Dependency Dashboard approvals on for most packages.\r\n\r\nWe don't update all packages at once since we have a massive amount of them here in this repo. We found that:\r\n- auto updating packages individually takes time away from reviewing contributions made by real people.\r\n- auto updating package in batches makes it pretty much impossible to troubleshoot when something goes wrong, you'll have to update on a package-by-package basis anyway.\r\n\r\nBoth approaches burnt people out as they had to deal with dependency updates all day. Some worked immediately, some did not. But all of them took visibility away from actual PRs people opened. So I pulled the plug on it and made Dashboard Approvals required. That means that if somebody has a few minutes to spare and they have Triage permissions on the repo, they can trigger and update and work through any problems without it always being ...[truncated]", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -9325,7 +9004,6 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-01-16T15:14:35Z", "body": "See linked PR that was manually updated.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -9354,7 +9032,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-01-05T15:42:49Z", "body": "Thank you for your contribution.\r\nYou will need to add a changelog and tests to this PR before we can merge it.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -9383,7 +9060,6 @@ "review_state": null, "root_timestamp": "2026-01-13T19:35:56Z", "body": "> You will need to add a changelog and tests to this PR before we can merge it.\r\n\r\n@maryliag This gets me and I have to check again almost everytime. The **core** repo requires PRs to explicitly include a CHANGELOG.md entry. The **contrib** repo does not, because the release process automatically adds changelog entries based on the commit titles.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -9412,7 +9088,6 @@ "review_state": null, "root_timestamp": "2026-01-13T21:28:16Z", "body": ">The contrib repo does not\r\n\r\nthis is what I get for having a bunch of tabs open to review and not checking the repo 🤦 \r\nNow just wait for tests to pass then 😄", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -9441,7 +9116,6 @@ "review_state": null, "root_timestamp": "2026-01-16T14:03:47Z", "body": "Silly question: is this not what the `View` concept is for (defining a custom aggregation based on meter+metric name?)\r\nhttps://opentelemetry.io/docs/languages/js/instrumentation/#configure-metric-views", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -9470,7 +9144,6 @@ "review_state": null, "root_timestamp": "2026-01-16T15:07:42Z", "body": "> > Silly question: is this not what the `View` concept is for (defining a custom aggregation based on meter+metric name?) https://opentelemetry.io/docs/languages/js/instrumentation/#configure-metric-views\r\n> \r\n> Does that work if the view buckets are more granular or have larger ranges than the underlying histogram? I've never heard of views and no idea how they work internally (and docs aren't clear)\r\n\r\nYes. The underlying mechanism the same for `View` and `advice` - when you use `Histogram#record()` it passes the data to the `Aggregation` that was created based on either the default, the `advice` or the `View`. In any case, you'll not loose data-granularity and there's no estimation going on.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -9499,7 +9172,6 @@ "review_state": null, "root_timestamp": "2026-02-02T15:03:51Z", "body": "@lukeramsden did you have time to review the latest comments?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -9528,7 +9200,6 @@ "review_state": null, "root_timestamp": "2026-04-15T16:59:16Z", "body": "Is there any sort of spec or documentation for this feature? Can we be certain this will always continue to work into the future?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -9557,7 +9228,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-03-09T17:12:30Z", "body": "Solution looks good, but I'm wondering whether we might want to tackle this from the instrumentation package given it's not technically unique to Lambda", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -9586,7 +9256,6 @@ "review_state": null, "root_timestamp": "2026-04-14T13:06:30Z", "body": "Hi @neilime \r\n\r\nThanks for working on this. I'm okay with having a new and more generic instrumentation (let's wait for more feedback). IMHO I would split this PR into smaller ones with this order\r\n\r\n- [ ] PR to add the new instrumentation (this one)\r\n- [ ] wait for the publication of the new instrumentation\r\n- [ ] PR to add the deprecation notice into `@opentelemetry/instrumentation-nestjs-core`\r\n- [ ] PR to replace it in auto instrumentations\r\n- [ ] after a period of time remove the deprecated instrumentation. Leaving a README for people looking at the instrumentation\r\n\r\n`@opentelemetry/instrumentation-fastify` had a similar process. Ref: https://github.com/open-telemetry/opentelemetry-js-contrib/issues/2647\r\n\r\nIf you agree with this process I think this PR should only contain the new instrumentation. And I'd prefer to not refactor the current instrumentation so we avoid maintenance on it.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -9615,7 +9284,6 @@ "review_state": null, "root_timestamp": "2026-04-29T08:59:09Z", "body": "@neilime any feedback from my previous comment?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -9644,7 +9312,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-06-23T00:55:09Z", "body": "Hey @garysassano, I was interested in the cold-start impact since this detector previously only read environment variables and this adds an HTTP request on the init path.\n\nI compared the current detector vs. this PR, on average (10 runs) the one-time cold-start cost was `+53 ms` at `128 MB` and `+5.5 ms` at `512 MB` of memory. So the performance impact seems okay, wondering if you had similar results or other thoughts?\n\nRegarding populating `cloud.availability_zone` with the AZ ID instead of the AZ name, it technically still fits the spec, but it will be inconsistent with the other AWS EC2 and ECS detectors in this package, which set the value to be the AZ name instead. So I suppose if consumers tries to ever query resources grouped by `cloud.availability_zone`, they'd need to account for this discrepancy. Given the limitation of Lambda only exposing the ID, I think this is okay for now, as you also mentioned in the description. I'm thinking that if the AZ name is ever exposed in the future (if that even makes sense for Lambda), we should switch to it as a breaking change. For now, could you leave a comment under `_fetchAvailabilityZone along` these lines?\n```\n// Lambda's metadata ...[truncated]", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -9673,7 +9340,6 @@ "review_state": null, "root_timestamp": "2026-05-13T13:56:35Z", "body": "@Genmin thanks for your contribution. Could you please sign the CLA so we can move on with this PR?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -9702,7 +9368,6 @@ "review_state": null, "root_timestamp": "2026-06-22T12:53:17Z", "body": "@Genmin \r\n\r\nCLA is still missing. Did you have any issue signing it?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -9731,7 +9396,6 @@ "review_state": null, "root_timestamp": "2026-05-05T17:04:00Z", "body": "ref #3379", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -9760,7 +9424,6 @@ "review_state": null, "root_timestamp": "2026-07-08T19:00:30Z", "body": "I briefly tried to get instr-koa working with `@koa/router@15` changes, but fell short and moved on.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -9789,7 +9452,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-13T16:22:25Z", "body": "thank you for working on this! You do have a lot of failed tests, so make sure those are fixed. I also added a few more test cases that should be added", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -9818,7 +9480,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-14T13:11:10Z", "body": "I added a few suggestion to help performance, but I'm still debating if this is the right approach for this feature.\n\nIn the spec we have: \"The collection name SHOULD NOT be extracted from db.query.text, when the database system supports query text with multiple collections in non-batch operations.\", which is the case for PG.\n\nPart of it is because the query can have several tables, so it can be hard to decide which one to select, but also because parsing itself can be very costly. If we have a lot of queries being executed, having to match the regex can be an issue for the performance, so I'm thinking there could be an opt-in somehow, at least initially.\nI'll discuss this with the maintainers on our weekly (wednesday), and get back to you.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -9847,7 +9508,6 @@ "review_state": null, "root_timestamp": "2026-07-15T19:21:05Z", "body": "@tejaswiverma121-byte we won't be able to merge this PR, because it doesn't follow the spec. \r\nA few other languages have the value of the collection easily available, but JS + PG doesn't, which is why you had to parse the query text, which is something that the spec tells you're not suppose to do.\r\nFor this reason, this feature can't be implemented here.\r\n\r\nI'm going to ask you to update the README instead, clarifying that this attribute is not being collected and that is expected. You can make the update in this PR (after reverting all the other changes) or if it's easier, just close this PR and open a new one with just the readme update.\r\n\r\nI appreciate you working on this either way!", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -9876,7 +9536,6 @@ "review_state": null, "root_timestamp": "2026-07-22T16:56:30Z", "body": "Same pattern is used in https://github.com/open-telemetry/opentelemetry-js-contrib/blob/main/scripts/bitrot.mjs it would be good to cover that one as well", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -9905,7 +9564,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-22T18:05:28Z", "body": "LGTM, I validated on my side, fix is working in Windows machines", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -9934,7 +9592,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-23T15:44:10Z", "body": "Thank you for working on this!\nCan you also update the readme for this package, to include the new metric", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -9963,7 +9620,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-24T17:17:32Z", "body": "Nice catch! Thanks", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -9992,7 +9648,6 @@ "review_state": "APPROVED", "root_timestamp": "2021-10-21T17:06:21Z", "body": "I don't have a way to verify the change. If anyone is able please do.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -10021,7 +9676,6 @@ "review_state": null, "root_timestamp": "2022-07-06T14:44:04Z", "body": "@open-telemetry/specs-approvers can anyone review/verify this PR? If no-one knows how I am going to close it as \"stale\".", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -10050,7 +9704,6 @@ "review_state": null, "root_timestamp": "2023-12-14T21:41:14Z", "body": "Related:\r\n\r\n* https://github.com/open-telemetry/opentelemetry-cpp/pull/2455", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -10079,7 +9732,6 @@ "review_state": null, "root_timestamp": "2023-12-18T09:13:59Z", "body": "@hdost \r\n\r\nPlease note that I used `-z`, lowercase, not `-Z`, uppercase, for https://github.com/open-telemetry/opentelemetry-cpp/pull/2455. Not sure which one you need to generate proto files.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -10108,7 +9760,6 @@ "review_state": null, "root_timestamp": "2023-12-18T15:24:28Z", "body": "@marcalff @ThomsonTan can you please review this PR? If we get both your approvals we should be able to move forward.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -10137,7 +9788,6 @@ "review_state": "APPROVED", "root_timestamp": "2023-12-19T09:10:32Z", "body": "Verified that code generation fails without the fix for SELINUX systems.\r\n\r\n```\r\n[malff@malff-desktop opentelemetry-proto]$ make\r\nrm -rf ./gen/cpp\r\nmkdir -p ./gen/cpp\r\ndocker run --rm -u 1000 -v/data/malff/CODE/MY_GITHUB/opentelemetry-proto:/data/malff/CODE/MY_GITHUB/opentelemetry-proto -w/data/malff/CODE/MY_GITHUB/opentelemetry-proto otel/build-protobuf:0.9.0 --proto_path=/data/malff/CODE/MY_GITHUB/opentelemetry-proto --cpp_out=./gen/cpp opentelemetry/proto/resource/v1/resource.proto\r\nEmulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.\r\nopentelemetry/proto/resource/v1/resource.proto: File does not reside within any path specified using --proto_path (or -I). You must specify a --proto_path which encompasses this file. Note that the proto_path must be an exact prefix of the .proto file names -- protoc is too dumb to figure out when two paths (e.g. absolute and relative) are equivalent (it's harder than you think).\r\nmake: *** [Makefile:56: gen-cpp] Error 1\r\n```\r\n\r\nVerified that code generation works with the fix for SELINUX systems.\r\n\r\nApproved, thanks for the fix.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -10166,7 +9816,6 @@ "review_state": null, "root_timestamp": "2024-03-17T22:25:00Z", "body": "@open-telemetry/specs-approvers please take a look. I think this is good to merge and verified to work by @marcalff.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -10195,7 +9844,6 @@ "review_state": null, "root_timestamp": "2026-03-15T23:20:36Z", "body": "@tigrannajaryan should we put the effort to merge this ?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -10224,7 +9872,6 @@ "review_state": "COMMENTED", "root_timestamp": "2025-05-30T06:31:32Z", "body": "In general, LGTM", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -10253,7 +9900,6 @@ "review_state": null, "root_timestamp": "2025-07-01T15:33:31Z", "body": "@DylanRussell, given https://github.com/open-telemetry/opentelemetry-proto/pull/669 is merged I think you can update this PR :wink:", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -10282,7 +9928,6 @@ "review_state": null, "root_timestamp": "2025-07-30T16:28:04Z", "body": "@open-telemetry/spec-sponsors, @open-telemetry/technical-committee, PTAL", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -10311,7 +9956,6 @@ "review_state": "COMMENTED", "root_timestamp": "2025-08-20T14:17:43Z", "body": "@DylanRussell can you please also attach a prototype implementation that shows how this works? You can take existing implementation in one of the languages or in the Collector and fork/modify it to demonstrate the change. Collectors otlp receiver/exporter fork would be ideal since it shows both the client and server side.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -10340,7 +9984,6 @@ "review_state": null, "root_timestamp": "2025-08-20T14:21:42Z", "body": "I think we need a description of how the new and old implementations should interoperate. Explain the operation in the following 3 scenarios:\r\n- Old client implementing the spec before this change connects to a new server implementing the spec after this change.\r\n- New client connects to old server.\r\n- New client connects to new server.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -10369,7 +10012,6 @@ "review_state": null, "root_timestamp": "2026-04-08T19:33:27Z", "body": "A bit unusual to have such a large value in the dictionary but makes sense overall.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -10398,7 +10040,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-17T18:42:39Z", "body": "Removing dependency from `rules_go` solves bazel 9.x issues. \r\n\r\nOn BCR release, unless bcr publish workflow allows generating MODULE.bazel similar to build-check.yaml, this might get stuck with same error.\r\n\r\n`Keeping bazel out of core` and `Automated BCR` seems mutually exclusive unless there is way include bazel files in release archive. Happy to be proven wrong, and learn something new.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -10427,7 +10068,6 @@ "review_state": null, "root_timestamp": "2026-07-17T19:43:47Z", "body": "> What we do _not_ do in this proto repo is generate client libraries that are consumable by others, we expect that to be owned/maintained donwstream. E.g. In many Otel SDKs / Collector there's bespoke / optimised OTLP proto generation that wouldn't use the protoc generated code.\r\n\r\nThe approach makes more sense given this ask. I'm still doubtful about overlay as I haven't seen any working example or doc references. I'll keep digging.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -10456,7 +10096,6 @@ "review_state": null, "root_timestamp": "2026-07-19T09:53:45Z", "body": "Overlay won't be useful as its a downstream mechanism/control for non bazel upstream deps. It can be done locally, and also in BCR by creating manual entry with Overlay (BCR release process so far).\r\n\r\nBut automated BCR needs the lib to be bazel module, and a bazel module cannot be overlayed (Immutable entry in BCR).\r\n\r\nAssuming automated BCR is still preferred, the cleanest way is to make it a bazel module, and release proto only as baseline, let downstream generate the lib bindings.\r\n\r\nIf more time is needed for downstream community, we have to include proto+lang bindings (same as last BCR overlay) before eventually switching to proto only release.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -10485,7 +10124,6 @@ "review_state": null, "root_timestamp": "2026-09-01T19:45:40Z", "body": "@Shriprasad-P the test is unfortunately still failing here", - "measurement_date": "2026-09-02", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -10514,7 +10152,6 @@ "review_state": null, "root_timestamp": "2026-07-07T16:30:16Z", "body": "@lmolkova \r\n\r\nDoes this pr have conflicts with https://github.com/open-telemetry/opentelemetry-python-genai/pull/90?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -10543,7 +10180,6 @@ "review_state": null, "root_timestamp": "2026-06-08T13:52:35Z", "body": "Hi @Nik-Reddy , will have to wait for a maintainer to merge.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -10572,7 +10208,6 @@ "review_state": null, "root_timestamp": "2026-06-21T19:19:23Z", "body": "@lmolkova I am thinking we can use openinference for adding instrumentation coverage for cohere. wdyt?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -10601,7 +10236,6 @@ "review_state": null, "root_timestamp": "2026-06-26T04:46:48Z", "body": "> @lmolkova I am thinking we can use openinference for adding instrumentation coverage for cohere. wdyt?\r\n\r\nit doesn't seem like they have instrumentation for it", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -10630,7 +10264,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-22T19:36:45Z", "body": "A few more comments. \n\nThe key question, @Nik-Reddy are you going to follow up with real instrumentation right away? We already have a couple of empty instrumentations in this repo and unless you'd like to work on actual cohere instrumentation, I would prefer to not add another one.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -10659,7 +10292,6 @@ "review_state": null, "root_timestamp": "2026-07-08T14:50:26Z", "body": "Everything LGTM!! We can wait till this https://github.com/open-telemetry/semantic-conventions-genai/issues/344 is addressed as I believe we need to update semantic conventions and bring in new semconv package. Lmk Wdyt @wrisa .", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -10688,7 +10320,6 @@ "review_state": null, "root_timestamp": "2026-06-19T21:10:46Z", "body": "Howdy there!! Thanks for your contribution. PR looks good to me. I had few questions around the direction we want to take for this issue, so I posted few questions on the issue. We will get to this once those are confirmed.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -10717,7 +10348,6 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-07-21T00:15:13Z", "body": "This PR covers up a bigger issue that we, instead of returning original stream, we return parsed one that has different API shape.\n\nI'm proposing to fix the bigger issue here - https://github.com/open-telemetry/opentelemetry-python-genai/pull/278 so that we keep instrumentation transparent and don't modify types / expectations from pure underlying libraries.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -10746,7 +10376,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-14T23:12:15Z", "body": "LGTM, just some minor questions and concerns around build and release", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -10775,7 +10404,6 @@ "review_state": null, "root_timestamp": "2026-07-15T01:48:49Z", "body": "@lmolkova do you think we should separate this into two PRs like say:\n\n1. Basic setup(examples, gh, lint and other setup) for agent scope like we did in python contrib.\n2. Add all the instrumentation as this is a donation.\n\nThis might basically reduce the cognitive load on reviewers. Wdyt?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -10804,7 +10432,6 @@ "review_state": null, "root_timestamp": "2026-07-15T02:06:40Z", "body": "@eternalcuriouslearner it's not a super-strong opinion, but I'd prefer to have minimal viable instrumentation in one PR. \r\n\r\n1. It allows us to simplify migration for openinference and other instrumentations\r\n2. it prevents shell instrumentations we have today (weaviate, claude agents sdk)\r\n3. My mental load is actually lower when I look at everything at once\r\n4. Given that instrumentation code in the library is super-thin and leverages util-genai, it's easy to review\r\n5. We'll improve these over time, conformance and general safety is the top priority for me at the moment.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -10833,7 +10460,6 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-07-21T00:12:21Z", "body": "it seems agentscope comes with native otel instrumentation (but in a broken way since it depends on the exporter) - do we know if instrumentation is good? does it follow conventions? \n\nSince there is an existing instrumentation, we should not invent another one. Was there an attempt to contribute directly to https://github.com/agentscope-ai/agentscope if there any gaps?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -10862,7 +10488,6 @@ "review_state": null, "root_timestamp": "2026-07-23T04:24:44Z", "body": "Ran into one concrete case of this while looking at the native extractor recently: gen_ai.response.finish_reasons is hardcoded as the JSON string '[\"stop\"]' (with a FIXME) where the registry type is string[] — https://github.com/agentscope-ai/agentscope/blob/7af58b119bddeb42b12d8d93460d424fad253234/src/agentscope/middleware/_tracing/_extractor.py#L337-L339", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -10891,7 +10516,6 @@ "review_state": null, "root_timestamp": "2026-07-23T04:43:38Z", "body": "@123liuziming @AgentGymLeader we should try checking if AgentScope authors would be open to contributions before moving on with instrumentation here.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -10920,7 +10544,6 @@ "review_state": null, "root_timestamp": "2026-07-15T22:49:34Z", "body": "Thanks for picking this up @lmolkova! I'm glad to see the streaming timing work from my original PR #13 moving forward. Happy to review this since I worked on the initial implementation.\n\nAlso happy to co-author this if it helps, since a lot of the design decisions here came out of the iteration on #13. Let me know!", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -10949,7 +10572,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-25T19:05:31Z", "body": "Couple of questions and minor comment to change tests to be more realistic, looks good otherwise!", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -10978,7 +10600,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-27T12:48:39Z", "body": "Thanks a lot for your contribution. Couple of things:\n\n1. Can you please check if groq has native instrumentation?\n2. If yes, can you see if they're emitting genai spans or not.\n3. After this analysis can you please cut down the verbosity of this pr? You can space it like: skeleton, inference spans, agent spans etc. For now if you don't mind can you please close this pr and first let me know if 1 & 2 are not in place before we proceed adding the telemetry support.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -11007,7 +10628,6 @@ "review_state": null, "root_timestamp": "2026-07-27T11:15:53Z", "body": "@srinjoy356 the `README.rst` and several modules point readers at `MIGRATION_REPORT.md` for the gap list, but that filename is excluded by the root `.gitignore` (\"Migration review reports generated by the review-migration skill\"), and it isn't in this PR's head or anywhere else in the tree.\n\nSince `README.rst` becomes the PyPI description, that leaves a dangling pointer for anyone installing the package. Dropping the references, or folding the parts readers need into the README itself, would probably be cleaner than pointing at a file that by design never gets committed.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -11036,7 +10656,6 @@ "review_state": null, "root_timestamp": "2026-07-27T11:33:41Z", "body": "@srinjoy356 checked 647446e — the references are gone from the package and the Known limitations section reads well on its own. Thanks for turning it around so fast.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -11065,7 +10684,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-27T12:47:14Z", "body": "Thanks a lot for your contribution @srinjoy356. Couple of things:\n\n1. Can you please check if haystack has native instrumentation?\n2. If yes, can you see if they're emitting genai spans or not.\n3. After this analysis can you please cut down the verbosity of this pr? You can space it like: skeleton, inference spans, agent spans etc. For now if you don't mind can you please close this pr and first let me know if 1 & 2 are not in place before we proceed adding the telemetry support.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -11094,7 +10712,6 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-07-28T19:04:25Z", "body": "This seems to be a duplicate of #24\n\nA more generic solution to this problem is coming via https://github.com/open-telemetry/opentelemetry-specification/pull/4931\n\nIn the current scope this problem can be solved with custom context key and custom log record processor which would be a recommended approach until context-scoped attributes land in OTel spec and python implementation. \n\nIs there a strong use-case to stamp information on events only and avoid spans?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -11123,7 +10740,6 @@ "review_state": null, "root_timestamp": "2026-07-28T19:39:14Z", "body": "> > According to the official OpenTelemetry GenAI Semantic Conventions (0.65b0), gen_ai.tool.definitions is only defined on inference/chat spans (chat, text_completion), not on agent invocation (invoke_agent) spans.\r\n> \r\n> This seems wrong -- why can't tool definitions be on invoke agent spans ? You can define tools on an agent\r\n\r\nwhere did it come from? let's fix, I agree it's wrong", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -11152,7 +10768,6 @@ "review_state": null, "root_timestamp": "2026-07-13T08:22:47Z", "body": "@maestro24 when you sign the CLA we can review the PR, thanks", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -11181,7 +10796,6 @@ "review_state": null, "root_timestamp": "2026-07-23T14:31:33Z", "body": "👋 Please update this PR's branch over the latest `main`: it predates our link-checker [switch to Lychee][#10911] and can hit CI failures unrelated to your changes. For the how — including conflicts and after-update steps — see [#10990][]. Thanks for your contribution!\n\n[#10911]: https://github.com/open-telemetry/opentelemetry.io/pull/10911\n[#10990]: https://github.com/open-telemetry/opentelemetry.io/issues/10990", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -11210,7 +10824,6 @@ "review_state": null, "root_timestamp": "2026-07-28T05:07:22Z", "body": "Hi @jraleigh, could you run the `npm run fix:link-cache` script locally and commit the results?\r\n\r\nIt looks like the maintainers don't have permission to push changes to this PR.\r\n\r\nThanks!", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -11239,7 +10852,6 @@ "review_state": null, "root_timestamp": "2026-07-16T06:24:26Z", "body": "Hi @Goran-n, could you please review and sign the CLA? Thanks!", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -11268,7 +10880,6 @@ "review_state": null, "root_timestamp": "2026-07-18T18:42:32Z", "body": "what @vitorvasc said re CLA, but this is also a vendor not an application integration", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -11297,7 +10908,6 @@ "review_state": null, "root_timestamp": "2026-07-23T14:31:36Z", "body": "👋 Please update this PR's branch over the latest `main`: it predates our link-checker [switch to Lychee][#10911] and can hit CI failures unrelated to your changes. For the how — including conflicts and after-update steps — see [#10990][]. Thanks for your contribution!\n\n[#10911]: https://github.com/open-telemetry/opentelemetry.io/pull/10911\n[#10990]: https://github.com/open-telemetry/opentelemetry.io/issues/10990", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -11326,7 +10936,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-17T22:23:19Z", "body": "I like where this is heading towards. However, I do have several questions about the terminology and whether the statements are precise or confusing. I don't see these as blockers though.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -11355,7 +10964,6 @@ "review_state": null, "root_timestamp": "2026-07-23T14:31:39Z", "body": "👋 Please update this PR's branch over the latest `main`: it predates our link-checker [switch to Lychee][#10911] and can hit CI failures unrelated to your changes. For the how — including conflicts and after-update steps — see [#10990][]. Thanks for your contribution!\n\n[#10911]: https://github.com/open-telemetry/opentelemetry.io/pull/10911\n[#10990]: https://github.com/open-telemetry/opentelemetry.io/issues/10990", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -11384,7 +10992,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-23T05:27:14Z", "body": "LGTM", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -11413,7 +11020,6 @@ "review_state": null, "root_timestamp": "2026-07-23T14:31:44Z", "body": "👋 Please update this PR's branch over the latest `main`: it predates our link-checker [switch to Lychee][#10911] and can hit CI failures unrelated to your changes. For the how — including conflicts and after-update steps — see [#10990][]. Thanks for your contribution!\n\n[#10911]: https://github.com/open-telemetry/opentelemetry.io/pull/10911\n[#10990]: https://github.com/open-telemetry/opentelemetry.io/issues/10990", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -11442,7 +11048,6 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-07-24T19:29:09Z", "body": "Thanks @fractalwrench! I've updated the branch over the latest `main`. One alert-syntax fix needed (suggestion below), then this is good to go from the docs side.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -11471,7 +11076,6 @@ "review_state": null, "root_timestamp": "2026-07-23T14:31:52Z", "body": "👋 Please update this PR's branch over the latest `main`: it predates our link-checker [switch to Lychee][#10911] and can hit CI failures unrelated to your changes. For the how — including conflicts and after-update steps — see [#10990][]. Thanks for your contribution!\n\n[#10911]: https://github.com/open-telemetry/opentelemetry.io/pull/10911\n[#10990]: https://github.com/open-telemetry/opentelemetry.io/issues/10990", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -11500,7 +11104,6 @@ "review_state": null, "root_timestamp": "2026-07-17T14:48:17Z", "body": "This seems to fix the issue, @chalin\r\n\r\n\"Screenshot", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -11529,7 +11132,6 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-07-18T11:05:28Z", "body": "- Thanks, I'll take a look soon.\n- @Babul422 please answer the questions added to the opening comment of the PR.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -11558,7 +11160,6 @@ "review_state": null, "root_timestamp": "2026-07-23T14:31:55Z", "body": "👋 Please update this PR's branch over the latest `main`: it predates our link-checker [switch to Lychee][#10911] and can hit CI failures unrelated to your changes. For the how — including conflicts and after-update steps — see [#10990][]. Thanks for your contribution!\n\n[#10911]: https://github.com/open-telemetry/opentelemetry.io/pull/10911\n[#10990]: https://github.com/open-telemetry/opentelemetry.io/issues/10990", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -11587,7 +11188,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-21T13:19:33Z", "body": "LGTM to ja docs", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -11616,7 +11216,6 @@ "review_state": null, "root_timestamp": "2026-07-23T14:32:01Z", "body": "👋 Please update this PR's branch over the latest `main`: it predates our link-checker [switch to Lychee][#10911] and can hit CI failures unrelated to your changes. For the how — including conflicts and after-update steps — see [#10990][]. Thanks for your contribution!\n\n[#10911]: https://github.com/open-telemetry/opentelemetry.io/pull/10911\n[#10990]: https://github.com/open-telemetry/opentelemetry.io/issues/10990", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -11645,7 +11244,6 @@ "review_state": null, "root_timestamp": "2026-07-23T14:32:04Z", "body": "👋 Please update this PR's branch over the latest `main`: it predates our link-checker [switch to Lychee][#10911] and can hit CI failures unrelated to your changes. For the how — including conflicts and after-update steps — see [#10990][]. Thanks for your contribution!\n\n[#10911]: https://github.com/open-telemetry/opentelemetry.io/pull/10911\n[#10990]: https://github.com/open-telemetry/opentelemetry.io/issues/10990", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -11674,7 +11272,6 @@ "review_state": null, "root_timestamp": "2026-07-27T10:41:10Z", "body": "Will do today, thanks @mwimpelberg28 🙏🏻", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -11703,7 +11300,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-27T11:49:34Z", "body": "Thanks @mwimpelberg28, looking great, just a few small suggestions, feel free to accept them or not. Except the `lock-in`, which I would not call bloque in Spanish, and the fediverse being fediverso, the rest is up to your taste.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -11732,7 +11328,6 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-07-21T09:00:13Z", "body": "1箇所、リンクの箇所だけコメントしました。\n\n本文は完璧です。", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -11761,7 +11356,6 @@ "review_state": null, "root_timestamp": "2026-07-23T14:32:10Z", "body": "👋 Please update this PR's branch over the latest `main`: it predates our link-checker [switch to Lychee][#10911] and can hit CI failures unrelated to your changes. For the how — including conflicts and after-update steps — see [#10990][]. Thanks for your contribution!\n\n[#10911]: https://github.com/open-telemetry/opentelemetry.io/pull/10911\n[#10990]: https://github.com/open-telemetry/opentelemetry.io/issues/10990", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -11790,7 +11384,6 @@ "review_state": null, "root_timestamp": "2026-07-23T14:32:12Z", "body": "👋 Please update this PR's branch over the latest `main`: it predates our link-checker [switch to Lychee][#10911] and can hit CI failures unrelated to your changes. For the how — including conflicts and after-update steps — see [#10990][]. Thanks for your contribution!\n\n[#10911]: https://github.com/open-telemetry/opentelemetry.io/pull/10911\n[#10990]: https://github.com/open-telemetry/opentelemetry.io/issues/10990", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -11819,7 +11412,6 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-07-27T12:11:46Z", "body": "Kindly correct the links on all sections, the rest LGTM, thanks @manduinca 🎉", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -11848,7 +11440,6 @@ "review_state": null, "root_timestamp": "2026-07-23T14:26:00Z", "body": "👋 Please update this PR's branch over the latest `main`: it predates our link-checker [switch to Lychee][#10911] and can hit CI failures unrelated to your changes. For the how — including conflicts and after-update steps — see [#10990][]. Thanks for your contribution!\n\n[#10911]: https://github.com/open-telemetry/opentelemetry.io/pull/10911\n[#10990]: https://github.com/open-telemetry/opentelemetry.io/issues/10990", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -11877,7 +11468,6 @@ "review_state": null, "root_timestamp": "2026-07-28T05:09:03Z", "body": "Hi @cxdy, could you run the `npm run fix:link-cache` script locally and commit the results?\r\n\r\nThanks!", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -11906,7 +11496,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-28T05:56:05Z", "body": "@cxdy, thanks!", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -11935,7 +11524,6 @@ "review_state": null, "root_timestamp": "2026-07-21T07:07:34Z", "body": "@snowmen233 please make sure you sign the CLA otherwise we can not accept the PR. Thank you.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -11964,7 +11552,6 @@ "review_state": null, "root_timestamp": "2026-07-23T14:25:47Z", "body": "👋 Please update this PR's branch over the latest `main`: it predates our link-checker [switch to Lychee][#10911] and can hit CI failures unrelated to your changes. For the how — including conflicts and after-update steps — see [#10990][]. Thanks for your contribution!\n\n[#10911]: https://github.com/open-telemetry/opentelemetry.io/pull/10911\n[#10990]: https://github.com/open-telemetry/opentelemetry.io/issues/10990", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -11993,7 +11580,6 @@ "review_state": null, "root_timestamp": "2026-07-22T13:33:24Z", "body": "@SergeyKleyman as second maintainer of the distro, can you please do a technical review and give us a thumbs up or down. After this PR is merged we can do the infra changes such that @open-telemetry/php-distro-approvers has ownership of these files.", - "measurement_date": "2026-08-01", "role": "context", "stability": null, "recorded_label": null, @@ -12021,7 +11607,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-22T14:10:31Z", "body": "LGTM for JA part", - "measurement_date": "2026-08-01", "role": "context", "stability": null, "recorded_label": null, @@ -12049,7 +11634,6 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-07-22T15:49:37Z", "body": "I reviewed with copilot, here's it's main comment when I asked to ensure completeness per our repo's ownership conventions:\r\n\r\n> The new child mappings overlap the existing content/en/docs/zero-code/go ownership and label rules. Our component-owner logic accumulates every prefix match, and the labeler applies every matching glob, so compile-time PRs will still involve go-approvers and go-instrumentation-approvers and receive both SIG labels. Please adjust the parent mappings so the compile-time subtree is excluded or otherwise represented without overlapping ownership.\r\n\r\nOf course, the shared ownership might have been intentional. Pls clarify.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -12078,7 +11662,6 @@ "review_state": null, "root_timestamp": "2026-07-23T13:52:58Z", "body": "👋 Please update this PR's branch over the latest `main`: it predates our link-checker [switch to Lychee][#10911] and can hit CI failures unrelated to your changes. For the how — including conflicts and after-update steps — see [#10990][]. Thanks for your contribution!\n\n[#10911]: https://github.com/open-telemetry/opentelemetry.io/pull/10911\n[#10990]: https://github.com/open-telemetry/opentelemetry.io/issues/10990", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -12107,7 +11690,6 @@ "review_state": null, "root_timestamp": "2026-07-23T13:58:54Z", "body": "(I've done the rebase :)", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -12136,7 +11718,6 @@ "review_state": null, "root_timestamp": "2026-07-28T02:14:03Z", "body": "@IrinaKarantoniou @dnanuti @elifsamedin Please add your reviews. Thanks!", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -12165,7 +11746,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-28T12:16:57Z", "body": "LGTM ✅", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -12194,7 +11774,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-28T05:09:59Z", "body": "Could you also run the `npm run fix:link-cache` script locally and commit the results?\n\nThanks!", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -12223,7 +11802,6 @@ "review_state": null, "root_timestamp": "2026-07-27T05:30:41Z", "body": "@mwimpelberg28 I believe the `co-authored` added by Claude messed up the EasyCLA.\r\nIt should be `assisted by` instead.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -12252,7 +11830,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-27T04:41:53Z", "body": "LGTM!", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -12281,7 +11858,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-27T04:39:31Z", "body": "LGTM!", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -12310,7 +11886,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-27T04:29:18Z", "body": "LGTM!", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -12339,7 +11914,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-27T04:31:19Z", "body": "LGTM!", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -12368,7 +11942,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-08T15:30:32Z", "body": "Thanks for pushing this through, and for the register-once fix. Reusing the signal streams across both waits is the right call to avoid dropping a second signal during the drain.\n\nOn the outstanding copilot-reviewer note about the dropped `JoinHandle`, I don't think it's a problem. In Rust the process terminates when `main` returns, and the detached signal-handler thread is reaped at that point rather than keeping the process alive. It would only block exit if something joined it, and nothing does. Your code comments already capture this, so I'm comfortable leaving the thread detached.\n\nThe double-signal convention matches the Go Collector, and a 60s drain deadline aligned with the default Kubernetes `terminationGracePeriodSeconds` is a sensible default. LGTM.\n\nOne optional and non-blocking follow-up: patch coverage on the new signal paths is on the lighter side, so a small test exercising the second-signal force-exit path would be welcome if it is easy to add.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -12397,7 +11970,6 @@ "review_state": null, "root_timestamp": "2026-07-20T20:36:22Z", "body": "@drewrelmas should we update this PR to reflect the progress in https://github.com/open-telemetry/otel-arrow/pull/3454?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -12426,7 +11998,6 @@ "review_state": null, "root_timestamp": "2026-07-20T21:17:00Z", "body": "Yes - @ThomsonTan could you please take a look at https://github.com/open-telemetry/otel-arrow/blob/main/rust/otap-dataflow/docs/telemetry/item-attributes.md and update this implementation accordingly?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -12455,7 +12026,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-06-30T16:03:32Z", "body": "I'm looking forward to seeing this proposal implemented!\n\nI haven't finished my review yet, but I'd rather send this first batch of feedback now and continue as soon as possible, probably tomorrow.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -12484,7 +12054,6 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-07-01T13:48:16Z", "body": "Thank you, Lalit, for putting together this excellent specification for integrating an eBPF-based load-balancing mechanism with `SO_REUSEPORT` socket groups.\n\nMy main comment is that we should split this document into two parts: one focused on NUMA discovery (see one of my comment), and the other on eBPF load balancing.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -12513,7 +12082,6 @@ "review_state": null, "root_timestamp": "2026-07-20T21:46:01Z", "body": "Note there is a heavy discussion about how to package eBPF artifacts for Collector.\r\n\r\nhttps://github.com/open-telemetry/opentelemetry-collector/issues/15430#issuecomment-4907834085", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -12542,7 +12110,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-20T21:42:23Z", "body": "Looks good. @lquerel should have a look at synchronization primitives, the Mutex. Note we merged https://github.com/open-telemetry/otel-arrow/pull/3323 as a foundation for this.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -12571,7 +12138,6 @@ "review_state": null, "root_timestamp": "2026-06-29T16:14:24Z", "body": "Nit: Can we update `rust/otap-dataflow/docs/memory-limiter-phase1.md` too? The “Receiver Behavior Under Hard Pressure” section still says Soft never rejects, but with `soft_action: shed`, Soft can now use the same receiver shedding behavior in `enforce` mode.\r\n\r\nAlso, should we rename `memory-limiter-phase1.md` to something like `global-memory-limiter.md` and replace the “Phase 1” wording in the title/intro with “global/ process-wide memory limiter”? This PR still changes the global limiter, while the retained-work budgeting / tenant/group isolation direction is tracked separately in #3272. A behavior-based doc name may avoid Phase 1/Phase 2 confusion as both efforts evolve.\r\n\r\nFor the same reason, could we also reword “Phase 2 of the process-wide memory limiter” in the PR description? Maybe “follow-up to the process-wide memory limiter” would be clearer.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -12600,7 +12166,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-06-29T16:15:05Z", "body": "Thanks. LGTM. Good to have doc update before merge.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -12629,7 +12194,6 @@ "review_state": null, "root_timestamp": "2026-06-30T17:52:49Z", "body": "> Rename memory-limiter-phase1.md → global-memory-limiter.md + drop the \"Phase 1\" wording — I'd prefer to keep this out of this PR and do it as a dedicated follow-up. The rename touches cross-references in docs/configuration.md (which this PR otherwise doesn't change), and the \"Phase 1 → global / process-wide\" reframing is really part of the naming evolution tracked in https://github.com/open-telemetry/otel-arrow/issues/3272, so it reads cleaner as its own focused change than mixing a doc-wide rename into the soft_action feature PR. I'm happy to open that follow-up right after this merges — target name global-memory-limiter.md, intro reworded to \"global / process-wide memory limiter\". Let me know if you'd rather I bundle it here instead.\r\n\r\nAgree on this.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -12658,7 +12222,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-09T19:19:36Z", "body": "Reviewed critically and validated locally (checked out the branch, ran targeted tests + compiled the new bench). Approving — everything below is non-blocking.\n\n## Validation\n\n- `config` 66 passed, `engine::memory_limiter` 24 passed (incl. all 8 new), `memory_admission` bench compiles, clippy clean on `config`.\n- All three `should_shed_ingress()` sites and every ingress consumer (otlp/otap/syslog_cef/user_events receivers + the tower layer) route through the new predicate; no stray `== Hard` shed comparisons remain.\n- `soft_action` propagation mirrors the existing `mode` pattern exactly (atomic on the process inner, plain field on receiver inners), and `configure()` runs before receivers snapshot.\n- Default path is byte-identical (`shed_decision(_, _, Observe)` == old `Enforce && Hard`). Hysteresis is sound: with `soft_action: shed`, the existing Soft→Normal reopen threshold (`soft_limit − hysteresis`) becomes the shed-reopen point, so no oscillation.\n- Doc sweep is thorough — grepped untouched receiver `.rs`/README files for stale \"Soft is informational / Hard-only\" wording; none remain.\n\n## Non-blocking notes\n\n1. **Process-wide `MemoryPressureState::should_shed_ingress()` has no p ...[truncated]", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -12687,7 +12250,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-10T04:54:39Z", "body": "@timr-dev Overall, this looks good to me. I think there's a small issue in the logic around the soft/hard limits combined with hysteresis.\n\n@lalit During the review of this PR, I noticed a few things related to a previous PR on the memory limiter side that seemed suboptimal. I'll file a separate report for those.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -12716,7 +12278,6 @@ "review_state": null, "root_timestamp": "2026-07-10T16:31:52Z", "body": "> @lalit During the review of this PR, I noticed a few things related to a previous PR on the memory limiter side that seemed suboptimal. I'll file a separate report for those.\r\n\r\nSure @lquerel. Thanks for reviewing that part. Will look forward to your findings.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -12745,7 +12306,6 @@ "review_state": null, "root_timestamp": "2026-07-13T22:35:54Z", "body": "> > @lalit During the review of this PR, I noticed a few things related to a previous PR on the memory limiter side that seemed suboptimal. I'll file a separate report for those.\r\n> \r\n> Sure @lquerel. Thanks for reviewing that part. Will look forward to your findings.\r\n\r\n@lquerel - I dug a bit more into the memory limiter after your comment. \r\n\r\nThe [docs](https://github.com/open-telemetry/otel-arrow/blob/main/rust/otap-dataflow/docs/memory-limiter-phase1.md#tradeoffs) describe the mechanics: sampling is periodic and enforcement happens only at ingress when the state reaches `Hard`. So the limitation is mostly implied rather than stated directly: this can reduce new memory growth, but it is not a strict cap and it cannot immediately reclaim memory already held inside the process.\r\n\r\n The real issues I found are more specific:\r\n\r\n - The default hysteresis is derived from the soft-to-hard gap and can become very large. With a wide gap, `Soft` can become sticky because memory must fall far below the soft limit to return to `Normal`. This mainly affects state reporting today because `Soft` does not reject ingress.\r\n - `source: auto` only checks the current/leaf cgroup limit. In nes ...[truncated]", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -12774,7 +12334,6 @@ "review_state": null, "root_timestamp": "2026-07-22T18:02:18Z", "body": "@timr-dev - One context update since this PR was first opened: while you were out, #3484 landed the pressure-aware rate throttling design, and #3529 is now implementing that path. That design uses Soft pressure as the trigger for selective receiver-local throttling, where over-rate scopes are throttled while within-rate scopes continue.\r\n\r\nGiven that newer direction, should we still keep `soft_action: shed` in this PR as a separate global Soft-pressure emergency mode? Or should this PR avoid adding that behavior now, and leave Soft-pressure throttling to the #3529 rate-throttling implementation? I realize this may mean some rework, but I think it is worth checking now so we do not land two overlapping Soft-pressure control models.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -12803,7 +12362,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-01T18:19:27Z", "body": "Looks good to me. @lalitb please take another look.\nThanks @AvinashDevX.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -12832,7 +12390,6 @@ "review_state": null, "root_timestamp": "2026-07-01T18:20:04Z", "body": "@AvinashDevX how would you feel setting the default batch size to 64KiB instead of disabled by default?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -12861,7 +12418,6 @@ "review_state": null, "root_timestamp": "2026-07-08T15:22:53Z", "body": "@AvinashDevX thanks for iterating on this. Two items before I merge.\n\n1. Are you still planning to switch `batch_size` from a record count to a byte size defaulting to 64 KiB, as we discussed on July 1? I don't see it in the current diff. I'd like the receiver to batch by default so downstream messages are reasonably sized without extra configuration.\n\n2. Could you confirm the early-return path lalitb flagged in `internal_telemetry_receiver/mod.rs`? His concern is that returning there without flushing can drop up to `threshold - 1` buffered records, and I want to be sure a shutdown or channel close on that branch cannot lose a partial batch. Tests covering the timer-flush and byte-split paths would also help lock this down.\n\nHappy to re-review once those are addressed. Thanks!", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -12890,7 +12446,6 @@ "review_state": null, "root_timestamp": "2026-07-14T16:02:26Z", "body": "@AvinashDevX this looks good to me - before merging can you please update the PR title and description to denote batching is now on by default for future viewing in Git history?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -12919,7 +12474,6 @@ "review_state": null, "root_timestamp": "2026-07-20T22:46:09Z", "body": "@AvinashDevX will you please resolve? Thanks!", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -12948,7 +12502,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-07T22:19:41Z", "body": "Thanks for looking into this change @ethanchewy! Have a few comments below to hopefully future proof this solution.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -12977,7 +12530,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-24T21:12:24Z", "body": "LGTM, left one small maintainability comment", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -13006,7 +12558,6 @@ "review_state": null, "root_timestamp": "2026-07-20T20:14:31Z", "body": "In @albertlockett’s absence, I’d like @JakeDern to review this PR. Thanks", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -13035,7 +12586,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-21T04:54:32Z", "body": "Thank you for working hard on this! I see there have been a lot of iterations between this PR and the previous.\n\nApologies if this has already been discussed elsewhere, but I'm wondering why the solution is not to call `OtapArrowRecords::decode_transport_optimized_ids` in the various `try_from` implementations? That would be a very small change (3 lines or so) and I think solve the problem.\n\nIn my opinion view creation should not fail unless we're seeing data corruption as there's no alternative for a caller in the failure case. This PR seems to take the same opinion, as we have updated all callers to decode before creating the view to avoid it. \n\nI'm wondering then why we're letting callers make the mistake of not decoding in a way that can only be found at runtime instead of proactively doing it in the implementation.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -13064,7 +12614,6 @@ "review_state": null, "root_timestamp": "2026-07-24T03:10:40Z", "body": "> > Thank you for working hard on this! I see there have been a lot of iterations between this PR and the previous.\r\n> \r\n> > \r\n> \r\n> > Apologies if this has already been discussed elsewhere, but I'm wondering why the solution is not to call `OtapArrowRecords::decode_transport_optimized_ids` in the various `try_from` implementations? That would be a very small change (3 lines or so) and I think solve the problem.\r\n> \r\n> > \r\n> \r\n> > In my opinion view creation should not fail unless we're seeing data corruption as there's no alternative for a caller in the failure case. This PR seems to take the same opinion, as we have updated all callers to decode before creating the view to avoid it.\r\n> \r\n> > \r\n> \r\n> > I'm wondering then why we're letting callers make the mistake of not decoding in a way that can only be found at runtime instead of proactively doing it in the implementation.\r\n> \r\n> \r\n> \r\n> Thanks for raising this. \r\n> \r\n> \r\n> \r\n> I agree that callers should not have to remember to decode first, and that this error should not normally surface in application code.\r\n> \r\n> \r\n> \r\n> The current TryFrom<&OtapArrowRecords> implementation only has immutable access, whereas decoding require ...[truncated]", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -13093,7 +12642,6 @@ "review_state": null, "root_timestamp": "2026-07-28T03:07:10Z", "body": "I think we should scope this PR to the main issue, which is preventing the construction of invalid views. The `DecodedOtapLogsResources` construct that the router and validator are using look like an unrelated optimization, as those components were calling the normal `try_from` implementation before this PR. \r\n\r\nBesides the optimizations being unrelated, I'm not sure that the solutions in this PR are the best we can do. For example, having a second `OtapLogsResourcesView` construct that is a subset of the `OtapLogsView` seems like a maintenance burden and complicates the API surface area. I don't have a perfect solution here, but I'll put a couple of ideas at the bottom. \r\n\r\nAdditionally, the benefit of these optimizations is not demonstrated anywhere that I can see and there may not be sufficient impact. For example, I previously considered optimizing the views for the temporal reaggregation processor, but after profiling I did not see significant time spent there. Did you make a different discovery?\r\n\r\nIn short, I think 2000 lines of code is a lot for the issue that this PR is meant to solve and I think each extra optimization needs to be examined separately to make sure they (1) ...[truncated]", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -13122,7 +12670,6 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-07-24T00:52:01Z", "body": "I believe there are few issues to fix before merging this PR. Thanks", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -13151,7 +12698,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-23T00:48:05Z", "body": "### 💡 Codex Review\n\nHere are some automated review suggestions for this pull request.\n\n**Reviewed commit:** `c05d8b7fc2`\n \n\n
ℹ️ About Codex in GitHub\n
\n\n[Your team has set up Codex to review pull requests in this repo](https://chatgpt.com/codex/cloud/settings/general). Reviews are triggered when you\n- Open a pull request for review\n- Mark a draft as ready\n- Comment \"@codex review\".\n\nIf Codex has suggestions, it will comment; otherwise it will react with 👍.\n\n\n\n\nCodex can also answer questions or update the PR. Try commenting \"@codex address that feedback\".\n \n
", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -13180,7 +12726,6 @@ "review_state": null, "root_timestamp": "2026-07-23T22:48:17Z", "body": "@daviddahl can you please take a look at this PR and how it may relate to https://github.com/open-telemetry/otel-arrow/issues/3435?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -13209,7 +12754,6 @@ "review_state": null, "root_timestamp": "2026-07-24T00:32:04Z", "body": "> @daviddahl can you please take a look at this PR and how it may relate to #3435?\r\n\r\nThis is very much complementary - we should converge if possible. A stability Enum could be added to the `component_inventory` macro - we discussed this a little bit but did not want to go all the way down to individual signal types as is done in the go collector as we are inventorying components that do not handle signals as well. Perhaps we can follow up my PR with a stability property and converge. What do you think @lquerel ?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -13238,7 +12782,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-24T18:57:32Z", "body": "Looks good to me! Excited to see the generic implementation coming in!", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -13267,7 +12810,6 @@ "review_state": null, "root_timestamp": "2026-07-25T00:27:13Z", "body": "Moved out from merge queue, as @pritishnahar95 is making some updates to align the design with the Azure identity extension.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -13296,7 +12838,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-27T21:15:25Z", "body": "LGTM. Thanks for putting together this detailed design and aligning it with the Azure identity extension. This will be a useful addition. \n\nI left a few comments to clarify config validation, the RFC 7523 flow, and the HTTP/TLS behavior. Approving with the expectation that these are addressed before merge.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -13325,7 +12866,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-24T16:57:58Z", "body": "Thanks", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -13354,7 +12894,6 @@ "review_state": null, "root_timestamp": "2026-07-24T16:58:56Z", "body": "@gnanirahulnutakki could you sign the CLA and mark the PR ready for review.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -13383,7 +12922,6 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-07-25T06:31:03Z", "body": "Thanks for working on this, i.e. the goal of keeping `--validate-and-exit` consistent with runtime startup is definitely right.\n\nAfter reproducing #3569 against the parent commit, we found that the original analysis attributing the failure to missing type-router outputs was incorrect. We’re sorry for the confusion.\n\nThe type router recognizes three well-known named routes: `logs`, `metrics`, and `traces`. Under the existing configuration model, the outputs list is an optional declaration/allowlist; the actual ports can be established directly by connections such as router[\"logs\"], router[\"metrics\"], and router[\"traces\"]. I verified that this configuration, without an outputs list, validates, starts ready, and processes all three signal types on the parent commit.\n\nThe exact configuration from #3569 actually fails because all traffic-generator signal weights default to zero: at least one of metric_weight, trace_weight, or log_weight must be > 0\n\nRuntime calls `TrafficConfig::validate()`, but static validation only deserializes Config, so `--validate-and-exit` misses this semantic constraint.\n\nSuggested fix:\n\n- Remove the minimum-declared-output requirement from type_router.\n- Reuse ...[truncated]", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -13412,7 +12950,6 @@ "review_state": null, "root_timestamp": "2026-07-27T16:51:05Z", "body": "@lquerel Thanks for detailed clarification. I have updated the title and description of #3569 to reflect the above.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -13441,7 +12978,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-27T18:01:55Z", "body": "Looks good! Could you potentially add a `trafficgen` example with a few variations of this processor to validate behavior is as expected and document the results in PR description? Something like https://github.com/open-telemetry/otel-arrow/blob/main/rust/otap-dataflow/configs/trafficgen-flow-metrics-demo.yaml and #3552", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -13470,7 +13006,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-28T13:42:44Z", "body": "LGTM! Left one further comment about changelog.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -13499,7 +13034,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-28T19:02:45Z", "body": "LGTM. I left a comment about further improving the changelog, not a blocker now.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -13528,7 +13062,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-28T19:03:03Z", "body": "LGTM. I left a comment about further improving the changelog, not a blocker now.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -13557,7 +13090,6 @@ "review_state": null, "root_timestamp": "2026-07-27T16:40:03Z", "body": "@Shaurya2k06 : Can we also parse hidden files as in the [example](https://raw.githubusercontent.com/lycheeverse/lychee/lychee-v0.24.2/lychee.example.toml) - `Lychee` skips hidden directories by default, so this currently misses files such as `rust/otap-dataflow/.chloggen/README.md`. That means broken relative links in those docs can still merge, contrary to the issue’s “all Markdown files” acceptance criterion.\r\n\r\n```toml\r\n# Do not skip hidden directories and files\r\nhidden = true\r\n```", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -13586,7 +13118,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-28T06:10:55Z", "body": "i noticed Copilot is complaining about `include_fragments = \"full\"`, but didn't `lychee` migrate this setting from a boolean to an enum (`\"none\"`, `\"anchor-only\"`, `\"full\"`) in recent versions (like v0.23+)? \n\nif so, setting it to `true` as Copilot suggests would actually cause a config parsing error now right? \n\nit looks like the author's original string value is correct here but just wanted to double-check with the team if I'm missing something", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -13615,7 +13146,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-28T19:32:34Z", "body": "Thanks for tackling this -- I'm fully behind the goal. A runtime error like \"No space left on device (os error 28)\" being reduced to `(1 dropped)` is exactly the kind of thing that makes internal logs useless when you need them most, and prioritizing the `error` field within the bounded record budget is the right instinct. The compact `error` / `error_kind` / `event_type` shape in the \"After\" output is a clear improvement over the old rendering.\n\nMy main concern is that a fair amount of this reimplements logic we already have, and I'd rather not carry two copies of the truncation machinery. Details below.\n\n## 1. Duplicated truncation logic\n\n`TruncatingBoundedBufFmt` + `encode_debug_string_truncating` reimplement what already exists in `crates/pdata/src/otlp/common.rs`:\n\n- `encode_string_bounded` / `encode_string_truncating` (common.rs:498-570) already handle suffix reservation, UTF-8 boundary truncation (via `truncate_utf8`), and the tri-state `Ok(false)` / `Ok(true)` / `Err(Dropped)` contract.\n- `BoundedBufFmt` (encoder.rs:238) is now a strict special case of the new adapter, so we carry two nearly identical `fmt::Write` adapters.\n- `encode_debug_attribute_truncating_to` is a copy ...[truncated]", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -13644,7 +13174,6 @@ "review_state": null, "root_timestamp": "2025-11-25T14:49:52Z", "body": "Nice! Thanks @thompson-tomo. I'll get back to you as soon as I can (it's a very busy EOM).", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -13673,7 +13202,6 @@ "review_state": null, "root_timestamp": "2025-11-26T03:19:09Z", "body": "> By switching to front matter directly it enables turning on the first line h1 check.\r\n>\r\n> Note the awk/sed command are necessary so that the document still passes the lint rules and we don't have a formatter to format it.\r\n\r\ncan these be done in a follow-up PR? if so, please revert those changes to keep this large mechanical PR easy to review", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -13702,7 +13230,6 @@ "review_state": "COMMENTED", "root_timestamp": "2025-11-26T19:51:28Z", "body": "A first few questions and comments:\r\n\r\n- Spec and semconv folks expressedly asked me to include the comment \"Hugo front matter used to generate the website version of this page\" so that all contributors know why the front matter is there (and hopefully realize that it's important to keep it up to date). Maybe this preference has changed. I'll let @open-telemetry/specs-maintainers @open-telemetry/specs-semconv-maintainers comment about this. I'm ok either way.\r\n- Why not keep the (inline) array syntax for aliases?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -13731,7 +13258,6 @@ "review_state": null, "root_timestamp": "2025-11-26T19:52:11Z", "body": "> The one thing which i am not sure on is if the aliases in [resource-and-entities.md](https://github.com/open-telemetry/semantic-conventions/pull/2971/files#diff-45c6c3aee5d1ce4f912148cf9efc0f39984b2c4c8f67f806bd99d6ce263dac21) are still valid. If not we could switch to a redirect\r\n\r\nYes the alias is valid, but I'd rather keep the inline array syntax, esp. in these cases that have a single entry.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -13760,7 +13286,6 @@ "review_state": null, "root_timestamp": "2025-11-26T19:54:44Z", "body": "FYI, I haven't had the time to look into this more, but in support of #1821 and https://github.com/open-telemetry/opentelemetry.io/issues/6101. We probably will need to have a `title` field in the front matter too.\r\n\r\nI'm wrapping up and will be OOO so I won't be able to get back to this until the new year (FYI).", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -13789,7 +13314,6 @@ "review_state": null, "root_timestamp": "2025-11-27T00:11:26Z", "body": "> Build issue due to current toc generator having known issues with front matter syntax & isn't mantained. Will need to be addressed by switching to https://www.npmjs.com/package/doctoc\r\n\r\nOMG, the last update was 8 years ago in NPM. doctoc seems like an interesting choice; https://npm-compare.com/doctoc,markdown-toc\r\n\r\nMaybe it would be worth switching toc tools before working further on this PR?\r\n\r\n/cc @vitorvasc", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -13818,7 +13342,6 @@ "review_state": null, "root_timestamp": "2025-11-27T13:03:55Z", "body": "Hmm. Ok, maybe another tool then.\r\n(Creating a markdownlint rule to handle tocs might be a fun project ;))", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -13847,7 +13370,6 @@ "review_state": null, "root_timestamp": "2025-11-28T20:10:22Z", "body": "Btw, if you still need to massage the files, rathr than awk & sed, I'd suggest using Perl because it is more portable across OSs and container environments in my experience (esp. if you avoid Perl modules that don't ship with Perl).", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -13876,7 +13398,6 @@ "review_state": null, "root_timestamp": "2026-01-27T18:34:24Z", "body": "> Note I am still working towards a release of doctoc which will eliminate the grep etc but that will be seperate PR.\r\n\r\nGreat that you've been able to get some doctoc changes merged. 🙌🏻 \r\n\r\nWhile I'm eager to make progress here, our current setup works. I'd rather not move forward until we have a full solution that includes:\r\n\r\n- `title` field in the front matter. Both the in-page h1 and title text should match (could be enforced in this repo)\r\n- I think that if we're going to change the toc tool, that should be done first\r\n- IMHO, we should reach a point where the only change introduced in the .md files by this PR would be to replace the HTML comment delimiters (used to wrap the front matter) by YAML `---` section delimiters.\r\n\r\nWDYT?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -13905,7 +13426,6 @@ "review_state": null, "root_timestamp": "2026-02-13T19:09:08Z", "body": "> There doesn't appear to be any usage of `title` front matter\r\n\r\n- The OTel.io layouts expect a `title`, so it needs to be there (at least for now).\r\n\r\n---\r\n\r\n- Thinking about the Hugo comment: at this point I think that there's enough maintainer / community experience that we could probably drop it.\r\n\r\n- I'd vote to switch tools first.\r\n\r\n---\r\n\r\n> I assume you don't mean to create a seperate PR to split the hugo comment to be on a separate line if you want to keep the hugo comment?\r\n\r\nYeah, assuming the Hugo comment goes it mostly looks like what is done in this PR (execpt for the array format changes that should be undone):\r\n\r\n\"image\"", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -13934,7 +13454,6 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-02-23T13:22:22Z", "body": "> @chalin i have now gone and added title front matter to all the pages, \n\nGreat!\n\nLet's follow convention and ensure that the `title` is the first front-matter field.\n\n> there is a custom textlint rule which ensures that they match if not report an error, \n\nNice, though I'd rather not introduce textlint into the toolchain if we can avoid it. I'd prefer that we use a custom Markdownlint rule instead (I've been writing custom mdl2 rules lately, so I can look into it if that can help).\n\nThat being said, if we don't want to delay the front-matter conversion of this PR, we can move the tooling bits to a followup PR. \n\n> @chalin this repo can now switch to doctoc as update is now available. PR to switch is #3463 ...\n\nWell done! I left a review comment with suggested changes. Looking forward to seeing that merged once suggestions are addressed.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -13963,7 +13482,6 @@ "review_state": null, "root_timestamp": "2026-02-23T14:36:52Z", "body": "> Let's do tooling seperate, would you rather I remove textlint from this PR or keep it and then remove the custom rule later?\r\n\r\nFactor it out into another PR (and remove it from this one).", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -13992,7 +13510,6 @@ "review_state": null, "root_timestamp": "2026-02-23T16:04:33Z", "body": "To followup on the textlint rule: I'm not against it, esp. if textlint does become a part of the toolchain (as it has in the main spec repo), but we can discuss that separately now that there will be another PR for the tool.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -14021,7 +13538,6 @@ "review_state": null, "root_timestamp": "2026-04-24T18:43:16Z", "body": "@thompson-tomo - let's get #3463 in first, I just submitted a review.\r\n\r\nBtw, I was OOO in March. Been juggling other priorities since then, but this is still on my radar. Note that the follow will be supportive of the work done in this PR:\r\n\r\n- https://github.com/open-telemetry/opentelemetry-specification/issues/5049", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14050,7 +13566,6 @@ "review_state": null, "root_timestamp": "2026-07-03T17:01:26Z", "body": "@thompson-tomo - can you ensure that this is up to date, and I'll start final tests on the otel.io side to ensure that this can land smoothly.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14079,7 +13594,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-03T17:28:25Z", "body": "@thompson-tomo - FYI, I'll be making some progress on this this month. See inline comments for a tweak and question.\n\nBtw, what's in place to ensure that the Hugo fm title and page title match. I thought that we had a checker in place, but I'm not seeing that in the scope of this PR.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14108,7 +13622,6 @@ "review_state": null, "root_timestamp": "2026-07-03T18:05:05Z", "body": "> I have just updated the branch and completed an audit so it is up to date.\r\n\r\nHow did you \"complete the audit\"? Do you have a script? Is it a part of this repo already?\r\n\r\n> So currently there is nothing in place to keep them in sync ...\r\n\r\nI'm ok landing this earlier and brining in the sync check later.\r\n\r\n> ... I did previously have a custom textlint rule in-place.\r\n\r\n~Right, that rings a bell. Where is that work? Is textlint already a part of this repo's toolchain? I vaguely recall commenting on this (in this repo or the main spec repo?); possibly even mentioning that I'd more likely favor use of markdownlint instead.~ (Nm, found the context) In any case, we can land the changes first so that this PR isn't delayed.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14137,7 +13650,6 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-07-06T07:14:09Z", "body": "Before even reviewing this, I'd like us to have a mechanism to:\n\n1. Check that each page has the frontmatter\n2. That it matches the header\n\nWithout it this will become stale, so I think it needs to be done together and not as a follow up.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14166,7 +13678,6 @@ "review_state": null, "root_timestamp": "2026-03-10T02:51:43Z", "body": "re-opening for discussion!\r\n\r\n@PascalSenn also check out https://github.com/open-telemetry/opentelemetry-specification/pull/4906\r\n\r\nthere has been quite a bit of progress since we discussed last year about the option of hosting semantic conventions outside of this repository. I think the work is pretty bleeding edge still but may be ready for external usage soon. In the meantime, let's review and discuss the GraphQL semantic conventions here.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14195,7 +13706,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-05-03T16:15:58Z", "body": "Just a couple of small doc tweaks which will need a fresh markdown generated.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14224,7 +13734,6 @@ "review_state": null, "root_timestamp": "2026-06-04T15:27:05Z", "body": "Any news on this feature ? Tks.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14253,7 +13762,6 @@ "review_state": null, "root_timestamp": "2026-06-22T12:29:43Z", "body": "@PascalSenn unfortunately i dont have permissions to. Ping @open-telemetry/specs-semconv-approvers @open-telemetry/specs-semconv-maintainers", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -14282,7 +13790,6 @@ "review_state": null, "root_timestamp": "2026-06-22T12:36:56Z", "body": "Here you go @PascalSenn", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -14311,7 +13818,6 @@ "review_state": null, "root_timestamp": "2026-07-13T12:35:05Z", "body": "Hi maintainers (@AlexanderWert , @arminru , @kamphaus , @joaopgrassi , @jsuereth , @lmolkova , @trask ), is there anything that's blocking progress on this? Would be really great to get the sem conv merged to unblock implementations.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -14340,7 +13846,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-17T18:29:19Z", "body": "A few minor suggestions, looks good otherwise!", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -14369,7 +13874,6 @@ "review_state": null, "root_timestamp": "2026-07-02T11:21:41Z", "body": "Not stale.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -14398,7 +13902,6 @@ "review_state": null, "root_timestamp": "2026-07-06T07:08:03Z", "body": "@chalin I guess \"code owner\" here would be you. Can you approve to make sure from the OTel website things look ok?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -14427,7 +13930,6 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-05-11T09:44:27Z", "body": "From a Profiling perspective this is a hard breaking change that will cause issues around symbolization.\nInstead of replacing `process.executable.path` with `process.entrypoint`, `process.entrypoint` should be a complementary attribute and independent from `process.executable.path`.\n\nE.g. in a process that uses pytorch to do something, there will be multiple `process.executable.path` from a Profiling perspective. One for `/usr/bin/python` (or any other path to a python runtime executable), one for something like `libtorch` and maybe something device/GPU specific.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14456,7 +13958,6 @@ "review_state": null, "root_timestamp": "2026-05-11T11:46:05Z", "body": "I view the `process.executable.*` namespace, which encompasses `build_id.gnu`, `build_id.go`, `build_id.htlhash`, `name`, and `path`, as a cohesive unit. Collectively, these attributes serve to uniquely identify an element.\r\n\r\nRedirecting the `process.executable.build_id.*` sub-namespace to `process.entrypoint` for executable identification does not seem appropriate. Consequently, I consider `process.entrypoint` to be a complementary addition rather than a replacement for `process.executable.path`.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14485,7 +13986,6 @@ "review_state": null, "root_timestamp": "2026-05-11T13:52:10Z", "body": ">> Redirecting the process.executable.build_id.* sub-namespace to process.entrypoint for executable identification does not seem appropriate.\r\n>>\r\n> Isn't the build_id.htlhash sufficient to uniquely identify the executable? \r\n\r\nYes - `process.executable.build_id.htlhash` can uniquely identify a executable. But as hashes are hard for human to comprehent and trigger actions, more descriptive attributes (like `process.executable.path`) are needed.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14514,7 +14014,6 @@ "review_state": null, "root_timestamp": "2026-06-01T13:15:59Z", "body": "My concern is just that this seems really hard to instrument, and really hard to implement as a general `process` namespace attribute. The instructions for how to parse the cmdline of the process to get the nominal \"entrypoint\" (like the example instructions for Python in the description) are probably going to need to exist for any runtime. Wouldn't it be better to introduce entities for the different runtimes that have their own proper instructions for determining entrypoint where that is actually useful? A majority of usecases in the general process world won't really need anything more than uniquely identifying the current executable of the process.\r\n\r\n> Compliment it with either process.launch_path or process.run_path to be capturing what is being launched/run by the entrypoint.\r\n\r\nTo my knowledge, there isn't a way to know this at any given time (short of parsing cmdline but often that doesn't actually contain the full path). You could only instrument it if you were the process yourself, guaranteeing you know when you start and reporting your own entrypoint at that point in time. Any other usecase couldn't guarantee that what they are reading is definitely the path to the exec ...[truncated]", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14543,7 +14042,6 @@ "review_state": null, "root_timestamp": "2026-06-23T17:15:26Z", "body": "> my updated proposal is to simply to rename the attribute and move the new attribute to the process entity. The note is made more explicit see suggestion https://github.com/open-telemetry/semantic-conventions/pull/3686#discussion_r3219625536\r\n\r\nI don't think we need to rename `process.executable.path` to add `process.entrypoint`, I think they're distinct. The proposal in the linked comment seems to describe `process.entrypoint` as being the same as `/proc/[pid]/exe`; to my understanding, that is explicitly not the goal of the attribute. Why can't we leave `process.executable.path` to mean that and `process.entrypoint` as a new attribute?\r\n\r\n> move the new attribute to the process entity\r\n\r\nThis attribute likely does make the most sense on the `process` entity, but I think we should keep `process.executable.path` in the `process.executable` entity. This attribute is different and not a sufficient replacement.\r\n\r\n> Yes this would be an opt-in self reported descriptive attribute and it could be discussed further in the future.\r\n\r\nI guess I can get behind it as long as it's clear that default instrumentation is unlikely to report it. The attribute can be designed for situations where ...[truncated]", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14572,7 +14070,6 @@ "review_state": null, "root_timestamp": "2026-07-23T15:09:00Z", "body": "> This PR seeks to rename Process.executable.path to process.launch_path, there is no change in how it is sourced.\r\n\r\nThat's actually even more incorrect. The expected instrumentation for the process entity could never tell at any given time that the detected executable for the process is the same as the path that it was launched with. It could be changed at any time. So the name is very misleading, and as written I don't think it should be on the entity.\r\n\r\n> This PR moves the attribute from the process.executable entity to the process entity. This is so process.executable supports the scenario of multiple identical executables on a system. If we didn' we would have un-necessary churn of the value of the descriptive attribute & could lead to wrong info. There would be no way to distinguish between the different copies on the same system.\r\n\r\nBut you still can't do that. Removing `process.executable.path` from the `process.executable` entity doesn't make it so you are able to do that. You have a very similar problem as before. You get rid of the path as a churn, but two different copies of the executable would still look like one to the backend since the only identifying attributes ...[truncated]", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14601,7 +14098,6 @@ "review_state": null, "root_timestamp": "2026-07-23T15:52:49Z", "body": "> If you are not a supporter of process.launch_path what about process.run_path/process.running_path that way we can say it is path to what is running in the process?\r\n\r\nMy favourite option is to keep it named `process.executable.path`. That's a literal description of what it is that matches common nomenclature. \r\n\r\nP.S. I think `process.executable.name` and `process.executable.path` being attributes on `process.executable` is actually kind of weird; `process.executable.path` is a reasonable name here because when describing this property of a process, yes it is the current path to the process's executable. But a `process.executable` entity is nominally just representing a file, so there is no reason for attributes like `process.executable.path` and `process.executable.name` to exist separate from simply `file.path` and `file.name`.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14630,7 +14126,6 @@ "review_state": null, "root_timestamp": "2026-07-23T16:54:32Z", "body": "> Then we have 2 usages/meanings for process.executable\r\n\r\nIt's not really different. It is the path to the process's executable. It's about an executable. That's not ambiguous to me at all.\r\n\r\n> what about process.executing_path or process.execution_path?\r\n\r\nNot a fan of those; non-standard nomenclature, and could be very confusing. What we're trying to describe is \"the path to the executable run by the process\", so calling that \"execution path\" isn't exactly stating that clearly.\r\n\r\n> The problem is profiling needs to be able to get a name for the executable identified by the id.\r\n\r\nYeah the `process.executable` entity can have a name and path, I think that's fine. My problem with the `process.executable` entity is that most of its properties are just normal properties of a file. Having special `process.executable` attributes for `path` and `name` don't make sense on that entity when you could communicate identical information with `file.path` and `file.name`. `process.executable.path` is a name that made sense in context of the `process` entity itself, because a \"process\" has an \"executable\" that it's running and you can retrieve the \"path\" to it. But all of that relevance was e ...[truncated]", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14659,7 +14154,6 @@ "review_state": null, "root_timestamp": "2026-07-27T16:39:18Z", "body": "> The ambiguity is, is it describing a file on the file system or is it an executable which can be deployed to 1 or more locations.\r\n\r\nThat doesn't make sense to me. An executable is always a file on the filesystem.\r\n\r\n> My preferred was *.executing_path as That would complement *.working_directory.\r\n\r\nI assume you just mean the pattern of naming is the complement, but just in case you mean otherwise; the path to the executable and the working directory of the process have absolutely no conceptual relationship.\r\n\r\n> That sounds like an entity relationship\r\n\r\n(Assuming Linux to simplify the technical explanation of this)\r\n\r\nYou could call it that. The process has an executable, which is a file on disk with an inode that at some point throughout the process's lifetime had a named hard link on the filesystem to the inode. You can identify that file by its inode. But at any given point, that file could be \"moved\" in the filesystem changing the name of the link, or be unlinked and thus no longer be accessible via the filesystem (but the inode will be present on disk until any process using it has concluded). It is conceptually a `has-a` relationship. But that relationship is to a file o ...[truncated]", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14688,7 +14182,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-06-24T09:19:49Z", "body": "Two things worth fixing:\r\n\r\n1. [BLOCKER] `TonicLogsClient` maps all errors to `failed` — \r\n`timed_out` case is missing. Match on `OTelSdkError::Timeout(_)` \r\nlike `BatchLogProcessor` does, otherwise the POC doesn't fully \r\nvalidate the spec.\r\n\r\n2. [NIT] `cfg_attr` on `component_name` will need manual update \r\nwhen a third feature uses it. Allocating it always is simpler.\r\n\r\nRest looks good for a POC.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14717,7 +14210,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-06-25T17:22:35Z", "body": "Look great! the only important comment I have is adding `error.type` instead of result attribute.\r\n\r\n[UPDATE] Added another comment on modeling it as a span", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14746,7 +14238,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-18T14:35:35Z", "body": "On a first pass it looked good however on thinking about usages etc, i get worried about the broadness of the definition and the explosion of spans that would be created. Which is why a prototype & mapping for other tools is important.\n\nMy suggestion is clearly narrow the scope of the span to cli operations which interact with a remote and complement it with 1 or more events to capture the cli calls.\n\nThese events should be in a seperate pr and i would suggest for the prototype you could use the github reciever in the collector to generate the events based on the scraped logs.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14775,7 +14266,6 @@ "review_state": null, "root_timestamp": "2026-07-20T21:10:00Z", "body": "> bringing it to the system semconv working group\r\n\r\nYes, that's a good way forward.\r\nAlso you might want to periodically merge in the latest changes from main.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -14804,7 +14294,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-07T18:53:10Z", "body": "### 💡 Codex Review\n\nHere are some automated review suggestions for this pull request.\n\n**Reviewed commit:** `eaf4a67aca`\n \n\n
ℹ️ About Codex in GitHub\n
\n\n[Your team has set up Codex to review pull requests in this repo](https://chatgpt.com/codex/cloud/settings/general). Reviews are triggered when you\n- Open a pull request for review\n- Mark a draft as ready\n- Comment \"@codex review\".\n\nIf Codex has suggestions, it will comment; otherwise it will react with 👍.\n\n\n\n\nCodex can also answer questions or update the PR. Try commenting \"@codex address that feedback\".\n \n
", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -14833,7 +14322,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-08T07:27:33Z", "body": "nice changes! just a couple of minor things:\r\n\r\n- in model/azure/registry.yaml, should we add stability: development to the new registry.azure.container_apps group too? just to be consistent with other groups like cosmosdb.\r\n\r\n - since replicas scale under a specific revision, did you think about adding azure.container_app.revision (mapped to CONTAINER_APP_REVISION_NAME)? might be useful for tracking version of the app later on", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14862,7 +14350,6 @@ "review_state": null, "root_timestamp": "2026-07-08T11:25:35Z", "body": "> since replicas scale under a specific revision, did you think about adding azure.container_app.revision (mapped to CONTAINER_APP_REVISION_NAME)? might be useful for tracking version of the app later on\n\nThis would reside on a seperate entity which is identified using this attribute and the name, just like done with the service entities.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -14891,7 +14378,6 @@ "review_state": null, "root_timestamp": "2026-07-08T15:48:19Z", "body": "looks great just a quick heads up yamllint check is failing because changelog note line is >200 chars once u wrap/shorten it pr looks goods", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14920,7 +14406,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-17T18:45:01Z", "body": "That's awesome!\n\nI'd like to check if we can deprecate or remove V$SESSION section", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14949,7 +14434,6 @@ "review_state": null, "root_timestamp": "2026-07-23T18:32:37Z", "body": "```\r\nRun make textlint\r\nsemantic-conventions@ /home/runner/work/semantic-conventions/semantic-conventions\r\n├─┬ textlint-filter-rule-allowlist@4.0.0\r\n│ └── textlint@15.7.1 deduped\r\n├─┬ textlint-rule-period-in-header@0.1.2\r\n│ └── textlint@15.7.1 deduped\r\n└── textlint@15.7.1\r\n\r\n\r\n/home/runner/work/semantic-conventions/semantic-conventions/.chloggen/web_vital-attributes.yaml\r\n 13:69 ✓ error Incorrect term: “id”, use “ID” instead terminology\r\n\r\n✖ 1 problem (1 error, 0 warnings, 0 infos)\r\n✓ 1 fixable problem.\r\nTry to run: $ textlint --fix [file]\r\nmake: *** [Makefile:115: textlint] Error 1\r\n```", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -14978,7 +14462,6 @@ "review_state": null, "root_timestamp": "2026-07-23T18:33:57Z", "body": "@copilot now go fix the violation and update title to say that we fixed it", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -15007,7 +14490,6 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-07-24T01:07:22Z", "body": "This actually removes functionality. Currently the filepath is used to report it as an annotation\n![image](https://github.com/user-attachments/assets/3bedefc4-12ec-484c-b198-8778bbe08a56)\n\nThis enables a reviewer to see directly on a changed file the issues hence is easier then scrolling through logs.\n\n![image](https://github.com/user-attachments/assets/433c6777-93b0-460b-9fb9-345cb4f2693c)\n\nAlso the fix is not correct as id represents a field name which is lowercased and not upper, to fix either chang config aka #3929 to ignore the directory or #3930 which uses backticks to indicate it is a field/attribute name.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -15036,7 +14518,6 @@ "review_state": null, "root_timestamp": "2026-07-24T02:00:13Z", "body": "Annotation is not as helpful as ci log, much harder to see regardless is you're a human or an agent. It's nice to have, but not at a cost of not having filename in the plain text.\r\n\r\nEspecially problematic when error is on the file that's not in the PR - current problem.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -15065,7 +14546,6 @@ "review_state": null, "root_timestamp": "2026-07-24T02:15:01Z", "body": "Current problem is an edge case which only occurs during merge process and files changing before merge.\n\nI am not following how it is harder on humans for issues going forward as for everyone reviewing the files via the changes tab can see issues on the file inline that way they have max context. They can also see them at the top of the check which is where they go to get their logs.\n\nIt should be easier for agents as now the check itself provides a list of errors & their location.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -15094,7 +14574,6 @@ "review_state": null, "root_timestamp": "2026-07-24T02:21:03Z", "body": "Edge case maybe, it's not the last time it happens - textlint will get new rules and they'll fail in random places. \r\n\r\nThere literally no information when it does on where the problem is\r\n\r\n\"Screenshot_20260723-190553\"\r\n \r\nI'm going to repeat that path on log is a must, github comment is nice to have. Going through all github comments is much harder than looking into ci logs.", - "measurement_date": "2026-08-01", "role": "context", "stability": null, "recorded_label": null, @@ -15122,7 +14601,6 @@ "review_state": null, "root_timestamp": "2026-07-24T02:29:17Z", "body": "It's right there in the annotations section at the top of the screen. Annotations are similar to comments but are different.\n\nAlso all rules are implemented as npm packages so we need to explicitly enable the additional npm package we have added to our project for it to result in a difference in behaviour.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -15151,7 +14629,6 @@ "review_state": null, "root_timestamp": "2026-07-24T16:56:28Z", "body": "It's wonderful to see annotations.\r\nIt's not wonderful that they show up collapsed - that's probably why I never look at them. Thanks for educating me they show up there.\r\nIt's not acceptable that they turn off file locations in the CI logs. \r\n\r\n\r\n------\r\n\r\nyamllint for example, does it well: https://github.com/open-telemetry/semantic-conventions/actions/runs/29971521674/job/89094348620?pr=3919\r\n\r\n\"image\"\r\n\r\nannotations and CI logs are not mutually exclusive. If textlint does it right, I'll be happy to enable github format.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -15180,7 +14657,6 @@ "review_state": null, "root_timestamp": "2026-07-26T13:23:00Z", "body": "No worries, key thing is annotations appear in 3 spots: workflow, job & file if the file location is in the diff view.\n\nIt is always hard as what is right for 1 person might not be for another especially in terms of log message content in a structured log.\n\nTbh i had expected when viewing the raw logs to see the details used to build the annotation but it is even removed from there. As such i have https://github.com/textlint/textlint/pull/2094 to log an additional line prior to the annotation with the location details. This avoids duplicated info appearing in the annotation while still providing location info in raw logs.\n\nGiven we have the annotations, all rules are dedicated npm packages hence we won't run into updates detecting new issues & i have a pr to log an additional line with the location. Could we leave it as is?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -15209,7 +14685,6 @@ "review_state": null, "root_timestamp": "2026-05-08T06:05:11Z", "body": "@Krishnachaitanyakc This is well thought out, the prior art table and design decisions section are really helpful. One thing I noticed, the scenarios only emit trigger=\"direct\" but never trigger=\"agent\". \r\n\r\nI get that the current scenarios don't naturally exercise delegation, but would it be worth adding a minimal multi-agent scenario (even a simple A delegates to B mock) to validate the agent value e2e? Otherwise LGTM on the model and registry changes.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -15238,7 +14713,6 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-05-14T12:01:25Z", "body": "Thanks @Krishnachaitanyakc - I agree we should just use `agent` and removing `direct` for the enum.\n\nMarking as Request Changes so we know it's waiting on an update from you 👍🏻", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -15267,7 +14741,6 @@ "review_state": null, "root_timestamp": "2026-05-14T22:42:49Z", "body": "@Krishnachaitanyakc Upon reading the motivation I believe the intention behind this enum is to provide visibility into delegation events. Did I get this right?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -15296,7 +14769,6 @@ "review_state": null, "root_timestamp": "2026-06-17T10:54:34Z", "body": "Like the direction of folding handoff into `execute_tool`, and scoping `invocation.trigger` down to what's actually derivable from typed state makes sense.\n\nCouple of things:\n\n1. The delegation link lives entirely on the `execute_tool` span's `handoff.target.name`, and the receiving `invoke_agent` is a sibling, right? In a run with concurrent handoffs, or where two agents share a name, joining the edge back to the right `invoke_agent` span comes down to name matching. Did you consider a span link from `execute_tool` to the target `invoke_agent`, or a stable agent id separate from the name? The blast-radius query in the motivation feels like it'd get ambiguous on name collisions too.\n2. Since the link sits in attributes rather than parentage, if that span gets dropped by sampling you keep both `invoke_agent` spans but lose the edge between them. Is that intended?\n\nBoth are edge-case-y, so feel free to punt if it's out of scope.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -15325,7 +14797,6 @@ "review_state": null, "root_timestamp": "2026-07-17T16:37:37Z", "body": "Hi @Krishnachaitanyakc - thanks for this PR.\r\n\r\n+1 to @AgentGymLeader point in https://github.com/open-telemetry/semantic-conventions-genai/pull/98#issuecomment-4729151864 about the edge relying on name matching.\r\n\r\nThe handoff creates 3 spans:\r\n\r\n1.  invoke_agent triage-agent \r\n2.  execute_tool transfer_to_billing_agent  (under 1)\r\n3.  invoke_agent billing-agent \r\n\r\nProblem: #3 sits beside #2, not under it — so no parent→child line connects the handoff to the agent it handed off to.\r\n\r\n• trace_id — shared by all spans in the run, so it can't tell which handoff went to which agent when there are several.\r\n• parent_span_id — would be the clean link, but #3's parent is the run root, not #2. So it's useless here.\r\n\r\nResult: the only way to connect them is matching the name — which breaks under concurrent handoffs or duplicate names.\r\n\r\nProposal: add  gen_ai.agent.handoff.{source,target}.id  alongside  .name mirroring  gen_ai.agent.id  on  invoke_agent , as a stable join key.\r\n\r\ncc @singankit, @trask", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -15354,7 +14825,6 @@ "review_state": null, "root_timestamp": "2026-05-11T19:15:02Z", "body": "@Mandark-droid can you fill out the new PR template? thanks! https://raw.githubusercontent.com/open-telemetry/semantic-conventions-genai/refs/heads/main/.github/PULL_REQUEST_TEMPLATE.md", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -15383,7 +14853,6 @@ "review_state": null, "root_timestamp": "2026-05-11T20:10:33Z", "body": "let's add the scenario to #142, get that merged, then you can update that scenario in this PR", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -15412,7 +14881,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-06-06T00:39:04Z", "body": "I like where it's going, but Blob part is just one of the parts that has large content problem.\n\nI'd prefer if we\n\n- documented a configuration option that limits size of any content in any part \n- if size is exceeded we wouldn't capture it; or, for text, we would rather trim to the size\n- we could let users know that content was there by including its size on each affected part (especially blob) as you're proposing in the https://github.com/open-telemetry/semantic-conventions-genai/pull/143, but also on the text part\n- we could potentially mimic OTLP 'dropped attributes' property and record something like that on a generic part (e.g. content_dropped: true) if that's necessary (presence of non-0 size would be an indication on its own)\n\nHaving a reason seems interesting, but it's not a common approach in OTel - if something is dropped, we don't record why, it could introduce interesting problems on its own.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -15441,7 +14909,6 @@ "review_state": null, "root_timestamp": "2026-05-21T17:35:25Z", "body": "@Jwrede the suggestion is to clarify description of the existing metric in OTel to align with the spirit behind ITL (which I believe it already does). OTel metrics are independent of vLLM definitions.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -15470,7 +14937,6 @@ "review_state": null, "root_timestamp": "2026-06-09T13:29:02Z", "body": "> **\"Per-request average\" framing:** This comes from vLLM's deprecation rationale in [vllm-project/vllm#24015](https://github.com/vllm-project/vllm/pull/24015). TPOT is computed as (total_generation_time - TTFT) / (output_tokens - 1) and recorded once per request. That single value is an average over all tokens in the request.\r\n\r\nTPOT can be calculated either after all requests have returned, or in the manner of ITL as you mentioned. I believe these two approaches are essentially equivalent, at least in terms of average values. I agree with Liudmila's perspective — in my view, TPOT and ITL refer to the same concept, and if you feel that TPOT is lacking in some way, perhaps the more appropriate approach would be to enrich and extend it rather than introducing a separate metric.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -15499,7 +14965,6 @@ "review_state": null, "root_timestamp": "2026-05-26T22:12:47Z", "body": "hi @hippoley, not sure what happened, but PR content appears empty now, thanks", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -15528,7 +14993,6 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-05-28T12:20:09Z", "body": "Adding a Request Changes as changes have been lost in the last force-push", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -15557,7 +15021,6 @@ "review_state": null, "root_timestamp": "2026-05-27T03:27:27Z", "body": "@hippoley Idea of evaluation result as event was that it can be linked to the span via traceId, spanId for any span be it invoke_agent span , chat span etc.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -15586,7 +15049,6 @@ "review_state": null, "root_timestamp": "2026-06-04T23:20:48Z", "body": "Can you please help provide clarity on following questions:\r\n- How will evaluation span be associated to operation being evaluated?\r\n- What is evaluation span supposed to capture? The process of evaluation or evaluation results or both?\r\n- Currently evaluation results can be association with span being evaluated. After this change will evaluation result be associated with both evaluation span and span being evaluated?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -15615,7 +15077,6 @@ "review_state": null, "root_timestamp": "2026-05-26T02:56:28Z", "body": "The `invoke_node` span fills a real gap — in multi-agent workflows the current span hierarchy jumps from `invoke_workflow` directly to `invoke_agent` or `execute_tool`, with no way to represent intermediate processing nodes (validators, routers, formatters, etc.).\n\nThe design is consistent with the existing `gen_ai.execute_tool.internal` / `gen_ai.plan.internal` pattern, which is good. A few observations:\n\n1. **Naming**: `invoke_node` reads naturally alongside `invoke_agent` and `invoke_workflow`. The `gen_ai.node.name` attribute is clean.\n\n2. **Relationship to `gen_ai.evaluation.internal`**: I have an open PR (#185) that adds a similar `gen_ai.evaluation.internal` span for evaluation steps. Both follow the same `gen_ai..internal` pattern. It might be worth a quick cross-check to make sure the attribute sets are consistent (both use `gen_ai.operation.name` as required, `gen_ai.agent.name` as conditionally required, etc.).\n\n3. **`gen_ai.client.workflow.node.invocation.details` event**: the name is quite long. Is there a shorter form that still conveys the scope? Something like `gen_ai.node.invocation.details` might be easier to type and remember.\n\nOverall this is a solid ...[truncated]", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -15644,7 +15105,6 @@ "review_state": null, "root_timestamp": "2026-05-27T01:29:41Z", "body": "hi @RKest, can you mark this as \"Resolves #187\" (assuming it does)? thanks!", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -15673,7 +15133,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-07T01:00:59Z", "body": "Looks good, just a few comments", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -15702,7 +15161,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-23T03:11:24Z", "body": "I support the PR - it helps us cover operations that are not categorized yet.\n\nNow bikeshed time: *node* vs *step* vs *task* \n\n## Agent frameworks\n\n| lib | native term | concerns with \"node\" | concerns with \"step\" | concerns with \"task\" |\n|---|---|---|---|---|\n| langchain (LangGraph) | node | 🟢 none — native | foreign, no collision | overloaded (generic) |\n| google-adk 2.0 | node + edge | 🟢 none — native | 🟢 none — ADK calls a node \"a single step\" | overloaded (generic) |\n| pydantic-ai (pydantic-graph) | node (BaseNode) | 🟢 none — native | foreign, no collision | overloaded (generic) |\n| autogen (GraphFlow) | node (= agent) | 🟢 none — native | foreign | wrong level — task = the input/overall job |\n| agent-framework (MAF) | executor + edge | foreign | foreign | overloaded (generic) |\n| haystack (Pipeline) | component | 🔴 deprecated v1 term | foreign | overloaded (generic) |\n| llamaindex (Workflows) | step (@step) | 🔴 collides with Node/TextNode | 🟢 none — native | 🔴 collides with Task/TaskStep (core agent API) |\n| crewai | step (Flow); Task (Crew) | foreign | 🟢 none — native (Flow) | 🔴 collides with Task (core class) |\n| dspy | module (forward) | quite foreign | foreign | overloaded ...[truncated]", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -15731,7 +15189,6 @@ "review_state": null, "root_timestamp": "2026-06-08T16:22:44Z", "body": "Hi @eternalcuriouslearner,\r\nThis is a rather large PR, is there any way we could break this down into separate PRs? Perhaps \"Spans and Attributes\", \"Metrics\", and \"Reference Scenarios\"? Thanks!", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -15760,7 +15217,6 @@ "review_state": null, "root_timestamp": "2026-06-08T22:46:00Z", "body": "Reference scenarios should live with the relevant semconv PR, since we use them to evaluate the semconv changes themselves.\r\n\r\nmaybe\r\n\r\n- Core A2A attributes + spans\r\n- Operation duration + response body size metrics\r\n- Streaming metrics", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -15789,7 +15245,6 @@ "review_state": null, "root_timestamp": "2026-06-08T22:56:58Z", "body": "Hey @eternalcuriouslearner,\r\nI've recently opened a more exhaustive proposal for A2A semantic conventions #254 (previous one was #70) and I'd like to align with you on that since the issue is still untriaged AFAIK. It seems compatible for the most part, with only some minor differences.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -15818,7 +15273,6 @@ "review_state": null, "root_timestamp": "2026-06-09T02:07:34Z", "body": "Thanks Surya. Let me know when this PR is ready for another look. Maybe we can split up a few PRs between yourself and @pwkowalski ?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -15847,7 +15301,6 @@ "review_state": null, "root_timestamp": "2026-06-09T05:21:29Z", "body": "I'd be happy to work on the implementation if it doesn't introduce unnecessary overhead - I wouldn't want to disrupt the workflow. So if there's some way I could help, please let me know.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -15876,7 +15329,6 @@ "review_state": null, "root_timestamp": "2026-06-12T13:03:49Z", "body": "Hey @eternalcuriouslearner, thanks for your hard work!\r\n\r\nI agree about the removal of `gen_ai.agent.name` and `gen_ai.agent.id`, I've documented it in #254 already. I think it should be a follow-up once #243 is decided, in order to not block work.\r\n\r\n`a2a.client.response.time_to_first_event` seems to be aligned with `gen_ai.client.operation.time_to_first_chunk` - is the server one actually necessary?\r\n\r\n`a2a.client.response.body.size` seems dependent on transport method. Not sure it should be there.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -15905,7 +15357,6 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-06-16T10:58:31Z", "body": "Looks good, had some feedback but it should be minor.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -15934,7 +15385,6 @@ "review_state": null, "root_timestamp": "2026-06-16T12:46:29Z", "body": "I'm also not sure about these method names in general - A2A doesn't define them per se, but uses Pascal Case. However, I think a good reason to keep them like that (`message/send` instead of `SendMessage`) is keeping them aligned with existing MCP semconv method names.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -15963,7 +15413,6 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-06-16T14:06:09Z", "body": "Sorry, one more thing I've noticed after re-reading.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -15992,7 +15441,6 @@ "review_state": null, "root_timestamp": "2026-06-18T09:19:02Z", "body": "Sorry for the slight delay @eternalcuriouslearner.\r\n\r\nFollowing up on \"splitting up the work\" from earlier, is there something I could help with?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -16021,7 +15469,6 @@ "review_state": null, "root_timestamp": "2026-06-19T04:53:23Z", "body": "One modeling question on the core attrs, while this one's getting wrapped up: `a2a.message.referenced_task_ids` and `a2a.task.artifact_ids` are ID arrays pointing at other operations. Have you weighed span links for the task references instead of an ID-array attribute? If a referenced task is itself a span, a link keeps the causal edge queryable without baking high-cardinality arrays into attributes. Artifacts probably stay fine as attributes since they aren't spans. Referenced tasks feel link-shaped though.\n\nNot blocking the split work. Just worth settling deliberately while the core set is still being shaped, and probably in the #254 alignment too.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -16050,7 +15497,6 @@ "review_state": null, "root_timestamp": "2026-06-21T21:58:54Z", "body": "@eternalcuriouslearner yeah, that layered split sounds right to me. `referenced_task_ids` is what instrumentation can always read straight off the payload, so it makes sense as the core attribute. The span link is the better causal edge, but you can only build it when the referenced task's span context is actually resolvable, which generic instrumentation often won't have.\n\nOnly tweak I'd suggest: instead of leaving the link purely optional, word it as \"emit a link when the context is resolvable.\" That keeps the queryable causal edge I was after originally, without forcing anything on instrumentation that only has the IDs.\n\nThe deciding factor is the SDK side though, so I'll leave that call to @pwkowalski: for a2a-python, do we actually get span/trace context for the tasks in `referenceTaskIds`, or just the IDs from the payload?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -16079,7 +15525,6 @@ "review_state": null, "root_timestamp": "2026-06-22T14:52:05Z", "body": "> > I'm also not sure about these method names in general - A2A doesn't define them per se, but uses Pascal Case. However, I think a good reason to keep them like that (`message/send` instead of `SendMessage`) is keeping them aligned with existing MCP semconv method names.\r\n> \r\n> Howdy @aabmass can you pitch in here. Should we use pascal case or keep it as `message/send`?\r\n\r\nThese are attributes values right? IMO we should copy what's in the protocol so keeping the pascal case, see the generic RPC conventions https://opentelemetry.io/docs/specs/semconv/registry/attributes/rpc/#rpc-method", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -16108,7 +15553,6 @@ "review_state": null, "root_timestamp": "2026-06-22T15:06:28Z", "body": "> @AgentGymLeader Good point. I’m trying to reason through how feasible this is for generic A2A instrumentation.\r\n\r\n+1 on making sure it's feasible to instrument. Let's push this to a follow up PR if nothing is blocking, I think regardless we should capture the `referenced_task_ids` which are sent on the wire.\r\n\r\n@AgentGymLeader can you file a follow up issue for documenting downstream span linking?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -16137,7 +15581,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-06-25T22:42:55Z", "body": "Looks great, thank you! The only real blocking question I have is on the inclusion of http or RPC span attributes.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -16166,7 +15609,6 @@ "review_state": null, "root_timestamp": "2026-05-26T20:15:43Z", "body": "@trask Read through the full PR including the FAQ and alternatives.\r\n Three questions on the choices made, framed against the three points I was planning to surface at SIG this week before #197 landed:\r\n \r\n **Three counters vs one with all axes.** The FAQ explains the cross-product issue cleanly: providers report cache and phase marginally, not jointly with modality, so a single counter with all four dimensions would force producers to invent cross-product cells they don't actually have. Accepting that. One follow-up: the three-counter split means `sum(inference.tokens)` and `sum(input_tokens_by_cache)` should reconcile on input total, and consumers will likely write queries that subtract or compare across them. Is it worth documenting the invariant explicitly in the metric description so downstream dashboards can rely on it (e.g. \"sum over `input_tokens_by_cache.cache` equals sum over inference.tokens where token.type=input\")?\r\n \r\n **Modality value set consistency.** `gen_ai.token.modality` uses `text | image | audio | video | document | unknown`. The existing `gen_ai.output.type` uses `text | json | image | speech` and the message-part schemas use `image | video | audio | docum ...[truncated]", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -16195,7 +15637,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-05-27T02:01:43Z", "body": "Wow, thanks for this proposal! Made a first pass with some questions", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -16224,7 +15665,6 @@ "review_state": null, "root_timestamp": "2026-05-27T09:09:02Z", "body": "> and reasoning output (priced separately from response output on o-series and Gemini 2.5+).\r\n\r\nPeople do want to track reasoning tokens, but I don't think they have their own pricing on current models, and if I'm wrong I'd love to know.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -16253,7 +15693,6 @@ "review_state": null, "root_timestamp": "2026-05-30T13:26:30Z", "body": "\"Reasoning\" tokens as far as I know aren't priced differently, they are important to track as the scratch work often ends up being a large differentiator of cost between different models ex.\n\nOpus 4.5, 4.6 , 4.7, 4.8 for example all currently priced the same main differentiator in cost I've observed has been the extent to which the models are configured to aggressively use the \"reasoning\" block revealing this to end users in an easy way to show what \"reasoning\" is costing them and to help them make the determination if the \"reasoning\" provides any value to a given use case.\n\nThank you for this pr from downstream \n\nIt should greatly simplify observability.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -16282,7 +15721,6 @@ "review_state": null, "root_timestamp": "2026-06-04T22:10:54Z", "body": "We're going to discuss this on an ad-hoc call tomorrow at 12pm PT: [Calendar](https://calendar.google.com/calendar/u/0/r/day/2026/6/5?eid=MnI0NDloamxmdGU3b2ZnYXNsYzR1ZmZoZ2wgY18yYmY3M2UzYjZiNTMwZGE0YmFiZDQ0NGU3MmI3NmE2YWQ4OTNhNWMzZjQzY2Y0MDQ2N2FiYzdhOWE4OTdmOTc3QGc), [Zoom](https://zoom.us/j/91357539844?pwd=MU9ZRUNyVUwrcHdiclU2b2lPOVBkQT09).\r\n\r\nPlease join if you can @alexmojaki @Nik-Reddy @aabmass @trask", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -16311,7 +15749,6 @@ "review_state": null, "root_timestamp": "2026-06-05T18:16:30Z", "body": "> making cost queries that match the provider pricing structure (per-modality, per-cache-state unit prices) answerable from metrics alone\r\n\r\ncosts can't be calculated from metrics in general anyway since the price per token often depends on the number of (input) tokens in a request, unless you added another metric attribute indicating the pricing bucket.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -16340,7 +15777,6 @@ "review_state": null, "root_timestamp": "2026-06-05T18:29:23Z", "body": "> Why don't gen_ai.token.modality and gen_ai.output.type use the same enum values?\r\n\r\nwas this removed?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -16369,7 +15805,6 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-06-08T13:52:56Z", "body": "Since Bedrock support isn't wired up to the right field yet, should remove the scenario until it does support it?\n\nAlso, I believe `invoke_agent.common` should be `invoke_agent.client`", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -16398,7 +15833,6 @@ "review_state": null, "root_timestamp": "2026-06-07T22:36:15Z", "body": "@singankit Can we add some test scenarios for this?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -16427,7 +15861,6 @@ "review_state": null, "root_timestamp": "2026-06-14T08:39:18Z", "body": "Thanks for porting this. One clarification that would help implementers: when an agent service already emits an HTTP/RPC server span for the incoming request, should **gen_ai.invoke_agent.server** be emitted as a child span for the agent operation boundary? A short note or reference scenario would make this easier to apply consistently across SDKs and hosted agent services.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -16456,7 +15889,6 @@ "review_state": null, "root_timestamp": "2026-06-08T22:41:28Z", "body": "@nagkumar91 this is a solid shape. The verdict-vs-action split is the right call — keeping \"what the guardrail returned\" separate from \"what the caller actually enforced\" matches how these break in practice (a framework can return block and the caller still lets it through, and you want both on the trace).\n\nTwo things I'd pin down while it's still in development:\n\n1. The blocked case should be readable from absence, not only from the finding event. If a guardrail returns block and the caller enforces it before the operation runs, the guarded op should have no success span. A terminal block plus no downstream execute span is what lets a reader tell \"blocked\" apart from \"never ran\" or \"telemetry got dropped.\" Worth stating normatively how the guardrail span and finding link back to the operation they gated (a span relationship, not just a shared trace), so the block and the missing span are tied together. This also lines up with the span-relationship discussion in #243.\n\n2. Keep external_finding_id an opaque, producer-defined correlation handle, and keep the finding record itself out of scope. The convention should define a reference you can join on (SIEM, incident, etc.), not reach ...[truncated]", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -16485,7 +15917,6 @@ "review_state": null, "root_timestamp": "2026-06-11T17:07:03Z", "body": "Hey @nagkumar91, does this address the feedback we went through in the call and in [this doc](https://docs.google.com/document/d/14CD0_ApP0W_ek9VNJN1N_2Bz5ifiJ978LTv5AD4mf_o/edit?tab=t.0)? Just lmk and I'll review and get someone from Model Armor to review.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -16514,7 +15945,6 @@ "review_state": null, "root_timestamp": "2026-06-11T18:02:14Z", "body": "Strong direction, and the privacy posture is already careful: `content.input.value`/`.output.value` are opt_in with sensitive-content warnings, `finding.evidence` is a non-content channel with a MUST NOT on sensitive data, and `verdict.type` (what the guardrail returned) is kept separate from `action.type` (what the caller enforced). Those boundaries are right, and the `run_guardrail.client` vs `.internal` split captures out-of-process vs in-process cleanly.\n\n`gen_ai.security.content.input.hash` is the field everyone gets pointed to as the privacy-preserving path, and as written it doesn't pin what it hashes. Its purpose is forensic correlation, but that only works if two implementations agree on the exact bytes and the algorithm, and \"hash of the input content\" leaves open the encoding, any normalization, and what the input even is for a structured `tool_call_input` (a JSON object, not a string). Two services inspecting the same content can produce different hashes, so the field can't do its one job.\n\nSmall fix: name a canonical byte form plus a hash. For structured targets, RFC 8785 (JCS) over the arguments object then SHA-256; for free-text, state UTF-8 and any normalization. Th ...[truncated]", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -16543,7 +15973,6 @@ "review_state": null, "root_timestamp": "2026-06-15T17:32:55Z", "body": "This is the right shape now: tagging the algorithm, naming the canonical byte form, and splitting text (UTF-8 plus a stated normalization) from structured (deterministic serialization for a tool_call) is what lets the hash do its forensic-correlation job. The piece that keeps it working across two implementations is a named default per target subtype, not only a tag: if each service tags whatever it used, two inspectors of the same tool_call still diverge, so one default canonical form plus algorithm per subtype (JCS then SHA-256 for tool_call, UTF-8 with a named normalization then SHA-256 for text) makes an unkeyed hash reproducible without coordination. The HMAC path for sensitive content is the right call; it correlates within a shared-key domain rather than across orgs, which is the honest bound to state alongside it.\n\nIf useful I can add two worked examples as a small follow-up, one tool_call and one free-text, with the exact bytes and resulting digest so a second implementation reproduces them from the example alone. Glad to keep it out of this review if you'd rather land the convention first.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -16572,7 +16001,6 @@ "review_state": null, "root_timestamp": "2026-06-15T17:55:25Z", "body": "Here are the two worked examples, reproducible from the bytes alone.\n\nStructured (`tool_call`), RFC 8785 (JCS) over the input object then SHA-256:\n\n- input: `{\"name\":\"query_table\",\"arguments\":{\"table\":\"employees\",\"limit\":10}}`\n- canonical bytes: `{\"arguments\":{\"limit\":10,\"table\":\"employees\"},\"name\":\"query_table\"}`\n- `sha256:40cfbce5aa3b2794c73996f4db4e6c2b45a5492cdc2a4bc71773e88eeebc3d95`\n\nFree-text, Unicode NFC then UTF-8 then SHA-256, on `café au lait`:\n\n- normalized bytes (hex): `636166c3a9206175206c616974`\n- `sha256:7c413039fbb2248e2b18b98e7a8d4d85bdcac7cd79b9477a0923f97e3a1f2b50`\n- the same text as NFC and as NFD (`e` + U+0301) both reach that digest once NFC-normalized; the raw NFD bytes hash to `sha256:2f97bdcfe9d8165b6d25df3f56d7b83e16637d199505d01eff4a3e94eb1d9e15` instead, which is why the normalization has to be named rather than implementation-defined.\n\nThe JCS recipe is exact for objects of strings and integers; floating-point values would also need RFC 8785 number formatting. Happy to drop these into the reference scenario or a doc example, whichever you prefer.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -16601,7 +16029,6 @@ "review_state": null, "root_timestamp": "2026-06-26T00:14:27Z", "body": "On correlation, I'd keep the shape from #132: alongside `external_finding_id` (SIEM-facing), keep a producer-side correlation that stays opaque and producer-defined, so the span stays lean and the heavier context lives in the producer's own store, joined by id at audit time. I'd leave the external record format unspecified rather than mandate a canonical form.\n\nOn `escalate`: it works well as a real \"doesn't proceed yet\" decision. Worth saying the later resolution (allow or block by a human or external workflow) is a separate event rather than an overwrite, so both the decision-time state and the outcome survive.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -16630,7 +16057,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-05T20:03:13Z", "body": "Could you please list the systems that were evaluated (that could be instrumented with run_guardrail span) and share research on which properties they have and how they map to attributes being defined.\n\nIt seems we don't have any scenarios that would show how auto-instrumentation can record this span - are there any open source libraries or clients for server-side guardrails that can be instrumented and then can we add them to reference scenarios?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -16659,7 +16085,6 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-06-17T15:03:27Z", "body": "Nice work @AgentGymLeader - I think this is a good start.\n\nI've left a couple of comments & suggestions. I think it's worth adding tests for the new parsing/counting paths so regressions get caught too.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -16688,7 +16113,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-06-22T17:57:48Z", "body": "Left a few comments, mainly wondering about the previous comment, as we can still derive token usage and operation duration from the existing span.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -16717,7 +16141,6 @@ "review_state": null, "root_timestamp": "2026-06-15T21:22:15Z", "body": "The namespace form is the right call. `gen_ai.agent.drift_score` acting as both a key and the parent of `...drift_score.method` has no precedent in the registry and trips the policy check, as you noted. `gen_ai.agent.{trust,drift}.{score,method}` and `gen_ai.agent.scan.{verdict,method}` validate cleanly and keep each score visibly paired with its method token. No objection to the rename from our side; the semantics are unchanged.\n\nOn Recommended vs Opt-In: Recommended wherever the corresponding score is emitted is correct. A producer-scoped score is close to uninterpretable to a consumer correlating against outcomes without knowing the method epoch that produced it, and the cardinality cost is low since the token only moves on a re-fit or re-threshold, not per span. Opt-In would let a producer emit the score without the token, which is the exact failure mode the token exists to prevent.\n\nOne addition worth pinning in the attribute notes: state explicitly that the `.method` token is opaque and compared for equality only, with no consumer parsing of the method or version substructure. That keeps producers free to change their internal versioning without consumers building brittle par ...[truncated]", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -16746,7 +16169,6 @@ "review_state": null, "root_timestamp": "2026-06-16T14:27:39Z", "body": "Picking up from the gap I noted in #180 (comment above, today) — after the PR merges, one gap remains: the eight attributes are all producer-scoped. A consumer comparing spans across producers, or auditing a single producer's records without trusting that producer, has no recomputable handle.\n\nProposing a ninth attribute to close it:\n\n`gen_ai.agent.action_ref`\n- type: string\n- brief: SHA-256 of the JCS-canonical (RFC 8785) preimage `{agent_id, action_type, scope, timestamp_ms}` per action-ref-v1.\n- requirement_level: recommended\n- note: A deterministic, cross-producer correlation key. Any implementation that follows the derivation produces the same 32-byte hex digest from the same preimage fields — no service call required (no_giskard_api invariant). A verifier comparing records across producers, or auditing without trusting the emitting producer, can recompute it independently.\n\nThe `.method` companion tokens (`trust.method`, `drift.method`, `scan.method`) handle within-producer stability. This attribute handles the cross-producer case.\n\nNormative reference: draft-giskard-aeoess-action-ref-00\nhttps://github.com/giskard09/draft-giskard-aeoess-action-ref\n\nThree independent productio ...[truncated]", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -16775,7 +16197,6 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-06-24T06:22:55Z", "body": "could you please add reference scenarios showing which instrumentation can capture these attributes, how they would do it, and which signals these attributes would appear on? Check out https://github.com/open-telemetry/semantic-conventions-genai/blob/main/CONTRIBUTING.md#4-update-reference-scenarios for the details", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -16804,7 +16225,6 @@ "review_state": null, "root_timestamp": "2026-06-24T19:46:47Z", "body": "Thank you, good to see this moving forward. If there is anything else we can help with, please just shout.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -16833,7 +16253,6 @@ "review_state": null, "root_timestamp": "2026-06-24T20:12:14Z", "body": "+1 to the inline note on the method tokens. A score and its `.method` token are one unit: a score emitted without the token that produced it is close to uninterpretable to a consumer correlating against outcomes, because they cannot tell a re-fit or re-threshold from a real move in the agent. A flat `opt_in` on the token permits exactly that, a producer emitting the score and dropping the token, which is the failure the token exists to prevent.\n\nTying each token to its score reads cleaner than a blanket opt_in: recommend (or conditionally require) `gen_ai.agent.trust.method` when `gen_ai.agent.trust.score` is set, and the same for drift and scan. The registry notes already say recommended wherever the score is emitted, so this just lines the span requirement level up with the note. The token only moves on a method change, not per span, so the cardinality cost stays low.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -16862,7 +16281,6 @@ "review_state": null, "root_timestamp": "2026-06-25T05:49:39Z", "body": "The attributes here are not things a GenAI framework's `invoke_agent` instrumentation can populate on its own. Instrumentation around the model call, the tools, and the response only sees what the model and the immediate execution environment produce. It does not see the agent's authorized capability, its trust or drift signals, or a scan verdict, because none of those are properties of the inference itself. They are outputs of whatever component made the authorization decision: a policy decision point, an agent gateway, or a governance layer sitting in front of the action.\n\nThat component already computes, or has access to, the capability being invoked, the authority or delegation basis the decision was measured against, and any producer-scoped trust, drift, or scan signals, as part of making its allow or deny decision. The instrumentation that populates these span attributes is therefore instrumentation of that decision, emitted on the `invoke_agent` span or correlated to it. The values come from the decision context, not from the model call and not from constants.\n\nThat is also why hardcoding the values does not demonstrate real instrumentation. A more useful scenario would mode ...[truncated]", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -16891,7 +16309,6 @@ "review_state": null, "root_timestamp": "2026-06-25T10:04:31Z", "body": "`argentum-core` implements the authorization gate that produces these values. The capability, trust/drift signals, and scan verdict are outputs of that gate's decision — the gate computes them and the instrumentation sets the span attributes from the returned decision object, not from the invocation framework.\n\nRunnable examples: `giskard09/argentum-core/examples/conformance/`", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -16920,7 +16337,6 @@ "review_state": null, "root_timestamp": "2026-06-25T13:01:32Z", "body": "@lmolkova you are right to push on this. The hardcoded values are a fair criticism of the scenario as written. To your question: yes, there is a real component, and it is not the model-call instrumentation. @aeoess described it well: these attributes are outputs of whatever made the authorization decision, a policy decision point, an agent gateway, or a governance layer in front of the action. The inference instrumentation never sees a trust or drift signal, because none of them are properties of the inference.\n\nWe work on the producer side of this, a control-plane layer that sits in front of agent actions, so this is the vantage point we see it from. The deciding component already holds the capability, the trust and drift signals, and the scan verdict, since those are the inputs to its allow or deny decision. The instrumentation that populates these attributes is instrumentation of that decision, correlated onto the `invoke_agent` span, not of the model call.\n\nFor the scenario, a minimal authorization step that returns a decision object, with the span attributes set from that returned decision rather than from literals, would show both the instrumentation point (the gate) and the ...[truncated]", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -16949,7 +16365,6 @@ "review_state": null, "root_timestamp": "2026-06-26T00:43:26Z", "body": "> these attributes are outputs of whatever made the authorization decision\r\n\r\nIf this is a shared library or component, please share the details about it. There seem to be no auto-instrumentations or infra pieces that are able to report it today, so it's not in scope of this project to cover such conventions. You can document them for your own app.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -16978,7 +16393,6 @@ "review_state": null, "root_timestamp": "2026-06-26T05:08:07Z", "body": "On \"is there a real component\" — I came across one in AGT: microsoft/agent-governance-toolkit#3190 wires decision events (allow/deny/warn…) over OTel across its SDKs, under an `acs_*` namespace. Whether that's in scope here or just app-level is your call — flagging it as a concrete producer in case it helps.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -17007,7 +16421,6 @@ "review_state": null, "root_timestamp": "2026-06-26T14:30:46Z", "body": "> On \"is there a real component\" — I came across one in AGT: [microsoft/agent-governance-toolkit#3190](https://github.com/microsoft/agent-governance-toolkit/pull/3190) wires decision events (allow/deny/warn…) over OTel across its SDKs, under an `acs_*` namespace. Whether that's in scope here or just app-level is your call — flagging it as a concrete producer in case it helps.\r\n\r\nThanks, this is a useful context! If there is a second example of a shared component, the path forward would be to:\r\n\r\n1. Decide which operations should be instrumented (not attributes emitted on arbitrary spans). Agent-level instrumentation won't have access to this information, so stamping on agent spans can't be done with auto-instrumentation\r\n2. Add reference scenarios for applicable shared components\r\n3. Think broad about agent identity and governance and how to model it in general rather than about a handful of attributes", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -17036,7 +16449,6 @@ "review_state": null, "root_timestamp": "2026-06-26T14:50:50Z", "body": "Building on my prior comment on the gate as producer —\n\nThe connection to AGT#3190 is direct: that PR defines `acs_intervention_{allow,deny,warn,escalate,transform}_total` counters per evaluation and explicitly omits `action_identity`. The gap it leaves: the counter records how many interventions occurred, not which specific action was decided on. `action_ref` fills that field — SHA-256(JCS({agent_id, action_type, scope, timestamp})) — recomputable by any downstream verifier without trusting the emitting component.\n\nOn the path you've outlined:\n\n**Operations to instrument** — the gate's evaluation call, not an agent span. We added §Telemetry Integration to the working draft today ([f3628b7](https://github.com/giskard09/draft-etcheverry-action-ref/commit/f3628b769d0ba953f1f53237e6c9b95801361606)) that formalizes this: \"Implementations MUST NOT derive action_ref at the invoking agent's span level. Agent-level instrumentation does not have access to the complete intent tuple as presented to the gate.\" The gate derives the value before returning the verdict; downstream spans carry it as a correlation key.\n\n**Reference scenarios** — [draft-etcheverry-action-ref](https://datatracker.ietf ...[truncated]", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -17065,7 +16477,6 @@ "review_state": null, "root_timestamp": "2026-06-26T14:56:51Z", "body": "@giskard09 I would not recommend bulk AI-generating semantic convention proposal for governance. It needs a lot of human judgement.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -17094,7 +16505,6 @@ "review_state": null, "root_timestamp": "2026-07-01T15:56:22Z", "body": "@thebenignhacker A first cut as the decision operation sounds right. On the enum/metric shape you'd coordinate cross-producer, one distinction is worth building into the outcome vocabulary from the start. The superset under discussion (`allow`/`deny`/`warn`/...) collapses two cases that an auditor reads differently:\n\n- **A control resolved an attempt.** The agent tried something and a policy denied it. There's a decision, and a terminal `deny`.\n- **Nothing was in the reachable surface.** There was no attempt, because there was nothing to attempt, so no decision happens at all.\n\nDownstream both read as \"didn't run\", but they aren't the same fact: one is \"a control fired\", the other is \"it was never on the table\". If the enum folds them together, that difference is lost.\n\nOne paired point on the telemetry:\n\n- A denied decision should still leave a trace. The deliberately-absent child execute span next to a terminal `deny` is what keeps the denial auditable after the fact.\n- If a blocked call emits nothing, \"denied\" and \"never attempted\" look identical downstream.\n\nBoth are structural properties of the decision point, separate from the measured signals (trust / drift) in this PR.\n\nOn ...[truncated]", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -17123,7 +16533,6 @@ "review_state": null, "root_timestamp": "2026-07-02T16:43:11Z", "body": "This is shaping up well, and making the authorization decision its own operation is the right call. Two thoughts on the `deny` span, since that is the load-bearing case.\n\n**1. The producer-scoped `.method` tokens are the correct default, and they also define the one thing a downstream auditor cannot do, which is recompute.** By design `trust.score` with `trust.method` lets an observer correlate a score against outcomes, but not re-derive the score or verify that a given span corresponds to a specific decision. For most telemetry that is exactly right. For the `deny` span you are making first-class (outcome plus reason plus policy, no child execute span beneath it), the property an auditor usually wants is not the score itself but \"is this recorded decision the one that actually gated the action, and can I check that offline without trusting the producer's number.\" That is a different need, and an opaque score cannot serve it.\n\n**2. An optional content-addressed decision reference closes that gap without making anything normative.** A single optional string attribute in `gen_ai.agent.*`, a hash over the canonical decision inputs (capability, policy version, outcome, the identity fro ...[truncated]", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -17152,7 +16561,6 @@ "review_state": null, "root_timestamp": "2026-07-02T17:48:03Z", "body": "That's the standalone proposal — draft-etcheverry-action-ref-01 (https://datatracker.ietf.org/doc/draft-etcheverry-action-ref/), single frozen JCS derivation profile, conformance vectors across independent implementers (argentum-core as reference implementation). Doesn't touch this operation's shape — separate primitive, separate surface, same distinction you're drawing here between trace structure and content-addressed identity.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -17181,7 +16589,6 @@ "review_state": null, "root_timestamp": "2026-07-02T17:49:59Z", "body": "@thebenignhacker Making it a structural invariant rather than an enum value is the right call. The absence of a child execute span is load-bearing in a way a producer can't fake by labeling, which is what makes it hold up for an auditor. The `execute_authorization` sketch reads clean too. Happy to help pressure-test the invariants as it lands.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -17210,7 +16617,6 @@ "review_state": null, "root_timestamp": "2026-07-19T15:49:09Z", "body": "Thanks for the mention, and for taking the fail-closed algorithm point on board.\n\nOne related thing we shipped today: `jcs_edge_v1`, an open (Apache-2.0) RFC 8785 conformance set pinning the canonicalisation edge cases a naive serialiser gets wrong (U+2028 and U+2029 as literal UTF-8, property ordering by UTF-16 code units, `1.0` versus `1`), validated byte-for-byte across ten independent JCS implementations.\n\nhttps://github.com/chopmob-cloud/algovoi-jcs-conformance-vectors/tree/main/vectors/jcs_edge_v1", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -17239,7 +16645,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-10T18:12:15Z", "body": "Can we go ahead and rebase and regenerate the `uv.lock`? Believe `opentelemetry-instrumentation==0.63b1` requires `opentelemetry-semantic-conventions==0.63b1` but we're currently on `opentelemetry-semantic-conventions==0.64b0`. Think we should consider upgrading `opentelemetry-instrumentation` or allow it to resolve a compatible version.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -17268,7 +16673,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-13T18:46:45Z", "body": "LGTM, can we go ahead and update the package version to `opentelemetry-util-genai==1.0b0` in the description and file a follow up issue(s) for Compaction in either this repo and/or the python genai repo based on any actionable findings here? Encryption via `encrypted_content` and support within the handler for `CompactionPart` are the two that came to mind, if not already present. Thanks!", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -17297,7 +16701,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-06-30T18:35:00Z", "body": "Small note, but I believe the Towncrier type in the changelog filename should enhancement? Otherwise, looks good!", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -17326,7 +16729,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-10T00:24:01Z", "body": "LGTM, just a few cosmetic suggestions", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -17355,7 +16757,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-14T15:29:18Z", "body": "LGTM", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -17384,7 +16785,6 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-07-21T02:31:48Z", "body": "I asked AI to do research on azure-ai-evals, deep-evals, dspy (the ones we're adding scenarios for) to check which of these attributes we could capture with generic instrumentations\n\n\n| Attribute | deepeval | dspy | azure-ai-evaluation |\n|---|---|---|---|\n| `evaluator.id` | **Constructed, not native.** Metric has `.name`; build id from name+model: `f\"{metric.__name__}-{metric.evaluation_model}\"` | **Constructed.** Metric is a callable → `metric.__name__` | **Constructed.** Evaluator is a class → `type(ev).__name__` + `model_config[\"model\"]` |\n| `evaluator.version` | **Not native** — no version concept. Scenario hardcodes `\"1.0\"` | **Not native.** Function has no version | **Not native** (not claimed in report). Would be hardcoded |\n| `evaluator.type` | **Inferable.** Native metrics use an LLM (`using_native_model`/`evaluation_model` set) → `llm_judge`; custom `BaseMetric` w/o model → `deterministic` | **Weak.** Metric is just a callable; DSPy can't tell if it calls an LLM. Scenario assumes `deterministic` | **Inferable from class:** AI-assisted (`RelevanceEvaluator`…) → `llm_judge`; NLP/math (`F1ScoreEvaluator`, `BleuScoreEvaluator`, `RougeScoreEvaluator`) → `deterministic` |\n| `r ...[truncated]", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -17413,7 +16813,6 @@ "review_state": null, "root_timestamp": "2026-07-25T16:23:06Z", "body": "We built this pattern independently (an app-level tag, `augmentloop.grade.source`, on `gen_ai.evaluation.result`) and it maps onto your `evaluator.type` enum: our `math` (a deterministic checker compares against a provably-correct answer) is your `deterministic`; our `ai_judge` is your `llm_judge`. We never let `ai_judge` grades into anything that feeds a headline \"correct\" number, filtered by source rather than by convention.\n\nOne gap: our third source, `reality`, has no slot. It's a grade that arrives asynchronously, after the decision's span has closed, from an actual outcome rather than a check or an opinion: the clip was actually kept, the appointment actually landed. Not `deterministic` (nothing computes it at eval time), not `llm_judge` (no model ever scores it). We handle it by re-emitting the event with a span link back to the original decision span, plus the same `gen_ai.response.id` as correlation fallback, so a late verdict stays one hop from the decision it judges. Might be worth a fifth value, or a line in the spec noting that resolution timing is orthogonal to evaluator type: `reality` could pair with any of the current four.\n\nOn @lmolkova's capturability table: it m ...[truncated]", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -17442,7 +16841,6 @@ "review_state": null, "root_timestamp": "2026-07-26T18:09:10Z", "body": "`INFORMED_BY` captures provenance between a tool result and a later generation.\nI measured a related quantity that current usage attributes cannot expose: how\nmuch of each model input is new versus logical replay from the preceding\nconversation context.\n\nI recorded two real coding-agent sessions and verified prompt continuity with\nblock-level prefix hashes; the quantity is the exact token LCP between adjacent\nprompts, computed while token IDs were in memory and stored as one integer. No\nraw text is retained. Counts here come from a local `cl100k_base` tokenizer\nrather than provider-reported usage, which matters for units — see below.\n\n| session | steps | total prompt tokens | logical replay |\n|---|---:|---:|---:|\n| A | 17 | 170,714 | 147,902 (87%) |\n| B | 27 | 400,211 | 368,620 (92%) |\n\nIn session B, one source step added 2,841 tokens to the next prompt. Those\ntokens appeared in 19 later prompts, totalling 53,979 replayed tokens (19x).\n\n`gen_ai.usage.input_tokens` records total input, while\n`gen_ai.usage.cache_read.input_tokens` records provider-cache delivery. Neither\nanswers whether input is new or already present in the logical agent context.\nCould a companion split on `chat` sp ...[truncated]", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -17471,7 +16869,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-22T09:38:14Z", "body": "The TC has write permissions on every repo, so their approval would be counted as someone with write access but the ruleset may not allow maintainers to merge the PR. It's a small edge case, but maybe it could be confusing?\n\nOtherwise looks good to me, if you think that this is not worth worrying about feel free to merge :)", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -17500,7 +16897,6 @@ "review_state": null, "root_timestamp": "2025-07-13T12:08:24Z", "body": "Had a quick look. This appears to be a freeform yaml block. Could `annotations` be used instead? We recently backed-out of spec change for `value_type` in favour of `annotations`. This feels the same to me.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -17529,7 +16925,6 @@ "review_state": null, "root_timestamp": "2025-07-14T22:32:45Z", "body": "I don't think we make any requirement on delta vs. cumulative, but we should have a way to specify histogram boundaries.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -17558,7 +16953,6 @@ "review_state": null, "root_timestamp": "2025-08-18T12:12:12Z", "body": "Are packages versioned? What's the difference between a package and just a repository directly?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -17587,7 +16981,6 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-01-07T18:12:20Z", "body": "We should change the tests to validate with the real archive URLs.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -17616,7 +17009,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-16T21:33:24Z", "body": "@lmolkova FYI - I forgot to approve this oh so long ago - Approving now if we still consider this a blocker - hopefully it's easy to bring this back up to date and merge if we want it.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -17645,7 +17037,6 @@ "review_state": null, "root_timestamp": "2026-07-23T12:25:17Z", "body": "Generally fix looks good - would approve, but I think Co-pilot found the bug in the windows build. Please fix tests and then I think we can merge.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -17674,7 +17065,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-23T18:31:22Z", "body": "Overall - this looks like a nice addition, thanks for contributing!", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -17703,7 +17093,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-24T16:16:35Z", "body": "@McGluut thanks for the contribution!\n\nI'd like to start release sometime soon and would love to get this change in - it'd be really useful in some tests we have in otel. \n\nIf you're around, could you please take a look at the open discussion. Otherwise I might just auto-apply the suggestion I left and merge the PR in a hope you won't mind it. Thanks!", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -17732,7 +17121,6 @@ "review_state": "CHANGES_REQUESTED", "root_timestamp": "2026-07-24T17:15:51Z", "body": "Thank you - I think this is pretty close and useful. I would like it to be aligned with how we handle `resource` though. And in solving how to pass the `instrumentation_scope` to rego we can do the same for `resource`.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -17761,7 +17149,6 @@ "review_state": "COMMENTED", "root_timestamp": "2026-07-25T21:43:14Z", "body": "We're very close. This is really good. Let's remove that Template section and make sure to update the docs: https://github.com/open-telemetry/weaver/tree/main/crates/weaver_live_check#custom-advisors", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -17790,7 +17177,6 @@ "review_state": null, "root_timestamp": "2026-07-27T19:53:54Z", "body": "> Middle's re-export is stamped with middle's schema_url instead of base's, so resolution fails with\r\n\r\nThis is a bug - I assume this happened from a previous release before we had better multi-dependency resolution?", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "author_action", @@ -17819,7 +17205,6 @@ "review_state": null, "root_timestamp": "2026-07-27T20:05:14Z", "body": "100% agree 2 + 3 are our bugs that must be fixed.\r\n\r\nFor 1 - it's an interesting, temporary, problem. Fixing the `include_unrefereenced` flag to remember provenance *should* fix this issue once published repositories upstream are fixed. If we have a fix - I think it should be a temporary issue.", - "measurement_date": "2026-08-01", "role": "scored", "stability": "flaky", "recorded_label": null, @@ -17848,7 +17233,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-28T15:55:10Z", "body": "Fix looks good, thanks for catching this bug. Looks like an opportunity to refacor/clean up the logic after a point fix release!", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -17877,7 +17261,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-28T15:59:38Z", "body": "Fix is good but lots of duplicated code or repeated logic.\n\nGiven urgency of the bugs we can clean this up post submission.\n\nThis is also one of those \"duh\" moments - we know attributes can change across version and treating the whole group as a version is a good thing", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", @@ -17906,7 +17289,6 @@ "review_state": "APPROVED", "root_timestamp": "2026-07-28T16:02:20Z", "body": "Another thing we should have fixed! Good catch", - "measurement_date": "2026-08-01", "role": "scored", "stability": "stable", "recorded_label": "no_author_action", diff --git a/.github/scripts/pull-request-dashboard/test_eval_baseline.py b/.github/scripts/pull-request-dashboard/test_eval_baseline.py index 4452236fe5a..2eb8712f3da 100644 --- a/.github/scripts/pull-request-dashboard/test_eval_baseline.py +++ b/.github/scripts/pull-request-dashboard/test_eval_baseline.py @@ -157,10 +157,6 @@ def test_counts_and_configuration_describe_the_new_measurement(self) -> None: rebuilt["baseline_generated_at"], rebuilt["measurements_updated_at"], ) - self.assertEqual( - {rebuilt["baseline_generated_at"]}, - {case["measurement_date"] for case in rebuilt["cases"]}, - ) self.assertNotIn("generated_at", rebuilt) diff --git a/.github/scripts/pull-request-dashboard/test_eval_fixture.py b/.github/scripts/pull-request-dashboard/test_eval_fixture.py index f4951de68fd..de5be41311a 100644 --- a/.github/scripts/pull-request-dashboard/test_eval_fixture.py +++ b/.github/scripts/pull-request-dashboard/test_eval_fixture.py @@ -42,18 +42,9 @@ def test_counts_match_the_cases(self) -> None: def test_measurement_dates_describe_the_mixed_vintage_corpus(self) -> None: baseline = date.fromisoformat(self.data["baseline_generated_at"]) updated = date.fromisoformat(self.data["measurements_updated_at"]) - measurements = [] self.assertLessEqual(baseline, updated) self.assertNotIn("generated_at", self.data) - for case in self.cases: - with self.subTest(case=case["id"]): - measured = date.fromisoformat(case["measurement_date"]) - self.assertLessEqual(baseline, measured) - self.assertLessEqual(measured, updated) - measurements.append(measured) - self.assertEqual(baseline, min(measurements)) - self.assertEqual(updated, max(measurements)) def test_case_ids_are_unique(self) -> None: ids = [case["id"] for case in self.cases] From 5328b5b95bf20ebbd761e3c1380ff8f2950be871 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Wed, 2 Sep 2026 12:48:09 -0700 Subject: [PATCH 08/30] Fix dashboard feedback lifecycle Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../pull-request-dashboard/copilot_review.py | 107 +++++++- .../dashboard_override.py | 8 +- .../dashboard_test_support.py | 3 + .../discussion_lifecycle.py | 51 +++- .../pull-request-dashboard/github_cli.py | 14 ++ .../pull_request_activity.py | 8 +- .../pull_request_evaluation.py | 6 +- .../pull_request_source.py | 56 ++++- .../test_copilot_review.py | 128 ++++++++++ .../pull-request-dashboard/test_dashboard.py | 234 ++++++++++++++++++ .../test_discussion_lifecycle.py | 129 ++++++++++ .../pull-request-dashboard/test_github_cli.py | 42 ++++ .../test_pull_request_source.py | 25 ++ 13 files changed, 781 insertions(+), 30 deletions(-) diff --git a/.github/scripts/pull-request-dashboard/copilot_review.py b/.github/scripts/pull-request-dashboard/copilot_review.py index 57056b40f4e..767d994cc94 100644 --- a/.github/scripts/pull-request-dashboard/copilot_review.py +++ b/.github/scripts/pull-request-dashboard/copilot_review.py @@ -53,11 +53,26 @@ def is_copilot_reviewer( def open_copilot_findings( review_threads: Sequence[ReviewThread], + *, + known_review_ids: frozenset[int] = frozenset(), + superseded_review_ids: frozenset[int] = frozenset(), + superseded_before: datetime | None = None, ) -> tuple[ReviewThread, ...]: # A review's own comment count never shrinks, so it still counts findings - # the author has since addressed. Unresolved threads are the live ones: - # GitHub marks a thread outdated once its anchor lines change, which is how - # the rest of the dashboard already recognises a pushed fix. + # that a later clean review has superseded. A review binding gives the exact + # ordering; creation time is the fallback for older cached source shapes. + # GitHub also marks a thread outdated once its anchor lines change. + def is_superseded(thread: ReviewThread) -> bool: + review_id = thread.comments[0].review_id + if review_id in superseded_review_ids: + return True + if review_id in known_review_ids: + return False + if superseded_before is None: + return False + created_at = parse_ts(thread.comments[0].created_at) + return created_at is not None and created_at < superseded_before + return tuple( thread for thread in review_threads @@ -66,6 +81,7 @@ def open_copilot_findings( and not thread.is_outdated and thread.comments and is_copilot_reviewer(thread.comments[0].actor) + and not is_superseded(thread) ) ) @@ -78,15 +94,79 @@ def open_copilot_finding_count( def open_copilot_finding_urls( review_threads: Sequence[ReviewThread], + reviews: Sequence[Review] = (), + head_sha: str = "", ) -> tuple[str, ...]: urls: list[str] = [] - for thread in open_copilot_findings(review_threads): + ( + known_review_ids, + superseded_review_ids, + superseded_before, + ) = _copilot_finding_supersession( + reviews, + head_sha, + ) + for thread in open_copilot_findings( + review_threads, + known_review_ids=known_review_ids, + superseded_review_ids=superseded_review_ids, + superseded_before=superseded_before, + ): url = thread.comments[0].url if url and url not in urls: urls.append(url) return tuple(urls) +def _copilot_finding_supersession( + reviews: Sequence[Review], + head_sha: str, +) -> tuple[frozenset[int], frozenset[int], datetime | None]: + clean_review_timestamps = [ + parse_ts(review.submitted_at) + for review in reviews + if ( + is_copilot_reviewer(review) + and review.commit_id == head_sha + and review.finding_count == 0 + ) + ] + latest_clean_review_at = max( + ( + timestamp + for timestamp in clean_review_timestamps + if timestamp is not None + ), + default=None, + ) + if latest_clean_review_at is None: + return frozenset(), frozenset(), None + known_review_ids = frozenset( + review.database_id + for review in reviews + if review.database_id and is_copilot_reviewer(review) + ) + superseded_review_ids = frozenset( + review.database_id + for review in reviews + if ( + review.database_id + and is_copilot_reviewer(review) + and ( + ( + bool(review.commit_id) + and review.commit_id != head_sha + ) + or ( + (submitted_at := parse_ts(review.submitted_at)) is not None + and submitted_at < latest_clean_review_at + ) + ) + ) + ) + return known_review_ids, superseded_review_ids, latest_clean_review_at + + def copilot_review_status( reviews: Sequence[Review], head_sha: str, @@ -108,7 +188,24 @@ def copilot_review_status( review.commit_id == head_sha for review in copilot_reviews ) - return True, stale, open_copilot_finding_count(review_threads) > 0 + ( + known_review_ids, + superseded_review_ids, + superseded_before, + ) = _copilot_finding_supersession( + copilot_reviews, + head_sha, + ) + return ( + True, + stale, + bool(open_copilot_findings( + review_threads, + known_review_ids=known_review_ids, + superseded_review_ids=superseded_review_ids, + superseded_before=superseded_before, + )), + ) def copilot_review_outstanding(facts: DashboardFacts, *, enabled: bool) -> bool: diff --git a/.github/scripts/pull-request-dashboard/dashboard_override.py b/.github/scripts/pull-request-dashboard/dashboard_override.py index e39c0dc143b..01d5a52617c 100644 --- a/.github/scripts/pull-request-dashboard/dashboard_override.py +++ b/.github/scripts/pull-request-dashboard/dashboard_override.py @@ -137,13 +137,7 @@ def latest_authorized_command( def _effective_command_timestamp(comment: IssueComment) -> str: - created_at = parse_ts(comment.created_at) - content_updated_at = parse_ts(comment.content_updated_at) - if content_updated_at is not None and ( - created_at is None or content_updated_at >= created_at - ): - return comment.content_updated_at - return comment.created_at + return comment.effective_content_timestamp def dashboard_override_facts( diff --git a/.github/scripts/pull-request-dashboard/dashboard_test_support.py b/.github/scripts/pull-request-dashboard/dashboard_test_support.py index 8bd0f95fdbd..9bf549e26c2 100644 --- a/.github/scripts/pull-request-dashboard/dashboard_test_support.py +++ b/.github/scripts/pull-request-dashboard/dashboard_test_support.py @@ -109,6 +109,7 @@ def review_source(**changes: Any) -> Review: "state": "COMMENTED", "submitted_at": "2026-08-16T07:00:00Z", "updated_at": "2026-08-16T07:00:00Z", + "content_updated_at": "2026-08-16T07:00:00Z", } values.update(changes) return Review(**values) @@ -130,9 +131,11 @@ def reaction_group( def review_thread_comment(**changes: Any) -> ReviewThreadComment: values = { "node_id": "PRRC_1", + "review_id": 0, "url": "https://example.test/review-comment/1", "body": "Please update this.", "created_at": "2026-08-16T07:00:00Z", + "updated_at": "2026-08-16T07:00:00Z", "actor": actor("reviewer"), } values.update(changes) diff --git a/.github/scripts/pull-request-dashboard/discussion_lifecycle.py b/.github/scripts/pull-request-dashboard/discussion_lifecycle.py index 2aea9f0bba6..bfe35af0c33 100644 --- a/.github/scripts/pull-request-dashboard/discussion_lifecycle.py +++ b/.github/scripts/pull-request-dashboard/discussion_lifecycle.py @@ -110,14 +110,18 @@ def _discussion_comment( reviewers: set[str], body: str, positive_reactors: set[str] | None = None, + activity_timestamp: str = "", ) -> dict[str, Any]: - return { + comment = { "timestamp": timestamp, "actor": actor, "actor_role": role_for(actor, author, reviewers), "body": truncate(body), "positive_reactors": sorted(positive_reactors or set()), } + if activity_timestamp and activity_timestamp != timestamp: + comment["activity_timestamp"] = activity_timestamp + return comment def _add_discussion_facts( @@ -169,6 +173,7 @@ def _group_review_threads( reviewers, comment.body, _positive_reaction_logins(comment), + comment.effective_content_timestamp, ) for comment in ordered ] @@ -219,6 +224,9 @@ def _derive_top_level_items(source: DiscussionInput) -> list[dict[str, Any]]: "body": body, "positive_reactors": [], } + activity_timestamp = event.get("timestamp") or root_timestamp + if activity_timestamp != root_timestamp: + comment["activity_timestamp"] = activity_timestamp if ( event.get("source_id") is not None and comment["actor"] @@ -272,7 +280,7 @@ def _derive_top_level_author_comment_items( ) items: list[dict[str, Any]] = [] for event in source.events: - timestamp = event.get("created_timestamp") or event.get("timestamp") or "" + timestamp = event.get("timestamp") or event.get("created_timestamp") or "" if ( event.get("kind") != "issue-comment" or event.get("actor_role") != "author" @@ -288,17 +296,30 @@ def _derive_top_level_author_comment_items( "body": truncate(event.get("body") or ""), "positive_reactors": [], } - candidate_feedback = [ - { + author_timestamp = parse_ts(timestamp) + candidate_feedback = [] + for item in top_level_items: + item_comments = item.get("comments") or [] + feedback_timestamp = parse_ts( + (item_comments[-1] if item_comments else {}).get( + "activity_timestamp" + ) + or item.get("root_timestamp") + or "" + ) + if ( + author_timestamp is None + or feedback_timestamp is None + or feedback_timestamp >= author_timestamp + ): + continue + candidate_feedback.append({ "discussion_id": item["discussion_id"], "body": "\n\n".join( item_comment.get("body") or "" - for item_comment in (item.get("comments") or []) + for item_comment in item_comments ), - } - for item in top_level_items - if (item.get("root_timestamp") or "") < timestamp - ] + }) items.append( _add_discussion_facts( { @@ -340,11 +361,21 @@ def _filter_handoff_feedback( if ( comment.get("actor_role") in _HUMAN_REVIEWER_ROLES and comment.get("actor") != pr_author - and after_cutoff(comment.get("timestamp") or "") + and after_cutoff( + comment.get("activity_timestamp") + or comment.get("timestamp") + or "" + ) ) ] if not comments: return None + comments.sort( + key=lambda comment: ( + comment.get("activity_timestamp") or comment.get("timestamp") or "", + comment.get("timestamp") or "", + ) + ) filtered = {**discussion, "comments": comments} filtered["requester"] = comments[-1].get("actor") or "" filtered["pr_author"] = pr_author diff --git a/.github/scripts/pull-request-dashboard/github_cli.py b/.github/scripts/pull-request-dashboard/github_cli.py index bd2e9556163..71143d682f1 100644 --- a/.github/scripts/pull-request-dashboard/github_cli.py +++ b/.github/scripts/pull-request-dashboard/github_cli.py @@ -178,6 +178,7 @@ def gh_pr_view(repo: str, number: int) -> dict[str, Any]: state submittedAt updatedAt + lastEditedAt author { login } @@ -295,6 +296,11 @@ def fetch_pr_reviews(owner: str, repo_name: str, number: int) -> list[dict[str, "body": review.get("body") or "", "submitted_at": review.get("submittedAt") or "", "updated_at": review.get("updatedAt") or "", + "content_updated_at": ( + review.get("lastEditedAt") + or review.get("submittedAt") + or "" + ), }) page_info = connection.get("pageInfo") or {} if not page_info.get("hasNextPage"): @@ -807,6 +813,10 @@ def load_reviewer_set(org: str, approver_team_slugs: list[str]) -> set[str]: url body createdAt + lastEditedAt + pullRequestReview { + fullDatabaseId + } author { login } @@ -843,6 +853,10 @@ def load_reviewer_set(org: str, approver_team_slugs: list[str]) -> set[str]: url body createdAt + lastEditedAt + pullRequestReview { + fullDatabaseId + } author { login } diff --git a/.github/scripts/pull-request-dashboard/pull_request_activity.py b/.github/scripts/pull-request-dashboard/pull_request_activity.py index b3fbf89d374..50ed89734d9 100644 --- a/.github/scripts/pull-request-dashboard/pull_request_activity.py +++ b/.github/scripts/pull-request-dashboard/pull_request_activity.py @@ -119,11 +119,7 @@ def _issue_comment_event( else comment.body ) login = reviewer_actor_login(comment.actor) - timestamp = ( - comment.content_updated_at - or comment.created_at - or comment.updated_at - ) + timestamp = comment.effective_content_timestamp return { "source_id": comment.database_id or None, "discussion_url": comment.url, @@ -173,7 +169,7 @@ def _review_event( "source_id": review.database_id or None, "discussion_url": review.url, "kind": "review-state", - "timestamp": review.submitted_at, + "timestamp": review.effective_content_timestamp, "created_timestamp": review.submitted_at, "actor": login, "actor_role": role_for(login, author, approver_logins), diff --git a/.github/scripts/pull-request-dashboard/pull_request_evaluation.py b/.github/scripts/pull-request-dashboard/pull_request_evaluation.py index 476ad1d3cde..9a736c0ae9d 100644 --- a/.github/scripts/pull-request-dashboard/pull_request_evaluation.py +++ b/.github/scripts/pull-request-dashboard/pull_request_evaluation.py @@ -568,7 +568,11 @@ def evaluate_pull_request( review_threads, pending_actions, ( - open_copilot_finding_urls(pr_source.review_threads) + open_copilot_finding_urls( + pr_source.review_threads, + pr_source.reviews, + facts.head_sha, + ) if facts.copilot_review_outstanding else () ), diff --git a/.github/scripts/pull-request-dashboard/pull_request_source.py b/.github/scripts/pull-request-dashboard/pull_request_source.py index 4701f26586a..5bec01aadf9 100644 --- a/.github/scripts/pull-request-dashboard/pull_request_source.py +++ b/.github/scripts/pull-request-dashboard/pull_request_source.py @@ -18,7 +18,7 @@ required_code_scanning_checks, unreported_required_contexts, ) -from utils import is_copilot_reviewer_login +from utils import is_copilot_reviewer_login, parse_ts def gh_pr_view(repo: str, number: int) -> dict[str, Any]: @@ -213,6 +213,18 @@ class IssueComment: actor: Actor = field(default_factory=Actor) performed_via_app_slug: str = "" + @property + def effective_content_timestamp(self) -> str: + created_at = parse_ts(self.created_at) + content_updated_at = parse_ts(self.content_updated_at) + if content_updated_at is not None and ( + created_at is None or content_updated_at >= created_at + ): + return self.content_updated_at + if created_at is not None: + return self.created_at + return self.updated_at if parse_ts(self.updated_at) is not None else "" + def is_from_app(self, app_slug: str) -> bool: return ( self.performed_via_app_slug == app_slug @@ -242,6 +254,17 @@ class Review: body: str = "" submitted_at: str = "" updated_at: str = "" + content_updated_at: str = "" + + @property + def effective_content_timestamp(self) -> str: + submitted_at = parse_ts(self.submitted_at) + content_updated_at = parse_ts(self.content_updated_at) + if content_updated_at is not None and ( + submitted_at is None or content_updated_at >= submitted_at + ): + return self.content_updated_at + return self.submitted_at @dataclass(frozen=True) @@ -270,12 +293,24 @@ def __post_init__(self) -> None: @dataclass(frozen=True) class ReviewThreadComment: node_id: str = "" + review_id: int = 0 url: str = "" body: str = "" created_at: str = "" + updated_at: str = "" actor: Actor = field(default_factory=Actor) reaction_groups: tuple[ReactionGroup, ...] = () + @property + def effective_content_timestamp(self) -> str: + created_at = parse_ts(self.created_at) + updated_at = parse_ts(self.updated_at) + if updated_at is not None and ( + created_at is None or updated_at >= created_at + ): + return self.updated_at + return self.created_at + def __post_init__(self) -> None: object.__setattr__( self, @@ -583,6 +618,12 @@ def normalize_reviews(values: Any) -> tuple[Review, ...]: item.get("submitted_at") or item.get("submittedAt") ), updated_at=_text(item.get("updated_at") or item.get("updatedAt")), + content_updated_at=_text( + item.get("content_updated_at") + or item.get("lastEditedAt") + or item.get("submitted_at") + or item.get("submittedAt") + ), )) return tuple(reviews) @@ -624,11 +665,24 @@ def normalize_review_threads(values: Any) -> tuple[ReviewThread, ...]: )) comments.append(ReviewThreadComment( node_id=_text(comment.get("id") or comment.get("node_id")), + review_id=_integer( + _mapping(comment.get("pullRequestReview")).get( + "fullDatabaseId" + ) + or comment.get("pull_request_review_id") + ), url=_text(comment.get("url") or comment.get("html_url")), body=str(comment.get("body") or ""), created_at=_text( comment.get("createdAt") or comment.get("created_at") ), + updated_at=_text( + comment.get("lastEditedAt") + or comment.get("updatedAt") + or comment.get("updated_at") + or comment.get("createdAt") + or comment.get("created_at") + ), actor=normalize_actor( comment.get("author") or comment.get("user") ), diff --git a/.github/scripts/pull-request-dashboard/test_copilot_review.py b/.github/scripts/pull-request-dashboard/test_copilot_review.py index 3874c187d30..cb7a2826e41 100644 --- a/.github/scripts/pull-request-dashboard/test_copilot_review.py +++ b/.github/scripts/pull-request-dashboard/test_copilot_review.py @@ -9,7 +9,9 @@ from copilot_review import ( REQUEST_CONFIRMATION_ATTEMPTS, + copilot_review_status, copilot_first_review_overdue, + open_copilot_finding_urls, set_copilot_first_review_missing_since, set_copilot_review_request_needed, stale_request_reason, @@ -19,7 +21,11 @@ record_copilot_review_observation, ) from dashboard_test_support import ( + actor, dashboard_facts, + review_source, + review_thread, + review_thread_comment, stored_dashboard_result, ) from routing_snapshot import build_routing_snapshot @@ -55,6 +61,128 @@ def review_result(route: str = "approver", **fact_changes): ) +class CopilotFindingLifecycleTest(unittest.TestCase): + def test_clean_current_review_supersedes_only_older_findings(self) -> None: + reviews = (review_source( + actor=actor("copilot"), + commit_id="current-head", + finding_count=0, + submitted_at="2026-07-20T02:00:00Z", + ),) + threads = ( + review_thread( + node_id="old", + comments=(review_thread_comment( + url="https://example.test/old", + actor=actor("copilot"), + created_at="2026-07-20T01:00:00Z", + updated_at="2026-07-20T01:00:00Z", + ),), + ), + review_thread( + node_id="new", + comments=(review_thread_comment( + url="https://example.test/new", + actor=actor("copilot"), + created_at="2026-07-20T03:00:00Z", + updated_at="2026-07-20T03:00:00Z", + ),), + ), + review_thread( + node_id="unknown", + comments=(review_thread_comment( + url="https://example.test/unknown", + actor=actor("copilot"), + created_at="", + updated_at="", + ),), + ), + ) + + self.assertEqual( + (True, False, True), + copilot_review_status(reviews, "current-head", threads), + ) + self.assertEqual( + ( + "https://example.test/new", + "https://example.test/unknown", + ), + open_copilot_finding_urls(threads, reviews, "current-head"), + ) + + def test_missing_clean_review_timestamp_does_not_supersede_findings( + self, + ) -> None: + reviews = (review_source( + actor=actor("copilot"), + commit_id="current-head", + finding_count=0, + submitted_at="", + ),) + threads = (review_thread(comments=(review_thread_comment( + actor=actor("copilot"), + created_at="2026-07-20T01:00:00Z", + updated_at="2026-07-20T01:00:00Z", + ),)),) + + self.assertEqual( + (True, False, True), + copilot_review_status(reviews, "current-head", threads), + ) + + def test_late_review_of_an_old_head_does_not_replace_current_clean_review( + self, + ) -> None: + reviews = ( + review_source( + database_id=10, + actor=actor("copilot"), + commit_id="current-head", + finding_count=0, + submitted_at="2026-07-20T02:00:00Z", + ), + review_source( + database_id=20, + actor=actor("copilot"), + commit_id="old-head", + finding_count=1, + submitted_at="2026-07-20T03:00:00Z", + ), + ) + threads = (review_thread(comments=(review_thread_comment( + review_id=20, + actor=actor("copilot"), + created_at="2026-07-20T03:00:00Z", + updated_at="2026-07-20T03:00:00Z", + ),)),) + + self.assertEqual( + (True, False, False), + copilot_review_status(reviews, "current-head", threads), + ) + + def test_unknown_review_binding_uses_timestamp_fallback(self) -> None: + reviews = (review_source( + database_id=10, + actor=actor("copilot"), + commit_id="current-head", + finding_count=0, + submitted_at="2026-07-20T02:00:00Z", + ),) + threads = (review_thread(comments=(review_thread_comment( + review_id=999, + actor=actor("copilot"), + created_at="2026-07-20T01:00:00Z", + updated_at="2026-07-20T01:00:00Z", + ),)),) + + self.assertEqual( + (True, False, False), + copilot_review_status(reviews, "current-head", threads), + ) + + class CopilotFirstReviewMissingSinceTest(unittest.TestCase): def test_starts_clock_when_review_is_missing(self) -> None: facts = set_copilot_first_review_missing_since( diff --git a/.github/scripts/pull-request-dashboard/test_dashboard.py b/.github/scripts/pull-request-dashboard/test_dashboard.py index 857b7d9810e..c41bbf7916b 100644 --- a/.github/scripts/pull-request-dashboard/test_dashboard.py +++ b/.github/scripts/pull-request-dashboard/test_dashboard.py @@ -639,6 +639,186 @@ def test_actionable_review_after_override_ends_handoff( self.assertEqual(len(classifier.requests), 1) self.assertEqual(len(classifier.reviewer_feedback_requests), 1) + @patch("pull_request_evaluation.fetch_pull_request_source") + def test_actionable_review_edited_after_override_ends_handoff( + self, fetch_raw: Mock + ) -> None: + fetch_raw.return_value = pull_request_source( + pull_request=pull_request_metadata(title="Routing integration"), + issue_comments=(issue_comment( + database_id=102, + body="/dashboard route:reviewers", + created_at="2026-08-16T08:00:00Z", + ),), + reviews=(review_source( + database_id=501, + url="https://example.test/pull/7#pullrequestreview-501", + state="COMMENTED", + submitted_at="2026-08-16T07:00:00Z", + updated_at="2026-08-16T09:00:00Z", + content_updated_at="2026-08-16T09:00:00Z", + body="Please update this.", + ),), + ) + classification = action_classification( + "pr-review-501", + DiscussionKind.TOP_LEVEL_FEEDBACK, + DiscussionAction.AUTHOR, + "The edited review requested a change.", + ) + classifier = FakeClassificationOperation( + DiscussionClassifications((), (classification,), ()), + reviewer_feedback_result=(classification,), + ) + + result = evaluate_pr( + {"number": 7}, + classification_service=classifier, + ) + + self.assertIsInstance(result, EvaluationSuccess) + assert isinstance(result, EvaluationSuccess) + self.assertEqual(DashboardRoute.AUTHOR, result.route) + self.assertTrue(result.facts.dashboard_override_cleared_by_feedback) + self.assertEqual( + "2026-08-16T07:00:00Z", + result.pending_actions["pr-review-501"]["since"], + ) + + @patch("pull_request_evaluation.fetch_pull_request_source") + def test_actionable_issue_feedback_after_override_ends_handoff( + self, fetch_raw: Mock + ) -> None: + fetch_raw.return_value = pull_request_source( + pull_request=pull_request_metadata(title="Routing integration"), + issue_comments=( + issue_comment( + database_id=102, + body="/dashboard route:reviewers", + created_at="2026-08-16T08:00:00Z", + ), + issue_comment( + database_id=501, + actor=actor("reviewer"), + body="The test is unfortunately still failing here.", + created_at="2026-08-16T09:00:00Z", + ), + ), + ) + feedback = action_classification( + "pr-issue-comment-501", + DiscussionKind.TOP_LEVEL_FEEDBACK, + DiscussionAction.AUTHOR, + "The reviewer reported that the defect remains.", + ) + classifier = FakeClassificationOperation( + DiscussionClassifications((), (feedback,), ()), + reviewer_feedback_result=(feedback,), + ) + + result = evaluate_pr( + {"number": 7}, + classification_service=classifier, + ) + + self.assertIsInstance(result, EvaluationSuccess) + assert isinstance(result, EvaluationSuccess) + self.assertEqual(DashboardRoute.AUTHOR, result.route) + self.assertTrue(result.facts.dashboard_override_cleared_by_feedback) + self.assertEqual((), result.facts.dashboard_command_replies) + self.assertEqual( + {"pr-issue-comment-501": { + "action": "author", + "since": "2026-08-16T09:00:00Z", + }}, + result.pending_actions, + ) + + @patch("pull_request_evaluation.fetch_pull_request_source") + def test_completed_author_response_retires_only_matching_review_work( + self, fetch_raw: Mock + ) -> None: + fetch_raw.return_value = pull_request_source( + pull_request=pull_request_metadata(title="Routing integration"), + issue_comments=( + issue_comment( + database_id=301, + body="The requested audit is complete.", + created_at="2026-08-16T09:00:00Z", + ), + ), + reviews=( + review_source( + database_id=201, + actor=actor("reviewer"), + state="CHANGES_REQUESTED", + body="Please complete the audit.", + submitted_at="2026-08-16T08:00:00Z", + ), + review_source( + database_id=202, + actor=actor("other-reviewer"), + state="COMMENTED", + body="Please update the release note.", + submitted_at="2026-08-16T08:30:00Z", + ), + ), + ) + audit = action_classification( + "pr-review-201", + DiscussionKind.TOP_LEVEL_FEEDBACK, + DiscussionAction.AUTHOR, + "The review requested an audit.", + ) + release_note = action_classification( + "pr-review-202", + DiscussionKind.TOP_LEVEL_FEEDBACK, + DiscussionAction.AUTHOR, + "The review requested a release note.", + ) + reply = ClassificationSuccess( + DiscussionIdentity( + "pr-author-reply-301", + DiscussionKind.TOP_LEVEL_AUTHOR_REPLY, + ), + AuthorCommentDecision(( + FeedbackOutcome( + "pr-review-201", + DiscussionAction.NONE, + "The author completed the audit.", + ), + FeedbackOutcome( + "pr-review-202", + DiscussionAction.AUTHOR, + "The author did not mention the release note.", + ), + )), + ) + classifier = FakeClassificationOperation( + DiscussionClassifications((), (audit, release_note), (reply,)) + ) + + result = evaluate_pr( + {"number": 7}, + classification_service=classifier, + ) + + self.assertIsInstance(result, EvaluationSuccess) + assert isinstance(result, EvaluationSuccess) + self.assertEqual(DashboardRoute.AUTHOR, result.route) + self.assertNotIn("pr-review-201", result.pending_actions) + self.assertEqual( + { + "action": "author", + "since": "2026-08-16T09:00:00Z", + }, + result.pending_actions["pr-review-202"], + ) + self.assertEqual( + {"reply": "2026-08-16T09:00:00Z"}, + result.top_level_history["pr-review-201"]["evidence"], + ) + @patch("pull_request_evaluation.fetch_pull_request_source") def test_non_actionable_review_keeps_handoff( self, fetch_raw: Mock @@ -1134,6 +1314,60 @@ def test_current_head_matches_latest_clean_copilot_review(self) -> None: self.assertTrue(facts.copilot_review_exists) self.assertFalse(facts.copilot_review_needed) + def test_current_clean_review_supersedes_older_open_copilot_findings( + self, + ) -> None: + facts = evaluation_facts( + { + "pr": { + "updatedAt": "2026-07-20T03:00:00Z", + "createdAt": "2026-07-20T01:00:00Z", + "author": {"login": "author"}, + "assignees": [], + "mergeStateStatus": "CLEAN", + "mergeable": "MERGEABLE", + "headRefOid": "current-head", + }, + "reviews": [ + { + "id": 10, + "commit_id": "old-head", + "finding_count": 1, + "user": {"login": "copilot"}, + "submitted_at": "2026-07-20T01:30:00Z", + }, + { + "id": 20, + "commit_id": "current-head", + "finding_count": 0, + "user": {"login": "copilot"}, + "submitted_at": "2026-07-20T02:30:00Z", + }, + ], + "review_threads": [{ + "id": "thread-1", + "isResolved": False, + "isOutdated": False, + "comments": { + "nodes": [{ + "id": "comment-1", + "url": "https://example.com/1", + "body": "Please fix this.", + "createdAt": "2026-07-20T01:30:00Z", + "author": {"login": "copilot"}, + }], + }, + }], + "checks": [], + }, + "author", + [], + ) + + self.assertTrue(facts.copilot_review_exists) + self.assertFalse(facts.copilot_review_stale) + self.assertFalse(facts.copilot_review_needed) + def test_late_stale_review_does_not_replace_clean_current_head_review(self) -> None: facts = evaluation_facts( { diff --git a/.github/scripts/pull-request-dashboard/test_discussion_lifecycle.py b/.github/scripts/pull-request-dashboard/test_discussion_lifecycle.py index 72ef192c148..ae10077703d 100644 --- a/.github/scripts/pull-request-dashboard/test_discussion_lifecycle.py +++ b/.github/scripts/pull-request-dashboard/test_discussion_lifecycle.py @@ -298,6 +298,90 @@ def test_orders_discussions_by_creation_time_not_edit_time(self) -> None: ["2026-07-14T02:00:00Z", "2026-07-14T03:00:00Z"], ) + def test_edited_author_response_uses_its_content_timestamp(self) -> None: + prepared = prepare_discussions( + DiscussionInput( + (), + ( + { + "kind": "issue-comment", + "source_id": 201, + "created_timestamp": "2026-07-14T02:00:00Z", + "timestamp": "2026-07-14T02:00:00Z", + "actor": "reviewer", + "actor_role": "approver", + "body": "Please update the description.", + }, + { + "kind": "issue-comment", + "source_id": 301, + "created_timestamp": "2026-07-14T03:00:00Z", + "timestamp": "2026-07-14T06:00:00Z", + "actor": "author", + "actor_role": "author", + "body": "Both requests are complete.", + }, + { + "kind": "issue-comment", + "source_id": 202, + "created_timestamp": "2026-07-14T05:00:00Z", + "timestamp": "2026-07-14T05:00:00Z", + "actor": "reviewer", + "actor_role": "approver", + "body": "Please add another test.", + }, + ), + "author", + frozenset({"reviewer"}), + "no", + ) + ) + + reply = prepared.top_level_author_comment_items[0] + self.assertEqual("2026-07-14T06:00:00Z", reply["comments"][0]["timestamp"]) + self.assertEqual( + ["pr-issue-comment-201", "pr-issue-comment-202"], + [ + feedback["discussion_id"] + for feedback in reply["candidate_feedback"] + ], + ) + + def test_author_response_excludes_feedback_edited_after_it(self) -> None: + prepared = prepare_discussions( + DiscussionInput( + (), + ( + { + "kind": "issue-comment", + "source_id": 201, + "created_timestamp": "2026-07-14T02:00:00Z", + "timestamp": "2026-07-14T07:00:00Z", + "actor": "reviewer", + "actor_role": "approver", + "body": "Please also update the tests.", + }, + { + "kind": "issue-comment", + "source_id": 301, + "created_timestamp": "2026-07-14T03:00:00Z", + "timestamp": "2026-07-14T06:00:00Z", + "actor": "author", + "actor_role": "author", + "body": "The original request is complete.", + }, + ), + "author", + frozenset({"reviewer"}), + "no", + ) + ) + + self.assertEqual( + [], + prepared.top_level_author_comment_items[0]["candidate_feedback"], + ) + def test_ignores_author_only_review_threads(self) -> None: prepared = prepare_discussions( DiscussionInput( @@ -1085,6 +1169,51 @@ def test_handoff_feedback_keeps_same_second_feedback_out_of_scope(self) -> None: self.assertEqual(PreparedDiscussions((), (), ()), filtered) + def test_handoff_feedback_uses_effective_content_timestamps(self) -> None: + source = DiscussionInput( + normalize_review_threads(({ + "id": "edited-thread", + "isResolved": False, + "isOutdated": False, + "comments": { + "nodes": [{ + "url": "https://example.test/thread/edited", + "body": "Please also update the tests.", + "createdAt": "2026-07-14T02:00:00Z", + "lastEditedAt": "2026-07-14T06:00:00Z", + "author": {"login": "reviewer"}, + }], + }, + },)), + ({ + "kind": "issue-comment", + "source_id": 201, + "created_timestamp": "2026-07-14T03:00:00Z", + "timestamp": "2026-07-14T05:00:00Z", + "actor": "reviewer", + "actor_role": "approver", + "body": "The test is still failing.", + },), + "author", + frozenset({"reviewer"}), + "no", + ) + + filtered = reviewer_handoff_feedback( + prepare_discussions(source), + "2026-07-14T04:00:00Z", + "author", + ) + + self.assertEqual( + ["edited-thread"], + [item["discussion_id"] for item in filtered.review_threads], + ) + self.assertEqual( + ["pr-issue-comment-201"], + [item["discussion_id"] for item in filtered.top_level_items], + ) + if __name__ == "__main__": unittest.main() diff --git a/.github/scripts/pull-request-dashboard/test_github_cli.py b/.github/scripts/pull-request-dashboard/test_github_cli.py index 402c711b636..1d75a581ed6 100644 --- a/.github/scripts/pull-request-dashboard/test_github_cli.py +++ b/.github/scripts/pull-request-dashboard/test_github_cli.py @@ -9,6 +9,7 @@ fetch_pr_issue_comments, fetch_pr_reviews, fetch_review_requests, + fetch_review_threads, fetch_latest_draft_transitions, gh_branch_rules, gh_pr_check_rollup, @@ -257,6 +258,42 @@ def test_fetch_pr_issue_comments_paginates(self, graphql) -> None: self.assertIn("isMinimized", graphql.call_args_list[0].args[0]) self.assertEqual(graphql.call_args_list[1].args[1]["after"], "cursor-1") self.assertEqual(graphql.call_count, 2) + @patch("github_cli.gh_graphql") + def test_requests_and_preserves_comment_edit_timestamps(self, graphql) -> None: + graphql.return_value = { + "data": { + "repository": { + "pullRequest": { + "reviewThreads": { + "nodes": [{ + "id": "PRRT_1", + "comments": { + "nodes": [{ + "id": "PRRC_1", + "createdAt": "2026-08-20T08:00:00Z", + "lastEditedAt": "2026-08-20T08:30:00Z", + "pullRequestReview": { + "fullDatabaseId": "13", + }, + }], + "pageInfo": {"hasNextPage": False}, + }, + }], + "pageInfo": {"hasNextPage": False}, + } + } + } + } + } + + threads = fetch_review_threads("open-telemetry", "example", 7) + + self.assertEqual( + "2026-08-20T08:30:00Z", + threads[0]["comments"]["nodes"][0]["lastEditedAt"], + ) + self.assertIn("lastEditedAt", graphql.call_args.args[0]) + self.assertIn("pullRequestReview", graphql.call_args.args[0]) @patch("github_cli.gh_graphql") def test_fetch_pr_issue_comments_rejects_missing_page_cursor( @@ -1468,6 +1505,7 @@ def test_fetch_pr_reviews_normalizes_paginated_reviews(self, graphql) -> None: "body": "Please clarify this.", "submittedAt": "2026-07-15T03:55:00Z", "updatedAt": "2026-07-15T03:57:33Z", + "lastEditedAt": "2026-07-15T03:56:00Z", } ], "pageInfo": { @@ -1495,6 +1533,7 @@ def test_fetch_pr_reviews_normalizes_paginated_reviews(self, graphql) -> None: "body": "Looks good.", "submittedAt": "2026-07-15T04:00:00Z", "updatedAt": "2026-07-15T04:00:00Z", + "lastEditedAt": None, } ], "pageInfo": {"hasNextPage": False}, @@ -1518,6 +1557,7 @@ def test_fetch_pr_reviews_normalizes_paginated_reviews(self, graphql) -> None: "body": "Please clarify this.", "submitted_at": "2026-07-15T03:55:00Z", "updated_at": "2026-07-15T03:57:33Z", + "content_updated_at": "2026-07-15T03:56:00Z", }, { "id": 5000000000, @@ -1529,12 +1569,14 @@ def test_fetch_pr_reviews_normalizes_paginated_reviews(self, graphql) -> None: "body": "Looks good.", "submitted_at": "2026-07-15T04:00:00Z", "updated_at": "2026-07-15T04:00:00Z", + "content_updated_at": "2026-07-15T04:00:00Z", }, ], ) review_query = graphql.call_args_list[0].args[0] self.assertIn("comments {", review_query) self.assertIn("totalCount", review_query) + self.assertIn("lastEditedAt", review_query) self.assertEqual(graphql.call_args_list[1].args[1]["after"], "cursor-1") self.assertEqual(graphql.call_count, 2) diff --git a/.github/scripts/pull-request-dashboard/test_pull_request_source.py b/.github/scripts/pull-request-dashboard/test_pull_request_source.py index 5051d025357..982a7c84585 100644 --- a/.github/scripts/pull-request-dashboard/test_pull_request_source.py +++ b/.github/scripts/pull-request-dashboard/test_pull_request_source.py @@ -7,6 +7,7 @@ from github_cli import TransientGhError from pull_request_source import ( Actor, + IssueComment, PullRequestMetadata, fetch_pull_request_source, normalize_actor, @@ -16,6 +17,16 @@ class PullRequestSourceNormalizationTest(unittest.TestCase): + def test_issue_comment_uses_updated_at_only_as_a_missing_source_fallback( + self, + ) -> None: + self.assertEqual( + "2026-08-20T04:00:00Z", + IssueComment( + updated_at="2026-08-20T04:00:00Z" + ).effective_content_timestamp, + ) + def test_non_numeric_issue_comment_database_id_normalizes_to_zero(self) -> None: comments = normalize_issue_comments([ {"databaseId": "not-a-number", "body": "Comment"} @@ -92,6 +103,7 @@ def test_normalizes_mixed_gh_rest_and_graphql_shapes(self) -> None: "author": {"login": "reviewer"}, "state": "approved", "submittedAt": "2026-08-20T07:00:00Z", + "lastEditedAt": "2026-08-20T07:30:00Z", } ], "review_requests": [ @@ -113,9 +125,13 @@ def test_normalizes_mixed_gh_rest_and_graphql_shapes(self) -> None: "nodes": [ { "id": "PRRC_1", + "pullRequestReview": { + "fullDatabaseId": "13", + }, "url": "https://example.test/thread/1", "body": "Please update this.", "createdAt": "2026-08-20T08:00:00Z", + "lastEditedAt": "2026-08-20T08:30:00Z", "author": {"login": "reviewer"}, "reactionGroups": [ { @@ -169,8 +185,17 @@ def test_normalizes_mixed_gh_rest_and_graphql_shapes(self) -> None: self.assertEqual("status[bot]", source.issue_comments[0].actor.login) self.assertEqual("reviewer", source.review_comments[0].actor.login) self.assertEqual("head-sha", source.reviews[0].commit_id) + self.assertEqual( + "2026-08-20T07:30:00Z", + source.reviews[0].content_updated_at, + ) self.assertEqual(13, source.reviews[0].database_id) self.assertEqual("maintainers", source.review_requests[0].login) + self.assertEqual( + "2026-08-20T08:30:00Z", + source.review_threads[0].comments[0].updated_at, + ) + self.assertEqual(13, source.review_threads[0].comments[0].review_id) self.assertEqual( ("author",), source.review_threads[0] From 2fdbbff5f90bca7966c1a31a96867403aa2c589a Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Wed, 2 Sep 2026 12:57:49 -0700 Subject: [PATCH 09/30] Order same-second Copilot reviews Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 378be2b9-85df-4ac8-a125-a99b653412ac --- .../pull-request-dashboard/copilot_review.py | 22 +++-- .../test_copilot_review.py | 87 +++++++++++++++++++ 2 files changed, 97 insertions(+), 12 deletions(-) diff --git a/.github/scripts/pull-request-dashboard/copilot_review.py b/.github/scripts/pull-request-dashboard/copilot_review.py index 767d994cc94..1460806c6ae 100644 --- a/.github/scripts/pull-request-dashboard/copilot_review.py +++ b/.github/scripts/pull-request-dashboard/copilot_review.py @@ -122,25 +122,20 @@ def _copilot_finding_supersession( reviews: Sequence[Review], head_sha: str, ) -> tuple[frozenset[int], frozenset[int], datetime | None]: - clean_review_timestamps = [ - parse_ts(review.submitted_at) + clean_review_order_keys = [ + (submitted_at, review.database_id or 0) for review in reviews if ( is_copilot_reviewer(review) and review.commit_id == head_sha and review.finding_count == 0 + and (submitted_at := parse_ts(review.submitted_at)) is not None ) ] - latest_clean_review_at = max( - ( - timestamp - for timestamp in clean_review_timestamps - if timestamp is not None - ), - default=None, - ) - if latest_clean_review_at is None: + if not clean_review_order_keys: return frozenset(), frozenset(), None + latest_clean_review_key = max(clean_review_order_keys) + latest_clean_review_at = latest_clean_review_key[0] known_review_ids = frozenset( review.database_id for review in reviews @@ -159,7 +154,10 @@ def _copilot_finding_supersession( ) or ( (submitted_at := parse_ts(review.submitted_at)) is not None - and submitted_at < latest_clean_review_at + and ( + submitted_at, + review.database_id, + ) < latest_clean_review_key ) ) ) diff --git a/.github/scripts/pull-request-dashboard/test_copilot_review.py b/.github/scripts/pull-request-dashboard/test_copilot_review.py index cb7a2826e41..acd924637a1 100644 --- a/.github/scripts/pull-request-dashboard/test_copilot_review.py +++ b/.github/scripts/pull-request-dashboard/test_copilot_review.py @@ -131,6 +131,93 @@ def test_missing_clean_review_timestamp_does_not_supersede_findings( copilot_review_status(reviews, "current-head", threads), ) + def test_same_second_clean_review_supersedes_lower_review_id(self) -> None: + reviews = ( + review_source( + database_id=10, + actor=actor("copilot"), + commit_id="current-head", + finding_count=1, + submitted_at="2026-07-20T02:00:00Z", + ), + review_source( + database_id=20, + actor=actor("copilot"), + commit_id="current-head", + finding_count=0, + submitted_at="2026-07-20T02:00:00Z", + ), + ) + threads = (review_thread(comments=(review_thread_comment( + review_id=10, + actor=actor("copilot"), + created_at="2026-07-20T02:00:00Z", + updated_at="2026-07-20T02:00:00Z", + ),)),) + + self.assertEqual( + (True, False, False), + copilot_review_status(reviews, "current-head", threads), + ) + + def test_same_second_clean_review_preserves_higher_review_id(self) -> None: + reviews = ( + review_source( + database_id=20, + actor=actor("copilot"), + commit_id="current-head", + finding_count=1, + submitted_at="2026-07-20T02:00:00Z", + ), + review_source( + database_id=10, + actor=actor("copilot"), + commit_id="current-head", + finding_count=0, + submitted_at="2026-07-20T02:00:00Z", + ), + ) + threads = (review_thread(comments=(review_thread_comment( + review_id=20, + actor=actor("copilot"), + created_at="2026-07-20T02:00:00Z", + updated_at="2026-07-20T02:00:00Z", + ),)),) + + self.assertEqual( + (True, False, True), + copilot_review_status(reviews, "current-head", threads), + ) + + def test_same_second_clean_review_without_id_preserves_finding(self) -> None: + reviews = ( + review_source( + database_id=20, + actor=actor("copilot"), + commit_id="current-head", + finding_count=1, + submitted_at="2026-07-20T02:00:00Z", + ), + review_source( + database_id=0, + actor=actor("copilot"), + commit_id="current-head", + finding_count=0, + submitted_at="2026-07-20T02:00:00Z", + ), + ) + threads = (review_thread(comments=(review_thread_comment( + review_id=20, + actor=actor("copilot"), + created_at="2026-07-20T02:00:00Z", + updated_at="2026-07-20T02:00:00Z", + ),)),) + + self.assertEqual( + (True, False, True), + copilot_review_status(reviews, "current-head", threads), + ) + def test_late_review_of_an_old_head_does_not_replace_current_clean_review( self, ) -> None: From 75c78ee2a016e4162d1ab8b026f22cd7e48aff67 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Wed, 2 Sep 2026 13:15:15 -0700 Subject: [PATCH 10/30] Document dashboard feedback lifecycle Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 378be2b9-85df-4ac8-a125-a99b653412ac --- .../pull-request-dashboard/RATIONALE.md | 41 +++++++++++-------- .../pull-request-dashboard/dashboard.py | 9 ++-- 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/.github/scripts/pull-request-dashboard/RATIONALE.md b/.github/scripts/pull-request-dashboard/RATIONALE.md index 7fcfbfbe69b..30b05092e3a 100644 --- a/.github/scripts/pull-request-dashboard/RATIONALE.md +++ b/.github/scripts/pull-request-dashboard/RATIONALE.md @@ -400,15 +400,20 @@ the implementation understandable and operationally cheap. actionable one routes the PR to "waiting on author." In that common path the gate never fires and no re-review is requested. - Findings are counted from unresolved, non-outdated review threads Copilot - started, not from the comment count on its review. A review's comment count - never shrinks, so it keeps counting feedback the author has since addressed - and holds the PR on work that is already done. + started, not from the comment count on its review. A later clean review of + the current head supersedes findings from earlier Copilot reviews; review + database IDs order submissions when GitHub records them in the same second. + Missing bindings or timestamps keep findings active. A review's comment + count never shrinks, so using it would keep counting feedback the author has + since addressed and hold the PR on work that is already done. - The gate's re-request path covers two states. A stale review means the author pushed, which is the one change a re-review can respond to. Findings on the current head sit on unchanged code, so asking Copilot to look at it again - would reach the same verdict and be requested again on the next pass; those - threads clear when the author resolves them or pushes a fix, which is a - re-request in its own right. + would reach the same verdict and be requested again on the next pass. Those + threads stop counting when GitHub marks them resolved or outdated, or when a + later clean review of the current head supersedes them. A push makes the + review stale and triggers a re-request, but the push alone does not prove that + a finding was fixed. - The other state is a first review that never arrived. The gate otherwise relies entirely on automatic Copilot code review to produce it, so when GitHub silently never starts one, the pull request waits on its author forever for a @@ -476,17 +481,19 @@ the implementation understandable and operationally cheap. Copilot review, merge conflicts, discussion actions, and approval routing. The author may be stuck or may need a person to explain a basic problem, so no automated blocker can prevent the handoff. A later push restores normal - routing and gates. Actionable human reviewer feedback posted after the command - also ends the handoff, because a reviewer has answered the request for help and - assigned the next action to the author. Praise, informational comments, bot - feedback, and feedback posted before the command do not end it. -- While a handoff is active, the dashboard classifies only newer human reviewer - feedback. Old discussions and classification failures therefore cannot block - the break-glass route. Once newer feedback produces an author action, normal - discussion classification and routing resume. The dashboard records that - transition in its live status comment so an author reply or a lost state cache - cannot reactivate the same command. A newer command can establish a new - handoff on the same head. + routing and gates. Actionable human reviewer feedback with an effective + content timestamp after the command also ends the handoff, because a reviewer + has answered the request for help and assigned the next action to the author. + An edit to older feedback counts when its content-edit timestamp is after the + command. Praise, informational comments, bot feedback, and feedback last + changed at or before the command do not end it. +- While a handoff is active, the dashboard classifies only human reviewer + feedback with content activity after the command. Older discussions and + classification failures therefore cannot block the break-glass route. Once + newer feedback produces an author action, normal discussion classification + and routing resume. The dashboard records that transition in its live status + comment so an author reply or a lost state cache cannot reactivate the same + command. A newer command can establish a new handoff on the same head. - The dashboard binds a command to the head it sees when it first reads that command, and records that head in an acknowledgement marker on either the command reply or the live status comment. The handoff is then a comparison of diff --git a/.github/scripts/pull-request-dashboard/dashboard.py b/.github/scripts/pull-request-dashboard/dashboard.py index b42dd8d8f7f..5f8637f72c3 100644 --- a/.github/scripts/pull-request-dashboard/dashboard.py +++ b/.github/scripts/pull-request-dashboard/dashboard.py @@ -160,10 +160,11 @@ presumed lost and the dashboard requests one. copilot_review_needed bool The review is stale or Copilot - owns open review threads, - meaning unresolved threads - GitHub has not marked - outdated. + owns open review threads not + superseded by a clean review + of the current head, meaning + unresolved threads GitHub has + not marked outdated. created_at str (iso) last_activity_at str (iso) Latest substantive activity by a PR participant, never earlier From 8ef51a889a41de076e358351ae7740114aa432e5 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Wed, 2 Sep 2026 13:31:51 -0700 Subject: [PATCH 11/30] Preserve review lifecycle test inputs Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 378be2b9-85df-4ac8-a125-a99b653412ac --- .../pull-request-dashboard/RATIONALE.md | 7 +- .../dashboard_test_support.py | 9 +++ .../test_routing_snapshot.py | 74 +++++++++++++++++++ 3 files changed, 87 insertions(+), 3 deletions(-) diff --git a/.github/scripts/pull-request-dashboard/RATIONALE.md b/.github/scripts/pull-request-dashboard/RATIONALE.md index 30b05092e3a..4f47b1d5e89 100644 --- a/.github/scripts/pull-request-dashboard/RATIONALE.md +++ b/.github/scripts/pull-request-dashboard/RATIONALE.md @@ -403,9 +403,10 @@ the implementation understandable and operationally cheap. started, not from the comment count on its review. A later clean review of the current head supersedes findings from earlier Copilot reviews; review database IDs order submissions when GitHub records them in the same second. - Missing bindings or timestamps keep findings active. A review's comment - count never shrinks, so using it would keep counting feedback the author has - since addressed and hold the PR on work that is already done. + An unrecognized or missing binding falls back to the thread timestamp; a + missing timestamp keeps the finding active. A review's comment count never + shrinks, so using it would keep counting feedback the author has since + addressed and hold the PR on work that is already done. - The gate's re-request path covers two states. A stale review means the author pushed, which is the one change a re-review can respond to. Findings on the current head sit on unchanged code, so asking Copilot to look at it again diff --git a/.github/scripts/pull-request-dashboard/dashboard_test_support.py b/.github/scripts/pull-request-dashboard/dashboard_test_support.py index 9bf549e26c2..5017536f9af 100644 --- a/.github/scripts/pull-request-dashboard/dashboard_test_support.py +++ b/.github/scripts/pull-request-dashboard/dashboard_test_support.py @@ -247,6 +247,7 @@ def pull_request_source( "body": value.body, "submitted_at": value.submitted_at, "updated_at": value.updated_at, + "content_updated_at": value.content_updated_at, } for value in reviews ], @@ -275,6 +276,14 @@ def pull_request_source( "url": comment.url, "body": comment.body, "createdAt": comment.created_at, + "lastEditedAt": comment.updated_at, + "pullRequestReview": ( + { + "fullDatabaseId": comment.review_id, + } + if comment.review_id + else None + ), "author": _actor_json(comment.actor), "reactionGroups": [ { diff --git a/.github/scripts/pull-request-dashboard/test_routing_snapshot.py b/.github/scripts/pull-request-dashboard/test_routing_snapshot.py index f76e93a2186..4b552309383 100644 --- a/.github/scripts/pull-request-dashboard/test_routing_snapshot.py +++ b/.github/scripts/pull-request-dashboard/test_routing_snapshot.py @@ -4,6 +4,13 @@ import unittest from unittest.mock import patch +from dashboard_test_support import ( + actor, + pull_request_source, + review_source, + review_thread, + review_thread_comment, +) from pull_request_source import normalize_pull_request_source from routing_snapshot import build_routing_snapshot, fetch_routing_snapshot @@ -38,6 +45,73 @@ def representative_raw() -> dict: class RoutingSnapshotTest(unittest.TestCase): + def test_fixture_preserves_review_lifecycle_fingerprint_fields(self) -> None: + source = pull_request_source( + reviews=(review_source( + database_id=17, + actor=actor("copilot"), + content_updated_at="2026-08-16T08:00:00Z", + ),), + review_threads=(review_thread(comments=(review_thread_comment( + review_id=17, + actor=actor("copilot"), + updated_at="2026-08-16T09:00:00Z", + ),)),), + ) + + inputs = source.fingerprint.routing_inputs() + + self.assertEqual( + "2026-08-16T08:00:00Z", + inputs["reviews"][0]["content_updated_at"], + ) + thread_comment = inputs["review_threads"][0]["comments"]["nodes"][0] + self.assertEqual( + "2026-08-16T09:00:00Z", + thread_comment["lastEditedAt"], + ) + self.assertEqual( + {"fullDatabaseId": 17}, + thread_comment["pullRequestReview"], + ) + + def test_fixture_fingerprint_tracks_review_lifecycle_changes(self) -> None: + def snapshot( + *, + review_edited_at: str = "2026-08-16T08:00:00Z", + thread_edited_at: str = "2026-08-16T09:00:00Z", + review_id: int = 17, + ): + return build_routing_snapshot(pull_request_source( + reviews=(review_source( + database_id=17, + actor=actor("copilot"), + content_updated_at=review_edited_at, + ),), + review_threads=(review_thread( + comments=(review_thread_comment( + review_id=review_id, + actor=actor("copilot"), + updated_at=thread_edited_at, + ),), + ),), + )) + + baseline = snapshot().routing_input_fingerprint + changes = ( + snapshot( + review_edited_at="2026-08-16T10:00:00Z" + ).routing_input_fingerprint, + snapshot( + thread_edited_at="2026-08-16T10:00:00Z" + ).routing_input_fingerprint, + snapshot(review_id=18).routing_input_fingerprint, + ) + + for changed in changes: + with self.subTest(changed=changed): + self.assertNotEqual(baseline, changed) + def test_preserves_characterized_fingerprints_and_component_digests(self) -> None: snapshot = build_routing_snapshot( normalize_pull_request_source(representative_raw()) From 84579bbb9461b6d8caa88e0f4053dab01418b333 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Wed, 2 Sep 2026 13:40:51 -0700 Subject: [PATCH 12/30] Use thread edit time for Copilot findings Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 378be2b9-85df-4ac8-a125-a99b653412ac --- .../pull-request-dashboard/copilot_review.py | 11 +++-- .../test_copilot_review.py | 40 +++++++++++++++++++ 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/.github/scripts/pull-request-dashboard/copilot_review.py b/.github/scripts/pull-request-dashboard/copilot_review.py index 1460806c6ae..65a24e87b92 100644 --- a/.github/scripts/pull-request-dashboard/copilot_review.py +++ b/.github/scripts/pull-request-dashboard/copilot_review.py @@ -60,7 +60,7 @@ def open_copilot_findings( ) -> tuple[ReviewThread, ...]: # A review's own comment count never shrinks, so it still counts findings # that a later clean review has superseded. A review binding gives the exact - # ordering; creation time is the fallback for older cached source shapes. + # ordering; content activity time is the fallback for older cached shapes. # GitHub also marks a thread outdated once its anchor lines change. def is_superseded(thread: ReviewThread) -> bool: review_id = thread.comments[0].review_id @@ -70,8 +70,13 @@ def is_superseded(thread: ReviewThread) -> bool: return False if superseded_before is None: return False - created_at = parse_ts(thread.comments[0].created_at) - return created_at is not None and created_at < superseded_before + content_updated_at = parse_ts( + thread.comments[0].effective_content_timestamp + ) + return ( + content_updated_at is not None + and content_updated_at < superseded_before + ) return tuple( thread diff --git a/.github/scripts/pull-request-dashboard/test_copilot_review.py b/.github/scripts/pull-request-dashboard/test_copilot_review.py index acd924637a1..ba2f3100e96 100644 --- a/.github/scripts/pull-request-dashboard/test_copilot_review.py +++ b/.github/scripts/pull-request-dashboard/test_copilot_review.py @@ -269,6 +269,46 @@ def test_unknown_review_binding_uses_timestamp_fallback(self) -> None: copilot_review_status(reviews, "current-head", threads), ) + def test_unknown_binding_edited_after_clean_preserves_finding(self) -> None: + reviews = (review_source( + database_id=10, + actor=actor("copilot"), + commit_id="current-head", + finding_count=0, + submitted_at="2026-07-20T02:00:00Z", + ),) + threads = (review_thread(comments=(review_thread_comment( + review_id=999, + actor=actor("copilot"), + created_at="2026-07-20T01:00:00Z", + updated_at="2026-07-20T03:00:00Z", + ),)),) + + self.assertEqual( + (True, False, True), + copilot_review_status(reviews, "current-head", threads), + ) + + def test_missing_binding_edited_before_clean_is_superseded(self) -> None: + reviews = (review_source( + database_id=10, + actor=actor("copilot"), + commit_id="current-head", + finding_count=0, + submitted_at="2026-07-20T02:00:00Z", + ),) + threads = (review_thread(comments=(review_thread_comment( + review_id=0, + actor=actor("copilot"), + created_at="2026-07-20T01:00:00Z", + updated_at="2026-07-20T01:30:00Z", + ),)),) + + self.assertEqual( + (True, False, False), + copilot_review_status(reviews, "current-head", threads), + ) + class CopilotFirstReviewMissingSinceTest(unittest.TestCase): def test_starts_clock_when_review_is_missing(self) -> None: From 45f079fa1d287ab7a78c8033cb20649095dace12 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Wed, 2 Sep 2026 13:58:27 -0700 Subject: [PATCH 13/30] Keep Copilot supersession routing consistent Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../pull-request-dashboard/RATIONALE.md | 10 +- .../pull-request-dashboard/copilot_review.py | 96 ++++++++++--------- .../pull_request_evaluation.py | 8 +- .../test_copilot_review.py | 84 ++++++++++++++++ .../pull-request-dashboard/test_dashboard.py | 46 +++++++++ 5 files changed, 195 insertions(+), 49 deletions(-) diff --git a/.github/scripts/pull-request-dashboard/RATIONALE.md b/.github/scripts/pull-request-dashboard/RATIONALE.md index 4f47b1d5e89..2416d307f05 100644 --- a/.github/scripts/pull-request-dashboard/RATIONALE.md +++ b/.github/scripts/pull-request-dashboard/RATIONALE.md @@ -403,10 +403,12 @@ the implementation understandable and operationally cheap. started, not from the comment count on its review. A later clean review of the current head supersedes findings from earlier Copilot reviews; review database IDs order submissions when GitHub records them in the same second. - An unrecognized or missing binding falls back to the thread timestamp; a - missing timestamp keeps the finding active. A review's comment count never - shrinks, so using it would keep counting feedback the author has since - addressed and hold the PR on work that is already done. + An unrecognized or missing binding falls back to the latest content activity + across every thread comment; any missing timestamp keeps the finding active. + The same supersession filter runs before discussion classification, so status + and routing cannot disagree. A review's comment count never shrinks, so using + it would keep counting feedback the author has since addressed and hold the + PR on work that is already done. - The gate's re-request path covers two states. A stale review means the author pushed, which is the one change a re-review can respond to. Findings on the current head sit on unchanged code, so asking Copilot to look at it again diff --git a/.github/scripts/pull-request-dashboard/copilot_review.py b/.github/scripts/pull-request-dashboard/copilot_review.py index 65a24e87b92..12e3bcdb0c1 100644 --- a/.github/scripts/pull-request-dashboard/copilot_review.py +++ b/.github/scripts/pull-request-dashboard/copilot_review.py @@ -53,16 +53,50 @@ def is_copilot_reviewer( def open_copilot_findings( review_threads: Sequence[ReviewThread], - *, - known_review_ids: frozenset[int] = frozenset(), - superseded_review_ids: frozenset[int] = frozenset(), - superseded_before: datetime | None = None, + reviews: Sequence[Review] = (), + head_sha: str = "", +) -> tuple[ReviewThread, ...]: + return tuple( + thread + for thread in filter_superseded_copilot_findings( + review_threads, + reviews, + head_sha, + ) + if _is_open_copilot_finding(thread) + ) + + +def _is_open_copilot_finding(thread: ReviewThread) -> bool: + return ( + not thread.is_resolved + and not thread.is_outdated + and bool(thread.comments) + and is_copilot_reviewer(thread.comments[0].actor) + ) + + +def filter_superseded_copilot_findings( + review_threads: Sequence[ReviewThread], + reviews: Sequence[Review] = (), + head_sha: str = "", ) -> tuple[ReviewThread, ...]: # A review's own comment count never shrinks, so it still counts findings # that a later clean review has superseded. A review binding gives the exact # ordering; content activity time is the fallback for older cached shapes. # GitHub also marks a thread outdated once its anchor lines change. + ( + known_review_ids, + superseded_review_ids, + superseded_before, + ) = _copilot_finding_supersession( + reviews, + head_sha, + ) + def is_superseded(thread: ReviewThread) -> bool: + if not _is_open_copilot_finding(thread): + return False review_id = thread.comments[0].review_id if review_id in superseded_review_ids: return True @@ -70,31 +104,27 @@ def is_superseded(thread: ReviewThread) -> bool: return False if superseded_before is None: return False - content_updated_at = parse_ts( - thread.comments[0].effective_content_timestamp - ) - return ( - content_updated_at is not None - and content_updated_at < superseded_before - ) + content_timestamps: list[datetime] = [] + for comment in thread.comments: + content_timestamp = parse_ts(comment.effective_content_timestamp) + if content_timestamp is None: + return False + content_timestamps.append(content_timestamp) + return max(content_timestamps) < superseded_before return tuple( thread for thread in review_threads - if ( - not thread.is_resolved - and not thread.is_outdated - and thread.comments - and is_copilot_reviewer(thread.comments[0].actor) - and not is_superseded(thread) - ) + if not is_superseded(thread) ) def open_copilot_finding_count( review_threads: Sequence[ReviewThread], + reviews: Sequence[Review] = (), + head_sha: str = "", ) -> int: - return len(open_copilot_findings(review_threads)) + return len(open_copilot_findings(review_threads, reviews, head_sha)) def open_copilot_finding_urls( @@ -103,19 +133,10 @@ def open_copilot_finding_urls( head_sha: str = "", ) -> tuple[str, ...]: urls: list[str] = [] - ( - known_review_ids, - superseded_review_ids, - superseded_before, - ) = _copilot_finding_supersession( - reviews, - head_sha, - ) for thread in open_copilot_findings( review_threads, - known_review_ids=known_review_ids, - superseded_review_ids=superseded_review_ids, - superseded_before=superseded_before, + reviews, + head_sha, ): url = thread.comments[0].url if url and url not in urls: @@ -191,23 +212,10 @@ def copilot_review_status( review.commit_id == head_sha for review in copilot_reviews ) - ( - known_review_ids, - superseded_review_ids, - superseded_before, - ) = _copilot_finding_supersession( - copilot_reviews, - head_sha, - ) return ( True, stale, - bool(open_copilot_findings( - review_threads, - known_review_ids=known_review_ids, - superseded_review_ids=superseded_review_ids, - superseded_before=superseded_before, - )), + bool(open_copilot_findings(review_threads, copilot_reviews, head_sha)), ) diff --git a/.github/scripts/pull-request-dashboard/pull_request_evaluation.py b/.github/scripts/pull-request-dashboard/pull_request_evaluation.py index 9a736c0ae9d..c10070ddccd 100644 --- a/.github/scripts/pull-request-dashboard/pull_request_evaluation.py +++ b/.github/scripts/pull-request-dashboard/pull_request_evaluation.py @@ -23,6 +23,7 @@ ) from copilot_review import ( copilot_review_status, + filter_superseded_copilot_findings, is_copilot_reviewer, open_copilot_finding_urls, ) @@ -443,8 +444,13 @@ def evaluate_pull_request( previous_facts, ) manual_reviewer_handoff = reviewer_handoff_active(facts) - discussion_input = DiscussionInput( + discussion_review_threads = filter_superseded_copilot_findings( pr_source.review_threads, + pr_source.reviews, + facts.head_sha, + ) + discussion_input = DiscussionInput( + discussion_review_threads, activity.events, author, config.approver_logins, diff --git a/.github/scripts/pull-request-dashboard/test_copilot_review.py b/.github/scripts/pull-request-dashboard/test_copilot_review.py index ba2f3100e96..1e9b502c284 100644 --- a/.github/scripts/pull-request-dashboard/test_copilot_review.py +++ b/.github/scripts/pull-request-dashboard/test_copilot_review.py @@ -309,6 +309,90 @@ def test_missing_binding_edited_before_clean_is_superseded(self) -> None: copilot_review_status(reviews, "current-head", threads), ) + def test_unknown_binding_uses_latest_comment_before_clean(self) -> None: + reviews = (review_source( + database_id=10, + actor=actor("copilot"), + commit_id="current-head", + finding_count=0, + submitted_at="2026-07-20T02:00:00Z", + ),) + threads = (review_thread(comments=( + review_thread_comment( + review_id=999, + actor=actor("copilot"), + created_at="2026-07-20T01:00:00Z", + updated_at="2026-07-20T01:00:00Z", + ), + review_thread_comment( + node_id="reply", + actor=actor("author"), + created_at="2026-07-20T01:30:00Z", + updated_at="2026-07-20T01:30:00Z", + ), + )),) + + self.assertEqual( + (True, False, False), + copilot_review_status(reviews, "current-head", threads), + ) + + def test_unknown_binding_preserves_reply_after_clean(self) -> None: + reviews = (review_source( + database_id=10, + actor=actor("copilot"), + commit_id="current-head", + finding_count=0, + submitted_at="2026-07-20T02:00:00Z", + ),) + threads = (review_thread(comments=( + review_thread_comment( + review_id=999, + actor=actor("copilot"), + created_at="2026-07-20T01:00:00Z", + updated_at="2026-07-20T01:00:00Z", + ), + review_thread_comment( + node_id="reply", + actor=actor("author"), + created_at="2026-07-20T03:00:00Z", + updated_at="2026-07-20T03:00:00Z", + ), + )),) + + self.assertEqual( + (True, False, True), + copilot_review_status(reviews, "current-head", threads), + ) + + def test_missing_binding_preserves_thread_with_undated_reply(self) -> None: + reviews = (review_source( + database_id=10, + actor=actor("copilot"), + commit_id="current-head", + finding_count=0, + submitted_at="2026-07-20T02:00:00Z", + ),) + threads = (review_thread(comments=( + review_thread_comment( + review_id=0, + actor=actor("copilot"), + created_at="2026-07-20T01:00:00Z", + updated_at="2026-07-20T01:00:00Z", + ), + review_thread_comment( + node_id="reply", + actor=actor("author"), + created_at="", + updated_at="", + ), + )),) + + self.assertEqual( + (True, False, True), + copilot_review_status(reviews, "current-head", threads), + ) + class CopilotFirstReviewMissingSinceTest(unittest.TestCase): def test_starts_clock_when_review_is_missing(self) -> None: diff --git a/.github/scripts/pull-request-dashboard/test_dashboard.py b/.github/scripts/pull-request-dashboard/test_dashboard.py index c41bbf7916b..d40b4a2a7fd 100644 --- a/.github/scripts/pull-request-dashboard/test_dashboard.py +++ b/.github/scripts/pull-request-dashboard/test_dashboard.py @@ -501,6 +501,52 @@ def test_unresolved_copilot_thread_remains_an_author_action( ungated_result.facts.author_action_review_thread_urls, ) + @patch("pull_request_evaluation.fetch_pull_request_source") + def test_superseded_copilot_thread_does_not_route_to_author( + self, + fetch_raw: Mock, + ) -> None: + fetch_raw.return_value = pull_request_source( + pull_request=pull_request_metadata(head_sha="current-head"), + reviews=( + review_source( + database_id=10, + actor=actor("copilot"), + commit_id="old-head", + finding_count=1, + submitted_at="2026-08-16T07:00:00Z", + ), + review_source( + database_id=20, + actor=actor("copilot"), + commit_id="current-head", + finding_count=0, + submitted_at="2026-08-16T08:00:00Z", + ), + ), + review_threads=(review_thread(comments=(review_thread_comment( + review_id=10, + actor=actor("copilot"), + created_at="2026-08-16T07:00:00Z", + updated_at="2026-08-16T07:00:00Z", + ),)),), + ) + classifier = FakeClassificationOperation() + + result = evaluate_pr( + {"number": 7}, + require_clean_copilot_review_branches=["main"], + classification_service=classifier, + ) + + self.assertIsInstance(result, EvaluationSuccess) + assert isinstance(result, EvaluationSuccess) + self.assertEqual(DashboardRoute.APPROVER, result.route) + self.assertEqual({}, result.pending_actions) + self.assertEqual((), result.facts.author_action_review_thread_urls) + self.assertFalse(result.facts.copilot_review_needed) + self.assertEqual((), classifier.requests[0].review_threads) + @patch("pull_request_evaluation.fetch_pull_request_source") def test_override_binds_to_the_observed_head_before_classification( self, fetch_raw: Mock From 3d52cb1bcb4d2dcc05ea02a90ad191a41a3b56a3 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Wed, 2 Sep 2026 14:02:02 -0700 Subject: [PATCH 14/30] Keep open Copilot threads actionable Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 378be2b9-85df-4ac8-a125-a99b653412ac --- .../pull-request-dashboard/RATIONALE.md | 22 +- .../pull-request-dashboard/copilot_review.py | 132 +------- .../pull-request-dashboard/dashboard.py | 9 +- .../dashboard_test_support.py | 8 - .../pull-request-dashboard/github_cli.py | 6 - .../pull_request_evaluation.py | 14 +- .../pull_request_source.py | 7 - .../test_copilot_review.py | 314 +----------------- .../pull-request-dashboard/test_dashboard.py | 67 +--- .../pull-request-dashboard/test_github_cli.py | 4 - .../test_pull_request_source.py | 4 - .../test_routing_snapshot.py | 12 +- 12 files changed, 59 insertions(+), 540 deletions(-) diff --git a/.github/scripts/pull-request-dashboard/RATIONALE.md b/.github/scripts/pull-request-dashboard/RATIONALE.md index 2416d307f05..f4502e74dce 100644 --- a/.github/scripts/pull-request-dashboard/RATIONALE.md +++ b/.github/scripts/pull-request-dashboard/RATIONALE.md @@ -400,23 +400,17 @@ the implementation understandable and operationally cheap. actionable one routes the PR to "waiting on author." In that common path the gate never fires and no re-review is requested. - Findings are counted from unresolved, non-outdated review threads Copilot - started, not from the comment count on its review. A later clean review of - the current head supersedes findings from earlier Copilot reviews; review - database IDs order submissions when GitHub records them in the same second. - An unrecognized or missing binding falls back to the latest content activity - across every thread comment; any missing timestamp keeps the finding active. - The same supersession filter runs before discussion classification, so status - and routing cannot disagree. A review's comment count never shrinks, so using - it would keep counting feedback the author has since addressed and hold the - PR on work that is already done. + started, not from the comment count on its review. A review's comment count + never shrinks, so it keeps counting feedback the author has since addressed + and holds the PR on work that is already done. - The gate's re-request path covers two states. A stale review means the author pushed, which is the one change a re-review can respond to. Findings on the current head sit on unchanged code, so asking Copilot to look at it again - would reach the same verdict and be requested again on the next pass. Those - threads stop counting when GitHub marks them resolved or outdated, or when a - later clean review of the current head supersedes them. A push makes the - review stale and triggers a re-request, but the push alone does not prove that - a finding was fixed. + would reach the same verdict and be requested again on the next pass; those + threads stop counting only when the author resolves them or GitHub marks them + outdated after the referenced code moves. A later clean review does not close + an existing thread. A push makes the review stale and triggers a re-request, + but the push alone does not prove that a finding was fixed. - The other state is a first review that never arrived. The gate otherwise relies entirely on automatic Copilot code review to produce it, so when GitHub silently never starts one, the pull request waits on its author forever for a diff --git a/.github/scripts/pull-request-dashboard/copilot_review.py b/.github/scripts/pull-request-dashboard/copilot_review.py index 12e3bcdb0c1..57056b40f4e 100644 --- a/.github/scripts/pull-request-dashboard/copilot_review.py +++ b/.github/scripts/pull-request-dashboard/copilot_review.py @@ -53,144 +53,40 @@ def is_copilot_reviewer( def open_copilot_findings( review_threads: Sequence[ReviewThread], - reviews: Sequence[Review] = (), - head_sha: str = "", -) -> tuple[ReviewThread, ...]: - return tuple( - thread - for thread in filter_superseded_copilot_findings( - review_threads, - reviews, - head_sha, - ) - if _is_open_copilot_finding(thread) - ) - - -def _is_open_copilot_finding(thread: ReviewThread) -> bool: - return ( - not thread.is_resolved - and not thread.is_outdated - and bool(thread.comments) - and is_copilot_reviewer(thread.comments[0].actor) - ) - - -def filter_superseded_copilot_findings( - review_threads: Sequence[ReviewThread], - reviews: Sequence[Review] = (), - head_sha: str = "", ) -> tuple[ReviewThread, ...]: # A review's own comment count never shrinks, so it still counts findings - # that a later clean review has superseded. A review binding gives the exact - # ordering; content activity time is the fallback for older cached shapes. - # GitHub also marks a thread outdated once its anchor lines change. - ( - known_review_ids, - superseded_review_ids, - superseded_before, - ) = _copilot_finding_supersession( - reviews, - head_sha, - ) - - def is_superseded(thread: ReviewThread) -> bool: - if not _is_open_copilot_finding(thread): - return False - review_id = thread.comments[0].review_id - if review_id in superseded_review_ids: - return True - if review_id in known_review_ids: - return False - if superseded_before is None: - return False - content_timestamps: list[datetime] = [] - for comment in thread.comments: - content_timestamp = parse_ts(comment.effective_content_timestamp) - if content_timestamp is None: - return False - content_timestamps.append(content_timestamp) - return max(content_timestamps) < superseded_before - + # the author has since addressed. Unresolved threads are the live ones: + # GitHub marks a thread outdated once its anchor lines change, which is how + # the rest of the dashboard already recognises a pushed fix. return tuple( thread for thread in review_threads - if not is_superseded(thread) + if ( + not thread.is_resolved + and not thread.is_outdated + and thread.comments + and is_copilot_reviewer(thread.comments[0].actor) + ) ) def open_copilot_finding_count( review_threads: Sequence[ReviewThread], - reviews: Sequence[Review] = (), - head_sha: str = "", ) -> int: - return len(open_copilot_findings(review_threads, reviews, head_sha)) + return len(open_copilot_findings(review_threads)) def open_copilot_finding_urls( review_threads: Sequence[ReviewThread], - reviews: Sequence[Review] = (), - head_sha: str = "", ) -> tuple[str, ...]: urls: list[str] = [] - for thread in open_copilot_findings( - review_threads, - reviews, - head_sha, - ): + for thread in open_copilot_findings(review_threads): url = thread.comments[0].url if url and url not in urls: urls.append(url) return tuple(urls) -def _copilot_finding_supersession( - reviews: Sequence[Review], - head_sha: str, -) -> tuple[frozenset[int], frozenset[int], datetime | None]: - clean_review_order_keys = [ - (submitted_at, review.database_id or 0) - for review in reviews - if ( - is_copilot_reviewer(review) - and review.commit_id == head_sha - and review.finding_count == 0 - and (submitted_at := parse_ts(review.submitted_at)) is not None - ) - ] - if not clean_review_order_keys: - return frozenset(), frozenset(), None - latest_clean_review_key = max(clean_review_order_keys) - latest_clean_review_at = latest_clean_review_key[0] - known_review_ids = frozenset( - review.database_id - for review in reviews - if review.database_id and is_copilot_reviewer(review) - ) - superseded_review_ids = frozenset( - review.database_id - for review in reviews - if ( - review.database_id - and is_copilot_reviewer(review) - and ( - ( - bool(review.commit_id) - and review.commit_id != head_sha - ) - or ( - (submitted_at := parse_ts(review.submitted_at)) is not None - and ( - submitted_at, - review.database_id, - ) < latest_clean_review_key - ) - ) - ) - ) - return known_review_ids, superseded_review_ids, latest_clean_review_at - - def copilot_review_status( reviews: Sequence[Review], head_sha: str, @@ -212,11 +108,7 @@ def copilot_review_status( review.commit_id == head_sha for review in copilot_reviews ) - return ( - True, - stale, - bool(open_copilot_findings(review_threads, copilot_reviews, head_sha)), - ) + return True, stale, open_copilot_finding_count(review_threads) > 0 def copilot_review_outstanding(facts: DashboardFacts, *, enabled: bool) -> bool: diff --git a/.github/scripts/pull-request-dashboard/dashboard.py b/.github/scripts/pull-request-dashboard/dashboard.py index 5f8637f72c3..b42dd8d8f7f 100644 --- a/.github/scripts/pull-request-dashboard/dashboard.py +++ b/.github/scripts/pull-request-dashboard/dashboard.py @@ -160,11 +160,10 @@ presumed lost and the dashboard requests one. copilot_review_needed bool The review is stale or Copilot - owns open review threads not - superseded by a clean review - of the current head, meaning - unresolved threads GitHub has - not marked outdated. + owns open review threads, + meaning unresolved threads + GitHub has not marked + outdated. created_at str (iso) last_activity_at str (iso) Latest substantive activity by a PR participant, never earlier diff --git a/.github/scripts/pull-request-dashboard/dashboard_test_support.py b/.github/scripts/pull-request-dashboard/dashboard_test_support.py index 5017536f9af..390cfa9fac9 100644 --- a/.github/scripts/pull-request-dashboard/dashboard_test_support.py +++ b/.github/scripts/pull-request-dashboard/dashboard_test_support.py @@ -131,7 +131,6 @@ def reaction_group( def review_thread_comment(**changes: Any) -> ReviewThreadComment: values = { "node_id": "PRRC_1", - "review_id": 0, "url": "https://example.test/review-comment/1", "body": "Please update this.", "created_at": "2026-08-16T07:00:00Z", @@ -277,13 +276,6 @@ def pull_request_source( "body": comment.body, "createdAt": comment.created_at, "lastEditedAt": comment.updated_at, - "pullRequestReview": ( - { - "fullDatabaseId": comment.review_id, - } - if comment.review_id - else None - ), "author": _actor_json(comment.actor), "reactionGroups": [ { diff --git a/.github/scripts/pull-request-dashboard/github_cli.py b/.github/scripts/pull-request-dashboard/github_cli.py index 71143d682f1..3f50a74c205 100644 --- a/.github/scripts/pull-request-dashboard/github_cli.py +++ b/.github/scripts/pull-request-dashboard/github_cli.py @@ -814,9 +814,6 @@ def load_reviewer_set(org: str, approver_team_slugs: list[str]) -> set[str]: body createdAt lastEditedAt - pullRequestReview { - fullDatabaseId - } author { login } @@ -854,9 +851,6 @@ def load_reviewer_set(org: str, approver_team_slugs: list[str]) -> set[str]: body createdAt lastEditedAt - pullRequestReview { - fullDatabaseId - } author { login } diff --git a/.github/scripts/pull-request-dashboard/pull_request_evaluation.py b/.github/scripts/pull-request-dashboard/pull_request_evaluation.py index c10070ddccd..476ad1d3cde 100644 --- a/.github/scripts/pull-request-dashboard/pull_request_evaluation.py +++ b/.github/scripts/pull-request-dashboard/pull_request_evaluation.py @@ -23,7 +23,6 @@ ) from copilot_review import ( copilot_review_status, - filter_superseded_copilot_findings, is_copilot_reviewer, open_copilot_finding_urls, ) @@ -444,13 +443,8 @@ def evaluate_pull_request( previous_facts, ) manual_reviewer_handoff = reviewer_handoff_active(facts) - discussion_review_threads = filter_superseded_copilot_findings( - pr_source.review_threads, - pr_source.reviews, - facts.head_sha, - ) discussion_input = DiscussionInput( - discussion_review_threads, + pr_source.review_threads, activity.events, author, config.approver_logins, @@ -574,11 +568,7 @@ def evaluate_pull_request( review_threads, pending_actions, ( - open_copilot_finding_urls( - pr_source.review_threads, - pr_source.reviews, - facts.head_sha, - ) + open_copilot_finding_urls(pr_source.review_threads) if facts.copilot_review_outstanding else () ), diff --git a/.github/scripts/pull-request-dashboard/pull_request_source.py b/.github/scripts/pull-request-dashboard/pull_request_source.py index 5bec01aadf9..9b1adddf691 100644 --- a/.github/scripts/pull-request-dashboard/pull_request_source.py +++ b/.github/scripts/pull-request-dashboard/pull_request_source.py @@ -293,7 +293,6 @@ def __post_init__(self) -> None: @dataclass(frozen=True) class ReviewThreadComment: node_id: str = "" - review_id: int = 0 url: str = "" body: str = "" created_at: str = "" @@ -665,12 +664,6 @@ def normalize_review_threads(values: Any) -> tuple[ReviewThread, ...]: )) comments.append(ReviewThreadComment( node_id=_text(comment.get("id") or comment.get("node_id")), - review_id=_integer( - _mapping(comment.get("pullRequestReview")).get( - "fullDatabaseId" - ) - or comment.get("pull_request_review_id") - ), url=_text(comment.get("url") or comment.get("html_url")), body=str(comment.get("body") or ""), created_at=_text( diff --git a/.github/scripts/pull-request-dashboard/test_copilot_review.py b/.github/scripts/pull-request-dashboard/test_copilot_review.py index 1e9b502c284..df2000ff838 100644 --- a/.github/scripts/pull-request-dashboard/test_copilot_review.py +++ b/.github/scripts/pull-request-dashboard/test_copilot_review.py @@ -62,7 +62,7 @@ def review_result(route: str = "approver", **fact_changes): class CopilotFindingLifecycleTest(unittest.TestCase): - def test_clean_current_review_supersedes_only_older_findings(self) -> None: + def test_clean_current_review_keeps_open_findings(self) -> None: reviews = (review_source( actor=actor("copilot"), commit_id="current-head", @@ -71,30 +71,32 @@ def test_clean_current_review_supersedes_only_older_findings(self) -> None: ),) threads = ( review_thread( - node_id="old", + node_id="open", comments=(review_thread_comment( - url="https://example.test/old", + url="https://example.test/open", actor=actor("copilot"), created_at="2026-07-20T01:00:00Z", updated_at="2026-07-20T01:00:00Z", ),), ), review_thread( - node_id="new", + node_id="resolved", + is_resolved=True, comments=(review_thread_comment( - url="https://example.test/new", + url="https://example.test/resolved", actor=actor("copilot"), - created_at="2026-07-20T03:00:00Z", - updated_at="2026-07-20T03:00:00Z", + created_at="2026-07-20T01:00:00Z", + updated_at="2026-07-20T01:00:00Z", ),), ), review_thread( - node_id="unknown", + node_id="outdated", + is_outdated=True, comments=(review_thread_comment( - url="https://example.test/unknown", + url="https://example.test/outdated", actor=actor("copilot"), - created_at="", - updated_at="", + created_at="2026-07-20T01:00:00Z", + updated_at="2026-07-20T01:00:00Z", ),), ), ) @@ -104,296 +106,10 @@ def test_clean_current_review_supersedes_only_older_findings(self) -> None: copilot_review_status(reviews, "current-head", threads), ) self.assertEqual( - ( - "https://example.test/new", - "https://example.test/unknown", - ), - open_copilot_finding_urls(threads, reviews, "current-head"), - ) - - def test_missing_clean_review_timestamp_does_not_supersede_findings( - self, - ) -> None: - reviews = (review_source( - actor=actor("copilot"), - commit_id="current-head", - finding_count=0, - submitted_at="", - ),) - threads = (review_thread(comments=(review_thread_comment( - actor=actor("copilot"), - created_at="2026-07-20T01:00:00Z", - updated_at="2026-07-20T01:00:00Z", - ),)),) - - self.assertEqual( - (True, False, True), - copilot_review_status(reviews, "current-head", threads), - ) - - def test_same_second_clean_review_supersedes_lower_review_id(self) -> None: - reviews = ( - review_source( - database_id=10, - actor=actor("copilot"), - commit_id="current-head", - finding_count=1, - submitted_at="2026-07-20T02:00:00Z", - ), - review_source( - database_id=20, - actor=actor("copilot"), - commit_id="current-head", - finding_count=0, - submitted_at="2026-07-20T02:00:00Z", - ), - ) - threads = (review_thread(comments=(review_thread_comment( - review_id=10, - actor=actor("copilot"), - created_at="2026-07-20T02:00:00Z", - updated_at="2026-07-20T02:00:00Z", - ),)),) - - self.assertEqual( - (True, False, False), - copilot_review_status(reviews, "current-head", threads), - ) - - def test_same_second_clean_review_preserves_higher_review_id(self) -> None: - reviews = ( - review_source( - database_id=20, - actor=actor("copilot"), - commit_id="current-head", - finding_count=1, - submitted_at="2026-07-20T02:00:00Z", - ), - review_source( - database_id=10, - actor=actor("copilot"), - commit_id="current-head", - finding_count=0, - submitted_at="2026-07-20T02:00:00Z", - ), - ) - threads = (review_thread(comments=(review_thread_comment( - review_id=20, - actor=actor("copilot"), - created_at="2026-07-20T02:00:00Z", - updated_at="2026-07-20T02:00:00Z", - ),)),) - - self.assertEqual( - (True, False, True), - copilot_review_status(reviews, "current-head", threads), - ) - - def test_same_second_clean_review_without_id_preserves_finding(self) -> None: - reviews = ( - review_source( - database_id=20, - actor=actor("copilot"), - commit_id="current-head", - finding_count=1, - submitted_at="2026-07-20T02:00:00Z", - ), - review_source( - database_id=0, - actor=actor("copilot"), - commit_id="current-head", - finding_count=0, - submitted_at="2026-07-20T02:00:00Z", - ), - ) - threads = (review_thread(comments=(review_thread_comment( - review_id=20, - actor=actor("copilot"), - created_at="2026-07-20T02:00:00Z", - updated_at="2026-07-20T02:00:00Z", - ),)),) - - self.assertEqual( - (True, False, True), - copilot_review_status(reviews, "current-head", threads), - ) - - def test_late_review_of_an_old_head_does_not_replace_current_clean_review( - self, - ) -> None: - reviews = ( - review_source( - database_id=10, - actor=actor("copilot"), - commit_id="current-head", - finding_count=0, - submitted_at="2026-07-20T02:00:00Z", - ), - review_source( - database_id=20, - actor=actor("copilot"), - commit_id="old-head", - finding_count=1, - submitted_at="2026-07-20T03:00:00Z", - ), - ) - threads = (review_thread(comments=(review_thread_comment( - review_id=20, - actor=actor("copilot"), - created_at="2026-07-20T03:00:00Z", - updated_at="2026-07-20T03:00:00Z", - ),)),) - - self.assertEqual( - (True, False, False), - copilot_review_status(reviews, "current-head", threads), - ) - - def test_unknown_review_binding_uses_timestamp_fallback(self) -> None: - reviews = (review_source( - database_id=10, - actor=actor("copilot"), - commit_id="current-head", - finding_count=0, - submitted_at="2026-07-20T02:00:00Z", - ),) - threads = (review_thread(comments=(review_thread_comment( - review_id=999, - actor=actor("copilot"), - created_at="2026-07-20T01:00:00Z", - updated_at="2026-07-20T01:00:00Z", - ),)),) - - self.assertEqual( - (True, False, False), - copilot_review_status(reviews, "current-head", threads), - ) - - def test_unknown_binding_edited_after_clean_preserves_finding(self) -> None: - reviews = (review_source( - database_id=10, - actor=actor("copilot"), - commit_id="current-head", - finding_count=0, - submitted_at="2026-07-20T02:00:00Z", - ),) - threads = (review_thread(comments=(review_thread_comment( - review_id=999, - actor=actor("copilot"), - created_at="2026-07-20T01:00:00Z", - updated_at="2026-07-20T03:00:00Z", - ),)),) - - self.assertEqual( - (True, False, True), - copilot_review_status(reviews, "current-head", threads), - ) - - def test_missing_binding_edited_before_clean_is_superseded(self) -> None: - reviews = (review_source( - database_id=10, - actor=actor("copilot"), - commit_id="current-head", - finding_count=0, - submitted_at="2026-07-20T02:00:00Z", - ),) - threads = (review_thread(comments=(review_thread_comment( - review_id=0, - actor=actor("copilot"), - created_at="2026-07-20T01:00:00Z", - updated_at="2026-07-20T01:30:00Z", - ),)),) - - self.assertEqual( - (True, False, False), - copilot_review_status(reviews, "current-head", threads), - ) - - def test_unknown_binding_uses_latest_comment_before_clean(self) -> None: - reviews = (review_source( - database_id=10, - actor=actor("copilot"), - commit_id="current-head", - finding_count=0, - submitted_at="2026-07-20T02:00:00Z", - ),) - threads = (review_thread(comments=( - review_thread_comment( - review_id=999, - actor=actor("copilot"), - created_at="2026-07-20T01:00:00Z", - updated_at="2026-07-20T01:00:00Z", - ), - review_thread_comment( - node_id="reply", - actor=actor("author"), - created_at="2026-07-20T01:30:00Z", - updated_at="2026-07-20T01:30:00Z", - ), - )),) - - self.assertEqual( - (True, False, False), - copilot_review_status(reviews, "current-head", threads), - ) - - def test_unknown_binding_preserves_reply_after_clean(self) -> None: - reviews = (review_source( - database_id=10, - actor=actor("copilot"), - commit_id="current-head", - finding_count=0, - submitted_at="2026-07-20T02:00:00Z", - ),) - threads = (review_thread(comments=( - review_thread_comment( - review_id=999, - actor=actor("copilot"), - created_at="2026-07-20T01:00:00Z", - updated_at="2026-07-20T01:00:00Z", - ), - review_thread_comment( - node_id="reply", - actor=actor("author"), - created_at="2026-07-20T03:00:00Z", - updated_at="2026-07-20T03:00:00Z", - ), - )),) - - self.assertEqual( - (True, False, True), - copilot_review_status(reviews, "current-head", threads), - ) - - def test_missing_binding_preserves_thread_with_undated_reply(self) -> None: - reviews = (review_source( - database_id=10, - actor=actor("copilot"), - commit_id="current-head", - finding_count=0, - submitted_at="2026-07-20T02:00:00Z", - ),) - threads = (review_thread(comments=( - review_thread_comment( - review_id=0, - actor=actor("copilot"), - created_at="2026-07-20T01:00:00Z", - updated_at="2026-07-20T01:00:00Z", - ), - review_thread_comment( - node_id="reply", - actor=actor("author"), - created_at="", - updated_at="", - ), - )),) - - self.assertEqual( - (True, False, True), - copilot_review_status(reviews, "current-head", threads), + ("https://example.test/open",), + open_copilot_finding_urls(threads), ) - class CopilotFirstReviewMissingSinceTest(unittest.TestCase): def test_starts_clock_when_review_is_missing(self) -> None: facts = set_copilot_first_review_missing_since( diff --git a/.github/scripts/pull-request-dashboard/test_dashboard.py b/.github/scripts/pull-request-dashboard/test_dashboard.py index d40b4a2a7fd..96095a3fbcf 100644 --- a/.github/scripts/pull-request-dashboard/test_dashboard.py +++ b/.github/scripts/pull-request-dashboard/test_dashboard.py @@ -441,9 +441,20 @@ def test_unresolved_copilot_thread_remains_an_author_action( thread_url = "https://example.test/review-comment/copilot" fetch_raw.return_value = pull_request_source( pull_request=pull_request_metadata(), - reviews=(review_source( - actor=actor("copilot"), - ),), + reviews=( + review_source( + actor=actor("copilot"), + commit_id="old-head", + finding_count=1, + submitted_at="2026-08-16T07:00:00Z", + ), + review_source( + actor=actor("copilot"), + commit_id="abcdef123456", + finding_count=0, + submitted_at="2026-08-16T08:00:00Z", + ), + ), review_threads=(review_thread( comments=( review_thread_comment( @@ -501,52 +512,6 @@ def test_unresolved_copilot_thread_remains_an_author_action( ungated_result.facts.author_action_review_thread_urls, ) - @patch("pull_request_evaluation.fetch_pull_request_source") - def test_superseded_copilot_thread_does_not_route_to_author( - self, - fetch_raw: Mock, - ) -> None: - fetch_raw.return_value = pull_request_source( - pull_request=pull_request_metadata(head_sha="current-head"), - reviews=( - review_source( - database_id=10, - actor=actor("copilot"), - commit_id="old-head", - finding_count=1, - submitted_at="2026-08-16T07:00:00Z", - ), - review_source( - database_id=20, - actor=actor("copilot"), - commit_id="current-head", - finding_count=0, - submitted_at="2026-08-16T08:00:00Z", - ), - ), - review_threads=(review_thread(comments=(review_thread_comment( - review_id=10, - actor=actor("copilot"), - created_at="2026-08-16T07:00:00Z", - updated_at="2026-08-16T07:00:00Z", - ),)),), - ) - classifier = FakeClassificationOperation() - - result = evaluate_pr( - {"number": 7}, - require_clean_copilot_review_branches=["main"], - classification_service=classifier, - ) - - self.assertIsInstance(result, EvaluationSuccess) - assert isinstance(result, EvaluationSuccess) - self.assertEqual(DashboardRoute.APPROVER, result.route) - self.assertEqual({}, result.pending_actions) - self.assertEqual((), result.facts.author_action_review_thread_urls) - self.assertFalse(result.facts.copilot_review_needed) - self.assertEqual((), classifier.requests[0].review_threads) - @patch("pull_request_evaluation.fetch_pull_request_source") def test_override_binds_to_the_observed_head_before_classification( self, fetch_raw: Mock @@ -1360,7 +1325,7 @@ def test_current_head_matches_latest_clean_copilot_review(self) -> None: self.assertTrue(facts.copilot_review_exists) self.assertFalse(facts.copilot_review_needed) - def test_current_clean_review_supersedes_older_open_copilot_findings( + def test_current_clean_review_keeps_older_open_copilot_findings( self, ) -> None: facts = evaluation_facts( @@ -1412,7 +1377,7 @@ def test_current_clean_review_supersedes_older_open_copilot_findings( self.assertTrue(facts.copilot_review_exists) self.assertFalse(facts.copilot_review_stale) - self.assertFalse(facts.copilot_review_needed) + self.assertTrue(facts.copilot_review_needed) def test_late_stale_review_does_not_replace_clean_current_head_review(self) -> None: facts = evaluation_facts( diff --git a/.github/scripts/pull-request-dashboard/test_github_cli.py b/.github/scripts/pull-request-dashboard/test_github_cli.py index 1d75a581ed6..420f461fb31 100644 --- a/.github/scripts/pull-request-dashboard/test_github_cli.py +++ b/.github/scripts/pull-request-dashboard/test_github_cli.py @@ -272,9 +272,6 @@ def test_requests_and_preserves_comment_edit_timestamps(self, graphql) -> None: "id": "PRRC_1", "createdAt": "2026-08-20T08:00:00Z", "lastEditedAt": "2026-08-20T08:30:00Z", - "pullRequestReview": { - "fullDatabaseId": "13", - }, }], "pageInfo": {"hasNextPage": False}, }, @@ -293,7 +290,6 @@ def test_requests_and_preserves_comment_edit_timestamps(self, graphql) -> None: threads[0]["comments"]["nodes"][0]["lastEditedAt"], ) self.assertIn("lastEditedAt", graphql.call_args.args[0]) - self.assertIn("pullRequestReview", graphql.call_args.args[0]) @patch("github_cli.gh_graphql") def test_fetch_pr_issue_comments_rejects_missing_page_cursor( diff --git a/.github/scripts/pull-request-dashboard/test_pull_request_source.py b/.github/scripts/pull-request-dashboard/test_pull_request_source.py index 982a7c84585..9346e2ee6a6 100644 --- a/.github/scripts/pull-request-dashboard/test_pull_request_source.py +++ b/.github/scripts/pull-request-dashboard/test_pull_request_source.py @@ -125,9 +125,6 @@ def test_normalizes_mixed_gh_rest_and_graphql_shapes(self) -> None: "nodes": [ { "id": "PRRC_1", - "pullRequestReview": { - "fullDatabaseId": "13", - }, "url": "https://example.test/thread/1", "body": "Please update this.", "createdAt": "2026-08-20T08:00:00Z", @@ -195,7 +192,6 @@ def test_normalizes_mixed_gh_rest_and_graphql_shapes(self) -> None: "2026-08-20T08:30:00Z", source.review_threads[0].comments[0].updated_at, ) - self.assertEqual(13, source.review_threads[0].comments[0].review_id) self.assertEqual( ("author",), source.review_threads[0] diff --git a/.github/scripts/pull-request-dashboard/test_routing_snapshot.py b/.github/scripts/pull-request-dashboard/test_routing_snapshot.py index 4b552309383..312e87dac53 100644 --- a/.github/scripts/pull-request-dashboard/test_routing_snapshot.py +++ b/.github/scripts/pull-request-dashboard/test_routing_snapshot.py @@ -45,7 +45,7 @@ def representative_raw() -> dict: class RoutingSnapshotTest(unittest.TestCase): - def test_fixture_preserves_review_lifecycle_fingerprint_fields(self) -> None: + def test_fixture_preserves_review_activity_fingerprint_fields(self) -> None: source = pull_request_source( reviews=(review_source( database_id=17, @@ -53,7 +53,6 @@ def test_fixture_preserves_review_lifecycle_fingerprint_fields(self) -> None: content_updated_at="2026-08-16T08:00:00Z", ),), review_threads=(review_thread(comments=(review_thread_comment( - review_id=17, actor=actor("copilot"), updated_at="2026-08-16T09:00:00Z", ),)),), @@ -70,17 +69,12 @@ def test_fixture_preserves_review_lifecycle_fingerprint_fields(self) -> None: "2026-08-16T09:00:00Z", thread_comment["lastEditedAt"], ) - self.assertEqual( - {"fullDatabaseId": 17}, - thread_comment["pullRequestReview"], - ) - def test_fixture_fingerprint_tracks_review_lifecycle_changes(self) -> None: + def test_fixture_fingerprint_tracks_review_activity_changes(self) -> None: def snapshot( *, review_edited_at: str = "2026-08-16T08:00:00Z", thread_edited_at: str = "2026-08-16T09:00:00Z", - review_id: int = 17, ): return build_routing_snapshot(pull_request_source( reviews=(review_source( @@ -90,7 +84,6 @@ def snapshot( ),), review_threads=(review_thread( comments=(review_thread_comment( - review_id=review_id, actor=actor("copilot"), updated_at=thread_edited_at, ),), @@ -105,7 +98,6 @@ def snapshot( snapshot( thread_edited_at="2026-08-16T10:00:00Z" ).routing_input_fingerprint, - snapshot(review_id=18).routing_input_fingerprint, ) for changed in changes: From 968488cf06bd43964e3ed5433f7b0c096e348c08 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Wed, 2 Sep 2026 14:19:47 -0700 Subject: [PATCH 15/30] Keep feedback timestamps role-specific Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 378be2b9-85df-4ac8-a125-a99b653412ac --- .../pull-request-dashboard/RATIONALE.md | 13 +++--- .../discussion_lifecycle.py | 25 ++++++++--- .../pull_request_activity.py | 5 ++- .../pull-request-dashboard/test_dashboard.py | 37 +++++++++++++++ .../test_discussion_lifecycle.py | 45 +++++++++++++++++++ .../test_pull_request_activity.py | 37 +++++++++++++++ 6 files changed, 149 insertions(+), 13 deletions(-) diff --git a/.github/scripts/pull-request-dashboard/RATIONALE.md b/.github/scripts/pull-request-dashboard/RATIONALE.md index f4502e74dce..0df1b060c68 100644 --- a/.github/scripts/pull-request-dashboard/RATIONALE.md +++ b/.github/scripts/pull-request-dashboard/RATIONALE.md @@ -624,8 +624,10 @@ the implementation understandable and operationally cheap. the classifier is told which kind of item it is reading, so the summary rule below applies only to them. Review state stays independent of that: a `CHANGES_REQUESTED` state affects only the reviewer's badge; it does not - affect dashboard actions or routing. Empty review summaries are ignored; - their inline comments, if any, define independent actions. + affect dashboard actions or routing. Formal review states are ordered by + submission time, while summary edits use their content timestamp for + lifecycle decisions. Empty review summaries are ignored; their inline + comments, if any, define independent actions. - Who a comment opens by addressing is extracted in code and passed to the classifier as `addressed_to`, rather than left for the model to find in the body. A reviewer routinely names other people, pull requests, and prior work @@ -650,10 +652,9 @@ the implementation understandable and operationally cheap. contains requests, so a reviewer whose only actionable point is in the summary should post it as its own comment. - The author reply that closed an item is retained in the cached PR result. It - is reused only when it is newer than the item's creation time, which an edit - never moves. Accepted tradeoff: a substantively rewritten request keeps the - reply that answered its earlier text, so a reviewer who needs the new text - answered should post it as a new comment. Ordinary requester-confirmation + is reused only when it is newer than the item's effective content timestamp. + Editing an older request with new work therefore reopens the item when the + edit is newer than the completed author reply. Ordinary requester-confirmation timestamps are not persisted. - Reviewers should prefer inline comments when feedback needs explicit closure. Blocking PR-wide feedback should use GitHub's **Request changes** review state; diff --git a/.github/scripts/pull-request-dashboard/discussion_lifecycle.py b/.github/scripts/pull-request-dashboard/discussion_lifecycle.py index bfe35af0c33..cf12086b03d 100644 --- a/.github/scripts/pull-request-dashboard/discussion_lifecycle.py +++ b/.github/scripts/pull-request-dashboard/discussion_lifecycle.py @@ -224,7 +224,11 @@ def _derive_top_level_items(source: DiscussionInput) -> list[dict[str, Any]]: "body": body, "positive_reactors": [], } - activity_timestamp = event.get("timestamp") or root_timestamp + activity_timestamp = ( + event.get("content_timestamp") + or event.get("timestamp") + or root_timestamp + ) if activity_timestamp != root_timestamp: comment["activity_timestamp"] = activity_timestamp if ( @@ -611,13 +615,13 @@ def _collect_author_evidence( author_comment_outcomes: list[AuthorCommentOutcome], author_comment_source_state: AuthorCommentSourceState | None, ) -> tuple[dict[str, str], int | None]: - root_timestamp = discussion.get("root_timestamp") or "" + feedback_timestamp = _top_level_feedback_timestamp(discussion) evidence: dict[str, str] = {} reply_source_id: int | None = None previous_reply = (previous_entry.get("evidence") or {}).get("reply") or "" previous_reply_source_id = previous_entry.get("reply_source_id") if ( - previous_reply > root_timestamp + previous_reply > feedback_timestamp and _should_restore_author_reply( author_comment_outcomes, author_comment_source_state, @@ -633,7 +637,7 @@ def _collect_author_evidence( completed_reply = _completed_author_reply_after( discussion["discussion_id"], - root_timestamp, + feedback_timestamp, author_comment_outcomes, ) if completed_reply: @@ -648,6 +652,16 @@ def _collect_author_evidence( return evidence, reply_source_id +def _top_level_feedback_timestamp(discussion: dict[str, Any]) -> str: + comments = discussion.get("comments") or () + root_comment = comments[0] if comments else {} + return ( + root_comment.get("activity_timestamp") + or discussion.get("root_timestamp") + or "" + ) + + def _pending_action_for(action: str) -> str: return "author" if action == "unclear" else action @@ -710,6 +724,7 @@ def _advance_top_level_actions( ) action = decision.action root_timestamp = discussion.get("root_timestamp") or "" + feedback_timestamp = _top_level_feedback_timestamp(discussion) if action not in ( DiscussionAction.AUTHOR, DiscussionAction.UNCLEAR, @@ -732,7 +747,7 @@ def _advance_top_level_actions( continue handoff = _latest_author_comment_handoff( discussion["discussion_id"], - root_timestamp, + feedback_timestamp, author_comment_outcomes, ) if handoff is not None: diff --git a/.github/scripts/pull-request-dashboard/pull_request_activity.py b/.github/scripts/pull-request-dashboard/pull_request_activity.py index 50ed89734d9..df866b935c9 100644 --- a/.github/scripts/pull-request-dashboard/pull_request_activity.py +++ b/.github/scripts/pull-request-dashboard/pull_request_activity.py @@ -169,7 +169,8 @@ def _review_event( "source_id": review.database_id or None, "discussion_url": review.url, "kind": "review-state", - "timestamp": review.effective_content_timestamp, + "timestamp": review.submitted_at, + "content_timestamp": review.effective_content_timestamp, "created_timestamp": review.submitted_at, "actor": login, "actor_role": role_for(login, author, approver_logins), @@ -220,7 +221,7 @@ def _latest_substantive_activity( actor_roles: set[str], ) -> datetime | None: timestamps = [ - parse_ts(event["timestamp"]) + parse_ts(event.get("content_timestamp") or event["timestamp"]) for event in events if event.get("actor_role") in actor_roles and is_substantive_activity(event) diff --git a/.github/scripts/pull-request-dashboard/test_dashboard.py b/.github/scripts/pull-request-dashboard/test_dashboard.py index 96095a3fbcf..f727ca13fec 100644 --- a/.github/scripts/pull-request-dashboard/test_dashboard.py +++ b/.github/scripts/pull-request-dashboard/test_dashboard.py @@ -399,6 +399,43 @@ def test_compute_facts_uses_prepared_approval_count(self) -> None: self.assertEqual(1, facts.approval_count) + @patch("pull_request_evaluation.fetch_pull_request_source") + def test_edited_old_changes_request_does_not_displace_newer_approval( + self, + fetch_raw: Mock, + ) -> None: + fetch_raw.return_value = pull_request_source( + pull_request=pull_request_metadata(), + reviews=( + review_source( + database_id=1, + state="CHANGES_REQUESTED", + body="", + submitted_at="2026-08-16T07:00:00Z", + content_updated_at="2026-08-16T09:00:00Z", + ), + review_source( + database_id=2, + state="APPROVED", + body="", + submitted_at="2026-08-16T08:00:00Z", + ), + ), + ) + + result = evaluate_pr({"number": 7}) + + self.assertIsInstance(result, EvaluationSuccess) + assert isinstance(result, EvaluationSuccess) + self.assertEqual(1, result.facts.approval_count) + reviewer = next( + reviewer + for reviewer in result.facts.reviewers + if reviewer.login == "reviewer" + ) + self.assertTrue(reviewer.approved) + self.assertFalse(reviewer.changes_requested) + @patch("pull_request_evaluation.resolve_routing", wraps=resolve_routing) @patch("pull_request_evaluation.fetch_pull_request_source") def test_evaluation_routes_pending_reviewers_and_projects_reviewer_rows( diff --git a/.github/scripts/pull-request-dashboard/test_discussion_lifecycle.py b/.github/scripts/pull-request-dashboard/test_discussion_lifecycle.py index ae10077703d..1ed5704a38b 100644 --- a/.github/scripts/pull-request-dashboard/test_discussion_lifecycle.py +++ b/.github/scripts/pull-request-dashboard/test_discussion_lifecycle.py @@ -913,6 +913,51 @@ def test_restores_legacy_history_without_source_id(self) -> None: self.assertEqual(outcome.pending_actions, {}) self.assertEqual(outcome.top_level_history, legacy_history) + def test_edited_feedback_invalidates_legacy_history_without_source_id( + self, + ) -> None: + prepared = prepare_discussions( + DiscussionInput( + (), + ({ + "kind": "issue-comment", + "source_id": 201, + "created_timestamp": "2026-07-14T07:00:00Z", + "timestamp": "2026-07-14T09:00:00Z", + "actor": "reviewer", + "actor_role": "approver", + "body": "Please update this.", + },), + "author", + frozenset({"reviewer"}), + "no", + ) + ) + outcome = resolve_discussions( + prepared, + DiscussionClassifications( + (), + (classification("pr-issue-comment-201", "author"),), + (), + ), + { + "pr-issue-comment-201": { + "evidence": {"reply": "2026-07-14T07:30:00Z"}, + } + }, + ) + + self.assertEqual( + outcome.pending_actions, + { + "pr-issue-comment-201": { + "action": "author", + "since": "2026-07-14T07:00:00Z", + } + }, + ) + self.assertEqual(outcome.top_level_history, {}) + def test_recovers_source_id_for_legacy_history(self) -> None: outcome = resolve_discussions( PreparedDiscussions( diff --git a/.github/scripts/pull-request-dashboard/test_pull_request_activity.py b/.github/scripts/pull-request-dashboard/test_pull_request_activity.py index f5f4a41cc66..f479fe1c1ef 100644 --- a/.github/scripts/pull-request-dashboard/test_pull_request_activity.py +++ b/.github/scripts/pull-request-dashboard/test_pull_request_activity.py @@ -236,6 +236,24 @@ def test_edited_comments_use_activity_time_but_creation_order(self) -> None: ], ) + def test_edited_review_keeps_submission_time_for_state_ordering(self) -> None: + event = activity( + reviews=[ + { + "database_id": 1, + "submitted_at": "2026-07-14T01:00:00Z", + "updated_at": "2026-07-14T03:00:00Z", + "content_updated_at": "2026-07-14T03:00:00Z", + "user": {"login": "reviewer"}, + "state": "CHANGES_REQUESTED", + "body": "Please update this.", + } + ], + ).events[0] + + self.assertEqual("2026-07-14T01:00:00Z", event["timestamp"]) + self.assertEqual("2026-07-14T03:00:00Z", event["content_timestamp"]) + def test_copilot_identity_shapes_are_normalized(self) -> None: timeline = activity( issue_comments=[ @@ -329,6 +347,25 @@ def test_review_states_and_body_text_determine_substantive_activity(self) -> Non timeline.latest_approver_activity_at, ) + def test_review_edit_updates_content_activity_clock(self) -> None: + timeline = activity( + reviews=[ + { + "id": 1, + "submitted_at": "2026-07-14T01:00:00Z", + "content_updated_at": "2026-07-14T03:00:00Z", + "user": {"login": "reviewer"}, + "state": "CHANGES_REQUESTED", + "body": "Please also update the tests.", + } + ], + ) + + self.assertEqual( + datetime(2026, 7, 14, 3, tzinfo=timezone.utc), + timeline.latest_approver_activity_at, + ) + def test_all_activity_clocks_are_independent(self) -> None: timeline = activity( issue_comments=[ From 7d80504a791f7cf879fd53b2e672035da5ac08f6 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Wed, 2 Sep 2026 14:39:53 -0700 Subject: [PATCH 16/30] Retire pre-handoff top-level feedback Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 378be2b9-85df-4ac8-a125-a99b653412ac --- .../pull-request-dashboard/RATIONALE.md | 47 ++++--- .../pull-request-dashboard/dashboard.py | 3 + .../dashboard_contracts.py | 1 + .../dashboard_override.py | 33 ++++- .../discussion_lifecycle.py | 50 ++++++-- .../pr_status_comment.py | 2 +- .../pull_request_evaluation.py | 6 +- .../scripts/pull-request-dashboard/state.py | 8 ++ .../pull-request-dashboard/test_dashboard.py | 121 ++++++++++++++++++ .../test_dashboard_override.py | 71 +++++++++- .../test_discussion_lifecycle.py | 90 +++++++++++++ .../pull-request-dashboard/test_state.py | 1 + 12 files changed, 396 insertions(+), 37 deletions(-) diff --git a/.github/scripts/pull-request-dashboard/RATIONALE.md b/.github/scripts/pull-request-dashboard/RATIONALE.md index 0df1b060c68..7d4d429715f 100644 --- a/.github/scripts/pull-request-dashboard/RATIONALE.md +++ b/.github/scripts/pull-request-dashboard/RATIONALE.md @@ -478,23 +478,30 @@ the implementation understandable and operationally cheap. Copilot review, merge conflicts, discussion actions, and approval routing. The author may be stuck or may need a person to explain a basic problem, so no automated blocker can prevent the handoff. A later push restores normal - routing and gates. Actionable human reviewer feedback with an effective - content timestamp after the command also ends the handoff, because a reviewer - has answered the request for help and assigned the next action to the author. - An edit to older feedback counts when its content-edit timestamp is after the - command. Praise, informational comments, bot feedback, and feedback last - changed at or before the command do not end it. + routing and gates, but top-level feedback last changed at or before the + command stays retired. Review threads are not retired by the command; after a + push, unresolved and non-outdated threads return to normal routing. Actionable + human reviewer feedback with an effective content timestamp after the command + also ends the handoff, because a reviewer has answered the request for help + and assigned the next action to the author. An edit to older feedback counts + when its content-edit timestamp is after the command. Praise, informational + comments, bot feedback, and feedback last changed at or before the command do + not end it. - While a handoff is active, the dashboard classifies only human reviewer feedback with content activity after the command. Older discussions and classification failures therefore cannot block the break-glass route. Once newer feedback produces an author action, normal discussion classification - and routing resume. The dashboard records that transition in its live status - comment so an author reply or a lost state cache cannot reactivate the same - command. A newer command can establish a new handoff on the same head. + and routing resume, subject to the permanent top-level feedback cutoff. + The dashboard records that transition in its live status comment so an author + reply or a lost state cache cannot reactivate the same command. A newer + command can establish a new handoff on the same head and advance the cutoff. - The dashboard binds a command to the head it sees when it first reads that command, and records that head in an acknowledgement marker on either the - command reply or the live status comment. The handoff is then a comparison of - two strings: the recorded head and the current one. The earlier design + command reply or the live status comment. The marker also records the + effective command timestamp used for the permanent top-level feedback cutoff. + A legacy marker without that timestamp does not retire feedback when the + original command is unavailable. The handoff is then a comparison of two + strings: the recorded head and the current one. The earlier design instead ordered the command against the push by comparing the comment timestamp with the head push time from `GET /repos/{repo}/activity`. Do not reintroduce that. Both timestamps have one-second resolution and come @@ -573,14 +580,16 @@ the implementation understandable and operationally cheap. deleted source comments authoritative without additional reconciliation. Cached classifications avoid repeated LLM calls, while dashboard state retains the author reply already observed for each item. -- An explicit author reply is the only thing that closes a top-level item. - Commits, PR title edits, and PR description edits are not tied to the item - they would close, so any push after the feedback arrived would close every - open item at once and hide feedback nobody had answered. The status comment - lists the exact open discussions and explains how to give each one an outcome, - which makes an explicit reply both cheap and unambiguous. An author's explicit - commitment to future work in the current PR is a self-deferral, not a - completed reply, so the item continues waiting on the author. +- In normal routing, an explicit author reply is the only thing that closes a + top-level item. Commits, PR title edits, and PR description edits are not tied + to the item they would close, so a push alone never closes feedback. The + break-glass reviewer command is the deliberate bulk exception: it retires + top-level items at or before its cutoff while leaving review threads open. + The status comment lists the exact open discussions and explains how to give + each one an outcome, which makes an explicit reply both cheap and unambiguous. + An author's explicit commitment to future work in the current PR is a + self-deferral, not a completed reply, so the item continues waiting on the + author. - Each model call classifies up to ten uncached top-level feedback items independently, while retaining a separate cache entry for every item. A refresh processes at most 200 such items per PR. Exceeding that cap means the diff --git a/.github/scripts/pull-request-dashboard/dashboard.py b/.github/scripts/pull-request-dashboard/dashboard.py index b42dd8d8f7f..7ee2e672843 100644 --- a/.github/scripts/pull-request-dashboard/dashboard.py +++ b/.github/scripts/pull-request-dashboard/dashboard.py @@ -128,6 +128,9 @@ been cleared by newer feedback. dashboard_override_since str (iso) Effective content timestamp of the command bound to the handoff. + dashboard_top_level_feedback_cutoff + str (iso) Top-level feedback at or before + this command timestamp is retired. dashboard_override_cleared_by_feedback bool Actionable human reviewer feedback ended the handoff. diff --git a/.github/scripts/pull-request-dashboard/dashboard_contracts.py b/.github/scripts/pull-request-dashboard/dashboard_contracts.py index 566d7de033f..03a5b79cc3b 100644 --- a/.github/scripts/pull-request-dashboard/dashboard_contracts.py +++ b/.github/scripts/pull-request-dashboard/dashboard_contracts.py @@ -85,6 +85,7 @@ class DashboardFacts: dashboard_override_bound_command_id: int = 0 dashboard_override_head_sha: str = "" dashboard_override_since: str = "" + dashboard_top_level_feedback_cutoff: str = "" dashboard_override_cleared_by_feedback: bool = False dashboard_command_replies: tuple[DashboardCommandReply, ...] = () copilot_review_requested: bool = False diff --git a/.github/scripts/pull-request-dashboard/dashboard_override.py b/.github/scripts/pull-request-dashboard/dashboard_override.py index 01d5a52617c..ca5df2b148e 100644 --- a/.github/scripts/pull-request-dashboard/dashboard_override.py +++ b/.github/scripts/pull-request-dashboard/dashboard_override.py @@ -47,6 +47,7 @@ class DashboardOverrideFacts: bound_command_id: int head_sha: str since: str + top_level_feedback_cutoff: str cleared_by_feedback: bool command_replies: tuple[DashboardCommandReply, ...] @@ -64,8 +65,9 @@ def author_override_guidance(staleness_note: str = "") -> str: "If you need reviewer or maintainer help, comment " "`/dashboard route:reviewers` to request routing from waiting on the " "author to waiting on reviewers. The dashboard binds the request to " - "the head it sees when it reads the command, and a later push restores " - "normal routing." + "the head it sees when it reads the command. Top-level feedback through " + "that command is retired; unresolved review threads remain open. A later " + "push restores normal routing for the remaining work." ) if staleness_note: guidance = f"{guidance} {staleness_note}" @@ -140,6 +142,17 @@ def _effective_command_timestamp(comment: IssueComment) -> str: return comment.effective_content_timestamp +def _latest_valid_timestamp(*values: str) -> str: + candidates = [ + (parsed, value) + for value in values + if (parsed := parse_ts(value)) is not None + ] + if not candidates: + return "" + return max(candidates, key=lambda item: item[0])[1] + + def dashboard_override_facts( source: DashboardOverrideInput, author: str, @@ -172,6 +185,11 @@ def dashboard_override_facts( if previous_facts is not None else "" ) + previous_top_level_feedback_cutoff = ( + previous_facts.dashboard_top_level_feedback_cutoff + if previous_facts is not None + else "" + ) previous_cleared = ( previous_facts.dashboard_override_cleared_by_feedback if previous_facts is not None @@ -204,6 +222,12 @@ def dashboard_override_facts( or _override_command_effective_at(source.issue_comments, bound_command_id) or acknowledgement_created_at ) + top_level_feedback_cutoff = _latest_valid_timestamp( + previous_top_level_feedback_cutoff, + command_created_at, + acknowledged_since, + _override_command_effective_at(source.issue_comments, bound_command_id), + ) cleared_command_id, cleared_head = status_reviewer_handoff_clearance( source.issue_comments ) @@ -226,6 +250,7 @@ def dashboard_override_facts( # binding from that acknowledgement. head_sha=bound_head, since=override_since, + top_level_feedback_cutoff=top_level_feedback_cutoff, cleared_by_feedback=cleared_by_feedback, command_replies=pending_command_replies(source, author, reviewers), ) @@ -420,6 +445,10 @@ def render_command_reply(reply: DashboardCommandReply) -> str: ) else: message = "this pull request was routed to reviewers." + message = ( + f"{message} Top-level feedback through this request will not return; " + "unresolved review threads remain open." + ) elif kind == "unknown_command": subcommand = reply.subcommand attempted = DASHBOARD_COMMAND_PREFIX + (f" {subcommand}" if subcommand else "") diff --git a/.github/scripts/pull-request-dashboard/discussion_lifecycle.py b/.github/scripts/pull-request-dashboard/discussion_lifecycle.py index cf12086b03d..230aec839f2 100644 --- a/.github/scripts/pull-request-dashboard/discussion_lifecycle.py +++ b/.github/scripts/pull-request-dashboard/discussion_lifecycle.py @@ -340,9 +340,43 @@ def _derive_top_level_author_comment_items( return items -def prepare_discussions(source: DiscussionInput) -> PreparedDiscussions: +def _top_level_feedback_timestamp(discussion: dict[str, Any]) -> str: + comments = discussion.get("comments") or () + root_comment = comments[0] if comments else {} + return ( + root_comment.get("activity_timestamp") + or discussion.get("root_timestamp") + or "" + ) + + +def _top_level_items_after_cutoff( + items: list[dict[str, Any]], + cutoff_value: str, +) -> list[dict[str, Any]]: + cutoff = parse_ts(cutoff_value) + if cutoff is None: + return items + return [ + item + for item in items + if ( + (activity := parse_ts(_top_level_feedback_timestamp(item))) is None + or activity > cutoff + ) + ] + + +def prepare_discussions( + source: DiscussionInput, + *, + top_level_feedback_cutoff: str = "", +) -> PreparedDiscussions: review_threads = _group_review_threads(source) - top_level_items = _derive_top_level_items(source) + top_level_items = _top_level_items_after_cutoff( + _derive_top_level_items(source), + top_level_feedback_cutoff, + ) top_level_author_comment_items = _derive_top_level_author_comment_items( source, top_level_items, @@ -396,7 +430,7 @@ def reviewer_handoff_feedback( override_since: str, pr_author: str, ) -> PreparedDiscussions: - """Select human reviewer feedback created after a reviewer handoff command.""" + """Select human reviewer feedback changed after a reviewer handoff command.""" cutoff = parse_ts(override_since) if cutoff is None: return PreparedDiscussions((), (), ()) @@ -652,16 +686,6 @@ def _collect_author_evidence( return evidence, reply_source_id -def _top_level_feedback_timestamp(discussion: dict[str, Any]) -> str: - comments = discussion.get("comments") or () - root_comment = comments[0] if comments else {} - return ( - root_comment.get("activity_timestamp") - or discussion.get("root_timestamp") - or "" - ) - - def _pending_action_for(action: str) -> str: return "author" if action == "unclear" else action diff --git a/.github/scripts/pull-request-dashboard/pr_status_comment.py b/.github/scripts/pull-request-dashboard/pr_status_comment.py index 90f598f401e..993044f8cd7 100644 --- a/.github/scripts/pull-request-dashboard/pr_status_comment.py +++ b/.github/scripts/pull-request-dashboard/pr_status_comment.py @@ -344,7 +344,7 @@ def render_status_comment( override_ack_marker( bound_command_id, bound_head, - facts.dashboard_override_since, + facts.dashboard_top_level_feedback_cutoff, ), ) optional_markers.append( diff --git a/.github/scripts/pull-request-dashboard/pull_request_evaluation.py b/.github/scripts/pull-request-dashboard/pull_request_evaluation.py index 476ad1d3cde..c69ad98e86f 100644 --- a/.github/scripts/pull-request-dashboard/pull_request_evaluation.py +++ b/.github/scripts/pull-request-dashboard/pull_request_evaluation.py @@ -221,6 +221,7 @@ def _compute_facts( dashboard_override_bound_command_id=override.bound_command_id, dashboard_override_head_sha=override.head_sha, dashboard_override_since=override.since, + dashboard_top_level_feedback_cutoff=override.top_level_feedback_cutoff, dashboard_override_cleared_by_feedback=override.cleared_by_feedback, dashboard_command_replies=override.command_replies, copilot_review_requested=any( @@ -450,7 +451,10 @@ def evaluate_pull_request( config.approver_logins, facts.conflicts, ) - prepared_discussions = prepare_discussions(discussion_input) + prepared_discussions = prepare_discussions( + discussion_input, + top_level_feedback_cutoff=facts.dashboard_top_level_feedback_cutoff, + ) if manual_reviewer_handoff: # Old discussions cannot block a break-glass handoff. Only newer # human feedback is classified to decide whether the reviewer has diff --git a/.github/scripts/pull-request-dashboard/state.py b/.github/scripts/pull-request-dashboard/state.py index 98320e3dccf..dd691389bd6 100644 --- a/.github/scripts/pull-request-dashboard/state.py +++ b/.github/scripts/pull-request-dashboard/state.py @@ -488,6 +488,10 @@ def decode_dashboard_facts(value: Any) -> DashboardFacts: value.get("dashboard_override_since", _MISSING), "facts.dashboard_override_since", ), + dashboard_top_level_feedback_cutoff=_string( + value.get("dashboard_top_level_feedback_cutoff", _MISSING), + "facts.dashboard_top_level_feedback_cutoff", + ), dashboard_override_cleared_by_feedback=_boolean( value.get("dashboard_override_cleared_by_feedback", _MISSING), "facts.dashboard_override_cleared_by_feedback", @@ -668,6 +672,10 @@ def encode_dashboard_facts(facts: DashboardFacts) -> dict[str, Any]: ) if facts.dashboard_override_since: stored["dashboard_override_since"] = facts.dashboard_override_since + if facts.dashboard_top_level_feedback_cutoff: + stored["dashboard_top_level_feedback_cutoff"] = ( + facts.dashboard_top_level_feedback_cutoff + ) if facts.dashboard_override_cleared_by_feedback: stored["dashboard_override_cleared_by_feedback"] = True if facts.ci_failing_count is not None: diff --git a/.github/scripts/pull-request-dashboard/test_dashboard.py b/.github/scripts/pull-request-dashboard/test_dashboard.py index f727ca13fec..c69c97586ec 100644 --- a/.github/scripts/pull-request-dashboard/test_dashboard.py +++ b/.github/scripts/pull-request-dashboard/test_dashboard.py @@ -633,6 +633,127 @@ def test_override_binds_to_the_observed_head_before_classification( ) self.assertEqual(classifier.requests, []) + @patch("pull_request_evaluation.fetch_pull_request_source") + def test_push_keeps_old_top_level_feedback_retired_but_restores_threads( + self, + fetch_raw: Mock, + ) -> None: + fetch_raw.return_value = pull_request_source( + pull_request=pull_request_metadata(head_sha="new-head"), + issue_comments=( + issue_comment( + database_id=101, + actor=actor("reviewer"), + body="Please update the top-level documentation.", + created_at="2026-08-16T07:00:00Z", + ), + issue_comment( + database_id=102, + actor=actor("author"), + body="/dashboard route:reviewers", + created_at="2026-08-16T08:00:00Z", + ), + issue_comment( + database_id=103, + actor=actor("opentelemetry-pr-dashboard[bot]"), + body=( + "" + ), + created_at="2026-08-16T08:01:00Z", + ), + issue_comment( + database_id=104, + url="https://example.test/comment/new", + actor=actor("reviewer"), + body="Please add a current example.", + created_at="2026-08-16T09:00:00Z", + ), + ), + review_threads=(review_thread( + node_id="old-thread", + comments=(review_thread_comment( + url="https://example.test/review-comment/old", + created_at="2026-08-16T07:00:00Z", + ),), + ),), + ) + classifier = FakeClassificationOperation( + DiscussionClassifications( + ( + action_classification( + "old-thread", + DiscussionKind.REVIEW_THREAD, + DiscussionAction.AUTHOR, + "The unresolved thread still needs author action.", + ), + ), + ( + action_classification( + "pr-issue-comment-104", + DiscussionKind.TOP_LEVEL_FEEDBACK, + DiscussionAction.AUTHOR, + "The new top-level request needs author action.", + ), + ), + (), + ) + ) + + result = evaluate_pr( + {"number": 7}, + previous_result=stored_dashboard_result( + 7, + route=DashboardRoute.AUTHOR, + top_level_history={ + "pr-issue-comment-101": { + "evidence": {"reply": "2026-08-16T07:30:00Z"}, + } + }, + ), + classification_service=classifier, + ) + + self.assertIsInstance(result, EvaluationSuccess) + assert isinstance(result, EvaluationSuccess) + self.assertEqual(DashboardRoute.AUTHOR, result.route) + self.assertEqual( + { + "old-thread": { + "action": "author", + "since": "2026-08-16T07:00:00Z", + }, + "pr-issue-comment-104": { + "action": "author", + "since": "2026-08-16T09:00:00Z", + }, + }, + result.pending_actions, + ) + self.assertEqual( + ("https://example.test/comment/new",), + result.facts.author_action_top_level_feedback_urls, + ) + self.assertEqual( + ("https://example.test/review-comment/old",), + result.facts.author_action_review_thread_urls, + ) + self.assertEqual({}, result.top_level_history) + self.assertEqual( + ("pr-issue-comment-104",), + tuple( + discussion.identity.discussion_id + for discussion in classifier.requests[0].top_level_items + ), + ) + self.assertEqual( + ("old-thread",), + tuple( + discussion.identity.discussion_id + for discussion in classifier.requests[0].review_threads + ), + ) + @patch("pull_request_evaluation.fetch_pull_request_source") def test_actionable_review_after_override_ends_handoff( self, fetch_raw: Mock diff --git a/.github/scripts/pull-request-dashboard/test_dashboard_override.py b/.github/scripts/pull-request-dashboard/test_dashboard_override.py index 74e6e61cbad..46cbca41bdd 100644 --- a/.github/scripts/pull-request-dashboard/test_dashboard_override.py +++ b/.github/scripts/pull-request-dashboard/test_dashboard_override.py @@ -32,6 +32,9 @@ def result_facts( "dashboard_override_bound_command_id": override.bound_command_id, "dashboard_override_head_sha": override.head_sha, "dashboard_override_since": override.since, + "dashboard_top_level_feedback_cutoff": ( + override.top_level_feedback_cutoff + ), "dashboard_override_cleared_by_feedback": override.cleared_by_feedback, "dashboard_command_replies": override.command_replies, } @@ -326,6 +329,11 @@ def test_renders_command_replies(self) -> None: self.assertIn(dashboard_override.command_reply_marker(4), routed) self.assertIn(dashboard_override.override_ack_marker(4), routed) self.assertIn("@author, this pull request was routed to reviewers.", routed) + self.assertIn( + "Top-level feedback through this request will not return; unresolved " + "review threads remain open.", + routed, + ) self.assertIn(dashboard_override.command_reply_marker(5), gate_held) self.assertIn( "@author, your reviewer-routing request was recorded; the reviewer " @@ -531,6 +539,62 @@ def test_acknowledged_command_keeps_its_recorded_cutoff(self) -> None: self.assertEqual("2026-08-16T09:00:00Z", facts.since) + def test_newer_command_advances_permanent_top_level_cutoff(self) -> None: + source = override_input( + issue_comment( + database_id=6, + body="/dashboard route:reviewers", + created_at="2026-08-16T10:00:00Z", + ), + ) + previous_facts = dashboard_facts( + dashboard_override_bound_command_id=5, + dashboard_override_head_sha="old-head", + dashboard_top_level_feedback_cutoff="2026-08-16T08:00:00Z", + ) + + facts = dashboard_override.dashboard_override_facts( + source, + "author", + None, + "new-head", + previous_facts, + ) + + self.assertEqual( + "2026-08-16T10:00:00Z", + facts.top_level_feedback_cutoff, + ) + + def test_missing_new_command_time_preserves_permanent_cutoff(self) -> None: + source = override_input( + issue_comment( + database_id=6, + body="/dashboard route:reviewers", + created_at="", + updated_at="", + content_updated_at="", + ), + ) + previous_facts = dashboard_facts( + dashboard_override_bound_command_id=5, + dashboard_override_head_sha="old-head", + dashboard_top_level_feedback_cutoff="2026-08-16T08:00:00Z", + ) + + facts = dashboard_override.dashboard_override_facts( + source, + "author", + None, + "new-head", + previous_facts, + ) + + self.assertEqual( + "2026-08-16T08:00:00Z", + facts.top_level_feedback_cutoff, + ) + def test_acknowledged_command_accepts_a_graphql_command_timestamp(self) -> None: source = override_input( issue_comment( @@ -601,6 +665,10 @@ def test_acknowledgement_recovers_cutoff_after_command_deletion(self) -> None: self.assertEqual(5, facts.bound_command_id) self.assertEqual("bound-head", facts.head_sha) self.assertEqual("2026-08-16T08:00:00Z", facts.since) + self.assertEqual( + "2026-08-16T08:00:00Z", + facts.top_level_feedback_cutoff, + ) def test_deleted_command_uses_acknowledgement_timestamp_as_cutoff(self) -> None: source = override_input( @@ -622,6 +690,7 @@ def test_deleted_command_uses_acknowledgement_timestamp_as_cutoff(self) -> None: self.assertEqual(5, facts.bound_command_id) self.assertEqual("bound-head", facts.head_sha) self.assertEqual("2026-08-16T08:00:00Z", facts.since) + self.assertEqual("", facts.top_level_feedback_cutoff) def test_status_marker_clears_only_its_bound_handoff(self) -> None: source = override_input( @@ -1289,7 +1358,7 @@ def test_delivers_command_acknowledgement(self, _load_state, _gh_api, run_gh) -> call([ "gh", "api", "--method", "POST", "repos/open-telemetry/example/issues/7/comments", - "-f", "body=\n\n@author, your reviewer-routing request was recorded; the reviewer handoff is waiting on the Copilot review.\n", + "-f", "body=\n\n@author, your reviewer-routing request was recorded; the reviewer handoff is waiting on the Copilot review. Top-level feedback through this request will not return; unresolved review threads remain open.\n", ]), ], run_gh.call_args_list, diff --git a/.github/scripts/pull-request-dashboard/test_discussion_lifecycle.py b/.github/scripts/pull-request-dashboard/test_discussion_lifecycle.py index 1ed5704a38b..d23e47a0652 100644 --- a/.github/scripts/pull-request-dashboard/test_discussion_lifecycle.py +++ b/.github/scripts/pull-request-dashboard/test_discussion_lifecycle.py @@ -382,6 +382,96 @@ def test_author_response_excludes_feedback_edited_after_it(self) -> None: prepared.top_level_author_comment_items[0]["candidate_feedback"], ) + def test_top_level_cutoff_retires_only_unchanged_old_feedback(self) -> None: + prepared = prepare_discussions( + DiscussionInput( + normalize_review_threads(({ + "id": "old-thread", + "isResolved": False, + "isOutdated": False, + "comments": { + "nodes": [{ + "url": "https://example.test/thread/old", + "body": "Please update the implementation.", + "createdAt": "2026-07-14T01:00:00Z", + "author": {"login": "reviewer"}, + }], + }, + },)), + ( + { + "kind": "issue-comment", + "source_id": 201, + "created_timestamp": "2026-07-14T01:00:00Z", + "timestamp": "2026-07-14T01:00:00Z", + "actor": "reviewer", + "actor_role": "approver", + "body": "Old top-level request.", + }, + { + "kind": "issue-comment", + "source_id": 202, + "created_timestamp": "2026-07-14T02:00:00Z", + "timestamp": "2026-07-14T05:00:00Z", + "actor": "reviewer", + "actor_role": "approver", + "body": "Edited top-level request.", + }, + { + "kind": "review-state", + "source_id": 203, + "created_timestamp": "2026-07-14T03:00:00Z", + "timestamp": "2026-07-14T03:00:00Z", + "content_timestamp": "2026-07-14T05:00:00Z", + "actor": "reviewer", + "actor_role": "approver", + "state": "COMMENTED", + "body": "Edited review summary.", + }, + { + "kind": "issue-comment", + "source_id": 204, + "created_timestamp": "2026-07-14T04:00:00Z", + "timestamp": "2026-07-14T04:00:00Z", + "actor": "reviewer", + "actor_role": "approver", + "body": "Same-second top-level request.", + }, + { + "kind": "issue-comment", + "source_id": 301, + "created_timestamp": "2026-07-14T06:00:00Z", + "timestamp": "2026-07-14T06:00:00Z", + "actor": "author", + "actor_role": "author", + "body": "I addressed the current requests.", + }, + ), + "author", + frozenset({"reviewer"}), + "no", + ), + top_level_feedback_cutoff="2026-07-14T04:00:00Z", + ) + + self.assertEqual( + ["old-thread"], + [thread["discussion_id"] for thread in prepared.review_threads], + ) + self.assertEqual( + ["pr-issue-comment-202", "pr-review-203"], + [item["discussion_id"] for item in prepared.top_level_items], + ) + self.assertEqual( + ["pr-issue-comment-202", "pr-review-203"], + [ + item["discussion_id"] + for item in prepared.top_level_author_comment_items[0][ + "candidate_feedback" + ] + ], + ) + def test_ignores_author_only_review_threads(self) -> None: prepared = prepare_discussions( DiscussionInput( diff --git a/.github/scripts/pull-request-dashboard/test_state.py b/.github/scripts/pull-request-dashboard/test_state.py index dcb614ea6b1..3ac4cd1e200 100644 --- a/.github/scripts/pull-request-dashboard/test_state.py +++ b/.github/scripts/pull-request-dashboard/test_state.py @@ -191,6 +191,7 @@ def test_dashboard_facts_codec_round_trip(self) -> None: dashboard_override_bound_command_id=91, dashboard_override_head_sha="current-head", dashboard_override_since="2026-08-16T08:00:00Z", + dashboard_top_level_feedback_cutoff="2026-08-16T08:00:00Z", dashboard_override_cleared_by_feedback=True, dashboard_command_replies=( DashboardCommandReply( From d61072a97198ba51a54f719d8083b1a928c8357c Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Wed, 2 Sep 2026 14:51:53 -0700 Subject: [PATCH 17/30] Freeze dashboard feedback cutoff Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 378be2b9-85df-4ac8-a125-a99b653412ac --- .../pull-request-dashboard/RATIONALE.md | 13 ++- .../dashboard_contracts.py | 1 + .../dashboard_override.py | 78 +++++++++++++++- .../pr_status_comment.py | 34 ++++++- .../scripts/pull-request-dashboard/state.py | 8 ++ .../pull-request-dashboard/test_dashboard.py | 2 + .../test_dashboard_override.py | 91 ++++++++++++++++++- .../test_pr_status_comment.py | 13 +++ .../pull-request-dashboard/test_state.py | 1 + 9 files changed, 228 insertions(+), 13 deletions(-) diff --git a/.github/scripts/pull-request-dashboard/RATIONALE.md b/.github/scripts/pull-request-dashboard/RATIONALE.md index 7d4d429715f..abedad37332 100644 --- a/.github/scripts/pull-request-dashboard/RATIONALE.md +++ b/.github/scripts/pull-request-dashboard/RATIONALE.md @@ -497,11 +497,14 @@ the implementation understandable and operationally cheap. command can establish a new handoff on the same head and advance the cutoff. - The dashboard binds a command to the head it sees when it first reads that command, and records that head in an acknowledgement marker on either the - command reply or the live status comment. The marker also records the - effective command timestamp used for the permanent top-level feedback cutoff. - A legacy marker without that timestamp does not retire feedback when the - original command is unavailable. The handoff is then a comparison of two - strings: the recorded head and the current one. The earlier design + command reply or the live status comment. A companion marker records the + frozen timestamp used for the permanent top-level feedback cutoff. A legacy + acknowledgement without that cutoff does not retire feedback, because the + command's current edit timestamp cannot reconstruct the original cutoff. The + first observation freezes the cutoff for that command, so editing the command + later cannot retire intervening feedback; only a newer command advances it. + The handoff is then a comparison of two strings: the recorded head and the + current one. The earlier design instead ordered the command against the push by comparing the comment timestamp with the head push time from `GET /repos/{repo}/activity`. Do not reintroduce that. Both timestamps have one-second resolution and come diff --git a/.github/scripts/pull-request-dashboard/dashboard_contracts.py b/.github/scripts/pull-request-dashboard/dashboard_contracts.py index 03a5b79cc3b..c76ab394186 100644 --- a/.github/scripts/pull-request-dashboard/dashboard_contracts.py +++ b/.github/scripts/pull-request-dashboard/dashboard_contracts.py @@ -37,6 +37,7 @@ class DashboardCommandReply: route: DashboardRoute | None = None held_gates: str = "" since: str = "" + top_level_feedback_cutoff: str = "" def __post_init__(self) -> None: if self.comment_id <= 0: diff --git a/.github/scripts/pull-request-dashboard/dashboard_override.py b/.github/scripts/pull-request-dashboard/dashboard_override.py index ca5df2b148e..176208edf6d 100644 --- a/.github/scripts/pull-request-dashboard/dashboard_override.py +++ b/.github/scripts/pull-request-dashboard/dashboard_override.py @@ -5,6 +5,7 @@ import re from collections.abc import Sequence from dataclasses import dataclass +from datetime import datetime from dashboard_contracts import ( DashboardCommandReply, @@ -37,6 +38,13 @@ r"" ) +TOP_LEVEL_FEEDBACK_CUTOFF_MARKER_PREFIX = ( + "" +) PRE_REVIEW_ROUTES = ("author",) @@ -222,11 +230,15 @@ def dashboard_override_facts( or _override_command_effective_at(source.issue_comments, bound_command_id) or acknowledgement_created_at ) + existing_command_binding = bool( + command_id + and command_id == previous_bound_command_id + ) top_level_feedback_cutoff = _latest_valid_timestamp( previous_top_level_feedback_cutoff, - command_created_at, + command_created_at if command_id and not existing_command_binding else "", acknowledged_since, - _override_command_effective_at(source.issue_comments, bound_command_id), + acknowledged_top_level_feedback_cutoff(source.issue_comments), ) cleared_command_id, cleared_head = status_reviewer_handoff_clearance( source.issue_comments @@ -304,6 +316,27 @@ def acknowledges_override(text: str, command_id: int, head_sha: str) -> bool: ) +def acknowledged_top_level_feedback_cutoff( + comments: Sequence[IssueComment], + command_id: int = 0, +) -> str: + candidates: list[tuple[datetime, int, str]] = [] + for comment in comments or []: + if not _is_dashboard_app_comment(comment): + continue + for match in _TOP_LEVEL_FEEDBACK_CUTOFF_MARKER_RE.finditer(comment.body): + marker_command_id = int(match.group(1)) + if command_id and marker_command_id != command_id: + continue + value = match.group(2) + parsed = parse_ts(value) + if parsed is not None: + candidates.append((parsed, marker_command_id, value)) + if not candidates: + return "" + return max(candidates, key=lambda item: (item[0], item[1]))[2] + + def acknowledged_override( comments: Sequence[IssueComment], ) -> tuple[int, str, str, str]: @@ -411,6 +444,13 @@ def override_ack_marker( return f"{OVERRIDE_ACK_MARKER_PREFIX}{comment_id}{head}{since} -->" +def top_level_feedback_cutoff_marker( + comment_id: int, + cutoff: str, +) -> str: + return f"{TOP_LEVEL_FEEDBACK_CUTOFF_MARKER_PREFIX}{comment_id}:{cutoff} -->" + + def render_command_reply(reply: DashboardCommandReply) -> str: user = reply.user mention = f"@{user}, " if user else "" @@ -445,10 +485,28 @@ def render_command_reply(reply: DashboardCommandReply) -> str: ) else: message = "this pull request was routed to reviewers." - message = ( - f"{message} Top-level feedback through this request will not return; " - "unresolved review threads remain open." + has_top_level_feedback_cutoff = ( + parse_ts(reply.top_level_feedback_cutoff) is not None ) + has_handoff_timestamp = parse_ts(reply.since) is not None + if has_top_level_feedback_cutoff and has_handoff_timestamp: + message = ( + f"{message} Top-level feedback through this request will not return; " + "unresolved review threads remain open." + ) + elif has_top_level_feedback_cutoff: + message = ( + f"{message} The existing top-level feedback cutoff remains in effect, " + "but no additional top-level feedback was retired because the " + "dashboard could not determine a safe command time; unresolved review " + "threads remain open." + ) + else: + message = ( + f"{message} No top-level feedback was retired because the dashboard " + "could not determine a safe command time; unresolved review threads " + "remain open." + ) elif kind == "unknown_command": subcommand = reply.subcommand attempted = DASHBOARD_COMMAND_PREFIX + (f" {subcommand}" if subcommand else "") @@ -472,6 +530,13 @@ def render_command_reply(reply: DashboardCommandReply) -> str: reply.since, ) ) + if has_top_level_feedback_cutoff: + markers.append( + top_level_feedback_cutoff_marker( + comment_id, + reply.top_level_feedback_cutoff, + ) + ) return "\n".join([ *markers, f"{mention}{message}", @@ -534,6 +599,9 @@ def append_command_ack_reply( else "" ), since=override_since, + top_level_feedback_cutoff=( + facts.dashboard_top_level_feedback_cutoff + ), ) if command_reply_exists(source.issue_comments, reply): return facts diff --git a/.github/scripts/pull-request-dashboard/pr_status_comment.py b/.github/scripts/pull-request-dashboard/pr_status_comment.py index 993044f8cd7..3051924a7ab 100644 --- a/.github/scripts/pull-request-dashboard/pr_status_comment.py +++ b/.github/scripts/pull-request-dashboard/pr_status_comment.py @@ -14,9 +14,11 @@ ) from dashboard_override import ( PRE_REVIEW_ROUTES, + acknowledged_top_level_feedback_cutoff, acknowledged_override, acknowledges_override, override_ack_marker, + top_level_feedback_cutoff_marker, ) from dashboard_contracts import ( DashboardFacts, @@ -344,9 +346,16 @@ def render_status_comment( override_ack_marker( bound_command_id, bound_head, - facts.dashboard_top_level_feedback_cutoff, + facts.dashboard_override_since, ), ) + if facts.dashboard_top_level_feedback_cutoff: + optional_markers.append( + top_level_feedback_cutoff_marker( + bound_command_id, + facts.dashboard_top_level_feedback_cutoff, + ) + ) optional_markers.append( reviewer_handoff_cleared_marker(bound_command_id, bound_head), ) @@ -436,10 +445,17 @@ def upsert_status_comment( if comments: comment = comments[0] if preserve_clearance: + normalized_comments = normalize_issue_comments(comments) command_id, head_sha = status_reviewer_handoff_clearance(comments) clearance_marker = reviewer_handoff_cleared_marker(command_id, head_sha) acknowledged_id, acknowledged_head, since, _ = acknowledged_override( - normalize_issue_comments(comments) + normalized_comments + ) + top_level_feedback_cutoff = ( + acknowledged_top_level_feedback_cutoff( + normalized_comments, + command_id, + ) ) acknowledgement_marker = override_ack_marker( command_id, @@ -457,6 +473,20 @@ def upsert_status_comment( preserved_markers.append(acknowledgement_marker) if clearance_marker not in body: preserved_markers.append(clearance_marker) + if ( + top_level_feedback_cutoff + and top_level_feedback_cutoff_marker( + command_id, + top_level_feedback_cutoff, + ) + not in body + ): + preserved_markers.append( + top_level_feedback_cutoff_marker( + command_id, + top_level_feedback_cutoff, + ) + ) if preserved_markers: lines = body.splitlines() lines[2:2] = preserved_markers diff --git a/.github/scripts/pull-request-dashboard/state.py b/.github/scripts/pull-request-dashboard/state.py index dd691389bd6..612b59cfbe6 100644 --- a/.github/scripts/pull-request-dashboard/state.py +++ b/.github/scripts/pull-request-dashboard/state.py @@ -414,6 +414,10 @@ def _decode_command_reply(value: Any) -> DashboardCommandReply: value.get("since", _MISSING), "facts.dashboard_command_replies.since", ), + top_level_feedback_cutoff=_string( + value.get("top_level_feedback_cutoff", _MISSING), + "facts.dashboard_command_replies.top_level_feedback_cutoff", + ), ) @@ -427,6 +431,10 @@ def _encode_command_reply(reply: DashboardCommandReply) -> dict[str, Any]: } if reply.since: stored["since"] = reply.since + if reply.top_level_feedback_cutoff: + stored["top_level_feedback_cutoff"] = ( + reply.top_level_feedback_cutoff + ) if reply.kind == "routed": # DashboardCommandReply refuses a routed reply without a route. stored["route"] = reply.route.value diff --git a/.github/scripts/pull-request-dashboard/test_dashboard.py b/.github/scripts/pull-request-dashboard/test_dashboard.py index c69c97586ec..501b4a508cb 100644 --- a/.github/scripts/pull-request-dashboard/test_dashboard.py +++ b/.github/scripts/pull-request-dashboard/test_dashboard.py @@ -627,6 +627,7 @@ def test_override_binds_to_the_observed_head_before_classification( head_sha="abcdef123456", route=DashboardRoute.APPROVER, since="2026-08-16T08:00:00Z", + top_level_feedback_cutoff="2026-08-16T08:00:00Z", ), ), result.facts.dashboard_command_replies, @@ -652,6 +653,7 @@ def test_push_keeps_old_top_level_feedback_retired_but_restores_threads( actor=actor("author"), body="/dashboard route:reviewers", created_at="2026-08-16T08:00:00Z", + content_updated_at="2026-08-16T10:00:00Z", ), issue_comment( database_id=103, diff --git a/.github/scripts/pull-request-dashboard/test_dashboard_override.py b/.github/scripts/pull-request-dashboard/test_dashboard_override.py index 46cbca41bdd..a03b1a841b1 100644 --- a/.github/scripts/pull-request-dashboard/test_dashboard_override.py +++ b/.github/scripts/pull-request-dashboard/test_dashboard_override.py @@ -295,6 +295,16 @@ def test_renders_command_replies(self) -> None: "routed", "author", route=DashboardRoute.APPROVER, + since="2026-08-16T08:00:00Z", + top_level_feedback_cutoff="2026-08-16T08:00:00Z", + ) + ) + conservative = dashboard_override.render_command_reply( + DashboardCommandReply( + 8, + "routed", + "author", + route=DashboardRoute.APPROVER, ) ) gate_held = dashboard_override.render_command_reply( @@ -334,6 +344,11 @@ def test_renders_command_replies(self) -> None: "review threads remain open.", routed, ) + self.assertIn( + "No top-level feedback was retired because the dashboard could not " + "determine a safe command time; unresolved review threads remain open.", + conservative, + ) self.assertIn(dashboard_override.command_reply_marker(5), gate_held) self.assertIn( "@author, your reviewer-routing request was recorded; the reviewer " @@ -449,6 +464,10 @@ def test_pending_command_keeps_its_first_observed_cutoff(self) -> None: ) self.assertEqual("2026-08-16T08:00:00Z", retry.since) + self.assertEqual( + "2026-08-16T08:00:00Z", + retry.top_level_feedback_cutoff, + ) def test_new_pending_command_binds_to_the_newly_observed_head(self) -> None: previous_source = override_input( @@ -505,6 +524,9 @@ def test_acknowledged_command_keeps_its_recorded_cutoff(self) -> None: dashboard_override_bound_command_id=5, dashboard_override_head_sha="bound-head", dashboard_override_since="2026-08-16T09:00:00Z", + dashboard_top_level_feedback_cutoff=( + "2026-08-16T09:00:00Z" + ), ), "", ), @@ -538,6 +560,10 @@ def test_acknowledged_command_keeps_its_recorded_cutoff(self) -> None: ) self.assertEqual("2026-08-16T09:00:00Z", facts.since) + self.assertEqual( + "2026-08-16T09:00:00Z", + facts.top_level_feedback_cutoff, + ) def test_newer_command_advances_permanent_top_level_cutoff(self) -> None: source = override_input( @@ -595,6 +621,66 @@ def test_missing_new_command_time_preserves_permanent_cutoff(self) -> None: facts.top_level_feedback_cutoff, ) + def test_acknowledgement_preserves_cutoff_across_cache_loss(self) -> None: + source = override_input( + issue_comment( + database_id=6, + body="/dashboard route:reviewers", + created_at="", + updated_at="", + content_updated_at="", + ), + ) + previous_facts = dashboard_facts( + dashboard_override_bound_command_id=5, + dashboard_override_head_sha="old-head", + dashboard_top_level_feedback_cutoff="2026-08-16T08:00:00Z", + ) + override = dashboard_override.dashboard_override_facts( + source, + "author", + None, + "new-head", + previous_facts, + ) + facts = dashboard_override.append_command_ack_reply( + source, + result_facts(override, author="author"), + DashboardRoute.APPROVER, + ) + + reply = facts.dashboard_command_replies[0] + body = dashboard_override.render_command_reply(reply) + self.assertIn( + "", + body, + ) + self.assertIn( + "The existing top-level feedback cutoff remains in effect, but no " + "additional top-level feedback was retired because the dashboard could " + "not determine a safe command time; unresolved review threads remain " + "open.", + body, + ) + + restored = dashboard_override.dashboard_override_facts( + override_input(issue_comment( + database_id=9, + actor=actor("opentelemetry-pr-dashboard[bot]"), + body=body, + created_at="2026-08-16T10:00:00Z", + )), + "author", + None, + "new-head", + ) + + self.assertEqual( + "2026-08-16T08:00:00Z", + restored.top_level_feedback_cutoff, + ) + def test_acknowledged_command_accepts_a_graphql_command_timestamp(self) -> None: source = override_input( issue_comment( @@ -1190,6 +1276,7 @@ def test_rebuilds_unacknowledged_reply_across_refreshes(self) -> None: head_sha="current-head", route=DashboardRoute.APPROVER, since="2026-08-16T07:00:00Z", + top_level_feedback_cutoff="2026-08-16T07:00:00Z", ), ), facts.dashboard_command_replies, @@ -1261,6 +1348,7 @@ def test_command_that_cleared_nothing_is_acknowledged_where_it_is_routed(self) - head_sha="current-head", route=DashboardRoute.AUTHOR, since="2026-08-16T07:00:00Z", + top_level_feedback_cutoff="2026-08-16T07:00:00Z", ), ), facts.dashboard_command_replies, @@ -1292,6 +1380,7 @@ def test_conflict_does_not_defer_override_acknowledgement(self) -> None: head_sha="current-head", route=DashboardRoute.APPROVER, since="2026-08-16T07:00:00Z", + top_level_feedback_cutoff="2026-08-16T07:00:00Z", ), ), facts.dashboard_command_replies, @@ -1358,7 +1447,7 @@ def test_delivers_command_acknowledgement(self, _load_state, _gh_api, run_gh) -> call([ "gh", "api", "--method", "POST", "repos/open-telemetry/example/issues/7/comments", - "-f", "body=\n\n@author, your reviewer-routing request was recorded; the reviewer handoff is waiting on the Copilot review. Top-level feedback through this request will not return; unresolved review threads remain open.\n", + "-f", "body=\n\n@author, your reviewer-routing request was recorded; the reviewer handoff is waiting on the Copilot review. No top-level feedback was retired because the dashboard could not determine a safe command time; unresolved review threads remain open.\n", ]), ], run_gh.call_args_list, 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 9a251567f79..afc3d25c7e5 100644 --- a/.github/scripts/pull-request-dashboard/test_pr_status_comment.py +++ b/.github/scripts/pull-request-dashboard/test_pr_status_comment.py @@ -118,6 +118,7 @@ def test_persists_feedback_cleared_handoff_in_status_comment(self) -> None: DashboardRoute.AUTHOR, dashboard_override_bound_command_id=12, dashboard_override_head_sha="abcdef123456", + dashboard_top_level_feedback_cutoff="2026-08-16T08:00:00Z", dashboard_override_cleared_by_feedback=True, ), ) @@ -136,6 +137,11 @@ def test_persists_feedback_cleared_handoff_in_status_comment(self) -> None: "12:abcdef123456 -->", body, ) + self.assertIn( + "", + body, + ) self.assertEqual( (98, "no-status-marker"), status_reviewer_handoff_clearance([ @@ -782,6 +788,8 @@ def test_does_not_update_unchanged_comment(self, _comments: object) -> None: "\n" "\n" + "\n" "" ), @@ -807,6 +815,11 @@ def test_updates_comment_and_deletes_duplicates(self, _comments: object) -> None "12:bound-head -->", self.commands[0][-1], ) + self.assertIn( + "", + self.commands[0][-1], + ) @patch.object(pr_status_comment, "managed_status_comments", return_value=[]) def test_does_not_create_comment_when_creation_is_disabled( diff --git a/.github/scripts/pull-request-dashboard/test_state.py b/.github/scripts/pull-request-dashboard/test_state.py index 3ac4cd1e200..e07e0df562a 100644 --- a/.github/scripts/pull-request-dashboard/test_state.py +++ b/.github/scripts/pull-request-dashboard/test_state.py @@ -202,6 +202,7 @@ def test_dashboard_facts_codec_round_trip(self) -> None: route=DashboardRoute.APPROVER, held_gates="the required checks", since="2026-08-16T08:00:00Z", + top_level_feedback_cutoff="2026-08-16T08:00:00Z", ), DashboardCommandReply( 91, From ed910f98536f802a612bb6c3b664965e11d528ba Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Wed, 2 Sep 2026 15:02:12 -0700 Subject: [PATCH 18/30] Preserve review thread conversation order Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 378be2b9-85df-4ac8-a125-a99b653412ac --- .../discussion_lifecycle.py | 32 +++++++++--- .../test_discussion_lifecycle.py | 52 +++++++++++++++++++ 2 files changed, 76 insertions(+), 8 deletions(-) diff --git a/.github/scripts/pull-request-dashboard/discussion_lifecycle.py b/.github/scripts/pull-request-dashboard/discussion_lifecycle.py index 230aec839f2..f41fd0d1f36 100644 --- a/.github/scripts/pull-request-dashboard/discussion_lifecycle.py +++ b/.github/scripts/pull-request-dashboard/discussion_lifecycle.py @@ -2,6 +2,7 @@ from collections.abc import Callable, Mapping from dataclasses import dataclass +from datetime import datetime, timezone from enum import Enum from typing import Any, TypedDict @@ -27,6 +28,7 @@ POSITIVE_ACK_REACTIONS = {"THUMBS_UP", "HOORAY", "HEART", "ROCKET"} _HUMAN_REVIEWER_ROLES = frozenset({"approver", "outsider"}) +_MIN_TIMESTAMP = datetime.min.replace(tzinfo=timezone.utc) class LifecycleMode(Enum): @@ -128,9 +130,12 @@ def _add_discussion_facts( discussion: dict[str, Any], comments: list[dict[str, Any]], conflicts: str, + *, + latest_comment: dict[str, Any] | None = None, ) -> dict[str, Any]: + latest = latest_comment if latest_comment is not None else comments[-1] discussion["discussion_facts"] = { - "latest_comment_role": comments[-1].get("actor_role"), + "latest_comment_role": latest.get("actor_role"), "current_conflicts": conflicts, } return discussion @@ -408,20 +413,31 @@ def _filter_handoff_feedback( ] if not comments: return None - comments.sort( - key=lambda comment: ( - comment.get("activity_timestamp") or comment.get("timestamp") or "", - comment.get("timestamp") or "", - ) - ) + latest_activity_comment = max( + enumerate(comments), + key=lambda item: ( + ( + parse_ts( + item[1].get("activity_timestamp") + or item[1].get("timestamp") + or "" + ) + or _MIN_TIMESTAMP + ), + parse_ts(item[1].get("timestamp") or "") + or _MIN_TIMESTAMP, + item[0], + ), + )[1] filtered = {**discussion, "comments": comments} - filtered["requester"] = comments[-1].get("actor") or "" + filtered["requester"] = latest_activity_comment.get("actor") or "" filtered["pr_author"] = pr_author return _add_discussion_facts( filtered, comments, (discussion.get("discussion_facts") or {}).get("current_conflicts") or "unknown", + latest_comment=latest_activity_comment, ) diff --git a/.github/scripts/pull-request-dashboard/test_discussion_lifecycle.py b/.github/scripts/pull-request-dashboard/test_discussion_lifecycle.py index d23e47a0652..180e516e842 100644 --- a/.github/scripts/pull-request-dashboard/test_discussion_lifecycle.py +++ b/.github/scripts/pull-request-dashboard/test_discussion_lifecycle.py @@ -7,6 +7,7 @@ AuthorCommentDecision, ClassificationDeferred, ClassificationDiagnostics, + ClassificationDiscussion, ClassificationFailure, ClassificationSuccess, DiscussionAction, @@ -14,6 +15,7 @@ DiscussionIdentity, DiscussionKind, FeedbackOutcome, + reviewer_feedback_prompt_input, ) from discussion_lifecycle import ( DiscussionInput, @@ -1349,6 +1351,56 @@ def test_handoff_feedback_uses_effective_content_timestamps(self) -> None: [item["discussion_id"] for item in filtered.top_level_items], ) + def test_handoff_feedback_preserves_conversation_order_after_edit(self) -> None: + source = DiscussionInput( + normalize_review_threads(({ + "id": "edited-thread", + "isResolved": False, + "isOutdated": False, + "comments": { + "nodes": [ + { + "url": "https://example.test/thread/edited", + "body": "Root request, edited later.", + "createdAt": "2026-07-14T02:00:00Z", + "lastEditedAt": "2026-07-14T06:00:00Z", + "author": {"login": "root-reviewer"}, + }, + { + "url": "https://example.test/thread/follow-up", + "body": "Newer follow-up.", + "createdAt": "2026-07-14T05:00:00Z", + "author": {"login": "follow-up-reviewer"}, + }, + ], + }, + },)), + (), + "author", + frozenset({"root-reviewer"}), + "no", + ) + + filtered = reviewer_handoff_feedback( + prepare_discussions(source), + "2026-07-14T04:00:00Z", + "author", + ) + + discussion = filtered.review_threads[0] + prompt_input = reviewer_feedback_prompt_input( + ClassificationDiscussion.from_record(discussion) + ) + self.assertEqual( + "Root request, edited later.\n\nNewer follow-up.", + prompt_input["body"], + ) + self.assertEqual("root-reviewer", prompt_input["requester"]) + self.assertEqual( + "approver", + discussion["discussion_facts"]["latest_comment_role"], + ) + if __name__ == "__main__": unittest.main() From 12778d9bb106a593e6b1ad9ce45737cc5fb5b7e1 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Wed, 2 Sep 2026 15:11:58 -0700 Subject: [PATCH 19/30] Version dashboard lifecycle state safely Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 378be2b9-85df-4ac8-a125-a99b653412ac --- .../scripts/pull-request-dashboard/CONTEXT.md | 9 +- .../pull-request-dashboard/RATIONALE.md | 4 + .../dashboard_override.py | 1 - .../scripts/pull-request-dashboard/state.py | 8 +- .../pull-request-dashboard/test_dashboard.py | 4 +- .../test_dashboard_override.py | 18 ++-- .../pull-request-dashboard/test_state.py | 96 ++++++++++++++++++- 7 files changed, 122 insertions(+), 18 deletions(-) diff --git a/.github/scripts/pull-request-dashboard/CONTEXT.md b/.github/scripts/pull-request-dashboard/CONTEXT.md index c341a33c0f8..1404099feb6 100644 --- a/.github/scripts/pull-request-dashboard/CONTEXT.md +++ b/.github/scripts/pull-request-dashboard/CONTEXT.md @@ -30,9 +30,12 @@ diagnostics keep typed classification results and freeze only the source discussion records. `state.py` owns the JSON boundary. Its dashboard facts, stored-result, and state -codecs translate the immutable contracts to the version 13 -`dashboard-state.json` shape. Malformed pull request entries are discarded -individually, so one bad entry does not prevent valid entries from loading. +codecs translate the immutable contracts to the version 16 +`dashboard-state.json` shape. Versions 11 through 13 migrate forward. Versions +14 and 15 belong to incompatible state shapes and regenerate instead; an +integration that combines those shapes must allocate a newer version. Malformed +pull request entries are discarded individually, so one bad entry does not +prevent valid entries from loading. `dashboard_state_update.py` owns the acceptance transaction for one pull request slot. It prepares the cached starting value, reconciles an evaluation with the diff --git a/.github/scripts/pull-request-dashboard/RATIONALE.md b/.github/scripts/pull-request-dashboard/RATIONALE.md index abedad37332..63918e8881e 100644 --- a/.github/scripts/pull-request-dashboard/RATIONALE.md +++ b/.github/scripts/pull-request-dashboard/RATIONALE.md @@ -74,6 +74,10 @@ the implementation understandable and operationally cheap. reminders and re-review requests already sent; the delivery version check makes it skip delivery instead. Rolling forward is the way out, and a paused dashboard is the cheaper failure. +- Dashboard state version 16 reads production versions 11 through 13. Versions + 14 and 15 describe incompatible state shapes from parallel work, so this + version regenerates them rather than guessing at compatibility. An integration + that combines those shapes must allocate a newer state version. ## Queue and Workflow Concurrency diff --git a/.github/scripts/pull-request-dashboard/dashboard_override.py b/.github/scripts/pull-request-dashboard/dashboard_override.py index 176208edf6d..a29e0dc4953 100644 --- a/.github/scripts/pull-request-dashboard/dashboard_override.py +++ b/.github/scripts/pull-request-dashboard/dashboard_override.py @@ -237,7 +237,6 @@ def dashboard_override_facts( top_level_feedback_cutoff = _latest_valid_timestamp( previous_top_level_feedback_cutoff, command_created_at if command_id and not existing_command_binding else "", - acknowledged_since, acknowledged_top_level_feedback_cutoff(source.issue_comments), ) cleared_command_id, cleared_head = status_reviewer_handoff_clearance( diff --git a/.github/scripts/pull-request-dashboard/state.py b/.github/scripts/pull-request-dashboard/state.py index 612b59cfbe6..704ea332543 100644 --- a/.github/scripts/pull-request-dashboard/state.py +++ b/.github/scripts/pull-request-dashboard/state.py @@ -35,7 +35,11 @@ # current vector, ordinary state loaders may regenerate mismatched disposable # caches. Every constant ending in _STATE_VERSION or _REVISION is included. # dashboard-state.json: accepted PR routing results and backfill readiness. -DASHBOARD_STATE_VERSION = 13 +# Versions 14 and 15 describe incompatible state shapes from parallel changes. +# Do not accept them here; an integration that combines those shapes must assign +# another version. +DASHBOARD_STATE_VERSION = 16 +DASHBOARD_STATE_COMPATIBLE_VERSIONS = (11, 12, 13) # backfill-state.json: round-robin cursor used by full dashboard refreshes. BACKFILL_STATE_VERSION = 3 # notification-state.json: pending and delivered Slack notification records. @@ -824,7 +828,7 @@ def load_dashboard_state_cache() -> DashboardState | None: state = load_state_file( dashboard_state_path(), DASHBOARD_STATE_VERSION, - compatible_versions=(11, 12), + compatible_versions=DASHBOARD_STATE_COMPATIBLE_VERSIONS, ) if state is None: return None diff --git a/.github/scripts/pull-request-dashboard/test_dashboard.py b/.github/scripts/pull-request-dashboard/test_dashboard.py index 501b4a508cb..2da862dfd98 100644 --- a/.github/scripts/pull-request-dashboard/test_dashboard.py +++ b/.github/scripts/pull-request-dashboard/test_dashboard.py @@ -660,7 +660,9 @@ def test_push_keeps_old_top_level_feedback_retired_but_restores_threads( actor=actor("opentelemetry-pr-dashboard[bot]"), body=( "" + "102:old-head:2026-08-16T08:00:00Z -->\n" + "" ), created_at="2026-08-16T08:01:00Z", ), diff --git a/.github/scripts/pull-request-dashboard/test_dashboard_override.py b/.github/scripts/pull-request-dashboard/test_dashboard_override.py index a03b1a841b1..441d7299bac 100644 --- a/.github/scripts/pull-request-dashboard/test_dashboard_override.py +++ b/.github/scripts/pull-request-dashboard/test_dashboard_override.py @@ -517,8 +517,8 @@ def test_acknowledged_command_keeps_its_bound_head(self) -> None: self.assertEqual("2026-08-16T08:00:00Z", facts.since) self.assertEqual("bound-head", facts.head_sha) - def test_acknowledged_command_keeps_its_recorded_cutoff(self) -> None: - for previous_facts, acknowledged_since in ( + def test_acknowledged_command_keeps_only_a_durable_cutoff(self) -> None: + for previous_facts, acknowledged_since, expected_cutoff in ( ( dashboard_facts( dashboard_override_bound_command_id=5, @@ -529,8 +529,9 @@ def test_acknowledged_command_keeps_its_recorded_cutoff(self) -> None: ), ), "", + "2026-08-16T09:00:00Z", ), - (None, "2026-08-16T09:00:00Z"), + (None, "2026-08-16T09:00:00Z", ""), ): with self.subTest(previous_facts=previous_facts): source = override_input( @@ -561,7 +562,7 @@ def test_acknowledged_command_keeps_its_recorded_cutoff(self) -> None: self.assertEqual("2026-08-16T09:00:00Z", facts.since) self.assertEqual( - "2026-08-16T09:00:00Z", + expected_cutoff, facts.top_level_feedback_cutoff, ) @@ -728,7 +729,7 @@ def test_deleted_command_keeps_previous_handoff_cutoff(self) -> None: self.assertEqual("2026-08-16T08:00:00Z", facts.since) - def test_acknowledgement_recovers_cutoff_after_command_deletion(self) -> None: + def test_legacy_acknowledgement_does_not_recover_permanent_cutoff(self) -> None: source = override_input( issue_comment( database_id=9, @@ -751,12 +752,9 @@ def test_acknowledgement_recovers_cutoff_after_command_deletion(self) -> None: self.assertEqual(5, facts.bound_command_id) self.assertEqual("bound-head", facts.head_sha) self.assertEqual("2026-08-16T08:00:00Z", facts.since) - self.assertEqual( - "2026-08-16T08:00:00Z", - facts.top_level_feedback_cutoff, - ) + self.assertEqual("", facts.top_level_feedback_cutoff) - def test_deleted_command_uses_acknowledgement_timestamp_as_cutoff(self) -> None: + def test_acknowledgement_timestamp_restores_only_handoff_since(self) -> None: source = override_input( issue_comment( database_id=9, diff --git a/.github/scripts/pull-request-dashboard/test_state.py b/.github/scripts/pull-request-dashboard/test_state.py index e07e0df562a..e741b2d1c69 100644 --- a/.github/scripts/pull-request-dashboard/test_state.py +++ b/.github/scripts/pull-request-dashboard/test_state.py @@ -23,6 +23,7 @@ AUTHOR_NUDGE_STATE_VERSION, BACKFILL_STATE_VERSION, COPILOT_REVIEW_REQUEST_STATE_VERSION, + DASHBOARD_STATE_COMPATIBLE_VERSIONS, DASHBOARD_STATE_VERSION, NOTIFICATION_STATE_VERSION, STATUS_COMMENT_ROLLOUT_STATE_VERSION, @@ -179,6 +180,82 @@ def test_dashboard_state_save_writes_explicit_shape(self) -> None: }, ) + def test_dashboard_state_migrates_safe_production_versions(self) -> None: + for version in DASHBOARD_STATE_COMPATIBLE_VERSIONS: + with self.subTest(version=version), tempfile.TemporaryDirectory() as temp_dir: + with patch("state._state_dir", Path(temp_dir)): + dashboard_state_path().write_text( + json.dumps({ + "version": version, + "initial_backfill_complete": True, + "prs": {}, + }), + encoding="utf-8", + ) + + self.assertEqual( + DashboardState(initial_backfill_complete=True), + load_dashboard_state_cache(), + ) + + def test_dashboard_state_rejects_incompatible_versions(self) -> None: + for version in (14, 15): + with self.subTest(version=version), tempfile.TemporaryDirectory() as temp_dir: + with ( + patch("state._state_dir", Path(temp_dir)), + redirect_stderr(StringIO()), + ): + dashboard_state_path().write_text( + json.dumps({ + "version": version, + "initial_backfill_complete": True, + "prs": {}, + }), + encoding="utf-8", + ) + + self.assertIsNone(load_dashboard_state_cache()) + + def test_version_thirteen_state_does_not_invent_durable_cutoff(self) -> None: + legacy_facts = dashboard_facts( + dashboard_override_since="2026-08-16T08:00:00Z", + dashboard_command_replies=( + DashboardCommandReply( + 91, + "routed", + "alice", + head_sha="current-head", + route=DashboardRoute.APPROVER, + since="2026-08-16T08:00:00Z", + ), + ), + ) + stored = encode_dashboard_state(dashboard_state( + stored_dashboard_result(facts=legacy_facts) + )) + stored["version"] = 13 + + with tempfile.TemporaryDirectory() as temp_dir, patch( + "state._state_dir", + Path(temp_dir), + ): + dashboard_state_path().write_text( + json.dumps(stored), + encoding="utf-8", + ) + + loaded = load_dashboard_state_cache() + + self.assertIsNotNone(loaded) + assert loaded is not None + facts = loaded.results[0].facts + self.assertEqual("2026-08-16T08:00:00Z", facts.dashboard_override_since) + self.assertEqual("", facts.dashboard_top_level_feedback_cutoff) + self.assertEqual( + "", + facts.dashboard_command_replies[0].top_level_feedback_cutoff, + ) + def test_dashboard_facts_codec_round_trip(self) -> None: facts = dashboard_facts( author="alice", @@ -528,7 +605,8 @@ def test_version_eleven_dashboard_state_migrates_to_current_shape(self) -> None: def test_notification_state_version_is_independent(self) -> None: self.assertEqual(BACKFILL_STATE_VERSION, 3) self.assertEqual(NOTIFICATION_STATE_VERSION, 3) - self.assertEqual(DASHBOARD_STATE_VERSION, 13) + self.assertEqual(DASHBOARD_STATE_VERSION, 16) + self.assertEqual(DASHBOARD_STATE_COMPATIBLE_VERSIONS, (11, 12, 13)) self.assertEqual(STATUS_COMMENT_ROLLOUT_STATE_VERSION, 2) self.assertEqual(AUTHOR_NUDGE_STATE_VERSION, 3) self.assertEqual(COPILOT_REVIEW_REQUEST_STATE_VERSION, 6) @@ -864,6 +942,22 @@ def test_new_delivery_version_rejects_older_workers(self) -> None: self.assertFalse(claim_delivery_versions()) self.assertEqual(newer, load_delivery_versions()) + def test_cutoff_state_version_blocks_pre_marker_delivery_worker(self) -> None: + current = current_delivery_versions() + old_worker = {**current, "DASHBOARD_STATE_VERSION": 13} + with tempfile.TemporaryDirectory() as temp_dir, patch( + "state._state_dir", + Path(temp_dir), + ): + with patch("state.current_delivery_versions", return_value=old_worker): + self.assertTrue(claim_delivery_versions()) + + self.assertTrue(claim_delivery_versions()) + + with patch("state.current_delivery_versions", return_value=old_worker): + self.assertFalse(claim_delivery_versions()) + self.assertEqual(current, load_delivery_versions()) + def test_delivery_versions_fail_closed(self) -> None: malformed_versions = [ "not json", From 8d6ef485a944a19d66febb3851425de4b29060f0 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Wed, 2 Sep 2026 15:40:45 -0700 Subject: [PATCH 20/30] Keep reviewer handoffs across pushes Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 378be2b9-85df-4ac8-a125-a99b653412ac --- .../scripts/pull-request-dashboard/CONTEXT.md | 22 +-- .../pull-request-dashboard/RATIONALE.md | 72 ++++---- .../pull-request-dashboard/dashboard.py | 8 +- .../dashboard_contracts.py | 8 + .../dashboard_override.py | 84 ++++++++-- .../pull_request_evaluation.py | 1 + .../routing_decision.py | 8 +- .../scripts/pull-request-dashboard/state.py | 20 ++- .../pull-request-dashboard/test_dashboard.py | 154 +++++++++++++++++- .../test_dashboard_contracts.py | 23 +++ .../test_dashboard_override.py | 85 +++++++++- .../test_routing_decision.py | 22 ++- .../pull-request-dashboard/test_state.py | 98 +++++++++-- 13 files changed, 512 insertions(+), 93 deletions(-) diff --git a/.github/scripts/pull-request-dashboard/CONTEXT.md b/.github/scripts/pull-request-dashboard/CONTEXT.md index 1404099feb6..19c097e3786 100644 --- a/.github/scripts/pull-request-dashboard/CONTEXT.md +++ b/.github/scripts/pull-request-dashboard/CONTEXT.md @@ -30,12 +30,12 @@ diagnostics keep typed classification results and freeze only the source discussion records. `state.py` owns the JSON boundary. Its dashboard facts, stored-result, and state -codecs translate the immutable contracts to the version 16 -`dashboard-state.json` shape. Versions 11 through 13 migrate forward. Versions -14 and 15 belong to incompatible state shapes and regenerate instead; an -integration that combines those shapes must allocate a newer version. Malformed -pull request entries are discarded individually, so one bad entry does not -prevent valid entries from loading. +codecs translate the immutable contracts to the version 17 +`dashboard-state.json` shape. Versions 11 through 13 and version 16 migrate +forward. Versions 14 and 15 belong to incompatible state shapes and regenerate +instead; an integration that combines those shapes must allocate a newer +version. Malformed pull request entries are discarded individually, so one bad +entry does not prevent valid entries from loading. `dashboard_state_update.py` owns the acceptance transaction for one pull request slot. It prepares the cached starting value, reconciles an evaluation with the @@ -157,10 +157,12 @@ reviewer routes keeps the existing wait. ### Reviewer handoff -An acknowledged dashboard override binds a reviewer handoff to one head SHA. -While that head remains current, the handoff routes directly to approvers and -bypasses discussions, approvals, conflicts, required checks, and the Copilot -gate. A push or newer actionable human reviewer feedback ends the handoff. +An acknowledged dashboard override records the observed head as its durable +binding identity. The handoff routes directly to approvers across later pushes +and bypasses discussions, approvals, conflicts, required checks, and the Copilot +gate. Newer actionable human reviewer feedback ends it, including a reply or +edit on a review thread that the handoff had suppressed. Legacy acknowledgements +without the persistence marker retain their original head-bound behavior. ## Routing snapshot diff --git a/.github/scripts/pull-request-dashboard/RATIONALE.md b/.github/scripts/pull-request-dashboard/RATIONALE.md index 63918e8881e..4a7c27a1eb7 100644 --- a/.github/scripts/pull-request-dashboard/RATIONALE.md +++ b/.github/scripts/pull-request-dashboard/RATIONALE.md @@ -74,10 +74,11 @@ the implementation understandable and operationally cheap. reminders and re-review requests already sent; the delivery version check makes it skip delivery instead. Rolling forward is the way out, and a paused dashboard is the cheaper failure. -- Dashboard state version 16 reads production versions 11 through 13. Versions - 14 and 15 describe incompatible state shapes from parallel work, so this - version regenerates them rather than guessing at compatibility. An integration - that combines those shapes must allocate a newer state version. +- Dashboard state version 17 reads production versions 11 through 13 and the + pre-persistent-handoff version 16. Versions 14 and 15 describe incompatible + state shapes from parallel work, so this version regenerates them rather than + guessing at compatibility. An integration that combines those shapes must + allocate a newer state version. ## Queue and Workflow Concurrency @@ -477,20 +478,18 @@ the implementation understandable and operationally cheap. stale review is the ordinary state between a push and the next re-review, and an icon that is always present says nothing about which PRs are actually waiting. -- An effective reviewer-routing override is a break-glass handoff for the - current head. It forces the reviewer route and bypasses required checks, - Copilot review, merge conflicts, discussion actions, and approval routing. The - author may be stuck or may need a person to explain a basic problem, so no - automated blocker can prevent the handoff. A later push restores normal - routing and gates, but top-level feedback last changed at or before the - command stays retired. Review threads are not retired by the command; after a - push, unresolved and non-outdated threads return to normal routing. Actionable - human reviewer feedback with an effective content timestamp after the command - also ends the handoff, because a reviewer has answered the request for help - and assigned the next action to the author. An edit to older feedback counts - when its content-edit timestamp is after the command. Praise, informational - comments, bot feedback, and feedback last changed at or before the command do - not end it. +- An effective reviewer-routing override is a break-glass handoff. It forces the + reviewer route across later pushes and bypasses required checks, Copilot + review, merge conflicts, discussion actions, and approval routing. The author + may be stuck or may need a person to explain a basic problem, so no automated + blocker can prevent the handoff. Top-level feedback last changed at or before + the command stays retired. Review threads are not retired by the command, but + their existing work does not override the handoff. Actionable human reviewer + feedback with an effective content timestamp after the command ends the + handoff, because a reviewer has answered the request for help and assigned the + next action to the author. That includes a new reply or edit on an older + review thread. Praise, informational comments, bot feedback, and feedback last + changed at or before the command do not end it. - While a handoff is active, the dashboard classifies only human reviewer feedback with content activity after the command. Older discussions and classification failures therefore cannot block the break-glass route. Once @@ -501,27 +500,22 @@ the implementation understandable and operationally cheap. command can establish a new handoff on the same head and advance the cutoff. - The dashboard binds a command to the head it sees when it first reads that command, and records that head in an acknowledgement marker on either the - command reply or the live status comment. A companion marker records the - frozen timestamp used for the permanent top-level feedback cutoff. A legacy - acknowledgement without that cutoff does not retire feedback, because the - command's current edit timestamp cannot reconstruct the original cutoff. The - first observation freezes the cutoff for that command, so editing the command - later cannot retire intervening feedback; only a newer command advances it. - The handoff is then a comparison of two strings: the recorded head and the - current one. The earlier design - instead ordered the command against the push by comparing the comment - timestamp with the head push time from `GET /repos/{repo}/activity`. - Do not reintroduce that. Both timestamps have one-second resolution and come - from different APIs, so the ordering is sometimes unknowable, which forces a - third "cannot tell" state that every later pass has to carry forward and every - failure path has to preserve by hand. It also fails unsafely in the case the - handoff exists for: when the activity lookup returns nothing, the command - hangs unacknowledged. Binding to the observed head removes the extra API call - and keeps the answer in GitHub rather than in `dashboard-state.json`, so a - failed pass or a dropped cache cannot corrupt it. The cost is that a push - between the command and the pass that reads it belongs to the handoff instead - of ending it, which is the safer direction: the author asked for help, and the - worst case is one extra handoff the author can end with another push. + command reply or the live status comment. A persistence marker makes the + cross-push behavior explicit. Legacy acknowledgements without that marker + remain head-bound, so deployment cannot reactivate old handoffs that had + already expired. A separate companion marker records the frozen timestamp + used for the permanent top-level feedback cutoff. A legacy acknowledgement + without that cutoff does not retire feedback, because the command's current + edit timestamp cannot reconstruct the original cutoff. The first observation + freezes the cutoff for that command, so editing the command later cannot + retire intervening feedback; only a newer command advances it. +- Recording the observed head also avoids ordering the command against a push by + comparing the comment timestamp with the head push time from + `GET /repos/{repo}/activity`. Both timestamps have one-second resolution and + come from different APIs, so the ordering is sometimes unknowable. Binding to + the observed head removes that extra API call. A push between the command and + the pass that first reads it belongs to the handoff, as does any later push + after the persistence marker is recorded. - The gate does not wait for the required checks before requesting the review, so the two run at once. A route computed while checks are still running is provisional, but the only outcome that matters here is a failure, and a diff --git a/.github/scripts/pull-request-dashboard/dashboard.py b/.github/scripts/pull-request-dashboard/dashboard.py index 7ee2e672843..47509675419 100644 --- a/.github/scripts/pull-request-dashboard/dashboard.py +++ b/.github/scripts/pull-request-dashboard/dashboard.py @@ -123,14 +123,16 @@ int ID of the command bound to the reviewer handoff. dashboard_override_head_sha str Head an override is bound to; - the handoff is active while it - equals head_sha and has not - been cleared by newer feedback. + identifies the durable command + binding. dashboard_override_since str (iso) Effective content timestamp of the command bound to the handoff. dashboard_top_level_feedback_cutoff str (iso) Top-level feedback at or before this command timestamp is retired. + dashboard_override_persistent bool Handoff remains active across + pushes until newer feedback + clears it. dashboard_override_cleared_by_feedback bool Actionable human reviewer feedback ended the handoff. diff --git a/.github/scripts/pull-request-dashboard/dashboard_contracts.py b/.github/scripts/pull-request-dashboard/dashboard_contracts.py index c76ab394186..e727bc8d7ee 100644 --- a/.github/scripts/pull-request-dashboard/dashboard_contracts.py +++ b/.github/scripts/pull-request-dashboard/dashboard_contracts.py @@ -38,6 +38,7 @@ class DashboardCommandReply: held_gates: str = "" since: str = "" top_level_feedback_cutoff: str = "" + persistent_handoff: bool = False def __post_init__(self) -> None: if self.comment_id <= 0: @@ -61,6 +62,12 @@ def __post_init__(self) -> None: ) if self.kind != "routed" and self.route is not None: raise ValueError("only routed dashboard command replies may include a route") + if self.kind != "routed" and self.persistent_handoff: + raise ValueError("only routed replies may persist a reviewer handoff") + if self.persistent_handoff and self.route is DashboardRoute.AUTHOR: + raise ValueError( + "persistent reviewer handoff replies require a reviewer route" + ) @dataclass(frozen=True) @@ -87,6 +94,7 @@ class DashboardFacts: dashboard_override_head_sha: str = "" dashboard_override_since: str = "" dashboard_top_level_feedback_cutoff: str = "" + dashboard_override_persistent: bool = False dashboard_override_cleared_by_feedback: bool = False dashboard_command_replies: tuple[DashboardCommandReply, ...] = () copilot_review_requested: bool = False diff --git a/.github/scripts/pull-request-dashboard/dashboard_override.py b/.github/scripts/pull-request-dashboard/dashboard_override.py index a29e0dc4953..c66eba039cc 100644 --- a/.github/scripts/pull-request-dashboard/dashboard_override.py +++ b/.github/scripts/pull-request-dashboard/dashboard_override.py @@ -29,9 +29,9 @@ r"" ) OVERRIDE_ACK_MARKER_PREFIX = "" ) +PERSISTENT_HANDOFF_MARKER_PREFIX = ( + "" +) PRE_REVIEW_ROUTES = ("author",) @@ -56,6 +63,7 @@ class DashboardOverrideFacts: head_sha: str since: str top_level_feedback_cutoff: str + persistent_handoff: bool cleared_by_feedback: bool command_replies: tuple[DashboardCommandReply, ...] @@ -72,10 +80,9 @@ def author_override_guidance(staleness_note: str = "") -> str: guidance = ( "If you need reviewer or maintainer help, comment " "`/dashboard route:reviewers` to request routing from waiting on the " - "author to waiting on reviewers. The dashboard binds the request to " - "the head it sees when it reads the command. Top-level feedback through " - "that command is retired; unresolved review threads remain open. A later " - "push restores normal routing for the remaining work." + "author to waiting on reviewers. Top-level feedback through that command " + "is retired; unresolved review threads remain open. The handoff remains " + "active across pushes until newer actionable human feedback arrives." ) if staleness_note: guidance = f"{guidance} {staleness_note}" @@ -198,6 +205,11 @@ def dashboard_override_facts( if previous_facts is not None else "" ) + previous_persistent_handoff = ( + previous_facts.dashboard_override_persistent + if previous_facts is not None + else False + ) previous_cleared = ( previous_facts.dashboard_override_cleared_by_feedback if previous_facts is not None @@ -239,6 +251,15 @@ def dashboard_override_facts( command_created_at if command_id and not existing_command_binding else "", acknowledged_top_level_feedback_cutoff(source.issue_comments), ) + persistent_handoff = bool( + command_id + or (previous_binding_matches and previous_persistent_handoff) + or acknowledges_persistent_handoff( + source.issue_comments, + bound_command_id, + bound_head, + ) + ) cleared_command_id, cleared_head = status_reviewer_handoff_clearance( source.issue_comments ) @@ -262,6 +283,7 @@ def dashboard_override_facts( head_sha=bound_head, since=override_since, top_level_feedback_cutoff=top_level_feedback_cutoff, + persistent_handoff=persistent_handoff, cleared_by_feedback=cleared_by_feedback, command_replies=pending_command_replies(source, author, reviewers), ) @@ -336,6 +358,24 @@ def acknowledged_top_level_feedback_cutoff( return max(candidates, key=lambda item: (item[0], item[1]))[2] +def acknowledges_persistent_handoff( + comments: Sequence[IssueComment], + command_id: int, + head_sha: str, +) -> bool: + if not command_id or not head_sha: + return False + return any( + _is_dashboard_app_comment(comment) + and any( + int(match.group(1)) == command_id + and match.group(2) == head_sha + for match in _PERSISTENT_HANDOFF_MARKER_RE.finditer(comment.body) + ) + for comment in comments or [] + ) + + def acknowledged_override( comments: Sequence[IssueComment], ) -> tuple[int, str, str, str]: @@ -450,6 +490,13 @@ def top_level_feedback_cutoff_marker( return f"{TOP_LEVEL_FEEDBACK_CUTOFF_MARKER_PREFIX}{comment_id}:{cutoff} -->" +def persistent_handoff_marker( + comment_id: int, + head_sha: str, +) -> str: + return f"{PERSISTENT_HANDOFF_MARKER_PREFIX}{comment_id}:{head_sha} -->" + + def render_command_reply(reply: DashboardCommandReply) -> str: user = reply.user mention = f"@{user}, " if user else "" @@ -464,9 +511,8 @@ def render_command_reply(reply: DashboardCommandReply) -> str: route = reply.route.value held_gates = reply.held_gates if route in PRE_REVIEW_ROUTES: - # An active handoff always routes to approvers, so a pre-review - # route means the command is bound to a head that has been pushed - # over. + # New handoffs persist across pushes. A pre-review route can remain + # only for a legacy acknowledgement without the persistence marker. message = ( "your reviewer-routing request is not active for the current " "pull request head; comment `/dashboard route:reviewers` again " @@ -484,6 +530,11 @@ def render_command_reply(reply: DashboardCommandReply) -> str: ) else: message = "this pull request was routed to reviewers." + if reply.persistent_handoff: + message = ( + f"{message} The handoff remains active across pushes until newer " + "actionable human feedback arrives." + ) has_top_level_feedback_cutoff = ( parse_ts(reply.top_level_feedback_cutoff) is not None ) @@ -536,6 +587,10 @@ def render_command_reply(reply: DashboardCommandReply) -> str: reply.top_level_feedback_cutoff, ) ) + if reply.persistent_handoff and reply.head_sha: + markers.append( + persistent_handoff_marker(comment_id, reply.head_sha) + ) return "\n".join([ *markers, f"{mention}{message}", @@ -562,10 +617,10 @@ def append_command_ack_reply( ) -> DashboardFacts: """Queue the reply that acknowledges an override command. - The reply carries the acknowledgement marker, which records the bound head - and feedback cutoff and stops the command from being processed again. A - command superseded by reviewer feedback is acknowledged in the status comment - instead of producing another top-level comment. + The reply carries markers that record the command binding, permanent + feedback cutoff, and persistent handoff. A command superseded by reviewer + feedback is acknowledged in the status comment instead of producing another + top-level comment. """ cleared_by_feedback = facts.dashboard_override_cleared_by_feedback command_id = ( @@ -601,6 +656,7 @@ def append_command_ack_reply( top_level_feedback_cutoff=( facts.dashboard_top_level_feedback_cutoff ), + persistent_handoff=facts.dashboard_override_persistent, ) if command_reply_exists(source.issue_comments, reply): return facts diff --git a/.github/scripts/pull-request-dashboard/pull_request_evaluation.py b/.github/scripts/pull-request-dashboard/pull_request_evaluation.py index c69ad98e86f..c6d27ffa3c5 100644 --- a/.github/scripts/pull-request-dashboard/pull_request_evaluation.py +++ b/.github/scripts/pull-request-dashboard/pull_request_evaluation.py @@ -222,6 +222,7 @@ def _compute_facts( dashboard_override_head_sha=override.head_sha, dashboard_override_since=override.since, dashboard_top_level_feedback_cutoff=override.top_level_feedback_cutoff, + dashboard_override_persistent=override.persistent_handoff, dashboard_override_cleared_by_feedback=override.cleared_by_feedback, dashboard_command_replies=override.command_replies, copilot_review_requested=any( diff --git a/.github/scripts/pull-request-dashboard/routing_decision.py b/.github/scripts/pull-request-dashboard/routing_decision.py index b53f5ad373d..335bdc8507e 100644 --- a/.github/scripts/pull-request-dashboard/routing_decision.py +++ b/.github/scripts/pull-request-dashboard/routing_decision.py @@ -48,10 +48,14 @@ class RoutingOutcome: def reviewer_handoff_active(facts: DashboardFacts) -> bool: - """Return whether the uncleared reviewer handoff matches the current head.""" + """Return whether the reviewer handoff remains active.""" return ( bool(facts.dashboard_override_head_sha) - and facts.dashboard_override_head_sha == facts.head_sha + and bool(facts.head_sha) + and ( + facts.dashboard_override_persistent + or facts.dashboard_override_head_sha == facts.head_sha + ) and not facts.dashboard_override_cleared_by_feedback ) diff --git a/.github/scripts/pull-request-dashboard/state.py b/.github/scripts/pull-request-dashboard/state.py index 704ea332543..098938f6a85 100644 --- a/.github/scripts/pull-request-dashboard/state.py +++ b/.github/scripts/pull-request-dashboard/state.py @@ -36,10 +36,10 @@ # caches. Every constant ending in _STATE_VERSION or _REVISION is included. # dashboard-state.json: accepted PR routing results and backfill readiness. # Versions 14 and 15 describe incompatible state shapes from parallel changes. -# Do not accept them here; an integration that combines those shapes must assign -# another version. -DASHBOARD_STATE_VERSION = 16 -DASHBOARD_STATE_COMPATIBLE_VERSIONS = (11, 12, 13) +# Version 16 is the pre-persistent-handoff lifecycle shape. An integration that +# combines the incompatible shapes must assign another version. +DASHBOARD_STATE_VERSION = 17 +DASHBOARD_STATE_COMPATIBLE_VERSIONS = (11, 12, 13, 16) # backfill-state.json: round-robin cursor used by full dashboard refreshes. BACKFILL_STATE_VERSION = 3 # notification-state.json: pending and delivered Slack notification records. @@ -422,6 +422,10 @@ def _decode_command_reply(value: Any) -> DashboardCommandReply: value.get("top_level_feedback_cutoff", _MISSING), "facts.dashboard_command_replies.top_level_feedback_cutoff", ), + persistent_handoff=_boolean( + value.get("persistent_handoff", _MISSING), + "facts.dashboard_command_replies.persistent_handoff", + ), ) @@ -439,6 +443,8 @@ def _encode_command_reply(reply: DashboardCommandReply) -> dict[str, Any]: stored["top_level_feedback_cutoff"] = ( reply.top_level_feedback_cutoff ) + if reply.persistent_handoff: + stored["persistent_handoff"] = True if reply.kind == "routed": # DashboardCommandReply refuses a routed reply without a route. stored["route"] = reply.route.value @@ -504,6 +510,10 @@ def decode_dashboard_facts(value: Any) -> DashboardFacts: value.get("dashboard_top_level_feedback_cutoff", _MISSING), "facts.dashboard_top_level_feedback_cutoff", ), + dashboard_override_persistent=_boolean( + value.get("dashboard_override_persistent", _MISSING), + "facts.dashboard_override_persistent", + ), dashboard_override_cleared_by_feedback=_boolean( value.get("dashboard_override_cleared_by_feedback", _MISSING), "facts.dashboard_override_cleared_by_feedback", @@ -688,6 +698,8 @@ def encode_dashboard_facts(facts: DashboardFacts) -> dict[str, Any]: stored["dashboard_top_level_feedback_cutoff"] = ( facts.dashboard_top_level_feedback_cutoff ) + if facts.dashboard_override_persistent: + stored["dashboard_override_persistent"] = True if facts.dashboard_override_cleared_by_feedback: stored["dashboard_override_cleared_by_feedback"] = True if facts.ci_failing_count is not None: diff --git a/.github/scripts/pull-request-dashboard/test_dashboard.py b/.github/scripts/pull-request-dashboard/test_dashboard.py index 2da862dfd98..ead7ad52631 100644 --- a/.github/scripts/pull-request-dashboard/test_dashboard.py +++ b/.github/scripts/pull-request-dashboard/test_dashboard.py @@ -628,6 +628,7 @@ def test_override_binds_to_the_observed_head_before_classification( route=DashboardRoute.APPROVER, since="2026-08-16T08:00:00Z", top_level_feedback_cutoff="2026-08-16T08:00:00Z", + persistent_handoff=True, ), ), result.facts.dashboard_command_replies, @@ -635,7 +636,7 @@ def test_override_binds_to_the_observed_head_before_classification( self.assertEqual(classifier.requests, []) @patch("pull_request_evaluation.fetch_pull_request_source") - def test_push_keeps_old_top_level_feedback_retired_but_restores_threads( + def test_actionable_feedback_after_push_ends_persistent_handoff( self, fetch_raw: Mock, ) -> None: @@ -662,7 +663,9 @@ def test_push_keeps_old_top_level_feedback_retired_but_restores_threads( "\n" "" + "102:2026-08-16T08:00:00Z -->\n" + "" ), created_at="2026-08-16T08:01:00Z", ), @@ -701,7 +704,15 @@ def test_push_keeps_old_top_level_feedback_retired_but_restores_threads( ), ), (), - ) + ), + reviewer_feedback_result=( + action_classification( + "pr-issue-comment-104", + DiscussionKind.TOP_LEVEL_FEEDBACK, + DiscussionAction.AUTHOR, + "The new top-level request needs author action.", + ), + ), ) result = evaluate_pr( @@ -721,6 +732,8 @@ def test_push_keeps_old_top_level_feedback_retired_but_restores_threads( self.assertIsInstance(result, EvaluationSuccess) assert isinstance(result, EvaluationSuccess) self.assertEqual(DashboardRoute.AUTHOR, result.route) + self.assertTrue(result.facts.dashboard_override_persistent) + self.assertTrue(result.facts.dashboard_override_cleared_by_feedback) self.assertEqual( { "old-thread": { @@ -758,6 +771,141 @@ def test_push_keeps_old_top_level_feedback_retired_but_restores_threads( ), ) + @patch("pull_request_evaluation.fetch_pull_request_source") + def test_push_does_not_end_persistent_handoff(self, fetch_raw: Mock) -> None: + fetch_raw.return_value = pull_request_source( + pull_request=pull_request_metadata(head_sha="new-head"), + issue_comments=( + issue_comment( + database_id=102, + body="/dashboard route:reviewers", + created_at="2026-08-16T08:00:00Z", + ), + issue_comment( + database_id=103, + actor=actor("opentelemetry-pr-dashboard[bot]"), + body=( + "\n" + "\n" + "" + ), + ), + ), + review_threads=(review_thread( + node_id="old-thread", + comments=(review_thread_comment( + body="Please update this.", + created_at="2026-08-16T07:00:00Z", + ),), + ),), + ) + classifier = FakeClassificationOperation( + error=AssertionError("old discussions must remain suppressed") + ) + + result = evaluate_pr( + {"number": 7}, + classification_service=classifier, + ) + + self.assertIsInstance(result, EvaluationSuccess) + assert isinstance(result, EvaluationSuccess) + self.assertEqual(DashboardRoute.APPROVER, result.route) + self.assertTrue(result.facts.dashboard_override_persistent) + self.assertFalse(result.facts.dashboard_override_cleared_by_feedback) + self.assertEqual({}, result.pending_actions) + self.assertEqual([], classifier.requests) + self.assertEqual([], classifier.reviewer_feedback_requests) + + @patch("pull_request_evaluation.fetch_pull_request_source") + def test_new_reply_on_old_thread_ends_persistent_handoff( + self, + fetch_raw: Mock, + ) -> None: + fetch_raw.return_value = pull_request_source( + pull_request=pull_request_metadata(head_sha="new-head"), + issue_comments=( + issue_comment( + database_id=102, + body="/dashboard route:reviewers", + created_at="2026-08-16T08:00:00Z", + ), + issue_comment( + database_id=103, + actor=actor("opentelemetry-pr-dashboard[bot]"), + body=( + "\n" + "\n" + "" + ), + ), + ), + review_threads=(review_thread( + node_id="thread-1", + comments=( + review_thread_comment( + body="Please update this.", + created_at="2026-08-16T07:00:00Z", + ), + review_thread_comment( + body="This still needs the requested update.", + created_at="2026-08-16T09:00:00Z", + ), + ), + ),), + ) + classification = action_classification( + "thread-1", + DiscussionKind.REVIEW_THREAD, + DiscussionAction.AUTHOR, + "The reviewer renewed the request.", + ) + classifier = FakeClassificationOperation( + DiscussionClassifications((classification,), (), ()), + reviewer_feedback_result=(classification,), + ) + + result = evaluate_pr( + {"number": 7}, + classification_service=classifier, + ) + + self.assertIsInstance(result, EvaluationSuccess) + assert isinstance(result, EvaluationSuccess) + self.assertEqual(DashboardRoute.AUTHOR, result.route) + self.assertTrue(result.facts.dashboard_override_cleared_by_feedback) + self.assertEqual(("thread-1",), tuple(result.pending_actions)) + self.assertEqual( + "author", + result.pending_actions["thread-1"]["action"], + ) + self.assertEqual( + "2026-08-16T09:00:00Z", + result.pending_actions["thread-1"]["since"], + ) + self.assertEqual( + ["This still needs the requested update."], + [ + comment.body + for comment in classifier.reviewer_feedback_requests[0] + .discussions[0] + .comments + ], + ) + self.assertEqual( + ["Please update this.", "This still needs the requested update."], + [ + comment.body + for comment in classifier.requests[0].review_threads[0].comments + ], + ) + @patch("pull_request_evaluation.fetch_pull_request_source") def test_actionable_review_after_override_ends_handoff( self, fetch_raw: Mock diff --git a/.github/scripts/pull-request-dashboard/test_dashboard_contracts.py b/.github/scripts/pull-request-dashboard/test_dashboard_contracts.py index 7ec9c672098..1f0bd46a156 100644 --- a/.github/scripts/pull-request-dashboard/test_dashboard_contracts.py +++ b/.github/scripts/pull-request-dashboard/test_dashboard_contracts.py @@ -32,6 +32,29 @@ def test_routed_reply_rejects_failure_routes(self) -> None: ): DashboardCommandReply(1, "routed", route=route) + def test_non_routed_reply_rejects_persistent_handoff(self) -> None: + with self.assertRaisesRegex( + ValueError, + "only routed replies may persist a reviewer handoff", + ): + DashboardCommandReply( + 1, + "unauthorized", + persistent_handoff=True, + ) + + def test_persistent_handoff_reply_rejects_author_route(self) -> None: + with self.assertRaisesRegex( + ValueError, + "persistent reviewer handoff replies require a reviewer route", + ): + DashboardCommandReply( + 1, + "routed", + route=DashboardRoute.AUTHOR, + persistent_handoff=True, + ) + class EvaluationResultContractTest(unittest.TestCase): def test_success_rejects_failure_routes(self) -> None: diff --git a/.github/scripts/pull-request-dashboard/test_dashboard_override.py b/.github/scripts/pull-request-dashboard/test_dashboard_override.py index 441d7299bac..391ca622c1f 100644 --- a/.github/scripts/pull-request-dashboard/test_dashboard_override.py +++ b/.github/scripts/pull-request-dashboard/test_dashboard_override.py @@ -35,6 +35,7 @@ def result_facts( "dashboard_top_level_feedback_cutoff": ( override.top_level_feedback_cutoff ), + "dashboard_override_persistent": override.persistent_handoff, "dashboard_override_cleared_by_feedback": override.cleared_by_feedback, "dashboard_command_replies": override.command_replies, } @@ -47,7 +48,7 @@ def test_override_guidance_matches_pre_review_route(self) -> None: guidance = dashboard_override.author_override_guidance() self.assertIn("waiting on the author to waiting on reviewers", guidance) - self.assertIn("the head it sees when it reads the command", guidance) + self.assertIn("remains active across pushes", guidance) self.assertNotIn("immediately", guidance) def test_dashboard_command_body_remainder(self) -> None: @@ -392,6 +393,7 @@ def test_ack_marker_records_the_bound_head(self) -> None: head_sha="abcdef123456", route=DashboardRoute.APPROVER, since="2026-08-16T08:00:00Z", + persistent_handoff=True, ) ) @@ -400,6 +402,16 @@ def test_ack_marker_records_the_bound_head(self) -> None: "7:abcdef123456:2026-08-16T08:00:00Z -->", body, ) + self.assertIn( + "", + body, + ) + self.assertIn( + "The handoff remains active across pushes until newer actionable " + "human feedback arrives.", + body, + ) def test_pending_command_binds_to_the_head_this_pass_observed(self) -> None: source = override_input( @@ -415,6 +427,44 @@ def test_pending_command_binds_to_the_head_this_pass_observed(self) -> None: self.assertEqual(5, facts.command_id) self.assertEqual("current-head", facts.head_sha) + self.assertTrue(facts.persistent_handoff) + + def test_persistent_marker_restores_handoff_after_push(self) -> None: + source = override_input(issue_comment( + database_id=9, + actor=actor("opentelemetry-pr-dashboard[bot]"), + body="\n".join([ + dashboard_override.override_ack_marker(5, "bound-head"), + dashboard_override.persistent_handoff_marker(5, "bound-head"), + ]), + )) + + facts = dashboard_override.dashboard_override_facts( + source, + "author", + None, + "later-head", + ) + + self.assertEqual(5, facts.bound_command_id) + self.assertEqual("bound-head", facts.head_sha) + self.assertTrue(facts.persistent_handoff) + + def test_legacy_acknowledgement_remains_head_bound(self) -> None: + source = override_input(issue_comment( + database_id=9, + actor=actor("opentelemetry-pr-dashboard[bot]"), + body=dashboard_override.override_ack_marker(5, "bound-head"), + )) + + facts = dashboard_override.dashboard_override_facts( + source, + "author", + None, + "later-head", + ) + + self.assertFalse(facts.persistent_handoff) def test_pending_command_keeps_its_first_observed_head(self) -> None: source = override_input( @@ -864,6 +914,32 @@ def test_forged_acknowledgement_does_not_bind_a_head(self) -> None: self.assertEqual(5, facts.command_id) self.assertEqual("current-head", facts.head_sha) + def test_forged_marker_does_not_persist_handoff(self) -> None: + source = override_input( + issue_comment( + database_id=9, + actor=actor("opentelemetry-pr-dashboard[bot]"), + body=dashboard_override.override_ack_marker(5, "bound-head"), + ), + issue_comment( + database_id=10, + actor=actor("outsider"), + body=dashboard_override.persistent_handoff_marker( + 5, + "bound-head", + ), + ), + ) + + facts = dashboard_override.dashboard_override_facts( + source, + "author", + None, + "later-head", + ) + + self.assertFalse(facts.persistent_handoff) + def test_appends_routed_reply_for_break_glass_command_that_cleared_nothing(self) -> None: facts = dashboard_facts( author="author", @@ -1275,6 +1351,7 @@ def test_rebuilds_unacknowledged_reply_across_refreshes(self) -> None: route=DashboardRoute.APPROVER, since="2026-08-16T07:00:00Z", top_level_feedback_cutoff="2026-08-16T07:00:00Z", + persistent_handoff=True, ), ), facts.dashboard_command_replies, @@ -1334,7 +1411,7 @@ def test_command_that_cleared_nothing_is_acknowledged_where_it_is_routed(self) - facts = dashboard_override.append_command_ack_reply( source, result_facts(override, author="author"), - DashboardRoute.AUTHOR, + DashboardRoute.APPROVER, ) self.assertEqual( @@ -1344,9 +1421,10 @@ def test_command_that_cleared_nothing_is_acknowledged_where_it_is_routed(self) - "routed", "author", head_sha="current-head", - route=DashboardRoute.AUTHOR, + route=DashboardRoute.APPROVER, since="2026-08-16T07:00:00Z", top_level_feedback_cutoff="2026-08-16T07:00:00Z", + persistent_handoff=True, ), ), facts.dashboard_command_replies, @@ -1379,6 +1457,7 @@ def test_conflict_does_not_defer_override_acknowledgement(self) -> None: route=DashboardRoute.APPROVER, since="2026-08-16T07:00:00Z", top_level_feedback_cutoff="2026-08-16T07:00:00Z", + persistent_handoff=True, ), ), facts.dashboard_command_replies, diff --git a/.github/scripts/pull-request-dashboard/test_routing_decision.py b/.github/scripts/pull-request-dashboard/test_routing_decision.py index 6361ad18b4b..ad44b239c7e 100644 --- a/.github/scripts/pull-request-dashboard/test_routing_decision.py +++ b/.github/scripts/pull-request-dashboard/test_routing_decision.py @@ -286,7 +286,7 @@ def test_required_check_failure_routes_human_authored_pr_to_author(self) -> None self.assertEqual("2026-07-17T01:00:00+00:00", outcome.facts.waiting_since) self.assertEqual("ci_failure", outcome.facts.waiting_age_basis) - def test_reviewer_handoff_is_bound_to_the_current_head(self) -> None: + def test_reviewer_handoff_supports_persistent_and_legacy_bindings(self) -> None: self.assertTrue( reviewer_handoff_active( dashboard_facts( @@ -304,6 +304,15 @@ def test_reviewer_handoff_is_bound_to_the_current_head(self) -> None: ) ) ) + self.assertTrue( + reviewer_handoff_active( + dashboard_facts( + dashboard_override_head_sha="old-head", + dashboard_override_persistent=True, + head_sha="new-head", + ) + ) + ) for facts in ( {"dashboard_override_head_sha": "old-head", "head_sha": "new-head"}, {"dashboard_override_head_sha": "", "head_sha": "current-head"}, @@ -314,6 +323,17 @@ def test_reviewer_handoff_is_bound_to_the_current_head(self) -> None: "head_sha": "current-head", "dashboard_override_cleared_by_feedback": True, }, + { + "dashboard_override_head_sha": "old-head", + "dashboard_override_persistent": True, + "head_sha": "new-head", + "dashboard_override_cleared_by_feedback": True, + }, + { + "dashboard_override_head_sha": "old-head", + "dashboard_override_persistent": True, + "head_sha": "", + }, ): with self.subTest(facts=facts): self.assertFalse( diff --git a/.github/scripts/pull-request-dashboard/test_state.py b/.github/scripts/pull-request-dashboard/test_state.py index e741b2d1c69..cd876262828 100644 --- a/.github/scripts/pull-request-dashboard/test_state.py +++ b/.github/scripts/pull-request-dashboard/test_state.py @@ -251,10 +251,57 @@ def test_version_thirteen_state_does_not_invent_durable_cutoff(self) -> None: facts = loaded.results[0].facts self.assertEqual("2026-08-16T08:00:00Z", facts.dashboard_override_since) self.assertEqual("", facts.dashboard_top_level_feedback_cutoff) + self.assertFalse(facts.dashboard_override_persistent) self.assertEqual( "", facts.dashboard_command_replies[0].top_level_feedback_cutoff, ) + self.assertFalse( + facts.dashboard_command_replies[0].persistent_handoff + ) + + def test_version_sixteen_state_does_not_invent_persistent_handoff(self) -> None: + legacy_facts = dashboard_facts( + dashboard_override_head_sha="bound-head", + dashboard_top_level_feedback_cutoff="2026-08-16T08:00:00Z", + dashboard_command_replies=( + DashboardCommandReply( + 91, + "routed", + "alice", + head_sha="bound-head", + route=DashboardRoute.APPROVER, + top_level_feedback_cutoff="2026-08-16T08:00:00Z", + ), + ), + ) + stored = encode_dashboard_state(dashboard_state( + stored_dashboard_result(facts=legacy_facts) + )) + stored["version"] = 16 + + with tempfile.TemporaryDirectory() as temp_dir, patch( + "state._state_dir", + Path(temp_dir), + ): + dashboard_state_path().write_text( + json.dumps(stored), + encoding="utf-8", + ) + + loaded = load_dashboard_state_cache() + + self.assertIsNotNone(loaded) + assert loaded is not None + facts = loaded.results[0].facts + self.assertEqual( + "2026-08-16T08:00:00Z", + facts.dashboard_top_level_feedback_cutoff, + ) + self.assertFalse(facts.dashboard_override_persistent) + self.assertFalse( + facts.dashboard_command_replies[0].persistent_handoff + ) def test_dashboard_facts_codec_round_trip(self) -> None: facts = dashboard_facts( @@ -269,6 +316,7 @@ def test_dashboard_facts_codec_round_trip(self) -> None: dashboard_override_head_sha="current-head", dashboard_override_since="2026-08-16T08:00:00Z", dashboard_top_level_feedback_cutoff="2026-08-16T08:00:00Z", + dashboard_override_persistent=True, dashboard_override_cleared_by_feedback=True, dashboard_command_replies=( DashboardCommandReply( @@ -280,6 +328,7 @@ def test_dashboard_facts_codec_round_trip(self) -> None: held_gates="the required checks", since="2026-08-16T08:00:00Z", top_level_feedback_cutoff="2026-08-16T08:00:00Z", + persistent_handoff=True, ), DashboardCommandReply( 91, @@ -389,6 +438,10 @@ def test_dashboard_facts_rejects_null_non_optional_fields(self) -> None: ({"author": None}, "facts.author must be a string"), ({"assignees": None}, "facts.assignees must be an array of strings"), ({"is_draft": None}, "facts.is_draft must be a boolean"), + ( + {"dashboard_override_persistent": None}, + "facts.dashboard_override_persistent must be a boolean", + ), ({"approval_count": None}, "facts.approval_count must be an integer"), ( {"dashboard_command_replies": None}, @@ -408,6 +461,18 @@ def test_dashboard_facts_rejects_null_non_optional_fields(self) -> None: }, "facts.dashboard_command_replies.comment_id must be an integer", ), + ( + { + "dashboard_command_replies": [{ + "comment_id": 1, + "kind": "routed", + "route": "approver", + "persistent_handoff": None, + }] + }, + "facts.dashboard_command_replies.persistent_handoff " + "must be a boolean", + ), ) for facts, message in cases: @@ -605,8 +670,8 @@ def test_version_eleven_dashboard_state_migrates_to_current_shape(self) -> None: def test_notification_state_version_is_independent(self) -> None: self.assertEqual(BACKFILL_STATE_VERSION, 3) self.assertEqual(NOTIFICATION_STATE_VERSION, 3) - self.assertEqual(DASHBOARD_STATE_VERSION, 16) - self.assertEqual(DASHBOARD_STATE_COMPATIBLE_VERSIONS, (11, 12, 13)) + self.assertEqual(DASHBOARD_STATE_VERSION, 17) + self.assertEqual(DASHBOARD_STATE_COMPATIBLE_VERSIONS, (11, 12, 13, 16)) self.assertEqual(STATUS_COMMENT_ROLLOUT_STATE_VERSION, 2) self.assertEqual(AUTHOR_NUDGE_STATE_VERSION, 3) self.assertEqual(COPILOT_REVIEW_REQUEST_STATE_VERSION, 6) @@ -942,21 +1007,26 @@ def test_new_delivery_version_rejects_older_workers(self) -> None: self.assertFalse(claim_delivery_versions()) self.assertEqual(newer, load_delivery_versions()) - def test_cutoff_state_version_blocks_pre_marker_delivery_worker(self) -> None: + def test_lifecycle_state_version_blocks_incompatible_delivery_workers(self) -> None: current = current_delivery_versions() - old_worker = {**current, "DASHBOARD_STATE_VERSION": 13} - with tempfile.TemporaryDirectory() as temp_dir, patch( - "state._state_dir", - Path(temp_dir), - ): - with patch("state.current_delivery_versions", return_value=old_worker): - self.assertTrue(claim_delivery_versions()) + for old_version in (13, 16): + old_worker = {**current, "DASHBOARD_STATE_VERSION": old_version} + with self.subTest(old_version=old_version), tempfile.TemporaryDirectory() as temp_dir: + with patch("state._state_dir", Path(temp_dir)): + with patch( + "state.current_delivery_versions", + return_value=old_worker, + ): + self.assertTrue(claim_delivery_versions()) - self.assertTrue(claim_delivery_versions()) + self.assertTrue(claim_delivery_versions()) - with patch("state.current_delivery_versions", return_value=old_worker): - self.assertFalse(claim_delivery_versions()) - self.assertEqual(current, load_delivery_versions()) + with patch( + "state.current_delivery_versions", + return_value=old_worker, + ): + self.assertFalse(claim_delivery_versions()) + self.assertEqual(current, load_delivery_versions()) def test_delivery_versions_fail_closed(self) -> None: malformed_versions = [ From 38fdac8c5c0d5991cb7bd0c988788c45c2202080 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Wed, 2 Sep 2026 15:53:58 -0700 Subject: [PATCH 21/30] Require heads for persistent handoffs Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 378be2b9-85df-4ac8-a125-a99b653412ac --- .../dashboard_override.py | 15 +++-- .../pull-request-dashboard/test_dashboard.py | 60 +++++++++++++++++++ 2 files changed, 69 insertions(+), 6 deletions(-) diff --git a/.github/scripts/pull-request-dashboard/dashboard_override.py b/.github/scripts/pull-request-dashboard/dashboard_override.py index c66eba039cc..f08170c61ab 100644 --- a/.github/scripts/pull-request-dashboard/dashboard_override.py +++ b/.github/scripts/pull-request-dashboard/dashboard_override.py @@ -252,12 +252,15 @@ def dashboard_override_facts( acknowledged_top_level_feedback_cutoff(source.issue_comments), ) persistent_handoff = bool( - command_id - or (previous_binding_matches and previous_persistent_handoff) - or acknowledges_persistent_handoff( - source.issue_comments, - bound_command_id, - bound_head, + bound_head + and ( + command_id + or (previous_binding_matches and previous_persistent_handoff) + or acknowledges_persistent_handoff( + source.issue_comments, + bound_command_id, + bound_head, + ) ) ) cleared_command_id, cleared_head = status_reviewer_handoff_clearance( diff --git a/.github/scripts/pull-request-dashboard/test_dashboard.py b/.github/scripts/pull-request-dashboard/test_dashboard.py index ead7ad52631..4fdcf85080c 100644 --- a/.github/scripts/pull-request-dashboard/test_dashboard.py +++ b/.github/scripts/pull-request-dashboard/test_dashboard.py @@ -635,6 +635,66 @@ def test_override_binds_to_the_observed_head_before_classification( ) self.assertEqual(classifier.requests, []) + @patch("pull_request_evaluation.fetch_pull_request_source") + def test_override_without_head_uses_non_persistent_acknowledgement( + self, + fetch_raw: Mock, + ) -> None: + fetch_raw.return_value = pull_request_source( + pull_request=pull_request_metadata(head_sha=""), + issue_comments=( + issue_comment( + database_id=102, + body="/dashboard route:reviewers", + created_at="2026-08-16T08:00:00Z", + ), + ), + review_threads=( + review_thread( + node_id="thread-1", + comments=( + review_thread_comment( + body="Please update this.", + created_at="2026-08-16T09:00:00Z", + ), + ), + ), + ), + ) + classification = action_classification( + "thread-1", + DiscussionKind.REVIEW_THREAD, + DiscussionAction.AUTHOR, + "The reviewer requested a change.", + ) + classifier = FakeClassificationOperation( + DiscussionClassifications((classification,), (), ()) + ) + + result = evaluate_pr( + {"number": 7}, + classification_service=classifier, + ) + + self.assertIsInstance(result, EvaluationSuccess) + assert isinstance(result, EvaluationSuccess) + self.assertEqual(DashboardRoute.AUTHOR, result.route) + self.assertEqual("", result.facts.dashboard_override_head_sha) + self.assertFalse(result.facts.dashboard_override_persistent) + self.assertEqual( + ( + DashboardCommandReply( + 102, + "routed", + "author", + route=DashboardRoute.AUTHOR, + since="2026-08-16T08:00:00Z", + top_level_feedback_cutoff="2026-08-16T08:00:00Z", + ), + ), + result.facts.dashboard_command_replies, + ) + @patch("pull_request_evaluation.fetch_pull_request_source") def test_actionable_feedback_after_push_ends_persistent_handoff( self, From 75cdd8ed9aade41191f4583abaee99637c640f95 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Wed, 2 Sep 2026 16:10:05 -0700 Subject: [PATCH 22/30] Use thread activity for lifecycle routing Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 378be2b9-85df-4ac8-a125-a99b653412ac --- .../pull-request-dashboard/RATIONALE.md | 23 ++-- .../classification_policy.py | 41 ++++++- .../discussion_lifecycle.py | 53 +++++---- .../pull-request-dashboard/test_dashboard.py | 109 +++++++++++++++--- 4 files changed, 167 insertions(+), 59 deletions(-) diff --git a/.github/scripts/pull-request-dashboard/RATIONALE.md b/.github/scripts/pull-request-dashboard/RATIONALE.md index 4a7c27a1eb7..fb052668c7e 100644 --- a/.github/scripts/pull-request-dashboard/RATIONALE.md +++ b/.github/scripts/pull-request-dashboard/RATIONALE.md @@ -485,11 +485,12 @@ the implementation understandable and operationally cheap. blocker can prevent the handoff. Top-level feedback last changed at or before the command stays retired. Review threads are not retired by the command, but their existing work does not override the handoff. Actionable human reviewer - feedback with an effective content timestamp after the command ends the - handoff, because a reviewer has answered the request for help and assigned the - next action to the author. That includes a new reply or edit on an older - review thread. Praise, informational comments, bot feedback, and feedback last - changed at or before the command do not end it. + feedback on an unresolved, non-outdated thread with an effective content + timestamp after the command ends the handoff, because a reviewer has answered + the request for help and assigned the next action to the author. That includes + a new reply or edit on an older active review thread. Praise, informational + comments, bot feedback, inactive threads, and feedback last changed at or + before the command do not end it. - While a handoff is active, the dashboard classifies only human reviewer feedback with content activity after the command. Older discussions and classification failures therefore cannot block the break-glass route. Once @@ -559,12 +560,12 @@ the implementation understandable and operationally cheap. `actions/stale` reads, so no PR in a dashboard repository could go stale. The dashboard app is never a PR's author, so `role_for` always classifies its comments as `bot` and they never count. -- An inline review thread's wait age and list position come from its last - comment's `createdAt`, never its edit time. Wait age is what makes a neglected - thread visible, so a reviewer fixing a typo in their own comment must not make - a weeks-old thread look freshly raised. Top-level feedback items date from - their creation time for the same reason, so editing a comment cannot reorder - the list or reset how long an item has been waiting. +- An inline review thread's list position comes from its last comment's + `createdAt`, never its edit time. Effective content activity selects the + latest participant and requester and supplies the pending action timestamp, + so editing an older request can reactivate it without reordering the + classifier transcript or list. Top-level feedback items still date from their + creation time, so editing a comment cannot reorder that list. ## Top-Level Feedback diff --git a/.github/scripts/pull-request-dashboard/classification_policy.py b/.github/scripts/pull-request-dashboard/classification_policy.py index 29ee9862291..0ad5c8be155 100644 --- a/.github/scripts/pull-request-dashboard/classification_policy.py +++ b/.github/scripts/pull-request-dashboard/classification_policy.py @@ -7,11 +7,12 @@ import re from collections.abc import Mapping, Sequence from dataclasses import dataclass, field, replace +from datetime import datetime, timezone from enum import Enum from types import MappingProxyType from typing import Any -from utils import truncate +from utils import parse_ts, truncate DISCUSSION_COMMENT_BODY_MAX_CHARS = 500 @@ -19,6 +20,7 @@ TOP_LEVEL_CLASSIFICATION_BATCH_SIZE = 10 AUTHOR_COMMENT_DIAGNOSTIC_ITEM_LIMIT = 10 PRAISE_MAX_CHARS = 80 +_MIN_TIMESTAMP = datetime.min.replace(tzinfo=timezone.utc) class _PromptTooLongError(ValueError): @@ -364,6 +366,7 @@ class DiscussionComment: timestamp: str = "" actor_role: str = "" body: str = "" + activity_timestamp: str = "" @dataclass(frozen=True) @@ -409,6 +412,9 @@ def from_record(cls, record: Mapping[str, Any]) -> ClassificationDiscussion: timestamp=str(comment.get("timestamp") or ""), actor_role=str(comment.get("actor_role") or ""), body=str(comment.get("body") or ""), + activity_timestamp=str( + comment.get("activity_timestamp") or "" + ), ) for comment in _mapping_items(record.get("comments")) ) @@ -1580,6 +1586,27 @@ def _could_be_praise(discussion: ClassificationDiscussion) -> bool: ) +def _latest_review_thread_comment( + comments: Sequence[DiscussionComment], +) -> DiscussionComment | None: + if not comments: + return None + return max( + enumerate(comments), + key=lambda item: ( + ( + parse_ts( + item[1].activity_timestamp + or item[1].timestamp + ) + or _MIN_TIMESTAMP + ), + parse_ts(item[1].timestamp) or _MIN_TIMESTAMP, + item[0], + ), + )[1] + + def resolve_review_thread_policy( discussions: Sequence[ClassificationDiscussion], praise_results: Mapping[str, ClassificationResult], @@ -1607,8 +1634,12 @@ def resolve_review_thread_policy( dropped = discussion_id in ignored if dropped: comments.pop() - if comments: - since_by_id[discussion_id] = comments[-1].timestamp + latest_comment = _latest_review_thread_comment(comments) + if latest_comment is not None: + since_by_id[discussion_id] = ( + latest_comment.activity_timestamp + or latest_comment.timestamp + ) if dropped and not comments: resolved[discussion_id] = ClassificationSuccess( discussion.identity, @@ -1617,14 +1648,14 @@ def resolve_review_thread_policy( "This thread is only praise.", ), ) - elif comments and comments[-1].actor_role == "author": + elif latest_comment is not None and latest_comment.actor_role == "author": author_replies.append(discussion.with_comments(comments)) else: resolved[discussion_id] = ClassificationSuccess( discussion.identity, ActionDecision( DiscussionAction.AUTHOR, - "The last comment on this unresolved thread is not the author's.", + "The latest activity on this unresolved thread is not the author's.", ), ) resolved = { diff --git a/.github/scripts/pull-request-dashboard/discussion_lifecycle.py b/.github/scripts/pull-request-dashboard/discussion_lifecycle.py index f41fd0d1f36..09493aa0d22 100644 --- a/.github/scripts/pull-request-dashboard/discussion_lifecycle.py +++ b/.github/scripts/pull-request-dashboard/discussion_lifecycle.py @@ -141,6 +141,26 @@ def _add_discussion_facts( return discussion +def _latest_activity_comment( + comments: list[dict[str, Any]], +) -> dict[str, Any]: + return max( + enumerate(comments), + key=lambda item: ( + ( + parse_ts( + item[1].get("activity_timestamp") + or item[1].get("timestamp") + or "" + ) + or _MIN_TIMESTAMP + ), + parse_ts(item[1].get("timestamp") or "") or _MIN_TIMESTAMP, + item[0], + ), + )[1] + + def _positive_reaction_logins( comment: ReviewThreadComment, ) -> set[str]: @@ -155,17 +175,11 @@ def _positive_reaction_logins( return logins -def _group_review_threads( - source: DiscussionInput, - *, - include_inactive: bool = False, -) -> list[dict[str, Any]]: +def _group_review_threads(source: DiscussionInput) -> list[dict[str, Any]]: discussions: list[dict[str, Any]] = [] reviewers = set(source.reviewers) for discussion in source.review_threads: - if ( - discussion.is_resolved or discussion.is_outdated - ) and not include_inactive: + if discussion.is_resolved or discussion.is_outdated: continue raw_comments = discussion.comments thread_url = raw_comments[0].url if raw_comments else "" @@ -187,6 +201,7 @@ def _group_review_threads( comment["actor_role"] == "author" for comment in comments ): continue + latest_activity_comment = _latest_activity_comment(comments) discussions.append( _add_discussion_facts( { @@ -199,10 +214,13 @@ def _group_review_threads( "line": discussion.line, "resolved": False, "discussion_url": thread_url, + "requester": latest_activity_comment.get("actor") or "", + "pr_author": source.author, "comments": comments, }, comments, source.conflicts, + latest_comment=latest_activity_comment, ) ) discussions.sort(key=lambda thread: thread["comments"][-1]["timestamp"]) @@ -413,22 +431,7 @@ def _filter_handoff_feedback( ] if not comments: return None - latest_activity_comment = max( - enumerate(comments), - key=lambda item: ( - ( - parse_ts( - item[1].get("activity_timestamp") - or item[1].get("timestamp") - or "" - ) - or _MIN_TIMESTAMP - ), - parse_ts(item[1].get("timestamp") or "") - or _MIN_TIMESTAMP, - item[0], - ), - )[1] + latest_activity_comment = _latest_activity_comment(comments) filtered = {**discussion, "comments": comments} filtered["requester"] = latest_activity_comment.get("actor") or "" filtered["pr_author"] = pr_author @@ -482,7 +485,7 @@ def prepare_reviewer_handoff_feedback( pr_author: str, ) -> PreparedDiscussions: prepared = PreparedDiscussions( - tuple(_group_review_threads(source, include_inactive=True)), + tuple(_group_review_threads(source)), tuple(_derive_top_level_items(source)), (), ) diff --git a/.github/scripts/pull-request-dashboard/test_dashboard.py b/.github/scripts/pull-request-dashboard/test_dashboard.py index 4fdcf85080c..7d982bcfed7 100644 --- a/.github/scripts/pull-request-dashboard/test_dashboard.py +++ b/.github/scripts/pull-request-dashboard/test_dashboard.py @@ -53,6 +53,7 @@ review_thread_comment, stored_dashboard_result, ) +from classification_execution import ClassificationExecutionRequest from classification_policy import ( ActionDecision, AuthorCommentDecision, @@ -64,6 +65,7 @@ DiscussionIdentity, DiscussionKind, FeedbackOutcome, + resolve_review_thread_policy, ) from classification_test_support import FakeClassificationOperation from pull_request_source import ( @@ -166,6 +168,16 @@ def evaluate_pr( ) +class ReviewThreadPolicyOperation(FakeClassificationOperation): + def classify( + self, + request: ClassificationExecutionRequest, + ) -> DiscussionClassifications: + self.requests.append(request) + plan = resolve_review_thread_policy(request.review_threads, {}) + return DiscussionClassifications(plan.resolved, (), ()) + + class AuthorNudgeEpisodeTest(unittest.TestCase): def test_preserves_episode_while_route_remains_author(self) -> None: facts = _assign_author_nudge_episode( @@ -1351,17 +1363,11 @@ def test_informational_inline_feedback_keeps_handoff( ) @patch("pull_request_evaluation.fetch_pull_request_source") - def test_inactive_inline_feedback_ends_handoff( + def test_inactive_edited_inline_feedback_keeps_handoff( self, fetch_raw: Mock ) -> None: - classification = action_classification( - "thread-1", - DiscussionKind.REVIEW_THREAD, - DiscussionAction.AUTHOR, - "The reviewer requested a change.", - ) classifier = FakeClassificationOperation( - reviewer_feedback_result=(classification,) + error=AssertionError("inactive threads must not be classified") ) for state in ({"is_resolved": True}, {"is_outdated": True}): with self.subTest(state=state): @@ -1377,7 +1383,8 @@ def test_inactive_inline_feedback_ends_handoff( **state, comments=(review_thread_comment( body="Please update this.", - created_at="2026-08-16T09:00:00Z", + created_at="2026-08-16T07:30:00Z", + updated_at="2026-08-16T09:00:00Z", ),), ),), ) @@ -1389,18 +1396,84 @@ def test_inactive_inline_feedback_ends_handoff( self.assertIsNotNone(result) assert result is not None - self.assertTrue( + self.assertIsInstance(result, EvaluationSuccess) + assert isinstance(result, EvaluationSuccess) + self.assertEqual(DashboardRoute.APPROVER, result.route) + self.assertFalse( result.facts.dashboard_override_cleared_by_feedback ) - request = classifier.reviewer_feedback_requests[-1] - self.assertEqual( - ["Please update this."], - [comment.body for comment in request.discussions[0].comments], - ) - self.assertEqual((), classifier.requests[-1].review_threads) + self.assertEqual({}, result.pending_actions) + + self.assertEqual([], classifier.reviewer_feedback_requests) + self.assertEqual([], classifier.requests) + + @patch("pull_request_evaluation.fetch_pull_request_source") + def test_edited_reviewer_request_after_author_reply_clears_to_author( + self, + fetch_raw: Mock, + ) -> None: + fetch_raw.return_value = pull_request_source( + pull_request=pull_request_metadata(title="Routing integration"), + issue_comments=(issue_comment( + database_id=102, + body="/dashboard route:reviewers", + created_at="2026-08-16T08:00:00Z", + ),), + review_threads=(review_thread( + node_id="thread-1", + comments=( + review_thread_comment( + actor=actor("root-reviewer"), + body="Please update the implementation and tests.", + created_at="2026-08-16T07:00:00Z", + updated_at="2026-08-16T09:00:00Z", + ), + review_thread_comment( + actor=actor("author"), + body="Handled in the latest commit.", + created_at="2026-08-16T07:30:00Z", + ), + ), + ),), + ) + classification = action_classification( + "thread-1", + DiscussionKind.REVIEW_THREAD, + DiscussionAction.AUTHOR, + "The edited request needs author action.", + ) + classifier = ReviewThreadPolicyOperation( + reviewer_feedback_result=(classification,) + ) + + result = evaluate_pr( + {"number": 7}, + classification_service=classifier, + ) - self.assertEqual(2, len(classifier.reviewer_feedback_requests)) - self.assertEqual(2, len(classifier.requests)) + self.assertIsInstance(result, EvaluationSuccess) + assert isinstance(result, EvaluationSuccess) + self.assertEqual(DashboardRoute.AUTHOR, result.route) + self.assertTrue(result.facts.dashboard_override_cleared_by_feedback) + self.assertEqual( + { + "thread-1": { + "action": "author", + "since": "2026-08-16T09:00:00Z", + }, + }, + result.pending_actions, + ) + self.assertEqual(1, len(classifier.requests)) + thread = classifier.requests[0].review_threads[0] + self.assertEqual("root-reviewer", thread.requester) + self.assertEqual( + [ + "Please update the implementation and tests.", + "Handled in the latest commit.", + ], + [comment.body for comment in thread.comments], + ) @patch("pull_request_evaluation.fetch_pull_request_source") def test_author_reply_does_not_reactivate_cleared_handoff( From 86355ef0d8439244f30ad2351cb3fe0a2f4f93da Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Wed, 2 Sep 2026 16:19:24 -0700 Subject: [PATCH 23/30] Use effective thread comment identity Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 378be2b9-85df-4ac8-a125-a99b653412ac --- .../pull-request-dashboard/RATIONALE.md | 4 +- .../classification_execution.py | 9 +- .../classification_policy.py | 96 ++++++++++++++----- .../test_classification_execution.py | 69 +++++++++++++ 4 files changed, 149 insertions(+), 29 deletions(-) diff --git a/.github/scripts/pull-request-dashboard/RATIONALE.md b/.github/scripts/pull-request-dashboard/RATIONALE.md index fb052668c7e..c99f11614a2 100644 --- a/.github/scripts/pull-request-dashboard/RATIONALE.md +++ b/.github/scripts/pull-request-dashboard/RATIONALE.md @@ -564,7 +564,9 @@ the implementation understandable and operationally cheap. `createdAt`, never its edit time. Effective content activity selects the latest participant and requester and supplies the pending action timestamp, so editing an older request can reactivate it without reordering the - classifier transcript or list. Top-level feedback items still date from their + classifier transcript or list. The same selected comment supplies praise and + author-reply classification, and removing praise leaves the remaining + conversation order intact. Top-level feedback items still date from their creation time, so editing a comment cannot reorder that list. ## Top-Level Feedback diff --git a/.github/scripts/pull-request-dashboard/classification_execution.py b/.github/scripts/pull-request-dashboard/classification_execution.py index ec69743838c..499a3e64436 100644 --- a/.github/scripts/pull-request-dashboard/classification_execution.py +++ b/.github/scripts/pull-request-dashboard/classification_execution.py @@ -719,9 +719,12 @@ def _classify_review_threads( by_id[discussion_id] = with_result_metadata( result, since=( - discussion.comments[-1].timestamp - if discussion.comments - else "" + discussion.selected_activity_timestamp + or ( + discussion.comments[-1].timestamp + if discussion.comments + else "" + ) ), ignored_last_comment=(discussion_id in ignored), ) diff --git a/.github/scripts/pull-request-dashboard/classification_policy.py b/.github/scripts/pull-request-dashboard/classification_policy.py index 0ad5c8be155..3fd4a9d6bbc 100644 --- a/.github/scripts/pull-request-dashboard/classification_policy.py +++ b/.github/scripts/pull-request-dashboard/classification_policy.py @@ -368,6 +368,16 @@ class DiscussionComment: body: str = "" activity_timestamp: str = "" + @property + def effective_activity_timestamp(self) -> str: + timestamp = parse_ts(self.timestamp) + activity_timestamp = parse_ts(self.activity_timestamp) + if activity_timestamp is not None and ( + timestamp is None or activity_timestamp >= timestamp + ): + return self.activity_timestamp + return self.timestamp if timestamp is not None else "" + @dataclass(frozen=True) class CandidateFeedback: @@ -383,6 +393,8 @@ class ClassificationDiscussion: pr_author: str = "" source_kind: str = "" candidate_feedback: tuple[CandidateFeedback, ...] = () + selected_comment_index: int | None = None + selected_activity_timestamp: str = "" def __post_init__(self) -> None: object.__setattr__(self, "comments", tuple(self.comments)) @@ -437,8 +449,16 @@ def from_record(cls, record: Mapping[str, Any]) -> ClassificationDiscussion: def with_comments( self, comments: Sequence[DiscussionComment], + *, + selected_comment_index: int | None = None, + selected_activity_timestamp: str = "", ) -> ClassificationDiscussion: - return replace(self, comments=tuple(comments)) + return replace( + self, + comments=tuple(comments), + selected_comment_index=selected_comment_index, + selected_activity_timestamp=selected_activity_timestamp, + ) @dataclass(frozen=True) @@ -808,10 +828,17 @@ def review_thread_author_reply_input( discussion: ClassificationDiscussion, ) -> dict[str, Any]: body = "" - for comment in reversed(discussion.comments): - if comment.actor_role == "author": - body = comment.body - break + selected_index = discussion.selected_comment_index + if ( + selected_index is not None + and 0 <= selected_index < len(discussion.comments) + ): + body = discussion.comments[selected_index].body + else: + for comment in reversed(discussion.comments): + if comment.actor_role == "author": + body = comment.body + break return { "discussion_id": discussion.identity.discussion_id, "body": body, @@ -821,9 +848,10 @@ def review_thread_author_reply_input( def praise_prompt_input( discussion: ClassificationDiscussion, ) -> dict[str, Any]: + selected = _latest_review_thread_comment(discussion.comments) return { "discussion_id": discussion.identity.discussion_id, - "body": discussion.comments[-1].body if discussion.comments else "", + "body": selected.comment.body if selected is not None else "", } @@ -1576,35 +1604,43 @@ def prepare_praise_candidates( def _could_be_praise(discussion: ClassificationDiscussion) -> bool: - comments = discussion.comments - role = comments[-1].actor_role if comments else "" - if not comments or role in ("author", "bot"): + selected = _latest_review_thread_comment(discussion.comments) + if selected is None or selected.comment.actor_role in ("author", "bot"): return False return ( - len(" ".join(comments[-1].body.split())) + len(" ".join(selected.comment.body.split())) <= PRAISE_MAX_CHARS ) +@dataclass(frozen=True) +class _ReviewThreadCommentSelection: + index: int + comment: DiscussionComment + activity_timestamp: str + + def _latest_review_thread_comment( comments: Sequence[DiscussionComment], -) -> DiscussionComment | None: +) -> _ReviewThreadCommentSelection | None: if not comments: return None - return max( + index, comment = max( enumerate(comments), key=lambda item: ( ( - parse_ts( - item[1].activity_timestamp - or item[1].timestamp - ) + parse_ts(item[1].effective_activity_timestamp) or _MIN_TIMESTAMP ), parse_ts(item[1].timestamp) or _MIN_TIMESTAMP, item[0], ), - )[1] + ) + return _ReviewThreadCommentSelection( + index, + comment, + comment.effective_activity_timestamp, + ) def resolve_review_thread_policy( @@ -1632,13 +1668,14 @@ def resolve_review_thread_policy( continue comments = list(discussion.comments) dropped = discussion_id in ignored - if dropped: - comments.pop() - latest_comment = _latest_review_thread_comment(comments) - if latest_comment is not None: + selected = _latest_review_thread_comment(comments) + if dropped and selected is not None: + comments.pop(selected.index) + selected = _latest_review_thread_comment(comments) + if selected is not None: since_by_id[discussion_id] = ( - latest_comment.activity_timestamp - or latest_comment.timestamp + selected.activity_timestamp + or selected.comment.timestamp ) if dropped and not comments: resolved[discussion_id] = ClassificationSuccess( @@ -1648,8 +1685,17 @@ def resolve_review_thread_policy( "This thread is only praise.", ), ) - elif latest_comment is not None and latest_comment.actor_role == "author": - author_replies.append(discussion.with_comments(comments)) + elif ( + selected is not None + and selected.comment.actor_role == "author" + ): + author_replies.append( + discussion.with_comments( + comments, + selected_comment_index=selected.index, + selected_activity_timestamp=selected.activity_timestamp, + ) + ) else: resolved[discussion_id] = ClassificationSuccess( discussion.identity, diff --git a/.github/scripts/pull-request-dashboard/test_classification_execution.py b/.github/scripts/pull-request-dashboard/test_classification_execution.py index 64e7a143635..0c6e6219907 100644 --- a/.github/scripts/pull-request-dashboard/test_classification_execution.py +++ b/.github/scripts/pull-request-dashboard/test_classification_execution.py @@ -1096,6 +1096,75 @@ def test_praise_after_completed_author_reply_hands_back_to_reviewer( self.assertEqual(result.since, "2026-03-12T00:00:00Z") self.assertTrue(result.ignored_last_comment) + def test_edited_older_praise_is_classified_and_removed_by_identity( + self, + ) -> None: + thread = self.thread( + ("approver", "Please fix this.", "2026-03-12T00:00:00Z"), + ("approver", "LGTM", "2026-04-12T00:00:00Z"), + ("author", "Fixed it.", "2026-05-20T00:00:00Z"), + ) + thread["comments"][1]["activity_timestamp"] = "2026-06-20T00:00:00Z" + + result, runner = self.classify( + thread, + responder=lambda request: successful_response( + request, + praise="praise", + author_reply="complete", + ), + ) + + self.assertEqual(len(runner.requests), 2) + self.assertEqual(prompt_items(runner.requests[0])[0]["body"], "LGTM") + self.assertEqual( + prompt_items(runner.requests[1])[0]["body"], + "Fixed it.", + ) + assert isinstance(result.decision, ActionDecision) + self.assertIs(result.decision.action, DiscussionAction.REVIEWER) + self.assertEqual(result.since, "2026-05-20T00:00:00Z") + self.assertTrue(result.ignored_last_comment) + + def test_edited_older_author_reply_supplies_body_and_result_time( + self, + ) -> None: + thread = self.thread( + ( + "approver", + "Please update the implementation and tests.", + "2026-03-12T00:00:00Z", + ), + ("author", "The complete fix is ready.", "2026-04-12T00:00:00Z"), + ("author", "One part remains.", "2026-05-20T00:00:00Z"), + ) + thread["comments"][1]["activity_timestamp"] = "2026-06-20T00:00:00Z" + + result, runner = self.classify( + thread, + responder=lambda request: successful_response( + request, + author_reply="deferral", + ), + ) + + self.assertEqual(len(runner.requests), 1) + self.assertEqual( + prompt_items(runner.requests[0])[0]["body"], + "The complete fix is ready.", + ) + assert isinstance(result.decision, ActionDecision) + self.assertIs(result.decision.action, DiscussionAction.AUTHOR) + self.assertEqual(result.since, "2026-06-20T00:00:00Z") + self.assertEqual( + [comment["body"] for comment in thread["comments"]], + [ + "Please update the implementation and tests.", + "The complete fix is ready.", + "One part remains.", + ], + ) + def test_failed_praise_and_author_reply_calls_fail_safe_to_author( self, ) -> None: From 4de33c3a38620c926aaa0fd57f9660c5619399bb Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Wed, 2 Sep 2026 16:32:16 -0700 Subject: [PATCH 24/30] Preserve ignored praise identity Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 378be2b9-85df-4ac8-a125-a99b653412ac --- .../pull-request-dashboard/RATIONALE.md | 6 ++ .../classification_execution.py | 1 + .../classification_policy.py | 23 +++++ .../discussion_lifecycle.py | 4 + .../pull-request-dashboard/reviewer_state.py | 14 ++- .../test_classification_execution.py | 27 ++++++ .../test_classification_policy.py | 6 ++ .../pull-request-dashboard/test_dashboard.py | 87 ++++++++++++++++++- 8 files changed, 165 insertions(+), 3 deletions(-) diff --git a/.github/scripts/pull-request-dashboard/RATIONALE.md b/.github/scripts/pull-request-dashboard/RATIONALE.md index c99f11614a2..59d6ddda85c 100644 --- a/.github/scripts/pull-request-dashboard/RATIONALE.md +++ b/.github/scripts/pull-request-dashboard/RATIONALE.md @@ -568,6 +568,12 @@ the implementation understandable and operationally cheap. author-reply classification, and removing praise leaves the remaining conversation order intact. Top-level feedback items still date from their creation time, so editing a comment cannot reorder that list. +- The ignored praise index is a current-evaluation projection used for reviewer + attribution. Classification caches retain the praise verdict but not its + position, so each refresh recomputes the index from current thread activity. + Pending actions are not persisted in dashboard state, which keeps state + version 17 valid. Legacy projections with only `ignored_last_comment` retain + creation-last behavior. ## Top-Level Feedback diff --git a/.github/scripts/pull-request-dashboard/classification_execution.py b/.github/scripts/pull-request-dashboard/classification_execution.py index 499a3e64436..3060c17f684 100644 --- a/.github/scripts/pull-request-dashboard/classification_execution.py +++ b/.github/scripts/pull-request-dashboard/classification_execution.py @@ -727,6 +727,7 @@ def _classify_review_threads( ) ), ignored_last_comment=(discussion_id in ignored), + ignored_comment_index=discussion.ignored_comment_index, ) return by_id diff --git a/.github/scripts/pull-request-dashboard/classification_policy.py b/.github/scripts/pull-request-dashboard/classification_policy.py index 3fd4a9d6bbc..c532f022589 100644 --- a/.github/scripts/pull-request-dashboard/classification_policy.py +++ b/.github/scripts/pull-request-dashboard/classification_policy.py @@ -395,6 +395,7 @@ class ClassificationDiscussion: candidate_feedback: tuple[CandidateFeedback, ...] = () selected_comment_index: int | None = None selected_activity_timestamp: str = "" + ignored_comment_index: int | None = None def __post_init__(self) -> None: object.__setattr__(self, "comments", tuple(self.comments)) @@ -452,12 +453,14 @@ def with_comments( *, selected_comment_index: int | None = None, selected_activity_timestamp: str = "", + ignored_comment_index: int | None = None, ) -> ClassificationDiscussion: return replace( self, comments=tuple(comments), selected_comment_index=selected_comment_index, selected_activity_timestamp=selected_activity_timestamp, + ignored_comment_index=ignored_comment_index, ) @@ -510,6 +513,7 @@ class ClassificationSuccess: cli_call: bool = False since: str = "" ignored_last_comment: bool = False + ignored_comment_index: int | None = None @property def failed(self) -> bool: @@ -528,6 +532,7 @@ class ClassificationFailure: cli_call: bool = False since: str = "" ignored_last_comment: bool = False + ignored_comment_index: int | None = None @property def failed(self) -> bool: @@ -544,6 +549,7 @@ class ClassificationDeferred: decision: ClassificationDecision since: str = "" ignored_last_comment: bool = False + ignored_comment_index: int | None = None @property def failed(self) -> bool: @@ -1662,6 +1668,7 @@ def resolve_review_thread_policy( resolved: dict[str, ClassificationResult] = dict(failed_praise) author_replies: list[ClassificationDiscussion] = [] since_by_id: dict[str, str] = {} + ignored_comment_index_by_id: dict[str, int] = {} for discussion in discussions: discussion_id = discussion.identity.discussion_id if discussion_id in failed_praise: @@ -1670,6 +1677,7 @@ def resolve_review_thread_policy( dropped = discussion_id in ignored selected = _latest_review_thread_comment(comments) if dropped and selected is not None: + ignored_comment_index_by_id[discussion_id] = selected.index comments.pop(selected.index) selected = _latest_review_thread_comment(comments) if selected is not None: @@ -1694,6 +1702,9 @@ def resolve_review_thread_policy( comments, selected_comment_index=selected.index, selected_activity_timestamp=selected.activity_timestamp, + ignored_comment_index=( + ignored_comment_index_by_id.get(discussion_id) + ), ) ) else: @@ -1709,6 +1720,9 @@ def resolve_review_thread_policy( result, since=since_by_id.get(discussion_id, ""), ignored_last_comment=(discussion_id in ignored), + ignored_comment_index=ignored_comment_index_by_id.get( + discussion_id + ), ) for discussion_id, result in resolved.items() } @@ -1720,6 +1734,7 @@ def with_result_metadata( *, since: str = "", ignored_last_comment: bool = False, + ignored_comment_index: int | None = None, ) -> ClassificationResult: return replace( result, @@ -1727,6 +1742,11 @@ def with_result_metadata( ignored_last_comment=( ignored_last_comment or result.ignored_last_comment ), + ignored_comment_index=( + ignored_comment_index + if ignored_comment_index is not None + else result.ignored_comment_index + ), ) @@ -1819,6 +1839,8 @@ def classification_result_to_record( record["since"] = result.since if result.ignored_last_comment: record["ignored_last_comment"] = True + if result.ignored_comment_index is not None: + record["ignored_comment_index"] = result.ignored_comment_index return record @@ -1835,6 +1857,7 @@ def cached_classification_record( "response_text", "stderr", "usage", + "ignored_comment_index", ) } diff --git a/.github/scripts/pull-request-dashboard/discussion_lifecycle.py b/.github/scripts/pull-request-dashboard/discussion_lifecycle.py index 09493aa0d22..48d6dcd98ae 100644 --- a/.github/scripts/pull-request-dashboard/discussion_lifecycle.py +++ b/.github/scripts/pull-request-dashboard/discussion_lifecycle.py @@ -739,6 +739,10 @@ def _review_thread_pending_actions( } if classification.ignored_last_comment: entry["ignored_last_comment"] = True + if classification.ignored_comment_index is not None: + entry["ignored_comment_index"] = ( + classification.ignored_comment_index + ) pending_actions[discussion_id] = entry return pending_actions diff --git a/.github/scripts/pull-request-dashboard/reviewer_state.py b/.github/scripts/pull-request-dashboard/reviewer_state.py index adbac1a6599..4db14ced3f4 100644 --- a/.github/scripts/pull-request-dashboard/reviewer_state.py +++ b/.github/scripts/pull-request-dashboard/reviewer_state.py @@ -143,7 +143,19 @@ def _reviewers_with_open_threads( continue comments = discussion.get("comments") or [] if entry.get("ignored_last_comment"): - comments = comments[:-1] + ignored_index = entry.get("ignored_comment_index") + if ( + isinstance(ignored_index, int) + and not isinstance(ignored_index, bool) + and 0 <= ignored_index < len(comments) + ): + comments = [ + comment + for index, comment in enumerate(comments) + if index != ignored_index + ] + else: + comments = comments[:-1] for comment in comments: if ( comment.get("actor_role") in ("approver", "outsider", "bot") diff --git a/.github/scripts/pull-request-dashboard/test_classification_execution.py b/.github/scripts/pull-request-dashboard/test_classification_execution.py index 0c6e6219907..f2fe048ef77 100644 --- a/.github/scripts/pull-request-dashboard/test_classification_execution.py +++ b/.github/scripts/pull-request-dashboard/test_classification_execution.py @@ -1126,6 +1126,33 @@ def test_edited_older_praise_is_classified_and_removed_by_identity( self.assertEqual(result.since, "2026-05-20T00:00:00Z") self.assertTrue(result.ignored_last_comment) + def test_cached_praise_recomputes_ignored_comment_index(self) -> None: + thread = self.thread( + ("approver", "Please fix this.", "2026-03-12T00:00:00Z"), + ("approver", "LGTM", "2026-04-12T00:00:00Z"), + ("author", "Fixed it.", "2026-05-20T00:00:00Z"), + ) + thread["comments"][1]["activity_timestamp"] = "2026-06-20T00:00:00Z" + request = execution_request(review_threads=(thread,)) + cache = MemoryClassificationCacheStore() + first_runner = FakeModelRunner( + responder=lambda model_request: successful_response( + model_request, + praise="praise", + author_reply="complete", + ) + ) + ClassificationService(first_runner, cache).classify(request) + cached_runner = FakeModelRunner() + + result = ClassificationService(cached_runner, cache).classify( + request + ).review_threads[0] + + self.assertEqual(cached_runner.requests, []) + self.assertTrue(result.ignored_last_comment) + self.assertEqual(result.ignored_comment_index, 1) + def test_edited_older_author_reply_supplies_body_and_result_time( self, ) -> None: diff --git a/.github/scripts/pull-request-dashboard/test_classification_policy.py b/.github/scripts/pull-request-dashboard/test_classification_policy.py index cd226947f1f..c1b7aed4321 100644 --- a/.github/scripts/pull-request-dashboard/test_classification_policy.py +++ b/.github/scripts/pull-request-dashboard/test_classification_policy.py @@ -483,6 +483,7 @@ def test_success_projection_matches_the_dashboard_record(self) -> None: cli_call=True, since="2026-01-02T03:04:05Z", ignored_last_comment=True, + ignored_comment_index=1, ) self.assertEqual( @@ -498,8 +499,13 @@ def test_success_projection_matches_the_dashboard_record(self) -> None: "_copilot_cli_call": True, "since": "2026-01-02T03:04:05Z", "ignored_last_comment": True, + "ignored_comment_index": 1, }, ) + self.assertNotIn( + "ignored_comment_index", + cached_classification_record(result), + ) def test_failure_and_cache_projections_preserve_the_old_shape(self) -> None: result = ClassificationFailure( diff --git a/.github/scripts/pull-request-dashboard/test_dashboard.py b/.github/scripts/pull-request-dashboard/test_dashboard.py index 7d982bcfed7..4869fad2d12 100644 --- a/.github/scripts/pull-request-dashboard/test_dashboard.py +++ b/.github/scripts/pull-request-dashboard/test_dashboard.py @@ -53,7 +53,10 @@ review_thread_comment, stored_dashboard_result, ) -from classification_execution import ClassificationExecutionRequest +from classification_execution import ( + ClassificationExecutionRequest, + ClassificationService, +) from classification_policy import ( ActionDecision, AuthorCommentDecision, @@ -67,7 +70,12 @@ FeedbackOutcome, resolve_review_thread_policy, ) -from classification_test_support import FakeClassificationOperation +from classification_test_support import ( + FakeClassificationOperation, + FakeModelRunner, + MemoryClassificationCacheStore, + successful_response, +) from pull_request_source import ( PullRequestSource, fetch_pull_request_source, @@ -1475,6 +1483,81 @@ def test_edited_reviewer_request_after_author_reply_clears_to_author( [comment.body for comment in thread.comments], ) + @patch("pull_request_evaluation.fetch_pull_request_source") + def test_edited_older_praise_attributes_remaining_thread_participants( + self, + fetch_raw: Mock, + ) -> None: + fetch_raw.return_value = pull_request_source( + pull_request=pull_request_metadata(title="Routing integration"), + review_threads=(review_thread( + node_id="thread-1", + comments=( + review_thread_comment( + actor=actor("root-reviewer"), + body="Please update the implementation and tests.", + created_at="2026-08-16T07:00:00Z", + ), + review_thread_comment( + actor=actor("praise-reviewer"), + body="LGTM", + created_at="2026-08-16T08:00:00Z", + updated_at="2026-08-16T10:00:00Z", + ), + review_thread_comment( + actor=actor("author"), + body="Fixed it.", + created_at="2026-08-16T09:00:00Z", + ), + ), + ),), + ) + classifier = ClassificationService( + FakeModelRunner( + responder=lambda request: successful_response( + request, + praise="praise", + author_reply="complete", + ) + ), + MemoryClassificationCacheStore(), + ) + + result = evaluate_pr( + {"number": 7}, + classification_service=classifier, + ) + + self.assertIsInstance(result, EvaluationSuccess) + assert isinstance(result, EvaluationSuccess) + self.assertEqual(DashboardRoute.APPROVER, result.route) + self.assertEqual( + { + "thread-1": { + "action": "reviewer", + "since": "2026-08-16T09:00:00Z", + "ignored_last_comment": True, + "ignored_comment_index": 1, + }, + }, + result.pending_actions, + ) + self.assertEqual( + (ReviewerSummary(login="root-reviewer", open_thread=True),), + result.facts.reviewers, + ) + self.assertEqual( + [ + "Please update the implementation and tests.", + "LGTM", + "Fixed it.", + ], + [ + comment["body"] + for comment in result.diagnostics.review_threads[0]["comments"] + ], + ) + @patch("pull_request_evaluation.fetch_pull_request_source") def test_author_reply_does_not_reactivate_cleared_handoff( self, fetch_raw: Mock From 935c6d8787e7b88b79bf431d5fdf75f34e01971b Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Wed, 2 Sep 2026 16:44:48 -0700 Subject: [PATCH 25/30] Keep unresolved Copilot findings author-owned Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 378be2b9-85df-4ac8-a125-a99b653412ac --- .../pull-request-dashboard/RATIONALE.md | 9 +-- .../classification_policy.py | 19 ++++- .../discussion_lifecycle.py | 10 ++- .../test_classification_execution.py | 14 ++++ .../test_classification_policy.py | 30 ++++++++ .../pull-request-dashboard/test_dashboard.py | 72 ++++++++++++++++++- 6 files changed, 145 insertions(+), 9 deletions(-) diff --git a/.github/scripts/pull-request-dashboard/RATIONALE.md b/.github/scripts/pull-request-dashboard/RATIONALE.md index 59d6ddda85c..21f79c3befb 100644 --- a/.github/scripts/pull-request-dashboard/RATIONALE.md +++ b/.github/scripts/pull-request-dashboard/RATIONALE.md @@ -400,10 +400,11 @@ the implementation understandable and operationally cheap. would hold every ready PR with its author waiting for a review that never runs, so only branches with automatic review are listed and PRs targeting other branches route normally. -- Copilot findings normally return a PR to the author through ordinary - discussion routing: an inline finding is an unresolved review thread, and an - actionable one routes the PR to "waiting on author." In that common path the - gate never fires and no re-review is requested. +- Every unresolved, non-outdated thread Copilot started is an author action, + even after an author reply or a later clean review. This ownership bypasses + generic praise and author-reply classification and ends only when the thread + is resolved or outdated. The gate therefore never needs its expiration path + to retain these findings. - Findings are counted from unresolved, non-outdated review threads Copilot started, not from the comment count on its review. A review's comment count never shrinks, so it keeps counting feedback the author has since addressed diff --git a/.github/scripts/pull-request-dashboard/classification_policy.py b/.github/scripts/pull-request-dashboard/classification_policy.py index c532f022589..096faa070d7 100644 --- a/.github/scripts/pull-request-dashboard/classification_policy.py +++ b/.github/scripts/pull-request-dashboard/classification_policy.py @@ -393,6 +393,7 @@ class ClassificationDiscussion: pr_author: str = "" source_kind: str = "" candidate_feedback: tuple[CandidateFeedback, ...] = () + strict_author_action: bool = False selected_comment_index: int | None = None selected_activity_timestamp: str = "" ignored_comment_index: int | None = None @@ -445,6 +446,7 @@ def from_record(cls, record: Mapping[str, Any]) -> ClassificationDiscussion: pr_author=str(record.get("pr_author") or ""), source_kind=str(record.get("source_kind") or ""), candidate_feedback=candidate_feedback, + strict_author_action=bool(record.get("strict_author_action")), ) def with_comments( @@ -1606,7 +1608,11 @@ def map_verdict_result( def prepare_praise_candidates( discussions: Sequence[ClassificationDiscussion], ) -> tuple[ClassificationDiscussion, ...]: - return tuple(discussion for discussion in discussions if _could_be_praise(discussion)) + return tuple( + discussion + for discussion in discussions + if not discussion.strict_author_action and _could_be_praise(discussion) + ) def _could_be_praise(discussion: ClassificationDiscussion) -> bool: @@ -1685,7 +1691,16 @@ def resolve_review_thread_policy( selected.activity_timestamp or selected.comment.timestamp ) - if dropped and not comments: + if discussion.strict_author_action: + resolved[discussion_id] = ClassificationSuccess( + discussion.identity, + ActionDecision( + DiscussionAction.AUTHOR, + "This unresolved Copilot finding remains author work until " + "the thread is resolved or outdated.", + ), + ) + elif dropped and not comments: resolved[discussion_id] = ClassificationSuccess( discussion.identity, ActionDecision( diff --git a/.github/scripts/pull-request-dashboard/discussion_lifecycle.py b/.github/scripts/pull-request-dashboard/discussion_lifecycle.py index 48d6dcd98ae..2b80a3615a2 100644 --- a/.github/scripts/pull-request-dashboard/discussion_lifecycle.py +++ b/.github/scripts/pull-request-dashboard/discussion_lifecycle.py @@ -210,6 +210,10 @@ def _group_review_threads(source: DiscussionInput) -> list[dict[str, Any]]: or f"review-discussion-{len(discussions) + 1}" ), "discussion_kind": "review-comment-thread", + "strict_author_action": bool( + raw_comments + and raw_comments[0].actor.is_copilot_reviewer + ), "path": discussion.path or None, "line": discussion.line, "resolved": False, @@ -724,9 +728,13 @@ def _review_thread_pending_actions( f"({classification.identity.kind.value}) requires " f"ActionDecision, got {type(decision).__name__}" ) - action = decision.action discussion_id = classification.identity.discussion_id discussion = by_id.get(discussion_id) + action = ( + DiscussionAction.AUTHOR + if (discussion or {}).get("strict_author_action") + else decision.action + ) comments = (discussion or {}).get("comments") or [] if action is not DiscussionAction.NONE and comments: entry = { diff --git a/.github/scripts/pull-request-dashboard/test_classification_execution.py b/.github/scripts/pull-request-dashboard/test_classification_execution.py index f2fe048ef77..e82ae413890 100644 --- a/.github/scripts/pull-request-dashboard/test_classification_execution.py +++ b/.github/scripts/pull-request-dashboard/test_classification_execution.py @@ -1063,6 +1063,20 @@ def test_long_reviewer_request_needs_no_model(self) -> None: assert isinstance(result.decision, ActionDecision) self.assertIs(result.decision.action, DiscussionAction.AUTHOR) + def test_unresolved_copilot_finding_does_not_use_reply_classifier(self) -> None: + thread = self.thread( + ("bot", "Please fix this.", "2026-03-12T00:00:00Z"), + ("author", "Fixed it.", "2026-05-20T00:00:00Z"), + ) + thread["strict_author_action"] = True + + result, runner = self.classify(thread) + + self.assertEqual(runner.requests, []) + assert isinstance(result.decision, ActionDecision) + self.assertIs(result.decision.action, DiscussionAction.AUTHOR) + self.assertEqual(result.since, "2026-05-20T00:00:00Z") + def test_praise_keeps_the_previous_request_and_wait_age(self) -> None: result, runner = self.classify(self.thread( ("approver", "Please fix this.", "2026-03-12T00:00:00Z"), diff --git a/.github/scripts/pull-request-dashboard/test_classification_policy.py b/.github/scripts/pull-request-dashboard/test_classification_policy.py index c1b7aed4321..0b90f7c25a3 100644 --- a/.github/scripts/pull-request-dashboard/test_classification_policy.py +++ b/.github/scripts/pull-request-dashboard/test_classification_policy.py @@ -57,6 +57,7 @@ def discussion( requester: str = "", pr_author: str = "", candidate_feedback: tuple[tuple[str, str], ...] = (), + strict_author_action: bool = False, ) -> ClassificationDiscussion: return ClassificationDiscussion( DiscussionIdentity(discussion_id, kind), @@ -73,6 +74,7 @@ def discussion( CandidateFeedback(feedback_id, feedback_body) for feedback_id, feedback_body in candidate_feedback ), + strict_author_action=strict_author_action, ) @@ -711,6 +713,34 @@ def test_long_reviewer_comment_needs_no_model_request(self) -> None: assert isinstance(result.decision, ActionDecision) self.assertEqual(result.decision.action, DiscussionAction.AUTHOR) + def test_unresolved_copilot_finding_bypasses_author_reply_policy(self) -> None: + thread = ClassificationDiscussion( + DiscussionIdentity("thread-1", DiscussionKind.REVIEW_THREAD), + ( + DiscussionComment( + "2026-01-02T03:04:05Z", + "bot", + "Please fix this.", + ), + DiscussionComment( + "2026-01-02T04:05:06Z", + "author", + "Done.", + ), + ), + strict_author_action=True, + ) + + self.assertEqual(prepare_praise_candidates([thread]), ()) + plan = resolve_review_thread_policy([thread], {}) + + self.assertEqual(plan.author_replies, ()) + result = plan.resolved[0] + self.assertIsInstance(result, ClassificationSuccess) + assert isinstance(result.decision, ActionDecision) + self.assertEqual(result.decision.action, DiscussionAction.AUTHOR) + self.assertEqual(result.since, "2026-01-02T04:05:06Z") + def test_praise_and_author_reply_shortcuts_preserve_handoffs(self) -> None: thread = discussion( "thread-1", diff --git a/.github/scripts/pull-request-dashboard/test_dashboard.py b/.github/scripts/pull-request-dashboard/test_dashboard.py index 4869fad2d12..631925ba699 100644 --- a/.github/scripts/pull-request-dashboard/test_dashboard.py +++ b/.github/scripts/pull-request-dashboard/test_dashboard.py @@ -491,7 +491,7 @@ def test_evaluation_routes_pending_reviewers_and_projects_reviewer_rows( ) @patch("pull_request_evaluation.fetch_pull_request_source") - def test_unresolved_copilot_thread_remains_an_author_action( + def test_unresolved_copilot_thread_with_completed_reply_remains_author_action( self, fetch_raw: Mock, ) -> None: @@ -564,10 +564,78 @@ def test_unresolved_copilot_thread_remains_an_author_action( self.assertIsInstance(ungated_result, EvaluationSuccess) assert isinstance(ungated_result, EvaluationSuccess) + self.assertEqual(DashboardRoute.AUTHOR, ungated_result.route) self.assertEqual( - (), + (thread_url,), ungated_result.facts.author_action_review_thread_urls, ) + self.assertEqual( + { + "PRRT_1": { + "action": "author", + "since": "2026-08-16T07:30:00Z", + }, + }, + ungated_result.pending_actions, + ) + self.assertIn( + ReviewerSummary( + login="copilot-pull-request-reviewer[bot]", + open_thread=True, + ), + ungated_result.facts.reviewers, + ) + + @patch("pull_request_evaluation.fetch_pull_request_source") + def test_resolved_or_outdated_copilot_thread_releases_author_hold( + self, + fetch_raw: Mock, + ) -> None: + for state in ({"is_resolved": True}, {"is_outdated": True}): + with self.subTest(state=state): + fetch_raw.return_value = pull_request_source( + pull_request=pull_request_metadata(), + reviews=( + review_source( + actor=actor("copilot"), + commit_id="old-head", + finding_count=1, + submitted_at="2026-08-16T07:00:00Z", + ), + review_source( + actor=actor("copilot"), + commit_id="abcdef123456", + finding_count=0, + submitted_at="2026-08-16T08:00:00Z", + ), + ), + review_threads=(review_thread( + comments=( + review_thread_comment(actor=actor("copilot")), + review_thread_comment( + node_id="PRRC_2", + body="Handled in a follow-up.", + created_at="2026-08-16T07:30:00Z", + actor=actor("author"), + ), + ), + **state, + ),), + ) + + result = evaluate_pr( + {"number": 7}, + require_clean_copilot_review_branches=["main"], + ) + + self.assertIsInstance(result, EvaluationSuccess) + assert isinstance(result, EvaluationSuccess) + self.assertEqual(DashboardRoute.APPROVER, result.route) + self.assertEqual({}, result.pending_actions) + self.assertEqual( + (), + result.facts.author_action_review_thread_urls, + ) @patch("pull_request_evaluation.fetch_pull_request_source") def test_override_binds_to_the_observed_head_before_classification( From 4175df1fcea438dc16ea24f33f86974759eea14e Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Wed, 2 Sep 2026 17:11:26 -0700 Subject: [PATCH 26/30] Keep lifecycle bindings command scoped Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 378be2b9-85df-4ac8-a125-a99b653412ac --- .../pull-request-dashboard/RATIONALE.md | 16 +- .../dashboard_override.py | 19 ++- .../discussion_lifecycle.py | 10 +- .../test_dashboard_override.py | 142 ++++++++++++++++++ .../test_discussion_lifecycle.py | 5 +- 5 files changed, 175 insertions(+), 17 deletions(-) diff --git a/.github/scripts/pull-request-dashboard/RATIONALE.md b/.github/scripts/pull-request-dashboard/RATIONALE.md index 21f79c3befb..e231b79b46b 100644 --- a/.github/scripts/pull-request-dashboard/RATIONALE.md +++ b/.github/scripts/pull-request-dashboard/RATIONALE.md @@ -504,13 +504,15 @@ the implementation understandable and operationally cheap. command, and records that head in an acknowledgement marker on either the command reply or the live status comment. A persistence marker makes the cross-push behavior explicit. Legacy acknowledgements without that marker - remain head-bound, so deployment cannot reactivate old handoffs that had - already expired. A separate companion marker records the frozen timestamp - used for the permanent top-level feedback cutoff. A legacy acknowledgement - without that cutoff does not retire feedback, because the command's current - edit timestamp cannot reconstruct the original cutoff. The first observation - freezes the cutoff for that command, so editing the command later cannot - retire intervening feedback; only a newer command advances it. + remain head-bound, as do pending bindings restored from compatible legacy + state, so deployment cannot reactivate old handoffs that had already expired. + A separate companion marker records the frozen timestamp used for the + permanent top-level feedback cutoff. Marker lookup is scoped to the bound + command. A legacy acknowledgement without that cutoff does not retire + feedback, because the command's current edit timestamp cannot reconstruct the + original cutoff. The first observation freezes the cutoff for that command, + so editing the command later cannot retire intervening feedback; only a newer + command advances it. - Recording the observed head also avoids ordering the command against a push by comparing the comment timestamp with the head push time from `GET /repos/{repo}/activity`. Both timestamps have one-second resolution and diff --git a/.github/scripts/pull-request-dashboard/dashboard_override.py b/.github/scripts/pull-request-dashboard/dashboard_override.py index f08170c61ab..0706fb94f93 100644 --- a/.github/scripts/pull-request-dashboard/dashboard_override.py +++ b/.github/scripts/pull-request-dashboard/dashboard_override.py @@ -230,8 +230,11 @@ def dashboard_override_facts( acknowledged_since, acknowledgement_created_at, ) = acknowledged_override(source.issue_comments) + previous_binding_command_id = ( + previous_bound_command_id or previous_command_id + ) previous_binding_matches = bool( - bound_command_id == previous_bound_command_id + bound_command_id == previous_binding_command_id and bound_head and bound_head == previous_head_sha ) @@ -244,17 +247,25 @@ def dashboard_override_facts( ) existing_command_binding = bool( command_id - and command_id == previous_bound_command_id + and command_id == previous_binding_command_id ) + new_command_binding = bool(command_id and not existing_command_binding) top_level_feedback_cutoff = _latest_valid_timestamp( previous_top_level_feedback_cutoff, command_created_at if command_id and not existing_command_binding else "", - acknowledged_top_level_feedback_cutoff(source.issue_comments), + ( + acknowledged_top_level_feedback_cutoff( + source.issue_comments, + bound_command_id, + ) + if bound_command_id + else "" + ), ) persistent_handoff = bool( bound_head and ( - command_id + new_command_binding or (previous_binding_matches and previous_persistent_handoff) or acknowledges_persistent_handoff( source.issue_comments, diff --git a/.github/scripts/pull-request-dashboard/discussion_lifecycle.py b/.github/scripts/pull-request-dashboard/discussion_lifecycle.py index 2b80a3615a2..23d669f9182 100644 --- a/.github/scripts/pull-request-dashboard/discussion_lifecycle.py +++ b/.github/scripts/pull-request-dashboard/discussion_lifecycle.py @@ -182,8 +182,8 @@ def _group_review_threads(source: DiscussionInput) -> list[dict[str, Any]]: if discussion.is_resolved or discussion.is_outdated: continue raw_comments = discussion.comments - thread_url = raw_comments[0].url if raw_comments else "" ordered = sorted(raw_comments, key=lambda comment: comment.created_at) + root_comment = ordered[0] if ordered else None comments = [ _discussion_comment( comment.created_at, @@ -211,13 +211,15 @@ def _group_review_threads(source: DiscussionInput) -> list[dict[str, Any]]: ), "discussion_kind": "review-comment-thread", "strict_author_action": bool( - raw_comments - and raw_comments[0].actor.is_copilot_reviewer + root_comment + and root_comment.actor.is_copilot_reviewer ), "path": discussion.path or None, "line": discussion.line, "resolved": False, - "discussion_url": thread_url, + "discussion_url": ( + root_comment.url if root_comment is not None else "" + ), "requester": latest_activity_comment.get("actor") or "", "pr_author": source.author, "comments": comments, diff --git a/.github/scripts/pull-request-dashboard/test_dashboard_override.py b/.github/scripts/pull-request-dashboard/test_dashboard_override.py index 391ca622c1f..2eff650a7e9 100644 --- a/.github/scripts/pull-request-dashboard/test_dashboard_override.py +++ b/.github/scripts/pull-request-dashboard/test_dashboard_override.py @@ -1,10 +1,14 @@ from __future__ import annotations +import json +from pathlib import Path +import tempfile import unittest from unittest.mock import call, patch import dashboard_override import dashboard_override_delivery +import state from dashboard_contracts import DashboardCommandReply, DashboardRoute from dashboard_test_support import ( actor, @@ -14,6 +18,7 @@ stored_dashboard_result, ) from pull_request_source import IssueComment +from routing_decision import reviewer_handoff_active def override_input( @@ -616,6 +621,46 @@ def test_acknowledged_command_keeps_only_a_durable_cutoff(self) -> None: facts.top_level_feedback_cutoff, ) + def test_cutoff_marker_is_bound_to_the_acknowledged_command(self) -> None: + source = override_input( + issue_comment( + database_id=9, + actor=actor("opentelemetry-pr-dashboard[bot]"), + body="\n".join(( + dashboard_override.override_ack_marker( + 5, + "bound-head", + "2026-08-16T08:00:00Z", + ), + dashboard_override.top_level_feedback_cutoff_marker( + 5, + "2026-08-16T08:00:00Z", + ), + )), + ), + issue_comment( + database_id=10, + actor=actor("opentelemetry-pr-dashboard[bot]"), + body=dashboard_override.top_level_feedback_cutoff_marker( + 6, + "2026-08-16T10:00:00Z", + ), + ), + ) + + facts = dashboard_override.dashboard_override_facts( + source, + "author", + None, + "current-head", + ) + + self.assertEqual(5, facts.bound_command_id) + self.assertEqual( + "2026-08-16T08:00:00Z", + facts.top_level_feedback_cutoff, + ) + def test_newer_command_advances_permanent_top_level_cutoff(self) -> None: source = override_input( issue_comment( @@ -643,6 +688,103 @@ def test_newer_command_advances_permanent_top_level_cutoff(self) -> None: facts.top_level_feedback_cutoff, ) + def test_legacy_pending_binding_does_not_gain_persistence(self) -> None: + source = override_input(issue_comment( + database_id=5, + body="/dashboard route:reviewers", + created_at="2026-08-16T08:00:00Z", + content_updated_at="2026-08-16T10:00:00Z", + )) + for version in (13, 16): + with ( + self.subTest(version=version), + tempfile.TemporaryDirectory() as temp_dir, + ): + legacy_facts = dashboard_facts( + dashboard_override_command_id=5, + dashboard_override_command_user="author", + dashboard_override_bound_command_id=( + 5 if version == 16 else 0 + ), + dashboard_override_head_sha="bound-head", + dashboard_override_since="2026-08-16T08:00:00Z", + dashboard_top_level_feedback_cutoff=( + "2026-08-16T08:00:00Z" + if version == 16 + else "" + ), + ) + stored = state.encode_dashboard_state(dashboard_state( + stored_dashboard_result(7, facts=legacy_facts) + )) + stored["version"] = version + with patch("state._state_dir", Path(temp_dir)): + state.dashboard_state_path().write_text( + json.dumps(stored), + encoding="utf-8", + ) + loaded = state.load_dashboard_state_cache() + self.assertIsNotNone(loaded) + assert loaded is not None + previous_facts = loaded.results[0].facts + + migrated = dashboard_override.dashboard_override_facts( + source, + "author", + None, + "bound-head", + previous_facts, + ) + + self.assertFalse(migrated.persistent_handoff) + self.assertEqual( + "2026-08-16T08:00:00Z", + migrated.since, + ) + self.assertEqual( + ( + "2026-08-16T08:00:00Z" + if version == 16 + else "" + ), + migrated.top_level_feedback_cutoff, + ) + acknowledgement = dashboard_override.append_command_ack_reply( + source, + result_facts(migrated, author="author"), + DashboardRoute.APPROVER, + ).dashboard_command_replies[0] + self.assertFalse(acknowledgement.persistent_handoff) + acknowledgement_body = dashboard_override.render_command_reply( + acknowledgement + ) + self.assertNotIn( + dashboard_override.PERSISTENT_HANDOFF_MARKER_PREFIX, + acknowledgement_body, + ) + delivered_source = override_input( + *source.issue_comments, + issue_comment( + database_id=9, + actor=actor("opentelemetry-pr-dashboard[bot]"), + body=acknowledgement_body, + ), + ) + + after_push = dashboard_override.dashboard_override_facts( + delivered_source, + "author", + None, + "new-head", + result_facts(migrated), + ) + self.assertFalse(after_push.persistent_handoff) + self.assertFalse( + reviewer_handoff_active( + result_facts(after_push, head_sha="new-head") + ) + ) + def test_missing_new_command_time_preserves_permanent_cutoff(self) -> None: source = override_input( issue_comment( diff --git a/.github/scripts/pull-request-dashboard/test_discussion_lifecycle.py b/.github/scripts/pull-request-dashboard/test_discussion_lifecycle.py index 180e516e842..5e8d7de46b5 100644 --- a/.github/scripts/pull-request-dashboard/test_discussion_lifecycle.py +++ b/.github/scripts/pull-request-dashboard/test_discussion_lifecycle.py @@ -155,7 +155,7 @@ def test_prepares_threads_feedback_and_author_replies(self) -> None: "url": "https://example.test/thread/root", "body": "Please fix this.", "createdAt": ROOT_TIMESTAMP, - "author": {"login": "reviewer"}, + "author": {"login": "copilot"}, }, ] }, @@ -208,8 +208,9 @@ def test_prepares_threads_feedback_and_author_replies(self) -> None: ) self.assertEqual( prepared.review_threads[0]["discussion_url"], - "https://example.test/thread/first", + "https://example.test/thread/root", ) + self.assertTrue(prepared.review_threads[0]["strict_author_action"]) self.assertEqual( [item["discussion_id"] for item in prepared.top_level_items], ["pr-issue-comment-201", "pr-issue-comment-202"], From 304849a4d94ff4cc602bce9607a808459858d106 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Wed, 2 Sep 2026 17:19:32 -0700 Subject: [PATCH 27/30] Roll out lifecycle status markers Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 378be2b9-85df-4ac8-a125-a99b653412ac --- .github/scripts/pull-request-dashboard/state.py | 2 +- .../pull-request-dashboard/test_pr_status_comment.py | 6 +++--- .github/scripts/pull-request-dashboard/test_state.py | 2 ++ 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/scripts/pull-request-dashboard/state.py b/.github/scripts/pull-request-dashboard/state.py index 098938f6a85..51f1e357302 100644 --- a/.github/scripts/pull-request-dashboard/state.py +++ b/.github/scripts/pull-request-dashboard/state.py @@ -52,7 +52,7 @@ STATUS_COMMENT_ROLLOUT_STATE_VERSION = 2 # Rendered status-comment behavior. Increment when existing comments need to # adopt a change; hourly runs durably roll it out to all open PRs. -STATUS_COMMENT_REVISION = 16 +STATUS_COMMENT_REVISION = 17 INITIAL_BACKFILL_COMPLETE_KEY = "initial_backfill_complete" _state_dir: Path | None = None 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 afc3d25c7e5..d42c52a930a 100644 --- a/.github/scripts/pull-request-dashboard/test_pr_status_comment.py +++ b/.github/scripts/pull-request-dashboard/test_pr_status_comment.py @@ -1290,15 +1290,15 @@ def test_targeted_update_completes_drained_rollout( def test_new_revision_queues_every_open_pr(self) -> None: state = pr_status_comment.prepare_rollout_state( { - "target_revision": 0, - "completed_revision": 0, + "target_revision": 16, + "completed_revision": 16, "pending_pr_numbers": [], }, {12, 34}, ) self.assertEqual(pr_status_comment.STATUS_COMMENT_REVISION, state["target_revision"]) - self.assertEqual(0, state["completed_revision"]) + self.assertEqual(16, state["completed_revision"]) self.assertEqual([12, 34], state["pending_pr_numbers"]) def test_current_revision_drops_closed_prs_from_queue(self) -> None: diff --git a/.github/scripts/pull-request-dashboard/test_state.py b/.github/scripts/pull-request-dashboard/test_state.py index cd876262828..20a4fb3a7fc 100644 --- a/.github/scripts/pull-request-dashboard/test_state.py +++ b/.github/scripts/pull-request-dashboard/test_state.py @@ -27,6 +27,7 @@ DASHBOARD_STATE_VERSION, NOTIFICATION_STATE_VERSION, STATUS_COMMENT_ROLLOUT_STATE_VERSION, + STATUS_COMMENT_REVISION, author_nudge_state_path, backfill_state_path, copilot_review_request_state_path, @@ -673,6 +674,7 @@ def test_notification_state_version_is_independent(self) -> None: self.assertEqual(DASHBOARD_STATE_VERSION, 17) self.assertEqual(DASHBOARD_STATE_COMPATIBLE_VERSIONS, (11, 12, 13, 16)) self.assertEqual(STATUS_COMMENT_ROLLOUT_STATE_VERSION, 2) + self.assertEqual(STATUS_COMMENT_REVISION, 17) self.assertEqual(AUTHOR_NUDGE_STATE_VERSION, 3) self.assertEqual(COPILOT_REVIEW_REQUEST_STATE_VERSION, 6) From a1363f006d950585f720092141046da0ff2fcecc Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Fri, 4 Sep 2026 15:16:54 -0700 Subject: [PATCH 28/30] Address Copilot review comments: keep measurement dates consistent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Copilot comment: `measurement_date` was already captured at function entry and assigned to every rebuilt case. Recomputing it here can cross UTC midnight, leaving the top-level baseline dates one day later than the cases generated in the same rebuild—the invariant that the removed test previously enforced. Reuse the existing value instead. Copilot comment: This scored case still contains measured `run_actions` and a `recorded_label`, but it is now the only case without the `measurement_date` required by the fixture's field definition. That makes the checked-in baseline untraceable and the relaxed fixture test no longer detects the malformed case; restore the date recorded by this measurement update. Analysis: The rebuild now captures UTC once and reuses that date for every case and both corpus-level fields. The appended scored case records its September 2 measurement date. The fixture test again requires every case date and checks the corpus bounds. Upsides: A rebuild cannot split its provenance across UTC midnight, and malformed checked-in cases fail the fixture test. Downsides: The fixture repeats a date on every case, which adds mechanical JSON data. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../eval/regenerate_baseline.py | 1 - .../eval/reviewer_feedback_cases.json | 1 + .../test_eval_baseline.py | 19 +++++++++++++++++++ .../test_eval_fixture.py | 9 +++++++++ 4 files changed, 29 insertions(+), 1 deletion(-) diff --git a/.github/scripts/pull-request-dashboard/eval/regenerate_baseline.py b/.github/scripts/pull-request-dashboard/eval/regenerate_baseline.py index 349b9c760d0..0b2ed1bfbe5 100644 --- a/.github/scripts/pull-request-dashboard/eval/regenerate_baseline.py +++ b/.github/scripts/pull-request-dashboard/eval/regenerate_baseline.py @@ -211,7 +211,6 @@ def rebuild(payload: dict, trials: list[dict[str, str]], model: str) -> dict: cases.sort(key=lambda c: (c["repo"], c["pull_request"], c["root_timestamp"])) roles = Counter(c["role"] for c in cases) stabilities = Counter(c["stability"] for c in cases) - measurement_date = datetime.now(UTC).strftime("%Y-%m-%d") return { **payload, "baseline_generated_at": measurement_date, diff --git a/.github/scripts/pull-request-dashboard/eval/reviewer_feedback_cases.json b/.github/scripts/pull-request-dashboard/eval/reviewer_feedback_cases.json index e923b41da22..711c43c0c10 100644 --- a/.github/scripts/pull-request-dashboard/eval/reviewer_feedback_cases.json +++ b/.github/scripts/pull-request-dashboard/eval/reviewer_feedback_cases.json @@ -10485,6 +10485,7 @@ "review_state": null, "root_timestamp": "2026-09-01T19:45:40Z", "body": "@Shriprasad-P the test is unfortunately still failing here", + "measurement_date": "2026-09-02", "role": "scored", "stability": "stable", "recorded_label": "author_action", diff --git a/.github/scripts/pull-request-dashboard/test_eval_baseline.py b/.github/scripts/pull-request-dashboard/test_eval_baseline.py index 2eb8712f3da..98678e95a39 100644 --- a/.github/scripts/pull-request-dashboard/test_eval_baseline.py +++ b/.github/scripts/pull-request-dashboard/test_eval_baseline.py @@ -1,4 +1,5 @@ import contextlib +from datetime import datetime import io import json import sys @@ -159,6 +160,24 @@ def test_counts_and_configuration_describe_the_new_measurement(self) -> None: ) self.assertNotIn("generated_at", rebuilt) + def test_rebuild_uses_one_measurement_date(self) -> None: + with patch.object(regenerate_baseline, "datetime") as clock: + clock.now.side_effect = [ + datetime.fromisoformat("2026-09-02T23:59:59+00:00"), + datetime.fromisoformat("2026-09-03T00:00:00+00:00"), + ] + + rebuilt = rebuild( + payload(case("a")), + [{"a": "author_action"}], + "model", + ) + + self.assertEqual("2026-09-02", rebuilt["baseline_generated_at"]) + self.assertEqual("2026-09-02", rebuilt["measurements_updated_at"]) + self.assertEqual("2026-09-02", rebuilt["cases"][0]["measurement_date"]) + clock.now.assert_called_once_with(regenerate_baseline.UTC) + class RunBatchCachingTest(unittest.TestCase): def setUp(self) -> None: diff --git a/.github/scripts/pull-request-dashboard/test_eval_fixture.py b/.github/scripts/pull-request-dashboard/test_eval_fixture.py index de5be41311a..f4951de68fd 100644 --- a/.github/scripts/pull-request-dashboard/test_eval_fixture.py +++ b/.github/scripts/pull-request-dashboard/test_eval_fixture.py @@ -42,9 +42,18 @@ def test_counts_match_the_cases(self) -> None: def test_measurement_dates_describe_the_mixed_vintage_corpus(self) -> None: baseline = date.fromisoformat(self.data["baseline_generated_at"]) updated = date.fromisoformat(self.data["measurements_updated_at"]) + measurements = [] self.assertLessEqual(baseline, updated) self.assertNotIn("generated_at", self.data) + for case in self.cases: + with self.subTest(case=case["id"]): + measured = date.fromisoformat(case["measurement_date"]) + self.assertLessEqual(baseline, measured) + self.assertLessEqual(measured, updated) + measurements.append(measured) + self.assertEqual(baseline, min(measurements)) + self.assertEqual(updated, max(measurements)) def test_case_ids_are_unique(self) -> None: ids = [case["id"] for case in self.cases] From 764435a8554dd4afd6ae18a5c6d7ab6581b7d5fc Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Fri, 4 Sep 2026 15:53:23 -0700 Subject: [PATCH 29/30] Address review finding: defer override acknowledgement without a reviewer route Review finding: This new contract rule turns a reachable state into a failed evaluation. `dashboard_override_facts` sets `persistent_handoff` whenever a bound head exists and the previous pass already recorded a persistent binding, without consulting the currently observed head. `reviewer_handoff_active` in routing_decision.py now requires `bool(facts.head_sha)`, so when a pass observes an empty head SHA the handoff is inactive and the route can resolve to AUTHOR while the command is still pending and unacknowledged. `append_command_ack_reply` then builds a routed reply with `persistent_handoff=True` and `route=DashboardRoute.AUTHOR`, and this check raises. I confirmed it end to end: with previous facts carrying `dashboard_override_persistent=True` and `dashboard_override_head_sha='abc'`, a pending command 5, `head_sha=''`, and one review thread classified as an author action, `evaluate_pull_request` returns `EvaluationFailure` with route UNKNOWN and error `ValueError('persistent reviewer handoff replies require a reviewer route')`. The same input with a matching head returns EvaluationSuccess/APPROVER, so the empty head is the only trigger. The PR itself treats an empty head as a real state: it added `test_override_without_head_uses_non_persistent_acknowledgement`, and it added the `bool(facts.head_sha)` guard to `reviewer_handoff_active`. Concrete fix: make the acknowledgement reply consistent with the route it reports, for example in `append_command_ack_reply` pass `persistent_handoff=facts.dashboard_override_persistent and route is not DashboardRoute.AUTHOR`, so a pre-review-route reply is acknowledged without the persistence marker instead of raising. Analysis: `DashboardCommandReply` rejects a persistent handoff on an author route, but `append_command_ack_reply` builds the reply from two values that no longer agree. `dashboard_override_persistent` comes from the stored binding, while `route` comes from routing, which `reviewer_handoff_active` now suppresses whenever the observed head is empty. A pending command plus an empty head therefore raises out of `evaluate_pull_request`, and `dashboard_state_update` refuses to publish a failed result. Acknowledging without the persistence marker would resolve the contract but lose the durable record: the marker in the delivered comment is how persistence survives a cache loss, and the reply would also tell the author the wrong thing. Holding the acknowledgement instead keeps the reply truthful and lets the next pass with a real head deliver it as a persistent reviewer handoff. Upsides: The evaluation no longer fails on a state the pull request already models. The delivered acknowledgement always records the handoff it announces, so the durable marker and the reply text stay consistent. A new test covers both the deferred pass and the delivery once the head returns. Downsides: A command stays unacknowledged for as long as the observed head is empty, so an author sees no reply in that window. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../dashboard_override.py | 6 +- .../pull-request-dashboard/test_dashboard.py | 94 +++++++++++++++++++ 2 files changed, 99 insertions(+), 1 deletion(-) diff --git a/.github/scripts/pull-request-dashboard/dashboard_override.py b/.github/scripts/pull-request-dashboard/dashboard_override.py index 0706fb94f93..b06f0891198 100644 --- a/.github/scripts/pull-request-dashboard/dashboard_override.py +++ b/.github/scripts/pull-request-dashboard/dashboard_override.py @@ -634,7 +634,9 @@ def append_command_ack_reply( The reply carries markers that record the command binding, permanent feedback cutoff, and persistent handoff. A command superseded by reviewer feedback is acknowledged in the status comment instead of producing another - top-level comment. + top-level comment. A persistent handoff that currently routes to the author + stays unacknowledged until a reviewer route returns, so the reply always + records the handoff it announces. """ cleared_by_feedback = facts.dashboard_override_cleared_by_feedback command_id = ( @@ -653,6 +655,8 @@ def append_command_ack_reply( ) if cleared_by_feedback: return facts.with_changes(dashboard_override_since=override_since) + if facts.dashboard_override_persistent and route is DashboardRoute.AUTHOR: + return facts.with_changes(dashboard_override_since=override_since) kind = "routed" replies = facts.dashboard_command_replies reply = DashboardCommandReply( diff --git a/.github/scripts/pull-request-dashboard/test_dashboard.py b/.github/scripts/pull-request-dashboard/test_dashboard.py index ac78815b641..daefbc29806 100644 --- a/.github/scripts/pull-request-dashboard/test_dashboard.py +++ b/.github/scripts/pull-request-dashboard/test_dashboard.py @@ -784,6 +784,100 @@ def test_override_without_head_uses_non_persistent_acknowledgement( result.facts.dashboard_command_replies, ) + @patch("pull_request_evaluation.fetch_pull_request_source") + def test_persistent_handoff_without_head_defers_acknowledgement( + self, + fetch_raw: Mock, + ) -> None: + fetch_raw.return_value = pull_request_source( + pull_request=pull_request_metadata(head_sha=""), + issue_comments=( + issue_comment( + database_id=102, + body="/dashboard route:reviewers", + created_at="2026-08-16T08:00:00Z", + ), + ), + review_threads=( + review_thread( + node_id="thread-1", + comments=( + review_thread_comment( + body="Please update this.", + created_at="2026-08-16T09:00:00Z", + ), + ), + ), + ), + ) + classification = action_classification( + "thread-1", + DiscussionKind.REVIEW_THREAD, + DiscussionAction.AUTHOR, + "The reviewer requested a change.", + ) + classifier = FakeClassificationOperation( + DiscussionClassifications((classification,), (), ()) + ) + + result = evaluate_pr( + {"number": 7}, + previous_result=stored_dashboard_result( + 7, + route=DashboardRoute.APPROVER, + facts=dashboard_facts( + dashboard_override_command_id=102, + dashboard_override_bound_command_id=102, + dashboard_override_command_user="author", + dashboard_override_head_sha="abcdef123456", + dashboard_override_since="2026-08-16T08:00:00Z", + dashboard_override_persistent=True, + dashboard_top_level_feedback_cutoff="2026-08-16T08:00:00Z", + ), + ), + classification_service=classifier, + ) + + self.assertIsInstance(result, EvaluationSuccess) + assert isinstance(result, EvaluationSuccess) + self.assertEqual(DashboardRoute.AUTHOR, result.route) + self.assertTrue(result.facts.dashboard_override_persistent) + self.assertEqual((), result.facts.dashboard_command_replies) + + fetch_raw.return_value = pull_request_source( + pull_request=pull_request_metadata(head_sha="abcdef123456"), + issue_comments=fetch_raw.return_value.issue_comments, + review_threads=fetch_raw.return_value.review_threads, + ) + resumed = evaluate_pr( + {"number": 7}, + previous_result=stored_dashboard_result( + 7, + route=result.route, + facts=result.facts, + ), + classification_service=classifier, + ) + + self.assertIsInstance(resumed, EvaluationSuccess) + assert isinstance(resumed, EvaluationSuccess) + self.assertEqual(DashboardRoute.APPROVER, resumed.route) + self.assertEqual( + ( + DashboardCommandReply( + 102, + "routed", + "author", + head_sha="abcdef123456", + route=DashboardRoute.APPROVER, + since="2026-08-16T08:00:00Z", + top_level_feedback_cutoff="2026-08-16T08:00:00Z", + persistent_handoff=True, + ), + ), + resumed.facts.dashboard_command_replies, + ) + @patch("pull_request_evaluation.fetch_pull_request_source") def test_actionable_feedback_after_push_ends_persistent_handoff( self, From 9b555768f3ceae535e3a077dc9c31ff23b13bc91 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Fri, 4 Sep 2026 15:54:19 -0700 Subject: [PATCH 30/30] Address review finding: roll out the lifecycle status markers Review finding: This PR changes what a rendered status comment says but pins `STATUS_COMMENT_REVISION` at its existing value of 19. The constant's own comment in state.py states the rule: 'Rendered status-comment behavior. Increment when existing comments need to adopt a change; hourly runs durably roll it out to all open PRs.' `author_override_guidance()` in dashboard_override.py changed from 'The dashboard binds the request to the head it sees when it reads the command, and a later push restores normal routing.' to text saying the handoff remains active across pushes and that top-level feedback through the command is retired. That guidance is rendered into every waiting-on-author status comment, so the old sentence is now factually wrong for open PRs. `render_status_comment` also now emits a `pull-request-dashboard-top-level-feedback-cutoff` marker that existing comments lack. Without a bump, `prepare_rollout_state` never queues open PRs, so a PR whose status comment is not otherwise re-rendered keeps telling its author that a push restores normal routing. Two precedents in this repository changed the same guidance and bumped the revision in the same commit: 5819c680 ('Clear open items on a reviewer-routing override', 14 to 15) and 225dbdb0 ('Keep conflicted PR routing actionable without gate failures', 15 to 16). Concrete fix: set `STATUS_COMMENT_REVISION = 20` in state.py and update this assertion to 20. Analysis: `render_status_comment` now emits a `pull-request-dashboard-top-level-feedback-cutoff` marker, and `upsert_status_comment` preserves it across later renders. That marker is the durable record of the permanent feedback cutoff, so a status comment written before this change carries no cutoff at all. The branch allocated a revision for exactly this rollout in 304849a4, which moved the constant from 16 to 17, but the merge with a6d35ab6 kept main's newer 19 and dropped the increment. The published rollout state already reads target 19 and completed 19 with an empty queue, so `prepare_rollout_state` queues nothing. A result that is otherwise unchanged does not enqueue a comment update, so an open pull request with no further activity would never adopt the marker. Moving to 20 restores the increment the branch intended. The finding's claim about `author_override_guidance()` does not hold: that helper is no longer rendered into a status comment, and only the marker rollout is at stake. Upsides: Every open pull request adopts the new lifecycle markers on the next hourly run, so the permanent feedback cutoff survives a state cache loss on pull requests that predate this change. Downsides: The rollout re-renders the status comment of every open pull request once, which costs one delivery pass per pull request. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/scripts/pull-request-dashboard/state.py | 2 +- .github/scripts/pull-request-dashboard/test_state.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/scripts/pull-request-dashboard/state.py b/.github/scripts/pull-request-dashboard/state.py index f7f3b0bfe52..7f35e7145a4 100644 --- a/.github/scripts/pull-request-dashboard/state.py +++ b/.github/scripts/pull-request-dashboard/state.py @@ -53,7 +53,7 @@ STATUS_COMMENT_ROLLOUT_STATE_VERSION = 2 # Rendered status-comment behavior. Increment when existing comments need to # adopt a change; hourly runs durably roll it out to all open PRs. -STATUS_COMMENT_REVISION = 19 +STATUS_COMMENT_REVISION = 20 INITIAL_BACKFILL_COMPLETE_KEY = "initial_backfill_complete" _state_dir: Path | None = None diff --git a/.github/scripts/pull-request-dashboard/test_state.py b/.github/scripts/pull-request-dashboard/test_state.py index d6e9c7f5691..4725ca936e2 100644 --- a/.github/scripts/pull-request-dashboard/test_state.py +++ b/.github/scripts/pull-request-dashboard/test_state.py @@ -789,7 +789,7 @@ def test_notification_state_version_is_independent(self) -> None: self.assertEqual(DASHBOARD_STATE_VERSION, 17) self.assertEqual(DASHBOARD_STATE_COMPATIBLE_VERSIONS, (11, 12, 13, 16)) self.assertEqual(STATUS_COMMENT_ROLLOUT_STATE_VERSION, 2) - self.assertEqual(STATUS_COMMENT_REVISION, 19) + self.assertEqual(STATUS_COMMENT_REVISION, 20) self.assertEqual(AUTHOR_NUDGE_STATE_VERSION, 3) self.assertEqual(COPILOT_REVIEW_REQUEST_STATE_VERSION, 6)