Skip to content

Add an AI_NOT_RELATED bucket for advisor-cleared failures - #8671

Open
izaitsevfb wants to merge 7 commits into
mainfrom
gh/izaitsevfb/1/head
Open

Add an AI_NOT_RELATED bucket for advisor-cleared failures#8671
izaitsevfb wants to merge 7 commits into
mainfrom
gh/izaitsevfb/1/head

Conversation

@izaitsevfb

@izaitsevfb izaitsevfb commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

✴️ iz2: opened on behalf of @izaitsevfb.

The AI CI Advisor already analyzes NEW and Unclassified failures and renders its verdict inline in the pytorchbot comment, but the verdict currently changes nothing: the job still sits in the NEW FAILURES section and still blocks the merge.

This moves the failures the advisor has cleared into their own non-blocking bucket, AI_NOT_RELATED, in the same place and shape as the existing CRCR_L3 block. Two things fall out of that for free:

  • The comment. newFailedJobs and the "N New Failures" header both derive from failedJobs, so cleared jobs leave the NEW section automatically. They get their own collapsed section, still carrying their AI verdict: line, and count toward "Unrelated".
  • Merge. failures[pr] is both the /api/drci/drci response and the Dr.CI check-run summary, so trymerge.py can read the new category with no extra request and no new auth surface. The merge-bot side is [trymerge] Stop blocking merges on failures the AI CI Advisor cleared pytorch#195503; nothing in this PR affects merges on its own.

Which verdicts are cleared

not_related, infra_issue and garbage, at high confidence. All three make the same claim about the PR under review — this failure is not evidence against it — and they are encoded as a total map over AdvisorVerdictType, so adding a verdict fails to compile until someone decides which side it falls on.

garbage sits with the cleared ones rather than with unsure because it is an evidenced claim about the signal, not the absence of one: the advisor reaches it by comparing the job against its own baselines and finding the red flips across unrelated commits. unsure is the real "cannot tell", and it keeps blocking, as do related and revert.

Measured over 30 days on PR-side verdicts at high confidence, joined to each job's own conclusion: not_related 1179 jobs, infra_issue 976, garbage 1.

The two flags, and why suppression depends on the comment

Two deployment flags, not one:

  • DRCI_ADVISOR_COMMENT_ENABLED — render the inline AI verdict: line. Display only.
  • DRCI_ADVISOR_SUPPRESSION_ENABLED — move cleared failures out of the blocking set.

Suppression requires the comment flag and is inert without it. Suppression with the comment dark would move a job into the non-blocking section while buildAdvisorVerdictLines returns nothing, so the reader is told the AI cleared the job but never which verdict cleared it or why — and trymerge would stop blocking on a flag that says nothing about merges. That combination has no use, so it is unreachable by construction rather than left to deployment discipline.

They stay two flags rather than one because the display half can be enabled on its own while the merge gate stays off, which is the intended rollout order. The whole flag surface is pinned by a truth table.

The rest of the predicate

Every branch fails toward blocking:

  • Confidence in the high bucket. Asked of confidenceBucket rather than restating its threshold, so retuning the badge scale cannot leave the comment saying "probably not related" while this still suppresses. A test pins the effective floor so that retune cannot be silent either.
  • The verdict must be strictly newer than the job's completed_at. Verdicts are keyed by (sha, job name) with no run identity, so a rerun at the same head would otherwise inherit the previous run's verdict.
  • The job must have concluded failure. A cancelled or timed-out job produced no outcome to clear. This gate is what keeps an infra fault that stopped the job before it concluded from being cleared.
  • Absent, ambiguous or unusable is not a verdict. Rows tied at the newest timestamp that disagree resolve to "no answer"; tied rows that agree resolve to the least confident of them; and a confidence that is not a real number in [0, 1] makes the whole key ambiguous.

One read, one resolver

The saved query advisor_verdicts_for_pr used to run twice per PR per pass — once for the inline badge, once for the gate — and the two callers resolved the current verdict by different rules, so rows tied at the newest timestamp could render a green "not related" badge on a job the gate kept blocking. drci.ts now reads once and hands the rows to both, through a shared resolveVerdict. The read is also skipped entirely when the PR has no new or unclassified failures, which is most PRs.

resolveVerdict picks the newest row by comparing the timestamp strings directly. That is safe rather than incidental: misc.autorevert_advisor_verdicts.timestamp is DateTime64(3) on a UTC server, so every row in a response renders fixed-width YYYY-MM-DD HH:MM:SS.mmm and lexicographic order is chronological order. dayjs is used in the one place the formats actually differ — comparing a ClickHouse timestamp against GitHub's ISO-8601 completed_at in the freshness gate.

Known limitations

  • Rerun freshness. The freshness check rejects the common stale-verdict ordering but not all of it: a verdict for run A that lands after a rerun B has finished still passes. Closing that needs the analyzed job id on the verdict row — the row's run_id is the advisor's own dispatch run, not the job's.
  • The badge does not apply that freshness check, only the gate does, so a stale verdict can render a confident badge on a job that is still blocking. Pre-existing and deliberate — the display module and the gate are separate by design — but worth deciding before the flag goes on.
  • An infra_issue verdict says a failure looks environmental, not that the PR did not cause it. A change to a submodule pin, workflow YAML, a Dockerfile or a dependency pin can produce a genuine infrastructure-shaped failure that is the PR's fault. [advisor] Make related outrank every dismissal, including infra_issue pytorch#195502 addresses this in the advisor prompt by making related outrank every dismissal.

All three are conditions on enabling, not on landing the code.

Safety

No-op unless both DRCI_ADVISOR_SUPPRESSION_ENABLED=true and DRCI_ADVISOR_COMMENT_ENABLED=true, and the repo is advisor-enabled. A ClickHouse error falls back to an empty set, so the worst case is today's behaviour. The lookup runs before the CRCR block so CRCR L4 jobs, which are pushed into failedJobs there and are never advisor-analyzed, cannot be eligible; extraction mutates the arrays in place because the same objects back both the failures dict and the comment renderer.

Test plan

  • torchci/test/advisorSuppression.test.ts — every predicate branch, the verdict set, verdict ambiguity, non-finite and out-of-range confidence in both orderings, timestamp staleness, the head-commit row filter, the dr_ci_<name> key mapping, the repo gate, and a truth table over both flags including the unset-is-off case.
  • torchci/test/advisorComment.test.ts — the badge side of the shared resolver, including that a conflicting tie renders no badge and that a tied pair renders the least-confident row whole.
  • torchci/test/advisorVerdictSource.test.ts — the read guard, including that a PR with no eligible jobs costs no query and that the suppression flag alone reads nothing.
  • Mutation-tested: each new test file was run against deliberate mutations with a positive control and a vacuity control, and every mutation was caught.
  • tsc --noEmit, prettier and next lint clean. Full jest run green: all 62 suites pass.
  • Reviewed cross-model at high effort over several rounds. Findings applied include the duplicate read and tie divergence above, a fail-open bug where a NaN confidence would have been discarded in favour of a usable one, a needless query on PRs with no failures, and the section wording, which said "NOT RELATED TO THIS PR" and would have been false for a job cleared as an infra fault.

Not covered: the drci.ts orchestration block itself has no automated test. Its constituent decisions are extracted and unit-tested instead; covering the orchestration itself would mean importing drci.ts and its whole dependency graph. Worth deciding before the flag goes on.

Ivan Zaitsev added 2 commits August 28, 2026 15:15
[ghstack-poisoned]
[ghstack-poisoned]
@vercel

vercel Bot commented Aug 28, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
torchci Ignored Ignored Sep 1, 2026 8:36pm UTC

Request Review

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Aug 28, 2026
@izaitsevfb
izaitsevfb changed the base branch from gh/izaitsevfb/1/base to main August 28, 2026 22:45
Ivan Zaitsev added 2 commits August 31, 2026 11:07
…h readers

Review of the AI_NOT_RELATED bucket raised three things about this file set.

The saved query `advisor_verdicts_for_pr` ran twice per PR per Dr.CI pass: once
to render the inline "AI verdict:" badge, once to decide which failures stop
blocking. Two reads can straddle a verdict landing, and the two callers resolved
the current verdict by different rules -- the badge took the first row per (sha,
signal key), the gate sorted and refused ties -- so rows tied at the newest
timestamp could show a green "not related" badge on a job that kept blocking.
drci.ts now reads once and hands the rows to both, and `resolveVerdict` moves to
advisorVerdictUtils so both resolve identically. It returns the representative
ROW so the badge's summary comes from the same row whose confidence the gate
judged, and among agreeing ties that is the least confident one.

Tests covered the pure predicates but not the wiring. `fetchSuppressibleJobIds`
loses its query and becomes the synchronous `suppressibleJobIds`, which makes
the flag gate, the head-commit row filter and the `dr_ci_<name>` key mapping
directly testable; the read decision becomes `shouldReadAdvisorVerdicts`, also
tested. New advisorComment tests cover the badge side of the shared resolver.

`MIN_SUPPRESSION_CONFIDENCE` restated `confidenceBucket`'s `high` threshold, so
retuning the badge scale would have left the comment saying "probably not
related" while the gate still suppressed. It now asks for the bucket. Because
that ties a merge gate to a UI scale, a test pins the effective floor: moving it
turns the test red rather than moving the gate quietly.

Two things this fixes that the review did not raise. Only ask for verdicts when
the PR actually has new or unclassified failures -- both consumers bail on an
empty job list, so reading for the common no-failure PR bought a round trip
nothing could use. And a confidence that is not a real number in [0, 1] now
makes the key ambiguous: "least confident wins" is a comparison, NaN loses every
comparison, so a NaN row beside a 0.95 row resolved to 0.95 and suppressed.

The flag predicates move to advisorFlags.ts. advisorConfig.ts is imported by a
React component and documents itself as free of server-only behavior, and these
read process.env; keeping them out of the consumers also lets the read decision
avoid importing the module that reaches the AWS SDK.

Suppression stays behind DRCI_ADVISOR_SUPPRESSION_ENABLED and off everywhere.
The display path and the gate still apply different freshness rules, which is
unchanged here and documented in advisorSuppression.ts.
All three verdicts make the same claim about the PR under review -- this
failure is not evidence against it -- so all three now leave the blocking set
at high confidence. Blocking a PR on a broken runner was never the intent.

`garbage` belongs with the cleared ones rather than with `unsure`, which keeps
blocking. It is not "we cannot tell": the advisor reaches it by comparing the
job against its own baselines and finding the signal flips red/green across
unrelated commits. `unsure` is the real absence of a claim.

The verdict set becomes a total map over AdvisorVerdictType rather than a list
of the cleared ones, so adding a verdict fails to compile until someone decides
which side it falls on -- verified by adding one and watching tsc name this
file. The comment also states the limit the confidence and freshness gates do
not cover: a verdict says a failure looks environmental, not that the PR is
innocent of causing it, and a change to CI config, a Dockerfile or a submodule
pin can produce a genuine infrastructure-shaped failure that is the PR's fault.
`producedATestOutcome` narrows that -- an infra fault leaving the job
`cancelled` never reaches here -- but it does not close it.

The rendered section said "NOT RELATED TO THIS PR" and "judged them unrelated
to this PR", which is false for a job cleared as an infra fault or an unusable
signal. It now names all three reasons.

Measured over 30 days on PR-side verdicts at high confidence, joined to each
job's own conclusion: not_related 1179 on `failure`, infra_issue 976, garbage 1.
So the suppressible population roughly doubles. The merge-side cap on how many
gates one merge may skip is deliberately unchanged.

Still behind DRCI_ADVISOR_SUPPRESSION_ENABLED, off everywhere.
@izaitsevfb
izaitsevfb marked this pull request as ready for review August 31, 2026 22:14
pytorchmergebot pushed a commit to pytorch/pytorch that referenced this pull request Aug 31, 2026
…ue (#195502)

✴️ iz2: opened on behalf of @izaitsevfb

## The gap

The AI CI Advisor prompt already knew that a flaky history must not override a causal chain to the suspect — the `garbage` bullet says exactly that. `infra_issue` carried no equivalent rule.

That matters because **a commit can cause an infrastructure-shaped failure.** Moving a submodule pointer, editing a workflow or action YAML, changing a Dockerfile or image tag, bumping a dependency pin, touching build configuration, a runner label, or a setup script — any of these produces a log that reads like an ordinary infra fault: a checkout that fails, an image that will not pull, a dependency that will not resolve, a build that dies before any test runs. Nothing in the prompt told the model to read the suspect diff before dismissing such a failure.

This is not hypothetical. Among live high-confidence PR-side verdicts there is an `infra_issue` reading:

> The build failed because CMake couldn't checkout the `torch-xpu-ops` git submodule at commit `2c058add…`. This is a git/network infrastructure failure unrelated to the s…

A bad submodule pin in the suspect produces precisely that.

## What changed

Three edits, all inside the prompt:

1. **New Principle 4.** For precedence purposes `not_related`, `infra_issue` and `garbage` form one dismissal family: none of them may override an established causal chain to the suspect. They stay distinct verdicts with distinct positive-evidence requirements. Causality is settled first.
2. **`infra_issue` now requires positive evidence that the suspect did not cause the breakage** — not merely the absence of a found chain.
3. **The closing paragraph states the decision order explicitly** instead of only tie-breaking among the dismissals.

## What this deliberately does *not* do

It is not a licence for a looser `related`, and the prompt says so in both directions:

- Merely touching CI machinery is **not** a causal chain. File overlap or a bare plausible mechanism stays `unsure` (Principle 2).
- Symmetrically, failing to find a chain does **not** establish that the suspect was independent — where both a suspect-caused and an external explanation remain plausible, that is `unsure` too, not a dismissal.

The existing bias toward `unsure` over any dismissal, and the existing "a false `related` wrongly blames a clean commit" guard, are unchanged.

## Why now

Dr.CI is gaining the ability to move high-confidence dismissals out of the merge-blocking set (pytorch/test-infra#8671, behind a flag and currently off). Once that is on, a false dismissal silently lets a broken PR merge, so the precedence rule needs to be explicit rather than implied for one verdict out of three.

## Test plan

Prompt-only change; no code paths altered.

- `.github/workflows/claude-autorevert-advisor.yml` parses as YAML, and the prompt string reached through the parsed document (not just the file text) contains each new clause — confirming the block scalar absorbed them correctly.
- The `--json-schema` verdict enum is byte-identical: `["related","unsure","not_related","infra_issue","garbage"]`. No new verdict value, so no consumer changes.
- Reviewed cross-model at high effort. That pass caught two real defects in my first draft, both fixed here: the original wording said "if the suspect touched the machinery that broke, that is `related`", which equated file overlap with causation and contradicted Principle 2; and the original decision order was binary (chain / no chain), which omitted "not established" and could have turned failure-to-prove-causation into proof of independence.

Pull Request resolved: #195502
Approved by: https://github.com/pytorchgreenlight
Ivan Zaitsev added 2 commits August 31, 2026 17:30
Per Ivan: the heading and blurb were doing too much work. "NEW FAILURES
SUPPRESSED BY AI" says what happened in the reader's vocabulary, and the
description stops at the claim that matters.

Dropping the tail also removes two defects. The blurb ended in a full stop
and the renderer appends its own colon, so the summary read "...shown beside
it.:". And that same sentence promised a per-job verdict that is absent
whenever suppression is on and the inline-verdict flag is off -- two
independent flags, so that combination is reachable.

Moved directly after NEW FAILURES, since these are the jobs lifted out of it
and belong beside it rather than at the end past CRCR.
… does not exist

Four comment/copy corrections. No behavior change.

The suppressed-jobs blurb said the advisor "judged the failures unrelated to
the change". That is true only for `not_related`. `infra_issue` says the
environment broke -- which this file's own comment notes a PR can itself cause
via a Dockerfile, CI config or submodule pin -- and `garbage` judges the signal
rather than the change. Shortening the section dropped the umbrella wording that
covered all three; restore it as "not evidence against this change", which is
still short, and record why so the next shortening keeps it.

VERDICT_DISPOSITION claimed the residual exposure is "bounded by the merge-side
cap on how many gates one merge may skip". That cap is in trymerge.py in
pytorch/pytorch#195503, which has not landed, so the bound does not exist today.
State the cap as proposed and the flag being off as what actually holds it.

confidentEnoughToSuppress justified deriving the gate from confidenceBucket by
saying the comment could otherwise read "probably not related" while the gate
suppresses. verdictBadge returns for `garbage` and `infra_issue` before it
consults the bucket, so those labels never hedge and the rationale describes
`not_related` only. Note the case that falls out: a sub-threshold `infra_issue`
renders an unhedged "infra issue" next to a job that still blocks, about 6% of
`infra_issue` rows in a recent 30-day sample. That is a label fix in
advisorBadge.ts, on the already-live comment path, so it stays a follow-up.

suppressibleJobIds said the shared read means comment and gate "cannot disagree
about a job". Sharing rows only stops them resolving different verdicts; the
gate still adds confidence, freshness and conclusion tests the badge does not,
so a confident badge beside a blocking job is expected, not a bug.
@huydhn

huydhn commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Are u planning to add AI_PENDING in another PR? I'm review this together with pytorch/pytorch#195503, so it's weird to not see AI_PENDING being handled somewhere, or do I miss it?

@huydhn

huydhn commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Is there any trial run on a PyTorch PR that you could share? I want to see if this overlaps with Greenlight section, also recently added.

// VERCEL_ENV (unlike auto-dispatch, which fires real workflow_dispatches).
export function advisorCommentEnabled(owner: string, repo: string): boolean {
return (
process.env.DRCI_ADVISOR_COMMENT_ENABLED === "true" &&

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

When this DRCI_ADVISOR_COMMENT_ENABLED flag is off, let's make sure that trymerge can work like it's today.

}
const newestTimestamp = rows
.map((r) => r.timestamp)
.reduce((a, b) => (a > b ? a : b));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Highlight this snippet where we compare timestamp as string, is that safe? Why not also use dayjs here to be sure

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

this should be fine

The string timestamp compare is safe — the column is DateTime64(3), server and column timezone both UTC, always 23 fixed-width chars, so lexicographic is chronological

repo: string
): boolean {
return (
process.env.DRCI_ADVISOR_SUPPRESSION_ENABLED === "true" &&

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nit: the agent recommends me against having 2 flags like this as it opens the door to mistake like setting DRCI_ADVISOR_SUPPRESSION_ENABLED=true while turning off DRCI_ADVISOR_COMMENT_ENABLED=false. Do we really need 2 flags here?

@izaitsevfb

Copy link
Copy Markdown
Contributor Author

Mock render — not a real Dr.CI verdict. The Dr.CI comment for pytorch/pytorch#180208 rendered through pytorch/test-infra#8671 at 5e2a080, with both advisor flags on.

Live header today: 7 New Failures, 3 Unrelated Failures, 2 Unclassified Failures. Below: 5 New Failures, 7 Unrelated Failures.


❌ 5 New Failures, 7 Unrelated Failures

As of commit a945a2028e52611cd20e2988a4a0420c03ab6104 with merge base 40e238cee98065a2526d8420affade6be07912d7 (image):

NEW FAILURES - The following jobs have failed:

NEW FAILURES SUPPRESSED BY AI (non-blocking) - The following jobs failed, but the AI CI Advisor judged the failures unrelated to the change:

CRCR (non-blocking) - The following CRCR downstream CI jobs failed but are non-blocking:

@georgehong
georgehong requested a review from jathu September 1, 2026 19:33
@izaitsevfb

Copy link
Copy Markdown
Contributor Author

@huydhn

Is there any trial run on a PyTorch PR that you could share? I want to see if this overlaps with Greenlight section, also recently added.

there should be no overlap, see this mock

Huy: "let's make sure that trymerge can work like it's today" when
DRCI_ADVISOR_COMMENT_ENABLED is off, and separately, two independent flags
"opens the door to mistake like setting DRCI_ADVISOR_SUPPRESSION_ENABLED=true
while turning off DRCI_ADVISOR_COMMENT_ENABLED=false".

Both point at one quadrant. Suppression keyed only to its own flag meant
comment-off + suppression-on moved a job out of the blocking set while
buildAdvisorVerdictLines returned nothing, so the comment named a section the
AI had cleared without naming the verdict that cleared it -- and trymerge
stopped blocking on a flag Huy's request never mentions.

advisorSuppressionEnabled now requires advisorCommentEnabled, so that
combination is unreachable rather than merely discouraged, and the comment
flag being off restores today's trymerge behaviour exactly.

Keeping two flags rather than collapsing to one: the split is the live
production state -- the comment flag is on and suppression is off -- so a
single flag would either enable suppression immediately or turn off a badge
that is already shipping.

The flag surface is now pinned by a truth table over both flags, plus the
unset-is-off case, and shouldReadAdvisorVerdicts no longer claims suppression
alone is a state that can ask for a read.
izaitsevfb pushed a commit to pytorch/pytorch that referenced this pull request Sep 1, 2026
Huy, reviewing this alongside pytorch/test-infra#8671: "it's weird to not see
AI_PENDING being handled somewhere, or do I miss it?"

He is right, in the other direction. Nothing in test-infra emits an AI_PENDING
key -- the producer was written as part of the original stack but never went
into #8671, so Dr.CI's classification dict has never contained the category.
is_ai_pending therefore reads an always-empty list, its branch in
get_classifications can never fire, and AdvisorWaitWindow has nothing to wait
for. Dead code guarding a case that cannot occur.

Remove the matcher, the classification branch, the pending branch in
categorize_checks, AdvisorWaitWindow and its budget constant, along with the
tests that exercised them, and stop popping AI_PENDING from the stale
check-run summary since it can never be in one.

The surviving AI_NOT_RELATED tests move into the existing TestAdvisorNotRelated
class rather than keeping a second class of their own.

The problem the feature aimed at is real -- `@pytorchbot merge` adds
ciflow/trunk, so trunk jobs start as the merge does and a fresh failure can
kill the merge before the advisor has looked at it. Nothing here addresses
that, and it should be taken up together with a producer rather than shipped
as a consumer waiting on a key no one sends.
@izaitsevfb
izaitsevfb requested a review from huydhn September 1, 2026 23:03
@izaitsevfb

Copy link
Copy Markdown
Contributor Author

@huydhn , you review nits should be addressed, please take another look when you have a chance

@huydhn huydhn left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM!

orrangetabby17 pushed a commit to orrangetabby17/pytorch that referenced this pull request Sep 2, 2026
…ue (pytorch#195502)

✴️ iz2: opened on behalf of @izaitsevfb

## The gap

The AI CI Advisor prompt already knew that a flaky history must not override a causal chain to the suspect — the `garbage` bullet says exactly that. `infra_issue` carried no equivalent rule.

That matters because **a commit can cause an infrastructure-shaped failure.** Moving a submodule pointer, editing a workflow or action YAML, changing a Dockerfile or image tag, bumping a dependency pin, touching build configuration, a runner label, or a setup script — any of these produces a log that reads like an ordinary infra fault: a checkout that fails, an image that will not pull, a dependency that will not resolve, a build that dies before any test runs. Nothing in the prompt told the model to read the suspect diff before dismissing such a failure.

This is not hypothetical. Among live high-confidence PR-side verdicts there is an `infra_issue` reading:

> The build failed because CMake couldn't checkout the `torch-xpu-ops` git submodule at commit `2c058add…`. This is a git/network infrastructure failure unrelated to the s…

A bad submodule pin in the suspect produces precisely that.

## What changed

Three edits, all inside the prompt:

1. **New Principle 4.** For precedence purposes `not_related`, `infra_issue` and `garbage` form one dismissal family: none of them may override an established causal chain to the suspect. They stay distinct verdicts with distinct positive-evidence requirements. Causality is settled first.
2. **`infra_issue` now requires positive evidence that the suspect did not cause the breakage** — not merely the absence of a found chain.
3. **The closing paragraph states the decision order explicitly** instead of only tie-breaking among the dismissals.

## What this deliberately does *not* do

It is not a licence for a looser `related`, and the prompt says so in both directions:

- Merely touching CI machinery is **not** a causal chain. File overlap or a bare plausible mechanism stays `unsure` (Principle 2).
- Symmetrically, failing to find a chain does **not** establish that the suspect was independent — where both a suspect-caused and an external explanation remain plausible, that is `unsure` too, not a dismissal.

The existing bias toward `unsure` over any dismissal, and the existing "a false `related` wrongly blames a clean commit" guard, are unchanged.

## Why now

Dr.CI is gaining the ability to move high-confidence dismissals out of the merge-blocking set (pytorch/test-infra#8671, behind a flag and currently off). Once that is on, a false dismissal silently lets a broken PR merge, so the precedence rule needs to be explicit rather than implied for one verdict out of three.

## Test plan

Prompt-only change; no code paths altered.

- `.github/workflows/claude-autorevert-advisor.yml` parses as YAML, and the prompt string reached through the parsed document (not just the file text) contains each new clause — confirming the block scalar absorbed them correctly.
- The `--json-schema` verdict enum is byte-identical: `["related","unsure","not_related","infra_issue","garbage"]`. No new verdict value, so no consumer changes.
- Reviewed cross-model at high effort. That pass caught two real defects in my first draft, both fixed here: the original wording said "if the suspect touched the machinery that broke, that is `related`", which equated file overlap with causation and contradicted Principle 2; and the original decision order was binary (chain / no chain), which omitted "not established" and could have turned failure-to-prove-causation into proof of independence.

Pull Request resolved: pytorch#195502
Approved by: https://github.com/pytorchgreenlight
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants